Skip to content

Commit

Permalink
Refactoring Table.set for GUI2 and other GUI fixes (#9128)
Browse files Browse the repository at this point in the history
- Added `regex` function to `Standard.Base` as a way to easily and cleanly make regular expressions.
- Added `expr` and `Expression.Value` to distinguish expressions from text values.
- Fixed issues with `Table.join` widget so dropdown exists.
- Needed fully qualified name.
- Added default empty text values for right column to provided text input boxes.
- Deprecate `Table.filter_by_expression` and allow `Table.filter` to take an `Expression`.
- Added `Simple_Expression` and deprecated `Column_Operation`. Changes the order so takes a column then a calculation.
- Rename `column` to `value` and `new_name` to `as` on `Table.set`.
- Rename `name_column` to `names` on `Table.cross_tab`.
- Removed `Column_Ref.Expression` in favour of using `Expression.Value`.
  • Loading branch information
jdunkerley authored Feb 21, 2024
1 parent 323d46e commit fa6fccb
Show file tree
Hide file tree
Showing 36 changed files with 561 additions and 407 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@
- [Implemented Write support for `S3_File`.][8921]
- [Separate `Group_By` from `columns` into new argument on `aggregate`.][9027]
- [Allow `copy_to` and `move_to` to work between local and S3 files.][9054]
- [Adjusted expression handling and new `Simple_Expression` type.][9128]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -890,6 +891,7 @@
[8921]: https://github.com/enso-org/enso/pull/8921
[9027]: https://github.com/enso-org/enso/pull/9027
[9054]: https://github.com/enso-org/enso/pull/9054
[9128]: https://github.com/enso-org/enso/pull/9128

#### Enso Compiler

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ expect_text : Any -> Any -> Any ! Type_Error
expect_text text_maybe ~action = case text_maybe of
_ : Text -> action
_ ->
Error.throw (Type_Error.Error Text (Meta.type_of text_maybe) "text_maybe")
Error.throw (Type_Error.Error Text text_maybe "text_maybe")

15 changes: 13 additions & 2 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text/Regex.enso
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ type Regex
text before matching on it.

If an empty regex is used, `compile` throws an Illegal_Argument error.
compile : Text -> Boolean | Nothing -> Regex ! Regex_Syntax_Error | Illegal_Argument
compile expression case_insensitive=False =
compile : Text -> Boolean -> Regex ! Regex_Syntax_Error | Illegal_Argument
compile expression:Text case_insensitive:Boolean=False =
if expression == '' then Error.throw (Illegal_Argument.Error "Regex cannot be the empty string") else
options_string = if case_insensitive == True then "usgi" else "usg"

Expand Down Expand Up @@ -489,3 +489,14 @@ type Regex_Syntax_Error
Provides a human-readable representation of the `Regex_Syntax_Error`.
to_display_text : Text
to_display_text self = "Regex Syntax Error:" + self.message

## Shorthand to create a regular expression from a Text value.

If an empty regex is used, throws an Illegal_Argument error.

Arguments
- expression: The text representing the regular expression that you want to
compile. Must be non-empty.
regex : Text -> Regex ! Regex_Syntax_Error | Illegal_Argument
regex expression:Text = Regex.compile expression

Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type Type_Error
_ -> ". Try to apply " + (missing_args.join ", ") + " arguments"
_ -> tpe.to_text

"Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+type_of_actual+"."
"Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+(if type_of_actual.is_a Text then type_of_actual else "<ERR>")+"."

@Builtin_Type
type Compile_Error
Expand Down
2 changes: 2 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ from project.Data.Index_Sub_Range.Index_Sub_Range import First, Last
from project.Data.Json.Extensions import all
from project.Data.Range.Extensions import all
from project.Data.Text.Extensions import all
from project.Data.Text.Regex import regex
from project.Errors.Problem_Behavior.Problem_Behavior import all
from project.Meta.Enso_Project import enso_project
from project.Network.Extensions import all
Expand Down Expand Up @@ -189,6 +190,7 @@ from project.Data.Numbers export Float, Integer, Number
from project.Data.Range.Extensions export all
from project.Data.Statistics export all hiding to_moment_statistic, wrap_java_call, calculate_correlation_statistics, calculate_spearman_rank, calculate_correlation_statistics_matrix, compute_fold, empty_value, is_valid
from project.Data.Text.Extensions export all
from project.Data.Text.Regex export regex
from project.Errors.Problem_Behavior.Problem_Behavior export all
from project.Function export all
from project.Meta.Enso_Project export enso_project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ from project.Metadata.Choice import Option
Creates a Regex / Text Widget for search and replace.
make_regex_text_widget : Widget
make_regex_text_widget =
make_single_choice [["Text", '""'], ["Regular Expression", '"^$".to_regex']]
make_single_choice [["<Text Value>", '""'], ["<Regular Expression>", '(regex "^$")']]

## PRIVATE
Creates a Single_Choice Widget for delimiters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,7 @@ type DB_Column
Value_Type.expect_text self <| Value_Type.expect_integer n <|
n2 = n.max 0
new_name = self.naming_helper.function_name "text_right" [self, n]
self.make_binary_op "RIGHT" n2 new_name
self.make_binary_op "RIGHT" n2 new_name

## GROUP Standard.Base.Logical
ICON preparation
Expand Down
75 changes: 40 additions & 35 deletions distribution/lib/Standard/Database/0.0.0-dev/src/Data/DB_Table.enso
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Standard.Table.Data.Join_Kind_Cross.Join_Kind_Cross
import Standard.Table.Data.Match_Columns as Match_Columns_Helpers
import Standard.Table.Data.Report_Unmatched.Report_Unmatched
import Standard.Table.Data.Row.Row
import Standard.Table.Data.Simple_Expression.Simple_Expression
import Standard.Table.Data.Table.Table
import Standard.Table.Data.Type.Value_Type_Helpers
import Standard.Table.Extensions.Table_Ref.Table_Ref
Expand Down Expand Up @@ -572,7 +573,7 @@ type DB_Table
Select people celebrating a jubilee.

people.filter "age" (age -> (age%10 == 0))
@column Widget_Helpers.make_column_name_selector
@column (Widget_Helpers.make_column_name_selector add_expression=True)
@filter Widget_Helpers.make_filter_condition_selector
filter : (DB_Column | Text | Integer) -> (Filter_Condition | (Any -> Boolean)) -> Problem_Behavior -> DB_Table ! No_Such_Column | Index_Out_Of_Bounds | Invalid_Value_Type
filter self column (filter : Filter_Condition | (Any -> Boolean) = Filter_Condition.Equal True) on_problems=Report_Warning = case column of
Expand All @@ -593,6 +594,7 @@ type DB_Table
missing arguments and then another error is more appropriate.
_ : Function -> Filter_Condition_Module.handle_constructor_missing_arguments filter <|
Error.throw (Unsupported_Database_Operation.Error "Filtering with a custom predicate is not supported in the database.")
_ : Expression -> self.filter (self.evaluate_expression column on_problems) filter on_problems
_ ->
table_at = self.at column
self.filter table_at filter on_problems
Expand Down Expand Up @@ -631,9 +633,10 @@ type DB_Table

people.filter_by_expression "[age] % 10 == 0"
filter_by_expression : Text -> Problem_Behavior -> DB_Table ! No_Such_Column | Invalid_Value_Type | Expression_Error
filter_by_expression self expression on_problems=Report_Warning =
column = self.evaluate_expression expression on_problems
self.filter column Filter_Condition.Is_True
filter_by_expression self expression:Text on_problems=Report_Warning =
column = self.evaluate_expression (Expression.Value expression) on_problems
result = self.filter column Filter_Condition.Is_True
Warning.attach (Deprecated.Warning "Standard.Database.Data.DB_Table.DB_Table" "filter_by_expression" "Deprecated: use `filter` with an `Expression` instead.") result

## ALIAS first, last, sample, slice
GROUP Standard.Base.Selections
Expand Down Expand Up @@ -702,7 +705,7 @@ type DB_Table
## PRIVATE
Filter out all rows.
remove_all_rows : DB_Table
remove_all_rows self = self.filter_by_expression "0==1"
remove_all_rows self = self.filter (Expression.Value "0==1")

## ALIAS add index column, rank, record id
GROUP Standard.Base.Values
Expand Down Expand Up @@ -814,14 +817,14 @@ type DB_Table
new_ctx = self.context.set_limit max_rows
self.updated_context new_ctx

## ALIAS add column, new column, update column
## ALIAS add column, new column, update column, formula
GROUP Standard.Base.Values
ICON dataframe_map_column
Sets the column value at the given name.

Arguments:
- column: The new column or expression to create column.
- new_name: Optional new name for the column.
- value: The value, expression or column to create column.
- as: Optional new name for the column.
- set_mode: Specifies the expected behaviour in regards to existing
column with the same name.
- on_problems: Specifies how to handle problems with expression
Expand Down Expand Up @@ -855,34 +858,36 @@ type DB_Table
example_set =
table = Examples.inventory_table
double_inventory = table.at "total_stock" * 2
table.set double_inventory new_name="total_stock"
table.set "2 * [total_stock]" new_name="total_stock_expr"
@new_name Widget_Helpers.make_column_name_selector
set : DB_Column | Text | Array | Vector | Range | Date_Range | Constant_Column | Column_Operation -> Text -> Set_Mode -> Problem_Behavior -> DB_Table ! Existing_Column | Missing_Column | No_Such_Column | Expression_Error
set self column (new_name : Text = "") (set_mode : Set_Mode = Set_Mode.Add_Or_Update) (on_problems : Problem_Behavior = Report_Warning) =
table.set double_inventory as="total_stock"
table.set (expr "2 * [total_stock]") as="total_stock_expr"
@value Simple_Expression.default_widget
set : DB_Column | Text | Expression | Array | Vector | Range | Date_Range | Constant_Column | Simple_Expression | Column_Operation -> Text -> Set_Mode -> Problem_Behavior -> DB_Table ! Existing_Column | Missing_Column | No_Such_Column | Expression_Error
set self value (as : Text = "") (set_mode : Set_Mode = Set_Mode.Add_Or_Update) (on_problems : Problem_Behavior = Report_Warning) =
problem_builder = Problem_Builder.new
unique = self.column_naming_helper.create_unique_name_strategy
unique.mark_used self.column_names

resolved = case column of
_ : Text -> self.evaluate_expression column on_problems
resolved = case value of
_ : Text -> self.make_constant_column value
_ : Expression -> self.evaluate_expression value on_problems
_ : DB_Column ->
if Helpers.check_integrity self column then column else
Error.throw (Integrity_Error.Error "Column "+column.name)
_ : Constant_Column -> self.make_constant_column column
_ : Column_Operation -> column.evaluate self (set_mode==Set_Mode.Update && new_name=="") on_problems
if Helpers.check_integrity self value then value else
Error.throw (Integrity_Error.Error "Column "+value.name)
_ : Constant_Column -> self.make_constant_column value
_ : Column_Operation -> (value:Simple_Expression).evaluate self (set_mode==Set_Mode.Update && as=="") on_problems
_ : Simple_Expression -> value.evaluate self (set_mode==Set_Mode.Update && as=="") on_problems
_ : Vector -> Error.throw (Unsupported_Database_Operation.Error "Cannot use `Vector` for `set` in the database.")
_ : Array -> Error.throw (Unsupported_Database_Operation.Error "Cannot use `Array` for `set` in the database.")
_ : Range -> Error.throw (Unsupported_Database_Operation.Error "Cannot use `Range` for `set` in the database.")
_ : Date_Range -> Error.throw (Unsupported_Database_Operation.Error "Cannot use `Date_Range` for `set` in the database.")
_ -> Error.throw (Illegal_Argument.Error "Unsupported type for `DB_Table.set`.")

## If `new_name` was specified, use that. Otherwise, if `column` is a
## If `as` was specified, use that. Otherwise, if `value` is a
`DB_Column`, use its name. In these two cases, do not make it unique.
Otherwise, make it unique. If set_mode is Update, however, do not
make it unique.
new_column_name = if new_name != "" then new_name else
if column.is_a DB_Column || set_mode==Set_Mode.Update || set_mode==Set_Mode.Add_Or_Update then resolved.name else unique.make_unique resolved.name
new_column_name = if as != "" then as else
if value.is_a DB_Column || set_mode==Set_Mode.Update || set_mode==Set_Mode.Add_Or_Update then resolved.name else unique.make_unique resolved.name
renamed = resolved.rename new_column_name
renamed.if_not_error <| self.column_naming_helper.check_ambiguity self.column_names renamed.name <|
index = self.internal_columns.index_of (c -> c.name == renamed.name)
Expand Down Expand Up @@ -921,12 +926,12 @@ type DB_Table
an `Arithmetic_Error`.
- If more than 10 rows encounter computation issues,
an `Additional_Warnings`.
evaluate_expression : Text -> Problem_Behavior -> DB_Column ! No_Such_Column | Invalid_Value_Type | Expression_Error
evaluate_expression self expression on_problems=Report_Warning =
evaluate_expression : Text | Expression -> Problem_Behavior -> DB_Column ! No_Such_Column | Invalid_Value_Type | Expression_Error
evaluate_expression self (expression:Text|Expression) on_problems=Report_Warning = if expression.is_a Text then self.evaluate_expression (Expression.Value expression) on_problems else
get_column name = self.at name
new_column = Expression.evaluate expression get_column self.make_constant_column "Standard.Database.Data.DB_Column" "DB_Column" DB_Column.var_args_functions
problems = Warning.get_all new_column . map .value
result = new_column.rename (self.connection.base_connection.column_naming_helper.sanitize_name expression)
result = new_column.rename (self.connection.base_connection.column_naming_helper.sanitize_name expression.expression)
on_problems.attach_problems_before problems <|
Warning.set result []

Expand Down Expand Up @@ -1842,7 +1847,7 @@ type DB_Table
if new_columns.is_empty then handle_no_output_columns else
result = self.updated_context_and_columns new_ctx new_columns subquery=True
if validated.old_style.not then result else
Warning.attach (Deprecated.Warning "Standard.Database.Data.Aggregate_Column.Aggregate_Column" "Group_By" "Deprecated: `Group_By` constructor has been deprecated, use the `group_by` argument instead.") result
Warning.attach (Deprecated.Warning "Standard.Table.Data.Aggregate_Column.Aggregate_Column" "Group_By" "Deprecated: `Group_By` constructor has been deprecated, use the `group_by` argument instead.") result

## GROUP Standard.Base.Calculations
ICON dataframe_map_row
Expand Down Expand Up @@ -1953,13 +1958,13 @@ type DB_Table
----|---------|---------
A | Example | France
@group_by Widget_Helpers.make_column_name_vector_selector
@name_column Widget_Helpers.make_column_name_selector
@names Widget_Helpers.make_column_name_selector
@values Widget_Helpers.make_aggregate_column_selector
cross_tab : Vector (Integer | Text | Regex | Aggregate_Column) | Text | Integer | Regex -> (Text | Integer) -> Aggregate_Column | Vector Aggregate_Column -> Problem_Behavior -> DB_Table ! Missing_Input_Columns | Invalid_Aggregate_Column | Floating_Point_Equality | Invalid_Aggregation | Unquoted_Delimiter | Additional_Warnings | Invalid_Column_Names
cross_tab self group_by name_column values=Aggregate_Column.Count (on_problems=Report_Warning) =
cross_tab self group_by names values=Aggregate_Column.Count (on_problems=Report_Warning) =
## Avoid unused arguments warning. We cannot rename arguments to `_`,
because we need to keep the API consistent with the in-memory table.
_ = [group_by, name_column, values, on_problems]
_ = [group_by, names, values, on_problems]
msg = "Cross tab of database tables is not supported, the table has to be materialized first with `read`."
Error.throw (Unsupported_Database_Operation.Error msg)

Expand Down Expand Up @@ -2023,7 +2028,7 @@ type DB_Table
selected = self.columns_helper.select_columns columns Case_Sensitivity.Default reorder=False error_on_missing_columns=error_on_missing_columns on_problems=on_problems error_on_empty=False . map self.make_column
selected.fold self table-> column_to_parse->
new_column = column_to_parse.parse type format on_problems
table.set new_column new_name=column_to_parse.name set_mode=Set_Mode.Update
table.set new_column as=column_to_parse.name set_mode=Set_Mode.Update

## GROUP Standard.Base.Conversions
ICON convert
Expand Down Expand Up @@ -2338,7 +2343,7 @@ type DB_Table
selected = self.columns_helper.select_columns columns Case_Sensitivity.Default reorder=False error_on_missing_columns=error_on_missing_columns on_problems=on_problems error_on_empty=False . map self.make_column
selected.fold self table-> column_to_cast->
new_column = column_to_cast.cast value_type on_problems
table.set new_column new_name=column_to_cast.name set_mode=Set_Mode.Update
table.set new_column as=column_to_cast.name set_mode=Set_Mode.Update

## GROUP Standard.Base.Conversions
ICON convert
Expand Down Expand Up @@ -2646,7 +2651,7 @@ type DB_Table
fill_nothing = table.fill_nothing ["col0", "col1"] 20.5
@columns Widget_Helpers.make_column_name_vector_selector
@default (self -> Widget_Helpers.make_fill_default_value_selector column_source=self add_text=True add_number=True add_boolean=True)
fill_nothing : Vector (Integer | Text | Regex) | Text | Integer | Regex -> DB_Column | Column_Ref | Previous_Value | Any -> DB_Table
fill_nothing : Vector (Integer | Text | Regex) | Text | Integer | Regex -> DB_Column | Column_Ref | Expression | Previous_Value | Any -> DB_Table
fill_nothing self (columns : Vector | Text | Integer | Regex) default =
resolved_default = (self:Table_Ref).resolve default
transformer col = col.fill_nothing resolved_default
Expand Down Expand Up @@ -2674,7 +2679,7 @@ type DB_Table
fill_empty = table.fill_empty ["col0", "col1"] "hello"
@columns Widget_Helpers.make_column_name_vector_selector
@default (self -> Widget_Helpers.make_fill_default_value_selector column_source=self add_text=True)
fill_empty : Vector (Integer | Text | Regex) | Text | Integer | Regex -> DB_Column | Column_Ref | Previous_Value | Any -> DB_Table
fill_empty : Vector (Integer | Text | Regex) | Text | Integer | Regex -> DB_Column | Column_Ref | Expression | Previous_Value | Any -> DB_Table
fill_empty self (columns : Vector | Text | Integer | Regex) default =
resolved_default = (self:Table_Ref).resolve default
transformer col = col.fill_empty resolved_default
Expand Down Expand Up @@ -2715,8 +2720,8 @@ type DB_Table
@columns Widget_Helpers.make_column_name_vector_selector
@term (Widget_Helpers.make_column_ref_by_name_selector add_regex=True add_text=True)
@new_text (Widget_Helpers.make_column_ref_by_name_selector add_text=True)
text_replace : Vector (Integer | Text | Regex) | Text | Integer | Regex -> Text | DB_Column | Column_Ref | Regex -> Text | DB_Column | Column_Ref -> Case_Sensitivity -> Boolean -> DB_Column
text_replace self columns (term : Text | DB_Column | Column_Ref | Regex = "") (new_text : Text | DB_Column | Column_Ref = "") case_sensitivity=Case_Sensitivity.Sensitive only_first=False =
text_replace : Vector (Integer | Text | Regex) | Text | Integer | Regex -> Text | DB_Column | Column_Ref | Expression | Regex -> Text | DB_Column | Column_Ref | Expression -> Case_Sensitivity -> Boolean -> DB_Column
text_replace self columns (term : Text | DB_Column | Column_Ref | Expression | Regex = "") (new_text : Text | DB_Column | Column_Ref | Expression = "") case_sensitivity=Case_Sensitivity.Sensitive only_first=False =
table_ref = Table_Ref.from self
resolved_term = table_ref.resolve term
resolved_new_text = table_ref.resolve new_text
Expand Down
Loading

0 comments on commit fa6fccb

Please sign in to comment.