From 9650bdfcaeaddfa628cb851b8748bedbf9c875c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 21 Apr 2023 18:39:14 +0200 Subject: [PATCH] align --- .../Database/0.0.0-dev/src/Data/Column.enso | 33 ++++++++++--------- .../Database/0.0.0-dev/src/Data/Dialect.enso | 7 ++++ .../Internal/SQLite/SQLite_Type_Mapping.enso | 13 +++++--- .../Column_Operations_Spec.enso | 10 ++++-- .../Types/SQLite_Type_Mapping_Spec.enso | 1 - 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso index 15e6beab0cb4..c120b00ed3d5 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso @@ -121,21 +121,24 @@ type Column is always passed as the first argument). - new_name: The name of the resulting column. make_op self op_kind operands new_name = - type_mapping = self.connection.dialect.get_type_mapping - prepare_operand operand = case operand of - other_column : Column -> - if Helpers.check_integrity self other_column then other_column.expression else - Error.throw <| Unsupported_Database_Operation.Error "Cannot use columns coming from different contexts in one expression without a join." - constant -> - SQL_Expression.Constant constant - - expressions = operands.map prepare_operand - new_expr = SQL_Expression.Operation op_kind ([self.expression] + expressions) - - infer_from_database_callback expression = - SQL_Type_Reference.new self.connection self.context expression - new_type_ref = type_mapping.infer_return_type infer_from_database_callback op_kind [self]+operands new_expr - Column.Value new_name self.connection new_type_ref new_expr self.context + checked_support = if self.connection.dialect.is_supported op_kind then True else + Error.throw (Unsupported_Database_Operation.Error "The operation "+op_kind+" is not supported by this backend.") + checked_support.if_not_error <| + type_mapping = self.connection.dialect.get_type_mapping + prepare_operand operand = case operand of + other_column : Column -> + if Helpers.check_integrity self other_column then other_column.expression else + Error.throw <| Unsupported_Database_Operation.Error "Cannot use columns coming from different contexts in one expression without a join." + constant -> + SQL_Expression.Constant constant + + expressions = operands.map prepare_operand + new_expr = SQL_Expression.Operation op_kind ([self.expression] + expressions) + + infer_from_database_callback expression = + SQL_Type_Reference.new self.connection self.context expression + new_type_ref = type_mapping.infer_return_type infer_from_database_callback op_kind [self]+operands new_expr + Column.Value new_name self.connection new_type_ref new_expr self.context ## PRIVATE diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso index c20ba26498e2..3d7f80b3bd10 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso @@ -179,6 +179,13 @@ type Dialect _ = aggregate Unimplemented.throw "This is an interface only." + ## PRIVATE + Checks if an operation is supported by the dialect. + is_supported : Text -> Boolean + is_supported self operation = + _ = operation + Unimplemented.throw "This is an interface only." + ## PRIVATE The dialect of SQLite databases. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Type_Mapping.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Type_Mapping.enso index 9e13e248bb4b..e9817a802774 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Type_Mapping.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Type_Mapping.enso @@ -137,7 +137,7 @@ operations_map = ## We actually re-use the logic from the in-memory backend, since the SQLite types essentially implement a very simple subset of our types. find_a_common_type arguments = - inputs_types = arguments.map find_type + types = arguments.map find_type unified = Value_Type_Helpers.find_common_type types strict=False # We remap Mixed to Char, to be consistent with our main mapping. result = if unified == Value_Type.Mixed then default_text else unified @@ -148,14 +148,17 @@ operations_map = Panic.throw (Illegal_State.Error "Impossible: IIF must have 3 arguments. This is a bug in the Database library.") find_a_common_type (arguments.drop 1) - always_boolean_ops = ["==", "!=", "equals_ignore_case", ">=", "<=", "<", ">", "BETWEEN", "AND", "OR", "NOT", "IS_NULL", "IS_NAN", "IS_EMPTY", "LIKE", "IS_IN", "starts_with", "ends_with", "contains"] + handle_cast _ = + Panic.throw (Illegal_State.Error "Cast relies on its own type inference logic, so this code should never be reached. This is a bug in the Database library.") + + always_boolean_ops = ["==", "!=", "equals_ignore_case", ">=", "<=", "<", ">", "BETWEEN", "AND", "OR", "NOT", "IS_NULL", "IS_EMPTY", "LIKE", "IS_IN", "IS_IN_COLUMN", "starts_with", "ends_with", "contains", "BOOL_OR"] always_floating_ops = ["/", "mod", "AVG", "STDDEV_POP", "STDDEV_SAMP"] always_text_ops = ["ADD_TEXT", "CONCAT", "CONCAT_QUOTE_IF_NEEDED", "MAKE_CASE_SENSITIVE", "FOLD_CASE", "TRIM", "LTRIM", "RTRIM"] always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] - merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FIRST", "LAST", "FIRST_NOT_NULL", "LAST_NOT_NULL", "FILL_NULL", "COALESCE"] - others = [["IIF", handle_iif]] - mapping = Map.from_vector <| + merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] + others = [["IIF", handle_iif], ["CAST", handle_cast]] + Map.from_vector <| v1 = always_boolean_ops.map [_, const SQLite_Types.boolean] v2 = always_floating_ops.map [_, const SQLite_Types.real] v3 = always_integer_ops.map [_, const SQLite_Types.integer] 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 3f9ed774ae32..98975fc93a04 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 @@ -5,7 +5,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument from Standard.Table import Value_Type from Standard.Table.Errors import all -from Standard.Database.Errors import SQL_Error +from Standard.Database.Errors import all from Standard.Test import Test, Problems import Standard.Test.Extensions @@ -328,11 +328,15 @@ spec setup = (y ^ "a").should_fail_with Invalid_Value_Type (y ^ 42).should_fail_with Invalid_Value_Type - if setup.test_selection.is_nan_and_nothing_distinct then - Test.specify "should support is_nan" <| + case setup.test_selection.is_nan_and_nothing_distinct of + True -> Test.specify "should support is_nan" <| t = table_builder [["X", [1.5, 2, Number.nan]], ["Y", [1, 2, 3]]] t.at "X" . is_nan . to_vector . should_equal [False, False, True] t.at "Y" . is_nan . should_fail_with Invalid_Value_Type + False -> Test.specify "should report that is_nan is not supported" <| + t = table_builder [["X", [1.5]]] + t.at "X" . is_nan . should_fail_with Unsupported_Database_Operation + Test.specify "should support is_blank" <| t = table_builder [["X", [1.5, 2, Number.nan, Nothing]], ["Y", [1, Nothing, 3, 4]]] t.at "X" . is_blank treat_nans_as_blank=True . to_vector . should_equal [False, False, True, True] diff --git a/test/Table_Tests/src/Database/Types/SQLite_Type_Mapping_Spec.enso b/test/Table_Tests/src/Database/Types/SQLite_Type_Mapping_Spec.enso index 44914d579e95..6c1b4cfe23e9 100644 --- a/test/Table_Tests/src/Database/Types/SQLite_Type_Mapping_Spec.enso +++ b/test/Table_Tests/src/Database/Types/SQLite_Type_Mapping_Spec.enso @@ -68,7 +68,6 @@ spec = t.compute 'fill_empty([b], "")' . value_type . should_equal Value_Type.Char t.compute 'is_blank([b])' . value_type . should_equal Value_Type.Boolean t.compute 'is_empty([b])' . value_type . should_equal Value_Type.Boolean - t.compute 'is_nan([d])' . value_type . should_equal Value_Type.Boolean t.compute 'is_nothing([a])' . value_type . should_equal Value_Type.Boolean t2 = t.aggregate [Aggregate_Column.Group_By "b", Aggregate_Column.Sum "a", Aggregate_Column.Maximum "a", Aggregate_Column.Minimum "d", Aggregate_Column.Count_Not_Nothing "c", Aggregate_Column.Concatenate "b", Aggregate_Column.Count]