From 26c711ab14ebf736744e5f3613cc1c1aab48aa24 Mon Sep 17 00:00:00 2001 From: AdRiley Date: Tue, 8 Oct 2024 20:57:36 +0100 Subject: [PATCH] Enable SQLServer select columns (#11270) * Enable SQLServer select columns * Code review feedback * Code Review feedback --- .../Database/0.0.0-dev/src/DB_Table.enso | 123 +++++++++--------- .../Database/0.0.0-dev/src/Feature.enso | 3 - .../src/Internal/SQLServer_Dialect.enso | 1 + .../src/Internal/SQLServer_Type_Mapping.enso | 7 + test/Microsoft_Tests/src/SQLServer_Spec.enso | 2 +- .../Column_Operations_Spec.enso | 13 +- .../Derived_Columns_Spec.enso | 8 +- 7 files changed, 70 insertions(+), 87 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/DB_Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/DB_Table.enso index 3ca2bfe993b7..0a20ba1cd1cf 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/DB_Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/DB_Table.enso @@ -139,10 +139,9 @@ type DB_Table @selector Widget_Helpers.make_column_name_selector at : Integer | Text -> DB_Column ! No_Such_Column | Index_Out_Of_Bounds at self (selector:(Integer | Text)=0) = - Feature.Column_Operations.if_supported_else_throw self.connection.dialect "at" <| - case selector of - _ : Integer -> self.make_column (self.internal_columns.at selector) - _ -> self.get selector (Error.throw (No_Such_Column.Error selector)) + case selector of + _ : Integer -> self.make_column (self.internal_columns.at selector) + _ -> self.get selector (Error.throw (No_Such_Column.Error selector)) ## ICON select_column Returns the column with the given name or index. @@ -153,11 +152,10 @@ type DB_Table @selector Widget_Helpers.make_column_name_selector get : Integer | Text -> Any -> DB_Column | Any get self (selector:(Integer | Text)=0) ~if_missing=Nothing = - Feature.Column_Operations.if_supported_else_throw self.connection.dialect "get" <| - internal_column = case selector of - _ : Integer -> self.internal_columns.get selector if_missing=Nothing - _ : Text -> self.internal_columns.find (p -> p.name == selector) if_missing=Nothing - if internal_column.is_nothing then if_missing else self.make_column internal_column + internal_column = case selector of + _ : Integer -> self.internal_columns.get selector if_missing=Nothing + _ : Text -> self.internal_columns.find (p -> p.name == selector) if_missing=Nothing + if internal_column.is_nothing then if_missing else self.make_column internal_column ## ALIAS cell value, get cell GROUP Standard.Base.Selections @@ -1010,46 +1008,45 @@ type DB_Table @value Simple_Expression.default_widget set : DB_Column | Text | Expression | Array | Vector | Range | Date_Range | Constant_Column | Simple_Expression -> Text -> Set_Mode -> Problem_Behavior -> DB_Table ! Existing_Column | Missing_Column | No_Such_Column | Expression_Error set self value:(DB_Column | Text | Expression | Array | Vector | Range | Date_Range | Constant_Column | Simple_Expression) (as : Text = "") (set_mode : Set_Mode = ..Add_Or_Update) (on_problems : Problem_Behavior = ..Report_Warning) = - Feature.Set.if_supported_else_throw self.connection.dialect "set" <| - problem_builder = Problem_Builder.new - unique = self.column_naming_helper.create_unique_name_strategy - unique.mark_used self.column_names - - resolved = case value of - _ : Text -> self.make_constant_column value - _ : Expression -> self.evaluate_expression value on_problems - _ : DB_Column -> - if Helpers.check_integrity self value then value else - Error.throw (Integrity_Error.Error "Column "+value.name) - _ : Constant_Column -> self.make_constant_column value - _ : Simple_Expression -> value.evaluate self (set_mode==Set_Mode.Update && as=="") on_problems - _ : Vector -> Error.throw (Unsupported_Database_Operation "`Vector` for `set`") - _ : Array -> Error.throw (Unsupported_Database_Operation.Error "`Array` for `set`") - _ : Range -> Error.throw (Unsupported_Database_Operation.Error "`Range` for `set`") - _ : Date_Range -> Error.throw (Unsupported_Database_Operation.Error "`Date_Range` for `set`") - _ -> Error.throw (Illegal_Argument.Error "Unsupported type for `DB_Table.set`.") - - ## 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 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) - check_add = case set_mode of - Set_Mode.Add_Or_Update -> True - Set_Mode.Add -> if index.is_nothing then True else Error.throw (Existing_Column.Error renamed.name) - Set_Mode.Update -> if index.is_nothing then Error.throw (Missing_Column.Error renamed.name) else True - new_table = check_add.if_not_error <| - new_col = renamed.as_internal - new_cols = if index.is_nothing then self.internal_columns + [new_col] else - Vector.new self.column_count i-> if i == index then new_col else self.internal_columns.at i - self.updated_columns new_cols - - problem_builder.report_unique_name_strategy unique - problem_builder.attach_problems_after on_problems new_table + problem_builder = Problem_Builder.new + unique = self.column_naming_helper.create_unique_name_strategy + unique.mark_used self.column_names + + resolved = case value of + _ : Text -> self.make_constant_column value + _ : Expression -> self.evaluate_expression value on_problems + _ : DB_Column -> + if Helpers.check_integrity self value then value else + Error.throw (Integrity_Error.Error "Column "+value.name) + _ : Constant_Column -> self.make_constant_column value + _ : Simple_Expression -> value.evaluate self (set_mode==Set_Mode.Update && as=="") on_problems + _ : Vector -> Error.throw (Unsupported_Database_Operation "`Vector` for `set`") + _ : Array -> Error.throw (Unsupported_Database_Operation.Error "`Array` for `set`") + _ : Range -> Error.throw (Unsupported_Database_Operation.Error "`Range` for `set`") + _ : Date_Range -> Error.throw (Unsupported_Database_Operation.Error "`Date_Range` for `set`") + _ -> Error.throw (Illegal_Argument.Error "Unsupported type for `DB_Table.set`.") + + ## 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 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) + check_add = case set_mode of + Set_Mode.Add_Or_Update -> True + Set_Mode.Add -> if index.is_nothing then True else Error.throw (Existing_Column.Error renamed.name) + Set_Mode.Update -> if index.is_nothing then Error.throw (Missing_Column.Error renamed.name) else True + new_table = check_add.if_not_error <| + new_col = renamed.as_internal + new_cols = if index.is_nothing then self.internal_columns + [new_col] else + Vector.new self.column_count i-> if i == index then new_col else self.internal_columns.at i + self.updated_columns new_cols + + problem_builder.report_unique_name_strategy unique + problem_builder.attach_problems_after on_problems new_table ## PRIVATE Given an expression, create a derived column where each value is the @@ -1076,17 +1073,16 @@ type DB_Table an `Additional_Warnings`. evaluate_expression : Text | Expression -> Problem_Behavior -> DB_Column ! No_Such_Column | Invalid_Value_Type | Expression_Error evaluate_expression self expression:(Text | Expression) on_problems:Problem_Behavior=..Report_Warning = - Feature.Set.if_supported_else_throw self.connection.dialect "evaluate_expression" <| - if expression.is_a Text then self.evaluate_expression (Expression.Value expression) on_problems else - get_column name = self.at name - make_constant_column value = case value of - _ : DB_Column -> value - _ -> self.make_constant_column value - new_column = Expression.evaluate expression get_column make_constant_column "Standard.Database.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.expression) - on_problems.attach_problems_before problems <| - Warning.set result [] + if expression.is_a Text then self.evaluate_expression (Expression.Value expression) on_problems else + get_column name = self.at name + make_constant_column value = case value of + _ : DB_Column -> value + _ -> self.make_constant_column value + new_column = Expression.evaluate expression get_column make_constant_column "Standard.Database.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.expression) + on_problems.attach_problems_before problems <| + Warning.set result [] ## PRIVATE A helper that creates a two-column table from a Dictionary. @@ -1176,10 +1172,9 @@ type DB_Table Returns the vector of columns contained in this table. columns : Vector DB_Column columns self = - Feature.Column_Operations.if_supported_else_throw self.connection.dialect "columns" <| - Vector.from_polyglot_array <| - Array_Proxy.new self.internal_columns.length i-> - self.make_column (self.internal_columns.at i) + Vector.from_polyglot_array <| + Array_Proxy.new self.internal_columns.length i-> + self.make_column (self.internal_columns.at i) ## GROUP Standard.Base.Metadata ICON metadata diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Feature.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Feature.enso index e8fbdc4b53a2..ac8880942439 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Feature.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Feature.enso @@ -40,9 +40,6 @@ type Feature ## PRIVATE currently blocks getting a DB_Column from a DB_Table, but will soon refine to operations on DB_Column. Column_Operations - ## PRIVATE - set operations on tables. - Set ## PRIVATE get_row, take, drop, limit Sample diff --git a/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Dialect.enso b/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Dialect.enso index 40349e0e3576..0c21aea2aaf7 100644 --- a/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Dialect.enso +++ b/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Dialect.enso @@ -243,6 +243,7 @@ type SQLServer_Dialect Checks if a feature is supported by the dialect. is_feature_supported self feature:Feature -> Boolean = case feature of + Feature.Select_Columns -> True _ -> False ## PRIVATE diff --git a/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Type_Mapping.enso b/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Type_Mapping.enso index f579220c96c3..1d654a2577c8 100644 --- a/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Type_Mapping.enso +++ b/distribution/lib/Standard/Microsoft/0.0.0-dev/src/Internal/SQLServer_Type_Mapping.enso @@ -145,6 +145,13 @@ type SQLServer_Type_Mapping should_warn_on_materialize (db_type : Value_Type) (in_memory_type : Value_Type) -> Boolean = SQL_Type_Mapping.default_should_warn_on_materialize db_type in_memory_type + ## PRIVATE + is_integer_type (value_type : Value_Type) -> Boolean = value_type.is_integer + + ## PRIVATE + is_same_type (value_type1 : Value_Type) (value_type2 : Value_Type) -> Boolean = + value_type1.is_same_type value_type2 + ## PRIVATE on_unknown_type sql_type = Value_Type.Unsupported_Data_Type sql_type.name sql_type diff --git a/test/Microsoft_Tests/src/SQLServer_Spec.enso b/test/Microsoft_Tests/src/SQLServer_Spec.enso index 06cec88e9a8b..c822d93d4c81 100644 --- a/test/Microsoft_Tests/src/SQLServer_Spec.enso +++ b/test/Microsoft_Tests/src/SQLServer_Spec.enso @@ -177,7 +177,7 @@ add_sqlserver_specs suite_builder create_connection_fn = materialize = .read - common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=True order_by_unicode_normalization_by_default=True allows_mixed_type_comparisons=False text_length_limited_columns=True fixed_length_text_columns=True removes_trailing_whitespace_casting_from_char_to_varchar=True char_max_size_after_substring=..Reset supports_decimal_type=True supported_replace_params=supported_replace_params run_advanced_edge_case_tests_by_default=True supports_date_time_without_timezone=False date_time=False is_nan_comparable=True + common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by_unicode_normalization_by_default=True allows_mixed_type_comparisons=False text_length_limited_columns=True fixed_length_text_columns=True removes_trailing_whitespace_casting_from_char_to_varchar=True char_max_size_after_substring=..Reset supports_decimal_type=True supported_replace_params=supported_replace_params run_advanced_edge_case_tests_by_default=True supports_date_time_without_timezone=False date_time=False is_nan_comparable=True aggregate_selection = Common_Table_Operations.Aggregate_Spec.Test_Selection.Config first_last_row_order=False aggregation_problems=False agg_in_memory_table = (enso_project.data / "data.csv") . read diff --git a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso index d1f1d8b96d57..c70a2892c2b5 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso @@ -146,18 +146,7 @@ type Connection_Data self.connection.close add_specs suite_builder setup = - if setup.is_feature_supported Feature.Filter then (add_column_operation_specs suite_builder setup) else - suite_builder.group setup.prefix+"(Column_Operations_Spec)" group_builder-> - group_builder.specify "at should report unsupported" <| - table_builder = setup.light_table_builder - t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", [100, 3, Nothing, 4, 12]], ["Y", [100, 4, 2, Nothing, 11]]] - t2 = t.at "X" - t2.should_fail_with (Unsupported_Database_Operation.Error "at") - group_builder.specify "get should report unsupported" <| - table_builder = setup.light_table_builder - t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", [100, 3, Nothing, 4, 12]], ["Y", [100, 4, 2, Nothing, 11]]] - t2 = t.get "X" - t2.should_fail_with (Unsupported_Database_Operation.Error "get") + if setup.is_feature_supported Feature.Column_Operations then (add_column_operation_specs suite_builder setup) add_column_operation_specs suite_builder setup = prefix = setup.prefix diff --git a/test/Table_Tests/src/Common_Table_Operations/Derived_Columns_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Derived_Columns_Spec.enso index db3109695e50..47ec1c4b0025 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Derived_Columns_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Derived_Columns_Spec.enso @@ -17,13 +17,7 @@ from project.Common_Table_Operations.Util import all main filter=Nothing = run_default_backend add_specs filter add_specs suite_builder setup = - if setup.is_feature_supported Feature.Set then (add_derived_columns_specs suite_builder setup) else - suite_builder.group setup.prefix+"(Derived_Columns_Spec) Table.set" group_builder-> - group_builder.specify "set should report unsupported" <| - table_builder = setup.light_table_builder - t1 = table_builder [["X", [1, 2, 3]], ["Y", [4, 5, 6]]] - t2 = t1.set (Simple_Expression.Simple_Expr (Column_Ref.Name "A") Simple_Calculation.Copy) "C" - t2.should_fail_with (Unsupported_Database_Operation.Error "set") + if setup.is_feature_supported Feature.Column_Operations then (add_derived_columns_specs suite_builder setup) add_derived_columns_specs suite_builder setup = prefix = setup.prefix