From bbc5cff232222b911b650b5bfcfa762b5b8a87af Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 7 Jun 2023 11:53:11 -0400 Subject: [PATCH 01/83] one test --- .../Database/0.0.0-dev/src/Data/Column.enso | 18 ++++++++++++++++-- .../0.0.0-dev/src/Internal/Base_Generator.enso | 2 +- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- .../Column_Operations_Spec.enso | 4 ++++ 4 files changed, 22 insertions(+), 4 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 97fe93c215db..c1b0d3b2e4fb 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 @@ -556,10 +556,24 @@ type Column _ = [decimal_places, use_bankers, on_problems] Error.throw <| Unsupported_Database_Operation.Error "`Column.round` is not implemented yet for the Database backends." - ## Truncating values is not supported in database columns. + ## ALIAS int + UNSTABLE + + If the column is numeric, truncate the floating-point values to an + integer by dropping the fractional part. This is equivalent to + "round-toward-zero". + + > Example + Truncate a column of floating-point values. + + import Standard.Examples + + example_truncate = Examples.decimal_column.truncate truncate : Column ! Invalid_Value_Type truncate self = - Error.throw <| Unsupported_Database_Operation.Error "`Column.truncate` is not implemented yet for the Database backends." + Value_Type.expect_numeric self <| + new_name = "trunc " + self.naming_helpers.to_expression_text self + self.make_unary_op "TRUNCATE" new_name ## Taking the ceiling of values is not supported in database columns. ceil : Column ! Invalid_Value_Type diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index f020784232b4..16f542d439c3 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -174,7 +174,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["TRUNCATE", make_function "TRUNC"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals 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 c56fc50f2867..bba3ab17c1f9 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 @@ -176,7 +176,7 @@ operations_map = 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"] + always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] 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 98975fc93a04..0d4ddc3f7dc9 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 @@ -440,6 +440,10 @@ spec setup = (x * Nothing).to_vector . should_equal nulls (x / Nothing).to_vector . should_equal nulls + Test.specify "should allow truncate" <| + y.truncate.to_vector . should_equal [2, 3, 5, Nothing] + y.truncate.value_type . is_integer . should_be_true + Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] s1 = t3.at "s1" From e6bed7b216afba494164ca60046c510d69e0cea3 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 7 Jun 2023 12:51:08 -0400 Subject: [PATCH 02/83] function_name --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c1b0d3b2e4fb..6c8e27904be5 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 @@ -572,7 +572,7 @@ type Column truncate : Column ! Invalid_Value_Type truncate self = Value_Type.expect_numeric self <| - new_name = "trunc " + self.naming_helpers.to_expression_text self + new_name = self.naming_helpers.function_name "truncate" [self] self.make_unary_op "TRUNCATE" new_name ## Taking the ceiling of values is not supported in database columns. From 347b46e3e9d423539f37c06b6ad790ab351102f2 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 7 Jun 2023 12:56:46 -0400 Subject: [PATCH 03/83] expression_spec --- .../Table_Tests/src/Common_Table_Operations/Expression_Spec.enso | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso index f2b0d71a6298..4c843eb8c319 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso @@ -190,6 +190,7 @@ spec detailed setup = expression_test "1_000.456/2" 500.228 expression_test "2^4" 16 expression_test "11%3" 2 + expression_test "truncate(3.3)" 3 specify_test "should be able to do basic arithmetic with order" expression_test-> expression_test "1+1*2+2" 5 From c78560d7d840d279149fbb3029543c715285e1fc Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 7 Jun 2023 13:46:51 -0400 Subject: [PATCH 04/83] gmt test --- .../Column_Operations_Spec.enso | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 0d4ddc3f7dc9..7fe7da80e532 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 @@ -120,6 +120,16 @@ spec setup = t2 = table_builder [["x", [1, 4, 5, Nothing]], ["y", [2.0, 3.25, 5.0, Nothing]]] x = t2.at "x" y = t2.at "y" + + Test.group "gmt" <| + Test.specify "should allow truncate" <| + y.truncate.to_vector . should_equal [2, 3, 5, Nothing] + IO.println "YYY" + IO.println y + IO.println y.truncate + IO.println y.truncate.value_type + y.truncate.value_type . is_integer . should_be_true + Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| (x == y).to_vector . should_equal [False, False, True, Nothing] From 2fa18467632655e3942a29fb2dfc7478160f6660 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 7 Jun 2023 14:10:23 -0400 Subject: [PATCH 05/83] sl --- .../Database/0.0.0-dev/src/Internal/Base_Generator.enso | 2 +- .../0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso | 7 ++++++- .../0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 16f542d439c3..f020784232b4 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -174,7 +174,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["TRUNCATE", make_function "TRUNC"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index b7286b9d910f..46f2856856c0 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -216,7 +216,7 @@ make_internal_generator_dialect = cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]] text = [starts_with, contains, ends_with, agg_shortest, agg_longest, make_case_sensitive]+concat_ops+cases+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] - arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] + arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"], truncate] bool = [bool_or] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] @@ -448,6 +448,11 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> decimal_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)" +## PRIVATE +truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> + IO.println "TRUNCY PG" + Builder.code "CAST(TRUNC(" ++ arg ++ ") AS INTEGER)" + ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 6ac22bbd2b07..1ba0125bc511 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [decimal_div, mod_op] + arith_extensions = [decimal_div, mod_op, truncate] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -361,6 +361,11 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> decimal_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)" +## PRIVATE +truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> + IO.println "TRUNCY SL" + Builder.code "TRUNC(" ++ arg ++ ")" + ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)) * " ++ y From b16d9eeffedc3a599a5afd2e41d5a390d9a16172 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 11:21:06 -0400 Subject: [PATCH 06/83] trunc tests --- .../Column_Operations_Spec.enso | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 7fe7da80e532..61ad62532aa1 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 @@ -122,13 +122,17 @@ spec setup = y = t2.at "y" Test.group "gmt" <| - Test.specify "should allow truncate" <| - y.truncate.to_vector . should_equal [2, 3, 5, Nothing] - IO.println "YYY" - IO.println y - IO.println y.truncate - IO.println y.truncate.value_type - y.truncate.value_type . is_integer . should_be_true + Test.specify "should allow truncate on a float column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . truncate + result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow truncate on an int column" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + result = table.at "x" . truncate + result.to_vector.should_equal [0, 3, -3, 1, -2] + result.value_type . is_integer . should_be_true Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| From be81b60ede93088fec9f90389ae57e9c46e62bdd Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 12:13:43 -0400 Subject: [PATCH 07/83] ceil floor --- .../Database/0.0.0-dev/src/Data/Column.enso | 32 ++++++++++-- .../Internal/Postgres/Postgres_Dialect.enso | 11 +++- .../src/Internal/SQLite/SQLite_Dialect.enso | 11 +++- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- .../Column_Operations_Spec.enso | 51 ++++++++++++++----- .../Expression_Spec.enso | 2 + 6 files changed, 86 insertions(+), 23 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 6c8e27904be5..d35e3ea89a90 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 @@ -575,15 +575,39 @@ type Column new_name = self.naming_helpers.function_name "truncate" [self] self.make_unary_op "TRUNCATE" new_name - ## Taking the ceiling of values is not supported in database columns. + ## UNSTABLE + + If the column is numeric, takes the ceiling of floating-point values, + returning integer values. + + > Example + Take the ceiling of a column of floating-point values. + + import Standard.Examples + + example_truncate = Examples.decimal_column.ceil ceil : Column ! Invalid_Value_Type ceil self = - Error.throw <| Unsupported_Database_Operation.Error "`Column.ceil` is not implemented yet for the Database backends." + Value_Type.expect_numeric self <| + new_name = self.naming_helpers.function_name "ceil" [self] + self.make_unary_op "CEIL" new_name - ## Taking the floor of values is not supported in database columns. + ## UNSTABLE + + If the column is numeric, takes the floor of floating-point values, + returning integer values. + + > Example + Take the floor of a column of floating-point values. + + import Standard.Examples + + example_truncate = Examples.decimal_column.floor floor : Column ! Invalid_Value_Type floor self = - Error.throw <| Unsupported_Database_Operation.Error "`Column.floor` is not implemented yet for the Database backends." + Value_Type.expect_numeric self <| + new_name = self.naming_helpers.function_name "floor" [self] + self.make_unary_op "FLOOR" new_name ## Returns a column of first non-`Nothing` value on each row of `self` and `values` list. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index 46f2856856c0..ee33ebc76d66 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -216,7 +216,7 @@ make_internal_generator_dialect = cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]] text = [starts_with, contains, ends_with, agg_shortest, agg_longest, make_case_sensitive]+concat_ops+cases+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] - arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"], truncate] + arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"], truncate, ceil, floor] bool = [bool_or] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] @@ -450,9 +450,16 @@ decimal_div = Base_Generator.lift_binary_op "/" x-> y-> ## PRIVATE truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - IO.println "TRUNCY PG" Builder.code "CAST(TRUNC(" ++ arg ++ ") AS INTEGER)" +## PRIVATE +ceil = Base_Generator.lift_unary_op "CEIL" arg-> + Builder.code "CAST(CEIL(" ++ arg ++ ") AS INTEGER)" + +## PRIVATE +floor = Base_Generator.lift_unary_op "FLOOR" arg-> + Builder.code "CAST(FLOOR(" ++ arg ++ ") AS INTEGER)" + ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 1ba0125bc511..1219d7709611 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [decimal_div, mod_op, truncate] + arith_extensions = [decimal_div, mod_op, truncate, ceil, floor] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -363,9 +363,16 @@ decimal_div = Base_Generator.lift_binary_op "/" x-> y-> ## PRIVATE truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - IO.println "TRUNCY SL" Builder.code "TRUNC(" ++ arg ++ ")" +## PRIVATE +ceil = Base_Generator.lift_unary_op "CEIL" arg-> + Builder.code "CEIL(" ++ arg ++ ")" + +## PRIVATE +floor = Base_Generator.lift_unary_op "FLOOR" arg-> + Builder.code "FLOOR(" ++ arg ++ ")" + ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)) * " ++ y 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 bba3ab17c1f9..edb54f4fc30b 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 @@ -176,7 +176,7 @@ operations_map = 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", "TRUNCATE"] + always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE", "CEIL", "FLOOR"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] 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 61ad62532aa1..def46924fb31 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 @@ -122,17 +122,8 @@ spec setup = y = t2.at "y" Test.group "gmt" <| - Test.specify "should allow truncate on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . truncate - result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true - - Test.specify "should allow truncate on an int column" <| - table = table_builder [["x", [0, 3, -3, 1, -2]]] - result = table.at "x" . truncate - result.to_vector.should_equal [0, 3, -3, 1, -2] - result.value_type . is_integer . should_be_true + Test.specify "should allow ceil on a float column" <| + 1 . should_equal 1 Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| @@ -454,9 +445,41 @@ spec setup = (x * Nothing).to_vector . should_equal nulls (x / Nothing).to_vector . should_equal nulls - Test.specify "should allow truncate" <| - y.truncate.to_vector . should_equal [2, 3, 5, Nothing] - y.truncate.value_type . is_integer . should_be_true + Test.specify "should allow truncate on a float column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . truncate + result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow truncate on an int column" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + result = table.at "x" . truncate + result.to_vector.should_equal [0, 3, -3, 1, -2] + result.value_type . is_integer . should_be_true + + Test.specify "should allow ceil on a float column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . ceil + result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow ceil on an int column" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + result = table.at "x" . ceil + result.to_vector.should_equal [0, 3, -3, 1, -2] + result.value_type . is_integer . should_be_true + + Test.specify "should allow floor on a float column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . floor + result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] + result.value_type . is_integer . should_be_true + + Test.specify "should allow floor on an int column" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + result = table.at "x" . floor + result.to_vector.should_equal [0, 3, -3, 1, -2] + result.value_type . is_integer . should_be_true Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] diff --git a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso index 4c843eb8c319..c3480e78e637 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso @@ -191,6 +191,8 @@ spec detailed setup = expression_test "2^4" 16 expression_test "11%3" 2 expression_test "truncate(3.3)" 3 + expression_test "ceil(5.3)" 6 + expression_test "floor(5.3)" 5 specify_test "should be able to do basic arithmetic with order" expression_test-> expression_test "1+1*2+2" 5 From 3b486b96d9ca18ac65e621974c2d35d328043d05 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 15:26:25 -0400 Subject: [PATCH 08/83] by hand rounding, sqlite parser stack overflow --- round.pg | 29 +++++++++++++++++++ round.sl | 29 +++++++++++++++++++ .../Column_Operations_Spec.enso | 3 ++ 3 files changed, 61 insertions(+) create mode 100644 round.pg create mode 100644 round.sl diff --git a/round.pg b/round.pg new file mode 100644 index 000000000000..15b8d12bb169 --- /dev/null +++ b/round.pg @@ -0,0 +1,29 @@ +with n as (select 123.235 as n) select * from ( +with dp as (select 2 as dp) select * from ( +with use_bankers as (select false as use_bankers) select * from ( +with scale as (select power(10, (select * from dp)) as scale) select * from ( +with scaled as (select (select * from n) * (select * from scale) as scaled) select * from ( +with round_base as (select floor((select * from scaled)) as round_base) select * from ( +with round_midpoint as (select (((select * from round_base) + 0.5) / (select * from scale)) as round_midpoint) select * from ( +with even_is_up as (select case when (select * from n) >= 0 then (select ((cast(trunc((select * from scaled)) as integer)) % 2) != 0) else (select ((cast(trunc((select * from scaled)) as integer)) % 2) = 0) end as even_is_up) select * from ( +with half_goes_up as (select case when (select * from use_bankers) then (select * from even_is_up) else true end as half_goes_up) select * from ( +with do_round_up as (select case when (select * from half_goes_up) then (select (select * from n) >= (select * from round_midpoint)) else (select (select * from n) > (select * from round_midpoint)) end as do_round_up) select * from ( +with result as (select case when (select * from do_round_up) then (select ((select * from round_base) + 1.0) / (select * from scale)) else (select ((select * from round_base)) / (select * from scale)) end as result) select * from ( + +select n.*, dp.*, use_bankers.*, scale.*, scaled.*, round_base.*, round_midpoint.*, even_is_up.*, half_goes_up.*, do_round_up.*, result.* + +from n, dp, use_bankers, scale, scaled, round_base, round_midpoint, even_is_up, half_goes_up, do_round_up, result + +) q0 +) q1 +) q2 +) q3 +) q4 +) q5 +) q6 +) q7 +) q8 +) q9 +) q10 + +; diff --git a/round.sl b/round.sl new file mode 100644 index 000000000000..15b8d12bb169 --- /dev/null +++ b/round.sl @@ -0,0 +1,29 @@ +with n as (select 123.235 as n) select * from ( +with dp as (select 2 as dp) select * from ( +with use_bankers as (select false as use_bankers) select * from ( +with scale as (select power(10, (select * from dp)) as scale) select * from ( +with scaled as (select (select * from n) * (select * from scale) as scaled) select * from ( +with round_base as (select floor((select * from scaled)) as round_base) select * from ( +with round_midpoint as (select (((select * from round_base) + 0.5) / (select * from scale)) as round_midpoint) select * from ( +with even_is_up as (select case when (select * from n) >= 0 then (select ((cast(trunc((select * from scaled)) as integer)) % 2) != 0) else (select ((cast(trunc((select * from scaled)) as integer)) % 2) = 0) end as even_is_up) select * from ( +with half_goes_up as (select case when (select * from use_bankers) then (select * from even_is_up) else true end as half_goes_up) select * from ( +with do_round_up as (select case when (select * from half_goes_up) then (select (select * from n) >= (select * from round_midpoint)) else (select (select * from n) > (select * from round_midpoint)) end as do_round_up) select * from ( +with result as (select case when (select * from do_round_up) then (select ((select * from round_base) + 1.0) / (select * from scale)) else (select ((select * from round_base)) / (select * from scale)) end as result) select * from ( + +select n.*, dp.*, use_bankers.*, scale.*, scaled.*, round_base.*, round_midpoint.*, even_is_up.*, half_goes_up.*, do_round_up.*, result.* + +from n, dp, use_bankers, scale, scaled, round_base, round_midpoint, even_is_up, half_goes_up, do_round_up, result + +) q0 +) q1 +) q2 +) q3 +) q4 +) q5 +) q6 +) q7 +) q8 +) q9 +) q10 + +; 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 def46924fb31..c565f2554034 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 @@ -123,6 +123,9 @@ spec setup = Test.group "gmt" <| Test.specify "should allow ceil on a float column" <| + t = table_builder [["a", [1, 2, 3]], ["b", ['x', 'y', 'z']], ["c", [1.0, 2.0, 3.0]], ["d", [True, False, True]]] + IO.println "HEY" + IO.println <| ((t.at "a") ^ (t.at "c")).to_vector 1 . should_equal 1 Test.group prefix+"Column Operations - Equality & Null Handling" <| From 9fe0841a112e40be762d2196121b4eb6c09ea050 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 15:57:13 -0400 Subject: [PATCH 09/83] wip --- round.pg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/round.pg b/round.pg index 15b8d12bb169..58b392edc1dd 100644 --- a/round.pg +++ b/round.pg @@ -1,3 +1,25 @@ +-- works in sl +with args as (select + 123.235 as n, + 2 as dp, + false as use_bankers) select * from ( +with v0 as (select + power(10, args.dp) as scale from args) select * from ( +with v1 as (select + args.n * v0.scale as scaled from args, v0) select * from ( +with v2 as ( + select floor(v1.scaled) as round_base from v1) select * from ( +with v3 as (select + (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) +select * from args, v0, v1, v2, v3 +) q3 +) q2 +) q1 +) q0 + +; +\q + with n as (select 123.235 as n) select * from ( with dp as (select 2 as dp) select * from ( with use_bankers as (select false as use_bankers) select * from ( From f649b73299071c030db2b9061f15fc1746256971 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 16:05:45 -0400 Subject: [PATCH 10/83] pg maybe smaller --- round.pg | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/round.pg b/round.pg index 58b392edc1dd..c73ab9c79884 100644 --- a/round.pg +++ b/round.pg @@ -10,8 +10,21 @@ with v1 as (select with v2 as ( select floor(v1.scaled) as round_base from v1) select * from ( with v3 as (select - (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) -select * from args, v0, v1, v2, v3 + (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) select * from ( +with v4 as (select + case when args.n >= 0 + then (((cast(trunc(v1.scaled) as integer)) % 2) != 0) + else (((cast(trunc(v1.scaled) as integer)) % 2) = 0) end as even_is_up from args, v1) select * from ( +with v5 as (select + case when args.use_bankers then v4.even_is_up else true end as half_goes_up from args, v4) select * from ( +with v6 as (select + case when v5.half_goes_up then args.n >= v3.round_midpoint else n > v3.round_midpoint end as do_round_up from args, v3, v5) select * from ( +select *, case when v6.do_round_up then (v2.round_base + 1.0) / v0.scale else v2.round_base / v0.scale end as result +from args, v0, v1, v2, v3, v4, v5, v6 +) q7 +) q6 +) q5 +) q4 ) q3 ) q2 ) q1 From 2f68d3545ba69eaccaae1c42f7b9ff49afd2bae2 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 8 Jun 2023 16:07:25 -0400 Subject: [PATCH 11/83] works in sqlite too --- round.pg | 1 - round.sl | 55 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/round.pg b/round.pg index c73ab9c79884..c1ef4a88810e 100644 --- a/round.pg +++ b/round.pg @@ -1,4 +1,3 @@ --- works in sl with args as (select 123.235 as n, 2 as dp, diff --git a/round.sl b/round.sl index 15b8d12bb169..407180d92957 100644 --- a/round.sl +++ b/round.sl @@ -1,29 +1,32 @@ -with n as (select 123.235 as n) select * from ( -with dp as (select 2 as dp) select * from ( -with use_bankers as (select false as use_bankers) select * from ( -with scale as (select power(10, (select * from dp)) as scale) select * from ( -with scaled as (select (select * from n) * (select * from scale) as scaled) select * from ( -with round_base as (select floor((select * from scaled)) as round_base) select * from ( -with round_midpoint as (select (((select * from round_base) + 0.5) / (select * from scale)) as round_midpoint) select * from ( -with even_is_up as (select case when (select * from n) >= 0 then (select ((cast(trunc((select * from scaled)) as integer)) % 2) != 0) else (select ((cast(trunc((select * from scaled)) as integer)) % 2) = 0) end as even_is_up) select * from ( -with half_goes_up as (select case when (select * from use_bankers) then (select * from even_is_up) else true end as half_goes_up) select * from ( -with do_round_up as (select case when (select * from half_goes_up) then (select (select * from n) >= (select * from round_midpoint)) else (select (select * from n) > (select * from round_midpoint)) end as do_round_up) select * from ( -with result as (select case when (select * from do_round_up) then (select ((select * from round_base) + 1.0) / (select * from scale)) else (select ((select * from round_base)) / (select * from scale)) end as result) select * from ( - -select n.*, dp.*, use_bankers.*, scale.*, scaled.*, round_base.*, round_midpoint.*, even_is_up.*, half_goes_up.*, do_round_up.*, result.* - -from n, dp, use_bankers, scale, scaled, round_base, round_midpoint, even_is_up, half_goes_up, do_round_up, result - -) q0 -) q1 -) q2 -) q3 -) q4 -) q5 -) q6 +with args as (select + 123.235 as n, + 2 as dp, + false as use_bankers) select * from ( +with v0 as (select + power(10, args.dp) as scale from args) select * from ( +with v1 as (select + args.n * v0.scale as scaled from args, v0) select * from ( +with v2 as ( + select floor(v1.scaled) as round_base from v1) select * from ( +with v3 as (select + (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) select * from ( +with v4 as (select + case when args.n >= 0 + then (((cast(trunc(v1.scaled) as integer)) % 2) != 0) + else (((cast(trunc(v1.scaled) as integer)) % 2) = 0) end as even_is_up from args, v1) select * from ( +with v5 as (select + case when args.use_bankers then v4.even_is_up else true end as half_goes_up from args, v4) select * from ( +with v6 as (select + case when v5.half_goes_up then args.n >= v3.round_midpoint else n > v3.round_midpoint end as do_round_up from args, v3, v5) select * from ( +select *, case when v6.do_round_up then (v2.round_base + 1.0) / v0.scale else v2.round_base / v0.scale end as result +from args, v0, v1, v2, v3, v4, v5, v6 ) q7 -) q8 -) q9 -) q10 +) q6 +) q5 +) q4 +) q3 +) q2 +) q1 +) q0 ; From 7f7324b8d2d5d60c1a4df405c0c72657db2975eb Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 9 Jun 2023 16:18:22 -0400 Subject: [PATCH 12/83] col math works --- .../Database/0.0.0-dev/src/Data/Column.enso | 79 +++++++++++++++++-- .../Database/0.0.0-dev/src/Data/Table.enso | 5 ++ .../src/Internal/Base_Generator.enso | 24 +++++- .../Internal/SQLite/SQLite_Type_Mapping.enso | 18 ++++- .../Column_Operations_Spec.enso | 14 +++- 5 files changed, 127 insertions(+), 13 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 d35e3ea89a90..5a435a02a23c 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 @@ -122,6 +122,7 @@ type Column - operands: A vector of additional operation arguments (the column itself is always passed as the first argument). - new_name: The name of the resulting column. + make_op : Text -> Vector Text -> (Text | Nothing) -> Column make_op self op_kind operands new_name = 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.") @@ -550,11 +551,61 @@ type Column op_result = self.make_op "IIF" [when_true, when_false] new_name adapt_unified_column op_result common_type - ## Rounding values is not supported in database columns. - round : Integer -> Boolean -> Problem_Behavior -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False on_problems=Report_Warning = - _ = [decimal_places, use_bankers, on_problems] - Error.throw <| Unsupported_Database_Operation.Error "`Column.round` is not implemented yet for the Database backends." + ## Round to a specified number of decimal places. + + By default, rounding uses "symmetric round-half-up", also known as + "round towards 0." If use_bankers=True, then it uses "round-half-even", + also known as "banker's rounding". + + If `decimal_places` > 0, `round` returns a column of the same type as the + input. Otherwise, it returns an `Integer` column. + + Arguments: + - decimal_places: The number of decimal places to round to. Can be + negative, which results in rounding to positive integer powers of 10. + Must be between -15 and 15 (inclusive). + - use_bankers: Rounds mid-point to nearest even number. + + ! Error Conditions + + If `decimal_places` is outside the range -15..15 (inclusive), an + `Illegal_Argument` error is thrown. + + ? Negative decimal place counts + Rounding to `n` digits can be thought of as "rounding to the nearest + multiple of 10^(-n)". For negative decimal counts, this results in + rounding to the nearest positive integer power of 10. + round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type + round self decimal_places=0 use_bankers=False = + _ = [use_bankers] + check_decimal_places decimal_places <| Value_Type.expect_numeric self <| + new_name = self.naming_helpers.function_name "round" [self] + # works + #self.make_binary_op "KONST" 37 new_name + #self.make_binary_op "KONST" True new_name + #self * 2 + + #self * 2 + scale = 10 ^ decimal_places + #scale = self.make_binary_op "POW_FLIP" 10 new_name + scaled = self * scale + round_base = scaled.floor + round_midpoint = (round_base + 0.5) / scale + even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) + half_goes_up = if use_bankers then even_is_up else (self.make_binary_op "KONST" True) + do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) + result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + result + + ## + scale = 10 ^ decimal_places + scaled = self * scale + round_base = scaled.floor + round_midpoint = (round_base + 0.5) / scale + even_is_up = if self >= 0 then (scaled.truncate % 2) != 0 else (scaled.truncate % 2) == 0 + half_goes_up = if use_bankers then even_is_up else True + do_round_up = if half_goes_up then self >= round_midpoint else self > round_midpoint + if do_round_up then ((round_base + 1.0) / scale) else (round_base / scale) ## ALIAS int UNSTABLE @@ -1274,3 +1325,21 @@ adapt_unified_column column expected_type = SQL_Type_Reference.new column.connection column.context expression adapted = dialect.adapt_unified_column column.as_internal expected_type infer_return_type Column.Value name=column.name connection=column.connection sql_type_reference=adapted.sql_type_reference expression=adapted.expression context=column.context + +## PRIVATE + The smallest allowed value for the `decimal_places` argument to `round` +round_min_decimal_places : Integer +round_min_decimal_places = -15 + +## PRIVATE + The largest allowed value for the `decimal_places` argument to `round` +round_max_decimal_places : Integer +round_max_decimal_places = 15 + +## PRIVATE + Restrict rounding decimal_places parameter. +check_decimal_places : Integer -> Any -> Any ! Illegal_Argument +check_decimal_places decimal_places ~action = + if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else + msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text + Error.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index e2e5d8cdd96c..e687ed1eae54 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -1729,6 +1729,11 @@ type Table Error.throw (Illegal_Argument.Error "Cannot create a table with no columns.") False -> sql = preprocessed.to_sql + ## + IO.println "Table.read" + IO.println preprocessed + IO.println sql + IO.println sql.unsafe_to_raw_sql column_type_suggestions = preprocessed.internal_columns.map .sql_type_reference self.connection.read_statement sql column_type_suggestions diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index f020784232b4..448fa5fb98b5 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -136,6 +136,26 @@ make_function name = arguments -> (Builder.code name) ++ (Builder.join ", " arguments . paren) +make_konst : Vector Builder -> Builder +make_konst = + arguments -> + #IO.println "make_konst" + #IO.println arguments + #IO.println <| arguments.at 1 + #(Builder.interpolation (arguments.at 1)) # . paren + arguments.at 1 . paren + +## PRIVATE + Create a binary function that swaps its two arguments. + This is used (for example) to implement 10^n as POW_FLIP(n, 10), which is + necessary because 10 is not a Column. +make_binary_function_flipped : Text -> Vector Builder -> Builder +make_binary_function_flipped name arguments = case arguments.length of + 2 -> + make_function name [arguments.at 1, arguments.at 0] + _ -> + Error.throw <| Illegal_State.Error ("Invalid amount of arguments for binary operation " + name) + ## PRIVATE A helper function to create an operation that takes no arguments. @@ -174,14 +194,14 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["POW_FLIP", make_binary_function_flipped "POW"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals compare = [eq, neq, bin "<", bin ">", bin "<=", bin ">=", ["BETWEEN", make_between]] functions = [["COALESCE", make_function "COALESCE"], ["ROW_MIN", make_function "MIN"], ["ROW_MAX", make_function "MAX"]] agg = [fun "MAX", fun "MIN", fun "AVG", fun "SUM"] - counts = [fun "COUNT", ["COUNT_ROWS", make_constant "COUNT(*)"]] + counts = [fun "COUNT", ["COUNT_ROWS", make_constant "COUNT(*)"], ["KONST", make_konst]] text = [is_empty, bin "LIKE", simple_equals_ignore_case, fold_case, make_case_sensitive] nulls = [["IS_NULL", make_right_unary_op "IS NULL"], ["FILL_NULL", make_function "COALESCE"]] contains = [["IS_IN", make_is_in], ["IS_IN_COLUMN", make_is_in_column]] 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 edb54f4fc30b..de240a389d6e 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 @@ -159,6 +159,20 @@ 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) + handle_konst arguments = + #IO.println 'handle_konst' + #IO.println arguments + v = arguments.at 1 + q = + if v.is_a Decimal then SQLite_Types.real else + if v.is_a Integer then SQLite_Types.integer else + if v.is_a Text then SQLite_Types.text else + if v.is_a Boolean then SQLite_Types.boolean else + Error.throw <| Illegal_Argument.Error <| "An unsupported SQL constant type: " + v.to_text + #IO.println "qqq" + #IO.println q + q + handle_case arguments = fallback = arguments.last cases = arguments.drop (Last 1) @@ -177,9 +191,9 @@ operations_map = 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", "TRUNCATE", "CEIL", "FLOOR"] - arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] + arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM", "POW_FLIP"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] - others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] + others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["KONST", handle_konst]] Map.from_vector <| v1 = always_boolean_ops.map [_, const SQLite_Types.boolean] v2 = always_floating_ops.map [_, const SQLite_Types.real] 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 c565f2554034..9a33cb58f640 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 @@ -123,10 +123,16 @@ spec setup = Test.group "gmt" <| Test.specify "should allow ceil on a float column" <| - t = table_builder [["a", [1, 2, 3]], ["b", ['x', 'y', 'z']], ["c", [1.0, 2.0, 3.0]], ["d", [True, False, True]]] - IO.println "HEY" - IO.println <| ((t.at "a") ^ (t.at "c")).to_vector - 1 . should_equal 1 + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9, -3.5, -2.5, -0.5, 0.5, 2.5, 3.5, 1234.2345]]] + col = table.at "x" + IO.println col + #r = col.round.to_vector + r = col . round . to_vector + IO.println 'RRR' + IO.println <| table.at "x" . round . to_vector + IO.println <| table.at "x" . round 2 . to_vector + IO.println <| table.at "x" . round -2 . to_vector + IO.println <| table.at "x" . round use_bankers=True . to_vector Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| From 9c4e0ff13005d5fd1e90846b0f3ef6098795b1fb Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 12 Jun 2023 12:12:18 -0400 Subject: [PATCH 13/83] clean up --- .../Database/0.0.0-dev/src/Data/Column.enso | 19 +------------------ .../Database/0.0.0-dev/src/Data/Table.enso | 5 ----- .../Column_Operations_Spec.enso | 5 +++++ 3 files changed, 6 insertions(+), 23 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 5a435a02a23c..c1e43a67b5d2 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 @@ -580,14 +580,7 @@ type Column _ = [use_bankers] check_decimal_places decimal_places <| Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "round" [self] - # works - #self.make_binary_op "KONST" 37 new_name - #self.make_binary_op "KONST" True new_name - #self * 2 - - #self * 2 scale = 10 ^ decimal_places - #scale = self.make_binary_op "POW_FLIP" 10 new_name scaled = self * scale round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale @@ -595,17 +588,7 @@ type Column half_goes_up = if use_bankers then even_is_up else (self.make_binary_op "KONST" True) do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - result - - ## - scale = 10 ^ decimal_places - scaled = self * scale - round_base = scaled.floor - round_midpoint = (round_base + 0.5) / scale - even_is_up = if self >= 0 then (scaled.truncate % 2) != 0 else (scaled.truncate % 2) == 0 - half_goes_up = if use_bankers then even_is_up else True - do_round_up = if half_goes_up then self >= round_midpoint else self > round_midpoint - if do_round_up then ((round_base + 1.0) / scale) else (round_base / scale) + result.rename new_name ## ALIAS int UNSTABLE diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index e687ed1eae54..e2e5d8cdd96c 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -1729,11 +1729,6 @@ type Table Error.throw (Illegal_Argument.Error "Cannot create a table with no columns.") False -> sql = preprocessed.to_sql - ## - IO.println "Table.read" - IO.println preprocessed - IO.println sql - IO.println sql.unsafe_to_raw_sql column_type_suggestions = preprocessed.internal_columns.map .sql_type_reference self.connection.read_statement sql column_type_suggestions 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 9a33cb58f640..8215bbcd99e1 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 @@ -129,6 +129,7 @@ spec setup = #r = col.round.to_vector r = col . round . to_vector IO.println 'RRR' + col.round.name . should_equal "round([x])" IO.println <| table.at "x" . round . to_vector IO.println <| table.at "x" . round 2 . to_vector IO.println <| table.at "x" . round -2 . to_vector @@ -454,6 +455,10 @@ spec setup = (x * Nothing).to_vector . should_equal nulls (x / Nothing).to_vector . should_equal nulls + Test.specify "should name a rounding column correctly" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + table.at "x" . round . name . should_equal "round([x])" + Test.specify "should allow truncate on a float column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . truncate From abaab2a0f55bb842ac10fa454f55d2c8627265f5 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 12 Jun 2023 13:04:38 -0400 Subject: [PATCH 14/83] tests, cast to int column --- .../Database/0.0.0-dev/src/Data/Column.enso | 7 +- .../Column_Operations_Spec.enso | 309 +++++++++++++++++- 2 files changed, 302 insertions(+), 14 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 c1e43a67b5d2..8f32eed5a5ea 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 @@ -587,7 +587,12 @@ type Column even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) half_goes_up = if use_bankers then even_is_up else (self.make_binary_op "KONST" True) do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + + should_cast_to_integer = decimal_places <= 0 + result = if should_cast_to_integer.not then result_float else + result_float.cast Value_Type.Integer + result.rename new_name ## ALIAS int 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 8215bbcd99e1..5b580612f9d3 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 @@ -122,18 +122,299 @@ spec setup = y = t2.at "y" Test.group "gmt" <| - Test.specify "should allow ceil on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9, -3.5, -2.5, -0.5, 0.5, 2.5, 3.5, 1234.2345]]] - col = table.at "x" - IO.println col - #r = col.round.to_vector - r = col . round . to_vector - IO.println 'RRR' - col.round.name . should_equal "round([x])" - IO.println <| table.at "x" . round . to_vector - IO.println <| table.at "x" . round 2 . to_vector - IO.println <| table.at "x" . round -2 . to_vector - IO.println <| table.at "x" . round use_bankers=True . to_vector + do_round n dp=0 use_bankers=False = + table = table_builder [["x", [n]]] + result = table.at "x" . round dp use_bankers + result.to_vector.at 0 + + Test.specify "Can round positive decimals correctly" <| + do_round 3.0 . should_equal 3 + do_round 3.00001 . should_equal 3 + do_round 3.3 . should_equal 3 + do_round 3.49999 . should_equal 3 + do_round 3.5 . should_equal 4 + do_round 3.50001 . should_equal 4 + do_round 3.99999 . should_equal 4 + + Test.specify "Can round negative decimals correctly" <| + do_round -3.0 . should_equal -3 + do_round -3.00001 . should_equal -3 + do_round -3.3 . should_equal -3 + do_round -3.49999 . should_equal -3 + do_round -3.5 . should_equal -3 + do_round -3.50001 . should_equal -4 + do_round -3.99999 . should_equal -4 + + Test.specify "Explicit and implicit 0 decimal places work the same" <| + do_round 3.00001 0 . should_equal 3 + do_round 3.3 0 . should_equal 3 + do_round 3.00001 . should_equal 3 + do_round 3.3 . should_equal 3 + + Test.specify "Can round zero and small decimals correctly" <| + do_round 0.0 . should_equal 0 + do_round 0.00001 . should_equal 0 + do_round -0.00001 . should_equal 0 + + Test.specify "Can round decimals to a specified number of decimal places" <| + do_round 3.0001 2 . should_equal 3.0 + do_round 3.1414 2 . should_equal 3.14 + do_round 3.1415 2 . should_equal 3.14 + do_round 3.1416 2 . should_equal 3.14 + do_round 3.9999 2 . should_equal 4.0 + + do_round 3.0001 3 . should_equal 3.0 + do_round 3.1414 3 . should_equal 3.141 + do_round 3.1415 3 . should_equal 3.142 + do_round 3.1416 3 . should_equal 3.142 + do_round 3.9999 3 . should_equal 4.0 + + Test.specify "Can round positive decimals to a specified negative number of decimal places" <| + do_round 1234.0 -1 . should_equal 1230 + do_round 1234.0 -2 . should_equal 1200 + do_round 1234.0 -3 . should_equal 1000 + do_round 1234.0 -4 . should_equal 0 + + do_round 1499.0 -1 . should_equal 1500 + do_round 1499.0 -2 . should_equal 1500 + do_round 1499.0 -3 . should_equal 1000 + + do_round 1495.0 -1 . should_equal 1500 + do_round 1494.0 -1 . should_equal 1490 + do_round 1495.0 -2 . should_equal 1500 + do_round 1494.0 -2 . should_equal 1500 + + Test.specify "Can round negative decimals to a specified negative number of decimal places" <| + do_round -1234.0 -1 . should_equal -1230 + do_round -1234.0 -2 . should_equal -1200 + do_round -1234.0 -3 . should_equal -1000 + do_round -1234.0 -4 . should_equal 0 + + do_round -1499.0 -1 . should_equal -1500 + do_round -1499.0 -2 . should_equal -1500 + do_round -1499.0 -3 . should_equal -1000 + + do_round -1495.0 -1 . should_equal -1490 + do_round -1494.0 -1 . should_equal -1490 + do_round -1495.0 -2 . should_equal -1500 + do_round -1494.0 -2 . should_equal -1500 + + Test.specify "Banker's rounding handles half-way values correctly" <| + do_round -3.5 use_bankers=True . should_equal -4 + do_round -2.5 use_bankers=True . should_equal -2 + do_round -1.5 use_bankers=True . should_equal -2 + do_round -0.5 use_bankers=True . should_equal 0 + do_round 0.5 use_bankers=True . should_equal 0 + do_round 1.5 use_bankers=True . should_equal 2 + do_round 2.5 use_bankers=True . should_equal 2 + do_round 3.5 use_bankers=True . should_equal 4 + + do_round 0.235 2 use_bankers=True . should_equal 0.24 + do_round 0.225 2 use_bankers=True . should_equal 0.22 + do_round -0.235 2 use_bankers=True . should_equal -0.24 + do_round -0.225 2 use_bankers=True . should_equal -0.22 + + do_round 12350.0 -2 use_bankers=True . should_equal 12400 + do_round 12250.0 -2 use_bankers=True . should_equal 12200 + do_round -12350.0 -2 use_bankers=True . should_equal -12400 + do_round -12250.0 -2 use_bankers=True . should_equal -12200 + + Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| + do_round 3.0 use_bankers=True . should_equal 3 + do_round 3.00001 use_bankers=True . should_equal 3 + do_round 3.3 use_bankers=True . should_equal 3 + do_round 3.49999 use_bankers=True . should_equal 3 + do_round 3.50001 use_bankers=True . should_equal 4 + do_round 3.99999 use_bankers=True . should_equal 4 + + do_round -3.0 . should_equal -3 + do_round -3.00001 . should_equal -3 + do_round -3.3 . should_equal -3 + do_round -3.49999 . should_equal -3 + do_round -3.50001 . should_equal -4 + do_round -3.99999 . should_equal -4 + + Test.specify "Returns the correct type" <| + do_round 231.2 1 . should_be_a Decimal + do_round 231.2 0 . should_be_a Integer + do_round 231.2 . should_be_a Integer + do_round 231.2 -1 . should_be_a Integer + + Test.specify "Can round correctly near the precision limit" <| + do_round 1.22222222225 10 . should_equal 1.2222222223 + do_round 1.222222222225 11 . should_equal 1.22222222223 + do_round 1.2222222222225 12 . should_equal 1.222222222223 + do_round 1.22222222222225 13 . should_equal 1.2222222222223 + do_round 1.222222222222225 14 . should_equal 1.22222222222223 + do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + + do_round -1.22222222225 10 . should_equal -1.2222222222 + do_round -1.222222222225 11 . should_equal -1.22222222222 + do_round -1.2222222222225 12 . should_equal -1.222222222222 + do_round -1.22222222222225 13 . should_equal -1.2222222222222 + do_round -1.222222222222225 14 . should_equal -1.22222222222222 + do_round -1.2222222222222225 15 . should_equal -1.222222222222222 + + do_round 1.22222222235 10 . should_equal 1.2222222224 + do_round 1.222222222235 11 . should_equal 1.22222222224 + do_round 1.2222222222235 12 . should_equal 1.222222222224 + do_round 1.22222222222235 13 . should_equal 1.2222222222224 + do_round 1.222222222222235 14 . should_equal 1.22222222222224 + do_round 1.2222222222222235 15 . should_equal 1.222222222222224 + + do_round -1.22222222235 10 . should_equal -1.2222222223 + do_round -1.222222222235 11 . should_equal -1.22222222223 + do_round -1.2222222222235 12 . should_equal -1.222222222223 + do_round -1.22222222222235 13 . should_equal -1.2222222222223 + do_round -1.222222222222235 14 . should_equal -1.22222222222223 + do_round -1.2222222222222235 15 . should_equal -1.222222222222223 + + Test.specify "Can round correctly near the precision limit, using banker's rounding" <| + do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 + do_round 1.222222222225 11 use_bankers=True . should_equal 1.22222222222 + do_round 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 + do_round 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 + do_round 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 + do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 + + do_round -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 + do_round -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 + do_round -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 + do_round -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 + do_round -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 + do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 + + do_round 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 + do_round 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 + do_round 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 + do_round 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 + do_round 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 + do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 + + do_round -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 + do_round -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 + do_round -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 + do_round -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 + do_round -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 + do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 + + Test.specify "Decimal places out of range" <| + do_round 3.1 16 . should_fail_with Illegal_Argument + do_round 3.1 -16 . should_fail_with Illegal_Argument + + Test.specify "NaN/Inf" <| + table = table_builder [["x", [Number.nan, Number.positive_infinity]]] + v = table.at "x" . to_vector + IO.println 'NNNN' + IO.println v + IO.println <| v.at 0 + IO.println <| v.at 1 + do_round Number.nan . should_equal Nothing + do_round Number.positive_infinity . should_equal 9223372036854775807 + do_round Number.negative_infinity . should_equal -9223372036854775808 + + Test.specify "Floating point imperfect representation counter-examples" <| + do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 + do_round 37.785 2 . should_equal 37.79 + + Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" + do_round 0 . should_equal 0 + do_round 3 . should_equal 3 + do_round -3 . should_equal -3 + do_round 3 0 . should_equal 3 + do_round -3 0 . should_equal -3 + do_round 3 1 . should_equal 3 + do_round -3 1 . should_equal -3 + + Test.specify "Can round integers to a specified number of negative places correctly" + do_round 0 -1 . should_equal 0 + do_round 4 -1 . should_equal 0 + do_round 5 -1 . should_equal 10 + do_round 6 -1 . should_equal 10 + do_round 9 -1 . should_equal 10 + do_round 10 -1 . should_equal 10 + do_round 11 -1 . should_equal 10 + do_round 24 -1 . should_equal 20 + do_round 25 -1 . should_equal 30 + do_round 29 -1 . should_equal 30 + do_round 30 -1 . should_equal 30 + do_round 31 -1 . should_equal 30 + + do_round 2000 -3 . should_equal 2000 + do_round 2001 -3 . should_equal 2000 + do_round 2412 -3 . should_equal 2000 + do_round 2499 -3 . should_equal 2000 + do_round 2500 -3 . should_equal 3000 + do_round 2501 -3 . should_equal 3000 + do_round 2511 -3 . should_equal 3000 + do_round 2907 -3 . should_equal 3000 + do_round 2999 -3 . should_equal 3000 + do_round 3000 -3 . should_equal 3000 + do_round 3001 -3 . should_equal 3000 + do_round 3098 -3 . should_equal 3000 + do_round 3101 -3 . should_equal 3000 + + Test.specify "Can round negative integers to a specified number of negative places correctly" + do_round -4 -1 . should_equal 0 + do_round -5 -1 . should_equal 0 + do_round -6 -1 . should_equal -10 + do_round -9 -1 . should_equal -10 + do_round -10 -1 . should_equal -10 + do_round -11 -1 . should_equal -10 + do_round -24 -1 . should_equal -20 + do_round -25 -1 . should_equal -20 + do_round -29 -1 . should_equal -30 + do_round -30 -1 . should_equal -30 + do_round -31 -1 . should_equal -30 + + do_round -2000 -3 . should_equal -2000 + do_round -2001 -3 . should_equal -2000 + do_round -2412 -3 . should_equal -2000 + do_round -2499 -3 . should_equal -2000 + do_round -2500 -3 . should_equal -2000 + do_round -2501 -3 . should_equal -3000 + do_round -2511 -3 . should_equal -3000 + do_round -2907 -3 . should_equal -3000 + do_round -2999 -3 . should_equal -3000 + do_round -3000 -3 . should_equal -3000 + do_round -3001 -3 . should_equal -3000 + do_round -3098 -3 . should_equal -3000 + do_round -3101 -3 . should_equal -3000 + + Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| + do_round 12300 -2 use_bankers=True . should_equal 12300 + do_round 12301 -2 use_bankers=True . should_equal 12300 + do_round 12330 -2 use_bankers=True . should_equal 12300 + do_round 12349 -2 use_bankers=True . should_equal 12300 + do_round 12350 -2 use_bankers=True . should_equal 12400 + do_round 12351 -2 use_bankers=True . should_equal 12400 + do_round 12370 -2 use_bankers=True . should_equal 12400 + do_round 12430 -2 use_bankers=True . should_equal 12400 + do_round 12470 -2 use_bankers=True . should_equal 12500 + + do_round 12249 -2 use_bankers=True . should_equal 12200 + do_round 12250 -2 use_bankers=True . should_equal 12200 + do_round 12251 -2 use_bankers=True . should_equal 12300 + + do_round -12300 -2 use_bankers=True . should_equal -12300 + do_round -12301 -2 use_bankers=True . should_equal -12300 + do_round -12330 -2 use_bankers=True . should_equal -12300 + do_round -12349 -2 use_bankers=True . should_equal -12300 + do_round -12350 -2 use_bankers=True . should_equal -12400 + do_round -12351 -2 use_bankers=True . should_equal -12400 + do_round -12370 -2 use_bankers=True . should_equal -12400 + do_round -12430 -2 use_bankers=True . should_equal -12400 + do_round -12470 -2 use_bankers=True . should_equal -12500 + + do_round -12249 -2 use_bankers=True . should_equal -12200 + do_round -12250 -2 use_bankers=True . should_equal -12200 + do_round -12251 -2 use_bankers=True . should_equal -12300 + + Test.specify "Returns the correct type" <| + do_round 231 1 . should_be_a Integer + do_round 231 0 . should_be_a Integer + do_round 231 . should_be_a Integer + do_round 231 -1 . should_be_a Integer Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| @@ -455,9 +736,10 @@ spec setup = (x * Nothing).to_vector . should_equal nulls (x / Nothing).to_vector . should_equal nulls + Test.group prefix+"Rounding-like operations" <| Test.specify "should name a rounding column correctly" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - table.at "x" . round . name . should_equal "round([x])" + table.at "x" . round . name . should_equal "`round`([x])" Test.specify "should allow truncate on a float column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] @@ -495,6 +777,7 @@ spec setup = result.to_vector.should_equal [0, 3, -3, 1, -2] result.value_type . is_integer . should_be_true + #Test.group prefix+"Rounding numeric tests" <| Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] s1 = t3.at "s1" From e20161a0a14a46b495df28993a9729dfb4ab1541 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 12 Jun 2023 15:51:34 -0400 Subject: [PATCH 15/83] rounding tests --- .../Internal/Postgres/Postgres_Dialect.enso | 6 +- .../Column_Operations_Spec.enso | 769 +++++++++--------- .../src/Database/Postgres_Spec.enso | 11 + .../Table_Tests/src/Database/SQLite_Spec.enso | 16 +- 4 files changed, 413 insertions(+), 389 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index ee33ebc76d66..557bf2d87cfe 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -450,15 +450,15 @@ decimal_div = Base_Generator.lift_binary_op "/" x-> y-> ## PRIVATE truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - Builder.code "CAST(TRUNC(" ++ arg ++ ") AS INTEGER)" + Builder.code "CAST(TRUNC(" ++ arg ++ ") AS BIGINT)" ## PRIVATE ceil = Base_Generator.lift_unary_op "CEIL" arg-> - Builder.code "CAST(CEIL(" ++ arg ++ ") AS INTEGER)" + Builder.code "CAST(CEIL(" ++ arg ++ ") AS BIGINT)" ## PRIVATE floor = Base_Generator.lift_unary_op "FLOOR" arg-> - Builder.code "CAST(FLOOR(" ++ arg ++ ") AS INTEGER)" + Builder.code "CAST(FLOOR(" ++ arg ++ ") AS BIGINT)" ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> 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 5b580612f9d3..e5f63af97628 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 @@ -127,417 +127,135 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "Can round positive decimals correctly" <| - do_round 3.0 . should_equal 3 - do_round 3.00001 . should_equal 3 - do_round 3.3 . should_equal 3 - do_round 3.49999 . should_equal 3 - do_round 3.5 . should_equal 4 - do_round 3.50001 . should_equal 4 - do_round 3.99999 . should_equal 4 + Test.specify "asdf" <| + table = table_builder [["x", [1.22222222225]]] + result = table.at "x" . round 10 + IO.println 'AAAA' + IO.println result.to_vector + do_round 1.22222222225 10 . should_equal 1.2222222223 - Test.specify "Can round negative decimals correctly" <| - do_round -3.0 . should_equal -3 - do_round -3.00001 . should_equal -3 - do_round -3.3 . should_equal -3 - do_round -3.49999 . should_equal -3 - do_round -3.5 . should_equal -3 - do_round -3.50001 . should_equal -4 - do_round -3.99999 . should_equal -4 + Test.group prefix+"Column Operations - Equality & Null Handling" <| + Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| + (x == y).to_vector . should_equal [False, False, True, Nothing] + (x != y).to_vector . should_equal [True, True, False, Nothing] + (x == 4).to_vector . should_equal [False, True, False, Nothing] + (x == Nothing).to_vector . should_equal [Nothing, Nothing, Nothing, Nothing] - Test.specify "Explicit and implicit 0 decimal places work the same" <| - do_round 3.00001 0 . should_equal 3 - do_round 3.3 0 . should_equal 3 - do_round 3.00001 . should_equal 3 - do_round 3.3 . should_equal 3 + Test.specify "should allow to check which values are null" + x.is_nothing.to_vector . should_equal [False, False, False, True] + (x + Nothing).is_nothing.to_vector . should_equal [True, True, True, True] + x.is_present.to_vector . should_equal [True, True, True, False] + (x + Nothing).is_present.to_vector . should_equal [False, False, False, False] - Test.specify "Can round zero and small decimals correctly" <| - do_round 0.0 . should_equal 0 - do_round 0.00001 . should_equal 0 - do_round -0.00001 . should_equal 0 + Test.specify "Column equality should handle nulls correctly" pending="TODO" <| + a = [2, 3, Nothing, Nothing] + b = [2, 4, Nothing, 5] + r = [True, False, True, False] + a.zip b (==) . should_equal r - Test.specify "Can round decimals to a specified number of decimal places" <| - do_round 3.0001 2 . should_equal 3.0 - do_round 3.1414 2 . should_equal 3.14 - do_round 3.1415 2 . should_equal 3.14 - do_round 3.1416 2 . should_equal 3.14 - do_round 3.9999 2 . should_equal 4.0 + t = table_builder [["A", a], ["B", b]] + c = (t.at "A") == (t.at "B") + c.to_vector . should_equal r + c.value_type.should_equal Value_Type.Boolean - do_round 3.0001 3 . should_equal 3.0 - do_round 3.1414 3 . should_equal 3.141 - do_round 3.1415 3 . should_equal 3.142 - do_round 3.1416 3 . should_equal 3.142 - do_round 3.9999 3 . should_equal 4.0 + Test.specify "equals_ignore_case for ASCII strings" <| + x = ["a", "B", "c", "DEF"] + y = ["aa", "b", "c", "dEf"] + r = [False, True, True, True] - Test.specify "Can round positive decimals to a specified negative number of decimal places" <| - do_round 1234.0 -1 . should_equal 1230 - do_round 1234.0 -2 . should_equal 1200 - do_round 1234.0 -3 . should_equal 1000 - do_round 1234.0 -4 . should_equal 0 + x.zip y (.equals_ignore_case) . should_equal r - do_round 1499.0 -1 . should_equal 1500 - do_round 1499.0 -2 . should_equal 1500 - do_round 1499.0 -3 . should_equal 1000 + t = table_builder [["X", x], ["Y", y]] + c = (t.at "X") . equals_ignore_case (t.at "Y") + c.to_vector . should_equal r + c.value_type.should_equal Value_Type.Boolean + (t.at "X") . equals_ignore_case "Def" . to_vector . should_equal [False, False, False, True] - do_round 1495.0 -1 . should_equal 1500 - do_round 1494.0 -1 . should_equal 1490 - do_round 1495.0 -2 . should_equal 1500 - do_round 1494.0 -2 . should_equal 1500 + Test.specify "equals_ignore_case should check types" <| + t = table_builder [["X", [1, 2, 3]], ["Y", ['a', 'b', 'c']]] - Test.specify "Can round negative decimals to a specified negative number of decimal places" <| - do_round -1234.0 -1 . should_equal -1230 - do_round -1234.0 -2 . should_equal -1200 - do_round -1234.0 -3 . should_equal -1000 - do_round -1234.0 -4 . should_equal 0 + r1 = (t.at "X") . equals_ignore_case (t.at "Y") . to_vector + r1.should_fail_with Invalid_Value_Type - do_round -1499.0 -1 . should_equal -1500 - do_round -1499.0 -2 . should_equal -1500 - do_round -1499.0 -3 . should_equal -1000 + r2 = (t.at "Y") . equals_ignore_case (t.at "X") . to_vector + r2.should_fail_with Invalid_Value_Type - do_round -1495.0 -1 . should_equal -1490 - do_round -1494.0 -1 . should_equal -1490 - do_round -1495.0 -2 . should_equal -1500 - do_round -1494.0 -2 . should_equal -1500 + r3 = (t.at "Y") . equals_ignore_case 42 . to_vector + r3.should_fail_with Invalid_Value_Type - Test.specify "Banker's rounding handles half-way values correctly" <| - do_round -3.5 use_bankers=True . should_equal -4 - do_round -2.5 use_bankers=True . should_equal -2 - do_round -1.5 use_bankers=True . should_equal -2 - do_round -0.5 use_bankers=True . should_equal 0 - do_round 0.5 use_bankers=True . should_equal 0 - do_round 1.5 use_bankers=True . should_equal 2 - do_round 2.5 use_bankers=True . should_equal 2 - do_round 3.5 use_bankers=True . should_equal 4 + Test.specify "Text Column equality (including case-insensitive) should handle nulls correctly" pending="TODO" <| + a = ["Z", "a", "b", Nothing, Nothing] + b = ["Z", "A", "C", Nothing, "d"] + r_sensitive = [True, False, False, True, False] + r_insensitive = [True, True, False, True, False] - do_round 0.235 2 use_bankers=True . should_equal 0.24 - do_round 0.225 2 use_bankers=True . should_equal 0.22 - do_round -0.235 2 use_bankers=True . should_equal -0.24 - do_round -0.225 2 use_bankers=True . should_equal -0.22 + a.zip b (==) . should_equal r_sensitive + a.zip b (x-> y-> if x.is_nothing || y.is_nothing then x == y else x.equals_ignore_case y) . should_equal r_insensitive - do_round 12350.0 -2 use_bankers=True . should_equal 12400 - do_round 12250.0 -2 use_bankers=True . should_equal 12200 - do_round -12350.0 -2 use_bankers=True . should_equal -12400 - do_round -12250.0 -2 use_bankers=True . should_equal -12200 + t = table_builder [["A", a], ["B", b]] + ((t.at "A") == (t.at "B")) . to_vector . should_equal r_sensitive + ((t.at "A").equals_ignore_case (t.at "B")) . to_vector . should_equal r_insensitive - Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| - do_round 3.0 use_bankers=True . should_equal 3 - do_round 3.00001 use_bankers=True . should_equal 3 - do_round 3.3 use_bankers=True . should_equal 3 - do_round 3.49999 use_bankers=True . should_equal 3 - do_round 3.50001 use_bankers=True . should_equal 4 - do_round 3.99999 use_bankers=True . should_equal 4 + Test.specify "should allow to fill empty/nothing values" <| + t = table_builder [["X", ["a", "", " ", Nothing, "b"]]] - do_round -3.0 . should_equal -3 - do_round -3.00001 . should_equal -3 - do_round -3.3 . should_equal -3 - do_round -3.49999 . should_equal -3 - do_round -3.50001 . should_equal -4 - do_round -3.99999 . should_equal -4 + c1 = t.at "X" . fill_nothing "NA" + c1.to_vector . should_equal ["a", "", " ", "NA", "b"] - Test.specify "Returns the correct type" <| - do_round 231.2 1 . should_be_a Decimal - do_round 231.2 0 . should_be_a Integer - do_round 231.2 . should_be_a Integer - do_round 231.2 -1 . should_be_a Integer + c2 = t.at "X" . fill_empty "" + c2.to_vector . should_equal ["a", "", " ", "", "b"] - Test.specify "Can round correctly near the precision limit" <| - do_round 1.22222222225 10 . should_equal 1.2222222223 - do_round 1.222222222225 11 . should_equal 1.22222222223 - do_round 1.2222222222225 12 . should_equal 1.222222222223 - do_round 1.22222222222225 13 . should_equal 1.2222222222223 - do_round 1.222222222222225 14 . should_equal 1.22222222222223 - do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + Test.specify "should report a warning if checking equality on floating point columns" <| + t = table_builder [["X", [1.0, 2.1, 3.2]], ["Y", [1.0, 2.0, 3.2]]] - do_round -1.22222222225 10 . should_equal -1.2222222222 - do_round -1.222222222225 11 . should_equal -1.22222222222 - do_round -1.2222222222225 12 . should_equal -1.222222222222 - do_round -1.22222222222225 13 . should_equal -1.2222222222222 - do_round -1.222222222222225 14 . should_equal -1.22222222222222 - do_round -1.2222222222222225 15 . should_equal -1.222222222222222 + r1 = (t.at "X") == (t.at "Y") + r1.to_vector . should_equal [True, False, True] + Problems.expect_warning Floating_Point_Equality r1 - do_round 1.22222222235 10 . should_equal 1.2222222224 - do_round 1.222222222235 11 . should_equal 1.22222222224 - do_round 1.2222222222235 12 . should_equal 1.222222222224 - do_round 1.22222222222235 13 . should_equal 1.2222222222224 - do_round 1.222222222222235 14 . should_equal 1.22222222222224 - do_round 1.2222222222222235 15 . should_equal 1.222222222222224 + r2 = (t.at "X") != (t.at "Y") + r2.to_vector . should_equal [False, True, False] + Problems.expect_warning Floating_Point_Equality r2 - do_round -1.22222222235 10 . should_equal -1.2222222223 - do_round -1.222222222235 11 . should_equal -1.22222222223 - do_round -1.2222222222235 12 . should_equal -1.222222222223 - do_round -1.22222222222235 13 . should_equal -1.2222222222223 - do_round -1.222222222222235 14 . should_equal -1.22222222222223 - do_round -1.2222222222222235 15 . should_equal -1.222222222222223 + Test.group prefix+"Column Comparisons" <| + Test.specify "should allow to compare numbers" <| + x.value_type . is_integer . should_be_true + y.value_type . is_floating_point . should_be_true - Test.specify "Can round correctly near the precision limit, using banker's rounding" <| - do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 - do_round 1.222222222225 11 use_bankers=True . should_equal 1.22222222222 - do_round 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 - do_round 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 - do_round 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 - do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 + (x < y).to_vector . should_equal [True, False, False, Nothing] + (x <= y).to_vector . should_equal [True, False, True, Nothing] + (x > y).to_vector . should_equal (x <= y).not.to_vector + (x >= y).to_vector . should_equal (x < y).not.to_vector - do_round -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 - do_round -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 - do_round -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 - do_round -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 - do_round -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 - do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 + (x < 1000).to_vector . should_equal [True, True, True, Nothing] - do_round 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 - do_round 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 - do_round 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 - do_round 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 - do_round 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 - do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 + [(<), (<=), (>), (>=)].each op-> + op x y . value_type . should_equal Value_Type.Boolean + op x y . to_vector . should_succeed + op x 23 . to_vector . should_succeed + op y 23 . to_vector . should_succeed + op x 1.5 . to_vector . should_succeed - do_round -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 - do_round -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 - do_round -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 - do_round -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 - do_round -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 - do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 + Test.specify "should allow to compare texts" <| + t0 = table_builder [["X", ["a", "b", "c"]], ["Y", ["a", "b", "d"]]] + t = t0.cast "X" (Value_Type.Char size=1 variable_length=False) - Test.specify "Decimal places out of range" <| - do_round 3.1 16 . should_fail_with Illegal_Argument - do_round 3.1 -16 . should_fail_with Illegal_Argument + [(<), (<=), (>), (>=)].each op-> + op (t.at "X") (t.at "Y") . value_type . should_equal Value_Type.Boolean + op (t.at "X") (t.at "Y") . to_vector . should_succeed + op (t.at "X") "abc" . to_vector . should_succeed - Test.specify "NaN/Inf" <| - table = table_builder [["x", [Number.nan, Number.positive_infinity]]] - v = table.at "x" . to_vector - IO.println 'NNNN' - IO.println v - IO.println <| v.at 0 - IO.println <| v.at 1 - do_round Number.nan . should_equal Nothing - do_round Number.positive_infinity . should_equal 9223372036854775807 - do_round Number.negative_infinity . should_equal -9223372036854775808 + Test.specify "should allow to compare booleans" <| + t = table_builder [["X", [True, False, True]], ["Y", [False, True, True]]] - Test.specify "Floating point imperfect representation counter-examples" <| - do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 - do_round 37.785 2 . should_equal 37.79 + ((t.at "X") < (t.at "Y")).to_vector . should_equal [False, True, False] + ((t.at "X") >= (t.at "Y")).to_vector . should_equal [True, False, True] + ((t.at "X") <= (t.at "Y")).to_vector . should_equal [False, True, True] + ((t.at "X") > (t.at "Y")).to_vector . should_equal [True, False, False] - Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" - do_round 0 . should_equal 0 - do_round 3 . should_equal 3 - do_round -3 . should_equal -3 - do_round 3 0 . should_equal 3 - do_round -3 0 . should_equal -3 - do_round 3 1 . should_equal 3 - do_round -3 1 . should_equal -3 - - Test.specify "Can round integers to a specified number of negative places correctly" - do_round 0 -1 . should_equal 0 - do_round 4 -1 . should_equal 0 - do_round 5 -1 . should_equal 10 - do_round 6 -1 . should_equal 10 - do_round 9 -1 . should_equal 10 - do_round 10 -1 . should_equal 10 - do_round 11 -1 . should_equal 10 - do_round 24 -1 . should_equal 20 - do_round 25 -1 . should_equal 30 - do_round 29 -1 . should_equal 30 - do_round 30 -1 . should_equal 30 - do_round 31 -1 . should_equal 30 - - do_round 2000 -3 . should_equal 2000 - do_round 2001 -3 . should_equal 2000 - do_round 2412 -3 . should_equal 2000 - do_round 2499 -3 . should_equal 2000 - do_round 2500 -3 . should_equal 3000 - do_round 2501 -3 . should_equal 3000 - do_round 2511 -3 . should_equal 3000 - do_round 2907 -3 . should_equal 3000 - do_round 2999 -3 . should_equal 3000 - do_round 3000 -3 . should_equal 3000 - do_round 3001 -3 . should_equal 3000 - do_round 3098 -3 . should_equal 3000 - do_round 3101 -3 . should_equal 3000 - - Test.specify "Can round negative integers to a specified number of negative places correctly" - do_round -4 -1 . should_equal 0 - do_round -5 -1 . should_equal 0 - do_round -6 -1 . should_equal -10 - do_round -9 -1 . should_equal -10 - do_round -10 -1 . should_equal -10 - do_round -11 -1 . should_equal -10 - do_round -24 -1 . should_equal -20 - do_round -25 -1 . should_equal -20 - do_round -29 -1 . should_equal -30 - do_round -30 -1 . should_equal -30 - do_round -31 -1 . should_equal -30 - - do_round -2000 -3 . should_equal -2000 - do_round -2001 -3 . should_equal -2000 - do_round -2412 -3 . should_equal -2000 - do_round -2499 -3 . should_equal -2000 - do_round -2500 -3 . should_equal -2000 - do_round -2501 -3 . should_equal -3000 - do_round -2511 -3 . should_equal -3000 - do_round -2907 -3 . should_equal -3000 - do_round -2999 -3 . should_equal -3000 - do_round -3000 -3 . should_equal -3000 - do_round -3001 -3 . should_equal -3000 - do_round -3098 -3 . should_equal -3000 - do_round -3101 -3 . should_equal -3000 - - Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| - do_round 12300 -2 use_bankers=True . should_equal 12300 - do_round 12301 -2 use_bankers=True . should_equal 12300 - do_round 12330 -2 use_bankers=True . should_equal 12300 - do_round 12349 -2 use_bankers=True . should_equal 12300 - do_round 12350 -2 use_bankers=True . should_equal 12400 - do_round 12351 -2 use_bankers=True . should_equal 12400 - do_round 12370 -2 use_bankers=True . should_equal 12400 - do_round 12430 -2 use_bankers=True . should_equal 12400 - do_round 12470 -2 use_bankers=True . should_equal 12500 - - do_round 12249 -2 use_bankers=True . should_equal 12200 - do_round 12250 -2 use_bankers=True . should_equal 12200 - do_round 12251 -2 use_bankers=True . should_equal 12300 - - do_round -12300 -2 use_bankers=True . should_equal -12300 - do_round -12301 -2 use_bankers=True . should_equal -12300 - do_round -12330 -2 use_bankers=True . should_equal -12300 - do_round -12349 -2 use_bankers=True . should_equal -12300 - do_round -12350 -2 use_bankers=True . should_equal -12400 - do_round -12351 -2 use_bankers=True . should_equal -12400 - do_round -12370 -2 use_bankers=True . should_equal -12400 - do_round -12430 -2 use_bankers=True . should_equal -12400 - do_round -12470 -2 use_bankers=True . should_equal -12500 - - do_round -12249 -2 use_bankers=True . should_equal -12200 - do_round -12250 -2 use_bankers=True . should_equal -12200 - do_round -12251 -2 use_bankers=True . should_equal -12300 - - Test.specify "Returns the correct type" <| - do_round 231 1 . should_be_a Integer - do_round 231 0 . should_be_a Integer - do_round 231 . should_be_a Integer - do_round 231 -1 . should_be_a Integer - - Test.group prefix+"Column Operations - Equality & Null Handling" <| - Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| - (x == y).to_vector . should_equal [False, False, True, Nothing] - (x != y).to_vector . should_equal [True, True, False, Nothing] - (x == 4).to_vector . should_equal [False, True, False, Nothing] - (x == Nothing).to_vector . should_equal [Nothing, Nothing, Nothing, Nothing] - - Test.specify "should allow to check which values are null" - x.is_nothing.to_vector . should_equal [False, False, False, True] - (x + Nothing).is_nothing.to_vector . should_equal [True, True, True, True] - x.is_present.to_vector . should_equal [True, True, True, False] - (x + Nothing).is_present.to_vector . should_equal [False, False, False, False] - - Test.specify "Column equality should handle nulls correctly" pending="TODO" <| - a = [2, 3, Nothing, Nothing] - b = [2, 4, Nothing, 5] - r = [True, False, True, False] - a.zip b (==) . should_equal r - - t = table_builder [["A", a], ["B", b]] - c = (t.at "A") == (t.at "B") - c.to_vector . should_equal r - c.value_type.should_equal Value_Type.Boolean - - Test.specify "equals_ignore_case for ASCII strings" <| - x = ["a", "B", "c", "DEF"] - y = ["aa", "b", "c", "dEf"] - r = [False, True, True, True] - - x.zip y (.equals_ignore_case) . should_equal r - - t = table_builder [["X", x], ["Y", y]] - c = (t.at "X") . equals_ignore_case (t.at "Y") - c.to_vector . should_equal r - c.value_type.should_equal Value_Type.Boolean - (t.at "X") . equals_ignore_case "Def" . to_vector . should_equal [False, False, False, True] - - Test.specify "equals_ignore_case should check types" <| - t = table_builder [["X", [1, 2, 3]], ["Y", ['a', 'b', 'c']]] - - r1 = (t.at "X") . equals_ignore_case (t.at "Y") . to_vector - r1.should_fail_with Invalid_Value_Type - - r2 = (t.at "Y") . equals_ignore_case (t.at "X") . to_vector - r2.should_fail_with Invalid_Value_Type - - r3 = (t.at "Y") . equals_ignore_case 42 . to_vector - r3.should_fail_with Invalid_Value_Type - - Test.specify "Text Column equality (including case-insensitive) should handle nulls correctly" pending="TODO" <| - a = ["Z", "a", "b", Nothing, Nothing] - b = ["Z", "A", "C", Nothing, "d"] - r_sensitive = [True, False, False, True, False] - r_insensitive = [True, True, False, True, False] - - a.zip b (==) . should_equal r_sensitive - a.zip b (x-> y-> if x.is_nothing || y.is_nothing then x == y else x.equals_ignore_case y) . should_equal r_insensitive - - t = table_builder [["A", a], ["B", b]] - ((t.at "A") == (t.at "B")) . to_vector . should_equal r_sensitive - ((t.at "A").equals_ignore_case (t.at "B")) . to_vector . should_equal r_insensitive - - Test.specify "should allow to fill empty/nothing values" <| - t = table_builder [["X", ["a", "", " ", Nothing, "b"]]] - - c1 = t.at "X" . fill_nothing "NA" - c1.to_vector . should_equal ["a", "", " ", "NA", "b"] - - c2 = t.at "X" . fill_empty "" - c2.to_vector . should_equal ["a", "", " ", "", "b"] - - Test.specify "should report a warning if checking equality on floating point columns" <| - t = table_builder [["X", [1.0, 2.1, 3.2]], ["Y", [1.0, 2.0, 3.2]]] - - r1 = (t.at "X") == (t.at "Y") - r1.to_vector . should_equal [True, False, True] - Problems.expect_warning Floating_Point_Equality r1 - - r2 = (t.at "X") != (t.at "Y") - r2.to_vector . should_equal [False, True, False] - Problems.expect_warning Floating_Point_Equality r2 - - Test.group prefix+"Column Comparisons" <| - Test.specify "should allow to compare numbers" <| - x.value_type . is_integer . should_be_true - y.value_type . is_floating_point . should_be_true - - (x < y).to_vector . should_equal [True, False, False, Nothing] - (x <= y).to_vector . should_equal [True, False, True, Nothing] - (x > y).to_vector . should_equal (x <= y).not.to_vector - (x >= y).to_vector . should_equal (x < y).not.to_vector - - (x < 1000).to_vector . should_equal [True, True, True, Nothing] - - [(<), (<=), (>), (>=)].each op-> - op x y . value_type . should_equal Value_Type.Boolean - op x y . to_vector . should_succeed - op x 23 . to_vector . should_succeed - op y 23 . to_vector . should_succeed - op x 1.5 . to_vector . should_succeed - - Test.specify "should allow to compare texts" <| - t0 = table_builder [["X", ["a", "b", "c"]], ["Y", ["a", "b", "d"]]] - t = t0.cast "X" (Value_Type.Char size=1 variable_length=False) - - [(<), (<=), (>), (>=)].each op-> - op (t.at "X") (t.at "Y") . value_type . should_equal Value_Type.Boolean - op (t.at "X") (t.at "Y") . to_vector . should_succeed - op (t.at "X") "abc" . to_vector . should_succeed - - Test.specify "should allow to compare booleans" <| - t = table_builder [["X", [True, False, True]], ["Y", [False, True, True]]] - - ((t.at "X") < (t.at "Y")).to_vector . should_equal [False, True, False] - ((t.at "X") >= (t.at "Y")).to_vector . should_equal [True, False, True] - ((t.at "X") <= (t.at "Y")).to_vector . should_equal [False, True, True] - ((t.at "X") > (t.at "Y")).to_vector . should_equal [True, False, False] - - ((t.at "X") < True).to_vector . should_equal [False, True, False] - ((t.at "X") >= True).to_vector . should_equal [True, False, True] - ((t.at "X") <= True).to_vector . should_equal [True, True, True] - ((t.at "X") > True).to_vector . should_equal [False, False, False] + ((t.at "X") < True).to_vector . should_equal [False, True, False] + ((t.at "X") >= True).to_vector . should_equal [True, False, True] + ((t.at "X") <= True).to_vector . should_equal [True, True, True] + ((t.at "X") > True).to_vector . should_equal [False, False, False] Test.specify "should report error if incomparable types are compared" <| t = table_builder [["X", [1, 2]], ["Y", ["a", "b"]], ["Z", [True, False]]] @@ -739,7 +457,7 @@ spec setup = Test.group prefix+"Rounding-like operations" <| Test.specify "should name a rounding column correctly" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - table.at "x" . round . name . should_equal "`round`([x])" + table.at "x" . round . name . should_equal "round([x])" Test.specify "should allow truncate on a float column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] @@ -777,7 +495,290 @@ spec setup = result.to_vector.should_equal [0, 3, -3, 1, -2] result.value_type . is_integer . should_be_true - #Test.group prefix+"Rounding numeric tests" <| + Test.group prefix+"Rounding numeric tests" <| + do_round n dp=0 use_bankers=False = + table = table_builder [["x", [n]]] + result = table.at "x" . round dp use_bankers + result.to_vector.at 0 + + Test.specify "Can round positive decimals correctly" <| + do_round 3.0 . should_equal 3 + do_round 3.00001 . should_equal 3 + do_round 3.3 . should_equal 3 + do_round 3.49999 . should_equal 3 + do_round 3.5 . should_equal 4 + do_round 3.50001 . should_equal 4 + do_round 3.99999 . should_equal 4 + + Test.specify "Can round negative decimals correctly" <| + do_round -3.0 . should_equal -3 + do_round -3.00001 . should_equal -3 + do_round -3.3 . should_equal -3 + do_round -3.49999 . should_equal -3 + do_round -3.5 . should_equal -3 + do_round -3.50001 . should_equal -4 + do_round -3.99999 . should_equal -4 + + Test.specify "Explicit and implicit 0 decimal places work the same" <| + do_round 3.00001 0 . should_equal 3 + do_round 3.3 0 . should_equal 3 + do_round 3.00001 . should_equal 3 + do_round 3.3 . should_equal 3 + + Test.specify "Can round zero and small decimals correctly" <| + do_round 0.0 . should_equal 0 + do_round 0.00001 . should_equal 0 + do_round -0.00001 . should_equal 0 + + Test.specify "Can round decimals to a specified number of decimal places" <| + do_round 3.0001 2 . should_equal 3.0 + do_round 3.1414 2 . should_equal 3.14 + do_round 3.1415 2 . should_equal 3.14 + do_round 3.1416 2 . should_equal 3.14 + do_round 3.9999 2 . should_equal 4.0 + + do_round 3.0001 3 . should_equal 3.0 + do_round 3.1414 3 . should_equal 3.141 + do_round 3.1415 3 . should_equal 3.142 + do_round 3.1416 3 . should_equal 3.142 + do_round 3.9999 3 . should_equal 4.0 + + Test.specify "Can round positive decimals to a specified negative number of decimal places" <| + do_round 1234.0 -1 . should_equal 1230 + do_round 1234.0 -2 . should_equal 1200 + do_round 1234.0 -3 . should_equal 1000 + do_round 1234.0 -4 . should_equal 0 + + do_round 1499.0 -1 . should_equal 1500 + do_round 1499.0 -2 . should_equal 1500 + do_round 1499.0 -3 . should_equal 1000 + + do_round 1495.0 -1 . should_equal 1500 + do_round 1494.0 -1 . should_equal 1490 + do_round 1495.0 -2 . should_equal 1500 + do_round 1494.0 -2 . should_equal 1500 + + Test.specify "Can round negative decimals to a specified negative number of decimal places" <| + do_round -1234.0 -1 . should_equal -1230 + do_round -1234.0 -2 . should_equal -1200 + do_round -1234.0 -3 . should_equal -1000 + do_round -1234.0 -4 . should_equal 0 + + do_round -1499.0 -1 . should_equal -1500 + do_round -1499.0 -2 . should_equal -1500 + do_round -1499.0 -3 . should_equal -1000 + + do_round -1495.0 -1 . should_equal -1490 + do_round -1494.0 -1 . should_equal -1490 + do_round -1495.0 -2 . should_equal -1500 + do_round -1494.0 -2 . should_equal -1500 + + Test.specify "Banker's rounding handles half-way values correctly" <| + do_round -3.5 use_bankers=True . should_equal -4 + do_round -2.5 use_bankers=True . should_equal -2 + do_round -1.5 use_bankers=True . should_equal -2 + do_round -0.5 use_bankers=True . should_equal 0 + do_round 0.5 use_bankers=True . should_equal 0 + do_round 1.5 use_bankers=True . should_equal 2 + do_round 2.5 use_bankers=True . should_equal 2 + do_round 3.5 use_bankers=True . should_equal 4 + + do_round 0.235 2 use_bankers=True . should_equal 0.24 + do_round 0.225 2 use_bankers=True . should_equal 0.22 + do_round -0.235 2 use_bankers=True . should_equal -0.24 + do_round -0.225 2 use_bankers=True . should_equal -0.22 + + do_round 12350.0 -2 use_bankers=True . should_equal 12400 + do_round 12250.0 -2 use_bankers=True . should_equal 12200 + do_round -12350.0 -2 use_bankers=True . should_equal -12400 + do_round -12250.0 -2 use_bankers=True . should_equal -12200 + + Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| + do_round 3.0 use_bankers=True . should_equal 3 + do_round 3.00001 use_bankers=True . should_equal 3 + do_round 3.3 use_bankers=True . should_equal 3 + do_round 3.49999 use_bankers=True . should_equal 3 + do_round 3.50001 use_bankers=True . should_equal 4 + do_round 3.99999 use_bankers=True . should_equal 4 + + do_round -3.0 . should_equal -3 + do_round -3.00001 . should_equal -3 + do_round -3.3 . should_equal -3 + do_round -3.49999 . should_equal -3 + do_round -3.50001 . should_equal -4 + do_round -3.99999 . should_equal -4 + + Test.specify "Returns the correct type" <| + do_round 231.2 1 . should_be_a Decimal + do_round 231.2 0 . should_be_a Integer + do_round 231.2 . should_be_a Integer + do_round 231.2 -1 . should_be_a Integer + + Test.specify "Can round correctly near the precision limit" <| + do_round 1.22222222225 10 . should_equal 1.2222222223 + do_round 1.222222222225 11 . should_equal 1.22222222223 + do_round 1.2222222222225 12 . should_equal 1.222222222223 + do_round 1.22222222222225 13 . should_equal 1.2222222222223 + do_round 1.222222222222225 14 . should_equal 1.22222222222223 + do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + + do_round -1.22222222225 10 . should_equal -1.2222222222 + do_round -1.222222222225 11 . should_equal -1.22222222222 + do_round -1.2222222222225 12 . should_equal -1.222222222222 + do_round -1.22222222222225 13 . should_equal -1.2222222222222 + do_round -1.222222222222225 14 . should_equal -1.22222222222222 + do_round -1.2222222222222225 15 . should_equal -1.222222222222222 + + do_round 1.22222222235 10 . should_equal 1.2222222224 + do_round 1.222222222235 11 . should_equal 1.22222222224 + do_round 1.2222222222235 12 . should_equal 1.222222222224 + do_round 1.22222222222235 13 . should_equal 1.2222222222224 + do_round 1.222222222222235 14 . should_equal 1.22222222222224 + do_round 1.2222222222222235 15 . should_equal 1.222222222222224 + + do_round -1.22222222235 10 . should_equal -1.2222222223 + do_round -1.222222222235 11 . should_equal -1.22222222223 + do_round -1.2222222222235 12 . should_equal -1.222222222223 + do_round -1.22222222222235 13 . should_equal -1.2222222222223 + do_round -1.222222222222235 14 . should_equal -1.22222222222223 + do_round -1.2222222222222235 15 . should_equal -1.222222222222223 + + Test.specify "Can round correctly near the precision limit, using banker's rounding" <| + do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 + do_round 1.222222222225 11 use_bankers=True . should_equal 1.22222222222 + do_round 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 + do_round 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 + do_round 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 + do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 + + do_round -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 + do_round -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 + do_round -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 + do_round -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 + do_round -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 + do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 + + do_round 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 + do_round 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 + do_round 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 + do_round 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 + do_round 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 + do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 + + do_round -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 + do_round -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 + do_round -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 + do_round -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 + do_round -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 + do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 + + Test.specify "Decimal places out of range" <| + do_round 3.1 16 . should_fail_with Illegal_Argument + do_round 3.1 -16 . should_fail_with Illegal_Argument + + Test.specify "Floating point imperfect representation counter-examples" <| + do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 + do_round 37.785 2 . should_equal 37.79 + + Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" + do_round 0 . should_equal 0 + do_round 3 . should_equal 3 + do_round -3 . should_equal -3 + do_round 3 0 . should_equal 3 + do_round -3 0 . should_equal -3 + do_round 3 1 . should_equal 3 + do_round -3 1 . should_equal -3 + + Test.specify "Can round integers to a specified number of negative places correctly" + do_round 0 -1 . should_equal 0 + do_round 4 -1 . should_equal 0 + do_round 5 -1 . should_equal 10 + do_round 6 -1 . should_equal 10 + do_round 9 -1 . should_equal 10 + do_round 10 -1 . should_equal 10 + do_round 11 -1 . should_equal 10 + do_round 24 -1 . should_equal 20 + do_round 25 -1 . should_equal 30 + do_round 29 -1 . should_equal 30 + do_round 30 -1 . should_equal 30 + do_round 31 -1 . should_equal 30 + + do_round 2000 -3 . should_equal 2000 + do_round 2001 -3 . should_equal 2000 + do_round 2412 -3 . should_equal 2000 + do_round 2499 -3 . should_equal 2000 + do_round 2500 -3 . should_equal 3000 + do_round 2501 -3 . should_equal 3000 + do_round 2511 -3 . should_equal 3000 + do_round 2907 -3 . should_equal 3000 + do_round 2999 -3 . should_equal 3000 + do_round 3000 -3 . should_equal 3000 + do_round 3001 -3 . should_equal 3000 + do_round 3098 -3 . should_equal 3000 + do_round 3101 -3 . should_equal 3000 + + Test.specify "Can round negative integers to a specified number of negative places correctly" + do_round -4 -1 . should_equal 0 + do_round -5 -1 . should_equal 0 + do_round -6 -1 . should_equal -10 + do_round -9 -1 . should_equal -10 + do_round -10 -1 . should_equal -10 + do_round -11 -1 . should_equal -10 + do_round -24 -1 . should_equal -20 + do_round -25 -1 . should_equal -20 + do_round -29 -1 . should_equal -30 + do_round -30 -1 . should_equal -30 + do_round -31 -1 . should_equal -30 + + do_round -2000 -3 . should_equal -2000 + do_round -2001 -3 . should_equal -2000 + do_round -2412 -3 . should_equal -2000 + do_round -2499 -3 . should_equal -2000 + do_round -2500 -3 . should_equal -2000 + do_round -2501 -3 . should_equal -3000 + do_round -2511 -3 . should_equal -3000 + do_round -2907 -3 . should_equal -3000 + do_round -2999 -3 . should_equal -3000 + do_round -3000 -3 . should_equal -3000 + do_round -3001 -3 . should_equal -3000 + do_round -3098 -3 . should_equal -3000 + do_round -3101 -3 . should_equal -3000 + + Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| + do_round 12300 -2 use_bankers=True . should_equal 12300 + do_round 12301 -2 use_bankers=True . should_equal 12300 + do_round 12330 -2 use_bankers=True . should_equal 12300 + do_round 12349 -2 use_bankers=True . should_equal 12300 + do_round 12350 -2 use_bankers=True . should_equal 12400 + do_round 12351 -2 use_bankers=True . should_equal 12400 + do_round 12370 -2 use_bankers=True . should_equal 12400 + do_round 12430 -2 use_bankers=True . should_equal 12400 + do_round 12470 -2 use_bankers=True . should_equal 12500 + + do_round 12249 -2 use_bankers=True . should_equal 12200 + do_round 12250 -2 use_bankers=True . should_equal 12200 + do_round 12251 -2 use_bankers=True . should_equal 12300 + + do_round -12300 -2 use_bankers=True . should_equal -12300 + do_round -12301 -2 use_bankers=True . should_equal -12300 + do_round -12330 -2 use_bankers=True . should_equal -12300 + do_round -12349 -2 use_bankers=True . should_equal -12300 + do_round -12350 -2 use_bankers=True . should_equal -12400 + do_round -12351 -2 use_bankers=True . should_equal -12400 + do_round -12370 -2 use_bankers=True . should_equal -12400 + do_round -12430 -2 use_bankers=True . should_equal -12400 + do_round -12470 -2 use_bankers=True . should_equal -12500 + + do_round -12249 -2 use_bankers=True . should_equal -12200 + do_round -12250 -2 use_bankers=True . should_equal -12200 + do_round -12251 -2 use_bankers=True . should_equal -12300 + + Test.specify "Returns the correct type" <| + do_round 231 1 . should_be_a Integer + do_round 231 0 . should_be_a Integer + do_round 231 . should_be_a Integer + do_round 231 -1 . should_be_a Integer + Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] s1 = t3.at "s1" diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 7347644ba361..51820e147201 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -191,6 +191,17 @@ postgres_specific_spec connection db_name setup = Test.with_clue "d.value_type="+d.value_type.to_display_text+": " <| d.value_type.variable_length.should_be_true + Test.group "[PostgreSQL] Math" <| + table_builder = setup.table_builder + do_round n dp=0 use_bankers=False = + table = table_builder [["x", [n]]] + result = table.at "x" . round dp use_bankers + result.to_vector.at 0 + + Test.specify "NaN/Inf" <| + do_round Number.nan . should_fail_with SQL_Error + do_round Number.positive_infinity . should_fail_with SQL_Error + do_round Number.negative_infinity . should_fail_with SQL_Error run_tests connection db_name = prefix = "[PostgreSQL] " diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 0453db287c51..96b95cd848ef 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -18,7 +18,7 @@ import project.Database.Types.SQLite_Type_Mapping_Spec import project.Database.Helpers.Name_Generator import project.Common_Table_Operations -sqlite_specific_spec prefix connection = +sqlite_specific_spec prefix connection setup = Test.group prefix+"Schemas and Databases" <| Test.specify "should be able to get current database and list databases" <| connection.database . should_equal Nothing @@ -118,6 +118,18 @@ sqlite_specific_spec prefix connection = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] + Test.group prefix+"Math" <| + table_builder = setup.table_builder + do_round n dp=0 use_bankers=False = + table = table_builder [["x", [n]]] + result = table.at "x" . round dp use_bankers + result.to_vector.at 0 + + Test.specify "NaN/Inf" <| + do_round Number.nan . should_equal Nothing + do_round Number.positive_infinity . should_equal 9223372036854775807 + do_round Number.negative_infinity . should_equal -9223372036854775808 + sqlite_spec connection prefix = name_counter = Ref.new 0 table_builder columns = @@ -130,7 +142,6 @@ sqlite_spec connection prefix = materialize = .read Common_Spec.spec prefix connection - sqlite_specific_spec prefix connection common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False @@ -148,6 +159,7 @@ sqlite_spec connection prefix = empty_agg_table = (agg_in_memory_table.take (First 0)).select_into_database_table connection (Name_Generator.random_name "Agg_Empty") primary_key=Nothing temporary=True setup = Common_Table_Operations.Main.Test_Setup.Config prefix agg_table empty_agg_table table_builder materialize is_database=True test_selection=common_selection aggregate_test_selection=aggregate_selection + sqlite_specific_spec prefix connection setup Common_Table_Operations.Main.spec setup connection.close From f42285e71f1f6a8ecca577772e53f024a0df16fb Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 12 Jun 2023 15:52:27 -0400 Subject: [PATCH 16/83] round expression spec --- .../Table_Tests/src/Common_Table_Operations/Expression_Spec.enso | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso index c3480e78e637..fed3230145ec 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Expression_Spec.enso @@ -193,6 +193,7 @@ spec detailed setup = expression_test "truncate(3.3)" 3 expression_test "ceil(5.3)" 6 expression_test "floor(5.3)" 5 + expression_test "round(5.5)" 6 specify_test "should be able to do basic arithmetic with order" expression_test-> expression_test "1+1*2+2" 5 From d802271f29eff1b6d12c490281c9c7b957e3e953 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 12:31:31 -0400 Subject: [PATCH 17/83] int in int out --- .../Standard/Database/0.0.0-dev/src/Data/Column.enso | 2 +- .../Column_Operations_Spec.enso | 11 +++++------ 2 files changed, 6 insertions(+), 7 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 8f32eed5a5ea..42d136e930a1 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 @@ -589,7 +589,7 @@ type Column do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - should_cast_to_integer = decimal_places <= 0 + should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer result = if should_cast_to_integer.not then result_float else result_float.cast Value_Type.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 e5f63af97628..8268a8dc2b98 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 @@ -127,12 +127,11 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "asdf" <| - table = table_builder [["x", [1.22222222225]]] - result = table.at "x" . round 10 - IO.println 'AAAA' - IO.println result.to_vector - do_round 1.22222222225 10 . should_equal 1.2222222223 + Test.specify "Returns the correct type" <| + do_round 231 1 . should_be_a Integer + do_round 231 0 . should_be_a Integer + do_round 231 . should_be_a Integer + do_round 231 -1 . should_be_a Integer Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| From f05f9e34cf329da738bbe2a3ae04a96272b71c79 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 12:43:50 -0400 Subject: [PATCH 18/83] full col tests --- .../Column_Operations_Spec.enso | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 8268a8dc2b98..a3cb8abf5319 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 @@ -458,6 +458,18 @@ spec setup = table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] table.at "x" . round . name . should_equal "round([x])" + Test.specify "should allow round on a float column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . round + result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] + result.value_type . is_integer . should_be_true + + Test.specify "should allow round on an int column" <| + table = table_builder [["x", [1, 9, 31, 39, -1, -9, -31, -39]]] + result = table.at "x" . round -1 + result.to_vector.should_equal [0, 10, 30, 40, 0, -10, -30, -40] + result.value_type . is_integer . should_be_true + Test.specify "should allow truncate on a float column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . truncate From ccda6aa7ef2f02110341948007ab7bb318c1cc86 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 12:55:04 -0400 Subject: [PATCH 19/83] round to float --- .../Column_Operations_Spec.enso | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 a3cb8abf5319..e234dc5f9a0c 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 @@ -127,11 +127,11 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "Returns the correct type" <| - do_round 231 1 . should_be_a Integer - do_round 231 0 . should_be_a Integer - do_round 231 . should_be_a Integer - do_round 231 -1 . should_be_a Integer + Test.specify "should allow round on a float column (to >0 decimal places)" <| + table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . round 1 + result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| @@ -458,6 +458,12 @@ spec setup = table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] table.at "x" . round . name . should_equal "round([x])" + Test.specify "should allow round on a float column (to >0 decimal places)" <| + table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . round 1 + result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false + Test.specify "should allow round on a float column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . round From 4ff6fa788181ac65421c5ab04f0697d0b5d5fbc5 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 12:58:24 -0400 Subject: [PATCH 20/83] dp out of range --- .../Column_Operations_Spec.enso | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 e234dc5f9a0c..e81af53b1cf0 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 @@ -127,11 +127,9 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "should allow round on a float column (to >0 decimal places)" <| - table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] - result = table.at "x" . round 1 - result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false + Test.specify "should fail on decimal_places out of range" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + table.at "x" . round 16 . should_fail_with Illegal_Argument Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| @@ -512,6 +510,10 @@ spec setup = result.to_vector.should_equal [0, 3, -3, 1, -2] result.value_type . is_integer . should_be_true + Test.specify "should fail on decimal_places out of range" <| + table = table_builder [["x", [0, 3, -3, 1, -2]]] + table.at "x" . round 16 . should_fail_with Illegal_Argument + Test.group prefix+"Rounding numeric tests" <| do_round n dp=0 use_bankers=False = table = table_builder [["x", [n]]] From 5b41821959fd692a57f5e515e42fef7f8bcb67e0 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 13:36:09 -0400 Subject: [PATCH 21/83] const tests --- .../Database/0.0.0-dev/src/Data/Column.enso | 26 ++++++++++++++++++- .../Column_Operations_Spec.enso | 23 ++++++++-------- 2 files changed, 36 insertions(+), 13 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 42d136e930a1..0567bf52b4c7 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 @@ -551,6 +551,30 @@ type Column op_result = self.make_op "IIF" [when_true, when_false] new_name adapt_unified_column op_result common_type + ## Creates a column from a single constant value. The value must be numeric, + text, or boolean. + + This method is not static; it must be called on an existing column. That + existing column is only used for its internal state; the contents of the + column are not used. + + Arguments: + - value: the constant value to use for the entire column. Must be + numeric, text, or boolean. + + > Example + Create a column of the value 42 + + column.konst 42 + konst : Any -> Column + konst self value = + case value.is_a Number || value.is_a Boolean || value.is_a Text of + True -> + self.make_binary_op "KONST" value + False -> + msg = "Cannot create a constant column of " + value.to_text + Error.throw (Illegal_Argument.Error msg) + ## Round to a specified number of decimal places. By default, rounding uses "symmetric round-half-up", also known as @@ -585,7 +609,7 @@ type Column round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else (self.make_binary_op "KONST" True) + half_goes_up = if use_bankers then even_is_up else (self.konst True) do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) 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 e81af53b1cf0..50c106a43cf0 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 @@ -127,10 +127,6 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "should fail on decimal_places out of range" <| - table = table_builder [["x", [0, 3, -3, 1, -2]]] - table.at "x" . round 16 . should_fail_with Illegal_Argument - Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| (x == y).to_vector . should_equal [False, False, True, Nothing] @@ -1107,11 +1103,14 @@ spec setup = t.at "a" . zip (t.at "b") [_, _] . name . should_equal "[a] x [b]" Test.group prefix+"Column.rename" <| - Test.specify "should not allow illegal names" <| - t = table_builder [["a", [1, 2, 3]]] - c = t.at "a" - - c.rename Nothing . should_fail_with Illegal_Argument - c.rename '' . should_fail_with Illegal_Argument - c.rename 'a\0b' . should_fail_with Illegal_Argument - c.rename '\0' . should_fail_with Illegal_Argument + Test.specify "Should allow the creation of constant columns" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . konst True . to_vector . should_equal [True, True, True] + t.at "x" . konst 12 . to_vector . should_equal [12, 12, 12] + t.at "x" . konst 12.3 . to_vector . should_equal [12.3, 12.3, 12.3] + t.at "x" . konst "asdf" . to_vector . should_equal ["asdf", "asdf", "asdf"] + + Test.specify "Should not allow the creation of constant columns of an invalid type" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . konst (Date.new 1997) . should_fail_with Illegal_Argument + From 93f4403745db4820273ee3e16aa249fef5927675 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 13:57:27 -0400 Subject: [PATCH 22/83] rename to const, name test --- .../Database/0.0.0-dev/src/Data/Column.enso | 11 +++++----- .../src/Internal/Base_Generator.enso | 17 +++++++--------- .../Internal/SQLite/SQLite_Type_Mapping.enso | 20 +++++++------------ .../Column_Operations_Spec.enso | 18 ++++++++++++----- 4 files changed, 33 insertions(+), 33 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 0567bf52b4c7..584cce523218 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 @@ -565,12 +565,13 @@ type Column > Example Create a column of the value 42 - column.konst 42 - konst : Any -> Column - konst self value = + column.const 42 + const : Any -> Column + const self value = case value.is_a Number || value.is_a Boolean || value.is_a Text of True -> - self.make_binary_op "KONST" value + new_name = "Constant " + value.to_text + self.make_binary_op "CONST" value new_name False -> msg = "Cannot create a constant column of " + value.to_text Error.throw (Illegal_Argument.Error msg) @@ -609,7 +610,7 @@ type Column round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else (self.konst True) + half_goes_up = if use_bankers then even_is_up else (self.const True) do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 448fa5fb98b5..488f4ebe606b 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -136,13 +136,10 @@ make_function name = arguments -> (Builder.code name) ++ (Builder.join ", " arguments . paren) -make_konst : Vector Builder -> Builder -make_konst = +make_const : Vector Builder -> Builder +make_const = arguments -> - #IO.println "make_konst" - #IO.println arguments - #IO.println <| arguments.at 1 - #(Builder.interpolation (arguments.at 1)) # . paren + # Ignores the contents of the `self` argument. arguments.at 1 . paren ## PRIVATE @@ -161,9 +158,9 @@ make_binary_function_flipped name arguments = case arguments.length of A helper function to create an operation that takes no arguments. Arguments: - - sql_code: The SQL code for the constant. -make_constant : Text -> (Vector Builder -> Builder) -make_constant sql_code = + - sql_code: The SQL code for the literal. +make_literal : Text -> (Vector Builder -> Builder) +make_literal sql_code = arguments -> if arguments.not_empty then Error.throw <| Illegal_State.Error "No arguments were expected" else Builder.code sql_code @@ -201,7 +198,7 @@ base_dialect = compare = [eq, neq, bin "<", bin ">", bin "<=", bin ">=", ["BETWEEN", make_between]] functions = [["COALESCE", make_function "COALESCE"], ["ROW_MIN", make_function "MIN"], ["ROW_MAX", make_function "MAX"]] agg = [fun "MAX", fun "MIN", fun "AVG", fun "SUM"] - counts = [fun "COUNT", ["COUNT_ROWS", make_constant "COUNT(*)"], ["KONST", make_konst]] + counts = [fun "COUNT", ["COUNT_ROWS", make_literal "COUNT(*)"], ["CONST", make_const]] text = [is_empty, bin "LIKE", simple_equals_ignore_case, fold_case, make_case_sensitive] nulls = [["IS_NULL", make_right_unary_op "IS NULL"], ["FILL_NULL", make_function "COALESCE"]] contains = [["IS_IN", make_is_in], ["IS_IN_COLUMN", make_is_in_column]] 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 de240a389d6e..d471c2b83644 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 @@ -159,19 +159,13 @@ 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) - handle_konst arguments = - #IO.println 'handle_konst' - #IO.println arguments + handle_const arguments = v = arguments.at 1 - q = - if v.is_a Decimal then SQLite_Types.real else - if v.is_a Integer then SQLite_Types.integer else - if v.is_a Text then SQLite_Types.text else - if v.is_a Boolean then SQLite_Types.boolean else - Error.throw <| Illegal_Argument.Error <| "An unsupported SQL constant type: " + v.to_text - #IO.println "qqq" - #IO.println q - q + if v.is_a Decimal then SQLite_Types.real else + if v.is_a Integer then SQLite_Types.integer else + if v.is_a Text then SQLite_Types.text else + if v.is_a Boolean then SQLite_Types.boolean else + Error.throw <| Illegal_Argument.Error <| "An unsupported SQL constant type: " + v.to_text handle_case arguments = fallback = arguments.last @@ -193,7 +187,7 @@ operations_map = always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE", "CEIL", "FLOOR"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM", "POW_FLIP"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] - others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["KONST", handle_konst]] + others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["CONST", handle_const]] Map.from_vector <| v1 = always_boolean_ops.map [_, const SQLite_Types.boolean] v2 = always_floating_ops.map [_, const SQLite_Types.real] 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 50c106a43cf0..fc87e8ccb3b5 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 @@ -127,6 +127,10 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 + Test.specify "Should create the correct column name" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const 12 . name . should_equal "Constant 12" + Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| (x == y).to_vector . should_equal [False, False, True, Nothing] @@ -1105,12 +1109,16 @@ spec setup = Test.group prefix+"Column.rename" <| Test.specify "Should allow the creation of constant columns" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . konst True . to_vector . should_equal [True, True, True] - t.at "x" . konst 12 . to_vector . should_equal [12, 12, 12] - t.at "x" . konst 12.3 . to_vector . should_equal [12.3, 12.3, 12.3] - t.at "x" . konst "asdf" . to_vector . should_equal ["asdf", "asdf", "asdf"] + t.at "x" . const True . to_vector . should_equal [True, True, True] + t.at "x" . const 12 . to_vector . should_equal [12, 12, 12] + t.at "x" . const 12.3 . to_vector . should_equal [12.3, 12.3, 12.3] + t.at "x" . const "asdf" . to_vector . should_equal ["asdf", "asdf", "asdf"] + + Test.specify "Should create the correct column name" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const 12 . name . should_equal "Constant 12" Test.specify "Should not allow the creation of constant columns of an invalid type" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . konst (Date.new 1997) . should_fail_with Illegal_Argument + t.at "x" . const (Date.new 1997) . should_fail_with Illegal_Argument From c60d3542e8de3296d2615039f80afaa928b392e9 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 14:00:02 -0400 Subject: [PATCH 23/83] nulls --- .../Column_Operations_Spec.enso | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 fc87e8ccb3b5..91666ce5bf54 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 @@ -127,9 +127,11 @@ spec setup = result = table.at "x" . round dp use_bankers result.to_vector.at 0 - Test.specify "Should create the correct column name" <| - t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const 12 . name . should_equal "Constant 12" + Test.specify "should allow round on a float column (to >0 decimal places)" <| + table = table_builder [["x", [Nothing, 0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . round 1 + result.to_vector.should_equal [Nothing, 0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| @@ -514,6 +516,12 @@ spec setup = table = table_builder [["x", [0, 3, -3, 1, -2]]] table.at "x" . round 16 . should_fail_with Illegal_Argument + Test.specify "should allow Nothing/NULL" <| + table = table_builder [["x", [Nothing, 0.51, 0.59, 3.51, Nothing, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . round 1 + result.to_vector.should_equal [Nothing, 0.5, 0.6, 3.5, Nothing, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false + Test.group prefix+"Rounding numeric tests" <| do_round n dp=0 use_bankers=False = table = table_builder [["x", [n]]] From a25e3d57f24367c47938ff4c1920da4f6e5e8e99 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 15:09:07 -0400 Subject: [PATCH 24/83] remove pow_flip --- .../Database/0.0.0-dev/src/Data/Column.enso | 19 +++++++++++-------- .../src/Internal/Base_Generator.enso | 13 +------------ .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- 3 files changed, 13 insertions(+), 21 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 584cce523218..5ffde1d707eb 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 @@ -600,9 +600,15 @@ type Column Rounding to `n` digits can be thought of as "rounding to the nearest multiple of 10^(-n)". For negative decimal counts, this results in rounding to the nearest positive integer power of 10. + + > Example + Round a column to two decimal places. + + import Standard.Examples + + example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - _ = [use_bankers] check_decimal_places decimal_places <| Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "round" [self] scale = 10 ^ decimal_places @@ -623,9 +629,8 @@ type Column ## ALIAS int UNSTABLE - If the column is numeric, truncate the floating-point values to an - integer by dropping the fractional part. This is equivalent to - "round-toward-zero". + Truncate the floating-point values to an integer by dropping the + fractional part. This is equivalent to "round-toward-zero". > Example Truncate a column of floating-point values. @@ -641,8 +646,7 @@ type Column ## UNSTABLE - If the column is numeric, takes the ceiling of floating-point values, - returning integer values. + Takes the ceiling of floating-point values, returning integer values. > Example Take the ceiling of a column of floating-point values. @@ -658,8 +662,7 @@ type Column ## UNSTABLE - If the column is numeric, takes the floor of floating-point values, - returning integer values. + Takes the floor of floating-point values, returning integer values. > Example Take the floor of a column of floating-point values. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 488f4ebe606b..815a11d28eff 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -142,17 +142,6 @@ make_const = # Ignores the contents of the `self` argument. arguments.at 1 . paren -## PRIVATE - Create a binary function that swaps its two arguments. - This is used (for example) to implement 10^n as POW_FLIP(n, 10), which is - necessary because 10 is not a Column. -make_binary_function_flipped : Text -> Vector Builder -> Builder -make_binary_function_flipped name arguments = case arguments.length of - 2 -> - make_function name [arguments.at 1, arguments.at 0] - _ -> - Error.throw <| Illegal_State.Error ("Invalid amount of arguments for binary operation " + name) - ## PRIVATE A helper function to create an operation that takes no arguments. @@ -191,7 +180,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["POW_FLIP", make_binary_function_flipped "POW"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals 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 d471c2b83644..d4b181322174 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 @@ -185,7 +185,7 @@ operations_map = 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", "TRUNCATE", "CEIL", "FLOOR"] - arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM", "POW_FLIP"] + arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["CONST", handle_const]] Map.from_vector <| From 52832555d25c6f4c794a6d5796f4a482327230c8 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 13 Jun 2023 15:31:03 -0400 Subject: [PATCH 25/83] make trunc, ceil, floor generic --- .../0.0.0-dev/src/Internal/Base_Generator.enso | 14 +++++++++++++- .../src/Internal/Postgres/Postgres_Dialect.enso | 14 +------------- .../src/Internal/SQLite/SQLite_Dialect.enso | 14 +------------- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 815a11d28eff..4d9cbe3b0a4b 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -180,7 +180,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], make_truncate, make_ceil, make_floor] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals @@ -353,6 +353,18 @@ make_equals a b = make_not_equals a b = a.paren ++ " != " ++ b.paren +## PRIVATE +make_truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> + Builder.code "CAST(TRUNC(" ++ arg ++ ") AS BIGINT)" + +## PRIVATE +make_ceil = Base_Generator.lift_unary_op "CEIL" arg-> + Builder.code "CAST(CEIL(" ++ arg ++ ") AS BIGINT)" + +## PRIVATE +make_floor = Base_Generator.lift_unary_op "FLOOR" arg-> + Builder.code "CAST(FLOOR(" ++ arg ++ ") AS BIGINT)" + ## PRIVATE Builds code for an ordering. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index 557bf2d87cfe..b7286b9d910f 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -216,7 +216,7 @@ make_internal_generator_dialect = cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]] text = [starts_with, contains, ends_with, agg_shortest, agg_longest, make_case_sensitive]+concat_ops+cases+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] - arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"], truncate, ceil, floor] + arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] bool = [bool_or] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] @@ -448,18 +448,6 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> decimal_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)" -## PRIVATE -truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - Builder.code "CAST(TRUNC(" ++ arg ++ ") AS BIGINT)" - -## PRIVATE -ceil = Base_Generator.lift_unary_op "CEIL" arg-> - Builder.code "CAST(CEIL(" ++ arg ++ ") AS BIGINT)" - -## PRIVATE -floor = Base_Generator.lift_unary_op "FLOOR" arg-> - Builder.code "CAST(FLOOR(" ++ arg ++ ") AS BIGINT)" - ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 1219d7709611..6ac22bbd2b07 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [decimal_div, mod_op, truncate, ceil, floor] + arith_extensions = [decimal_div, mod_op] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -361,18 +361,6 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> decimal_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)" -## PRIVATE -truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - Builder.code "TRUNC(" ++ arg ++ ")" - -## PRIVATE -ceil = Base_Generator.lift_unary_op "CEIL" arg-> - Builder.code "CEIL(" ++ arg ++ ")" - -## PRIVATE -floor = Base_Generator.lift_unary_op "FLOOR" arg-> - Builder.code "FLOOR(" ++ arg ++ ")" - ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)) * " ++ y From 4aed89c243aa2b6ee190831158286a0faebb4343 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 14 Jun 2023 14:18:53 -0400 Subject: [PATCH 26/83] do not restrict const arg --- .../Database/0.0.0-dev/src/Data/Column.enso | 15 ++++----------- 1 file changed, 4 insertions(+), 11 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 5ffde1d707eb..0793e6bcd220 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 @@ -551,16 +551,14 @@ type Column op_result = self.make_op "IIF" [when_true, when_false] new_name adapt_unified_column op_result common_type - ## Creates a column from a single constant value. The value must be numeric, - text, or boolean. + ## Creates a column from a single constant value. This method is not static; it must be called on an existing column. That existing column is only used for its internal state; the contents of the column are not used. Arguments: - - value: the constant value to use for the entire column. Must be - numeric, text, or boolean. + - value: the constant value to use for the entire column. > Example Create a column of the value 42 @@ -568,13 +566,8 @@ type Column column.const 42 const : Any -> Column const self value = - case value.is_a Number || value.is_a Boolean || value.is_a Text of - True -> - new_name = "Constant " + value.to_text - self.make_binary_op "CONST" value new_name - False -> - msg = "Cannot create a constant column of " + value.to_text - Error.throw (Illegal_Argument.Error msg) + new_name = "Constant " + value.to_text + self.make_binary_op "CONST" value new_name ## Round to a specified number of decimal places. From fbecfd8df90de29f649134fb42c7a953f2eff2b4 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 14 Jun 2023 15:29:38 -0400 Subject: [PATCH 27/83] review --- .../Database/0.0.0-dev/src/Data/Column.enso | 41 ++++++++---- .../src/Internal/Base_Generator.enso | 9 +-- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- round.pg | 63 ------------------- round.sl | 32 ---------- .../Column_Operations_Spec.enso | 12 ---- 6 files changed, 35 insertions(+), 124 deletions(-) delete mode 100644 round.pg delete mode 100644 round.sl 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 0793e6bcd220..a1c7963bacaf 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 @@ -122,25 +122,31 @@ type Column - operands: A vector of additional operation arguments (the column itself is always passed as the first argument). - new_name: The name of the resulting column. - make_op : Text -> Vector Text -> (Text | Nothing) -> Column - make_op self op_kind operands new_name = + - include_self: If True, then self is used as the first argument; + otherwise, it is omitted. + make_op : Text -> Vector Text -> (Text | Nothing) -> Boolean -> Column + make_op self op_kind operands new_name include_self=True = 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 + ## self and other_column do not have to be compatible if self is not going to be used as an argument to the op. + if include_self == False || 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 + self_maybe = if include_self then [self] else [] + self_expression_maybe = if include_self then [self.expression] else [] + expressions = operands.map prepare_operand - new_expr = SQL_Expression.Operation op_kind ([self.expression] + expressions) + new_expr = SQL_Expression.Operation op_kind (self_expression_maybe + 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 + new_type_ref = type_mapping.infer_return_type infer_from_database_callback op_kind self_maybe+operands new_expr Column.Value new_name self.connection new_type_ref new_expr self.context ## PRIVATE @@ -151,11 +157,13 @@ type Column - op_kind: The kind of binary operator. - operand: The right operand to the binary operator. - new_name: The name of the resulting column. - make_binary_op : Text -> Text -> (Text | Nothing) -> Column - make_binary_op self op_kind operand new_name=Nothing = + - include_self: If True, then self is used as the first argument; + otherwise, it is omitted. + make_binary_op : Text -> Text -> (Text | Nothing) -> Boolean -> Column + make_binary_op self op_kind operand new_name=Nothing include_self=True = effective_new_name = new_name.if_nothing <| self.naming_helpers.binary_operation_name op_kind self operand - self.make_op op_kind [operand] effective_new_name + self.make_op op_kind [operand] effective_new_name include_self ## PRIVATE @@ -551,14 +559,18 @@ type Column op_result = self.make_op "IIF" [when_true, when_false] new_name adapt_unified_column op_result common_type - ## Creates a column from a single constant value. + ## PRIVATE + + Creates a column from a single constant value. The value must be numeric, + text, or boolean. This method is not static; it must be called on an existing column. That existing column is only used for its internal state; the contents of the column are not used. Arguments: - - value: the constant value to use for the entire column. + - value: the constant value to use for the entire column. Must be + numeric, text, or boolean. > Example Create a column of the value 42 @@ -566,8 +578,13 @@ type Column column.const 42 const : Any -> Column const self value = - new_name = "Constant " + value.to_text - self.make_binary_op "CONST" value new_name + case value.is_a Number || value.is_a Boolean || value.is_a Text of + True -> + new_name = "Constant " + value.to_text + self.make_binary_op "CONST" value new_name include_self=False + False -> + msg = "Cannot create a constant column of " + value.to_text + Error.throw (Illegal_Argument.Error msg) ## Round to a specified number of decimal places. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index aa19a0434f5f..4227cd2f1f7f 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -137,11 +137,11 @@ make_function name = arguments -> (Builder.code name) ++ (Builder.join ", " arguments . paren) +## PRIVATE make_const : Vector Builder -> Builder make_const = arguments -> - # Ignores the contents of the `self` argument. - arguments.at 1 . paren + arguments.at 0 . paren ## PRIVATE @@ -188,12 +188,13 @@ base_dialect = compare = [eq, neq, bin "<", bin ">", bin "<=", bin ">=", ["BETWEEN", make_between]] functions = [["COALESCE", make_function "COALESCE"], ["ROW_MIN", make_function "MIN"], ["ROW_MAX", make_function "MAX"]] agg = [fun "MAX", fun "MIN", fun "AVG", fun "SUM"] - counts = [fun "COUNT", ["COUNT_ROWS", make_literal "COUNT(*)"], ["CONST", make_const]] + counts = [fun "COUNT", ["COUNT_ROWS", make_literal "COUNT(*)"]] + constants = [["CONST", make_const]] text = [is_empty, bin "LIKE", simple_equals_ignore_case, fold_case, make_case_sensitive] nulls = [["IS_NULL", make_right_unary_op "IS NULL"], ["FILL_NULL", make_function "COALESCE"]] contains = [["IS_IN", make_is_in], ["IS_IN_COLUMN", make_is_in_column]] types = [simple_cast] - base_map = Map.from_vector (arith + logic + compare + functions + agg + counts + text + nulls + contains + types) + base_map = Map.from_vector (arith + logic + compare + functions + agg + counts + constants + text + nulls + contains + types) Internal_Dialect.Value base_map wrap_in_quotes ## PRIVATE 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 90af41fc7c8d..e0d003c08a0c 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 @@ -161,7 +161,7 @@ operations_map = find_a_common_type (arguments.drop 1) handle_const arguments = - v = arguments.at 1 + v = arguments.at 0 if v.is_a Decimal then SQLite_Types.real else if v.is_a Integer then SQLite_Types.integer else if v.is_a Text then SQLite_Types.text else diff --git a/round.pg b/round.pg deleted file mode 100644 index c1ef4a88810e..000000000000 --- a/round.pg +++ /dev/null @@ -1,63 +0,0 @@ -with args as (select - 123.235 as n, - 2 as dp, - false as use_bankers) select * from ( -with v0 as (select - power(10, args.dp) as scale from args) select * from ( -with v1 as (select - args.n * v0.scale as scaled from args, v0) select * from ( -with v2 as ( - select floor(v1.scaled) as round_base from v1) select * from ( -with v3 as (select - (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) select * from ( -with v4 as (select - case when args.n >= 0 - then (((cast(trunc(v1.scaled) as integer)) % 2) != 0) - else (((cast(trunc(v1.scaled) as integer)) % 2) = 0) end as even_is_up from args, v1) select * from ( -with v5 as (select - case when args.use_bankers then v4.even_is_up else true end as half_goes_up from args, v4) select * from ( -with v6 as (select - case when v5.half_goes_up then args.n >= v3.round_midpoint else n > v3.round_midpoint end as do_round_up from args, v3, v5) select * from ( -select *, case when v6.do_round_up then (v2.round_base + 1.0) / v0.scale else v2.round_base / v0.scale end as result -from args, v0, v1, v2, v3, v4, v5, v6 -) q7 -) q6 -) q5 -) q4 -) q3 -) q2 -) q1 -) q0 - -; -\q - -with n as (select 123.235 as n) select * from ( -with dp as (select 2 as dp) select * from ( -with use_bankers as (select false as use_bankers) select * from ( -with scale as (select power(10, (select * from dp)) as scale) select * from ( -with scaled as (select (select * from n) * (select * from scale) as scaled) select * from ( -with round_base as (select floor((select * from scaled)) as round_base) select * from ( -with round_midpoint as (select (((select * from round_base) + 0.5) / (select * from scale)) as round_midpoint) select * from ( -with even_is_up as (select case when (select * from n) >= 0 then (select ((cast(trunc((select * from scaled)) as integer)) % 2) != 0) else (select ((cast(trunc((select * from scaled)) as integer)) % 2) = 0) end as even_is_up) select * from ( -with half_goes_up as (select case when (select * from use_bankers) then (select * from even_is_up) else true end as half_goes_up) select * from ( -with do_round_up as (select case when (select * from half_goes_up) then (select (select * from n) >= (select * from round_midpoint)) else (select (select * from n) > (select * from round_midpoint)) end as do_round_up) select * from ( -with result as (select case when (select * from do_round_up) then (select ((select * from round_base) + 1.0) / (select * from scale)) else (select ((select * from round_base)) / (select * from scale)) end as result) select * from ( - -select n.*, dp.*, use_bankers.*, scale.*, scaled.*, round_base.*, round_midpoint.*, even_is_up.*, half_goes_up.*, do_round_up.*, result.* - -from n, dp, use_bankers, scale, scaled, round_base, round_midpoint, even_is_up, half_goes_up, do_round_up, result - -) q0 -) q1 -) q2 -) q3 -) q4 -) q5 -) q6 -) q7 -) q8 -) q9 -) q10 - -; diff --git a/round.sl b/round.sl deleted file mode 100644 index 407180d92957..000000000000 --- a/round.sl +++ /dev/null @@ -1,32 +0,0 @@ -with args as (select - 123.235 as n, - 2 as dp, - false as use_bankers) select * from ( -with v0 as (select - power(10, args.dp) as scale from args) select * from ( -with v1 as (select - args.n * v0.scale as scaled from args, v0) select * from ( -with v2 as ( - select floor(v1.scaled) as round_base from v1) select * from ( -with v3 as (select - (v2.round_base + 0.5) / v0.scale as round_midpoint from v2, v0) select * from ( -with v4 as (select - case when args.n >= 0 - then (((cast(trunc(v1.scaled) as integer)) % 2) != 0) - else (((cast(trunc(v1.scaled) as integer)) % 2) = 0) end as even_is_up from args, v1) select * from ( -with v5 as (select - case when args.use_bankers then v4.even_is_up else true end as half_goes_up from args, v4) select * from ( -with v6 as (select - case when v5.half_goes_up then args.n >= v3.round_midpoint else n > v3.round_midpoint end as do_round_up from args, v3, v5) select * from ( -select *, case when v6.do_round_up then (v2.round_base + 1.0) / v0.scale else v2.round_base / v0.scale end as result -from args, v0, v1, v2, v3, v4, v5, v6 -) q7 -) q6 -) q5 -) q4 -) q3 -) q2 -) q1 -) q0 - -; 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 91666ce5bf54..fa8f9d1a2344 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 @@ -121,18 +121,6 @@ spec setup = x = t2.at "x" y = t2.at "y" - Test.group "gmt" <| - do_round n dp=0 use_bankers=False = - table = table_builder [["x", [n]]] - result = table.at "x" . round dp use_bankers - result.to_vector.at 0 - - Test.specify "should allow round on a float column (to >0 decimal places)" <| - table = table_builder [["x", [Nothing, 0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] - result = table.at "x" . round 1 - result.to_vector.should_equal [Nothing, 0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false - Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| (x == y).to_vector . should_equal [False, False, True, Nothing] From 593982b4e3a43afad1b7209d6032f3d7b1c5c8b1 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 14 Jun 2023 15:35:07 -0400 Subject: [PATCH 28/83] nan/inf docs --- .../Standard/Database/0.0.0-dev/src/Data/Column.enso | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 a1c7963bacaf..574c74ef36c2 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 @@ -601,6 +601,17 @@ type Column Must be between -15 and 15 (inclusive). - use_bankers: Rounds mid-point to nearest even number. + ! NaN/Inf + `round` behaves differently on different database engines. + - Postgres: + - NaN: Error + - +Inf: Error + - -Inf: Error + - SQLite: + - NaN: Returns Nothing + - +Inf: Returns Java `Long.MAX_VALUE` + - -Inf: Returns Java `Long.MIN_VALUE` + ! Error Conditions If `decimal_places` is outside the range -15..15 (inclusive), an From 3ff04b517fe976c2e8c579c21e4c5dd6b54342d4 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 14 Jun 2023 16:05:38 -0400 Subject: [PATCH 29/83] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0ff80e2f9a..592c1344055e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -485,6 +485,8 @@ - [Speed improvements to `Column` `.truncate`, `.ceil`, and `.floor`.][6941] - [Implemented addition and subtraction for `Date_Period` and `Time_Period`.][6956] +- [Added `round`, `ceil`, `floor`, `truncate` to the In-Database Column type] + [6988] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -704,6 +706,7 @@ [6925]: https://github.com/enso-org/enso/pull/6925 [6941]: https://github.com/enso-org/enso/pull/6941 [6956]: https://github.com/enso-org/enso/pull/6956 +[6988]: https://github.com/enso-org/enso/pull/6988 #### Enso Compiler From 05fc15e1ec69c7cdbefbfff99d489389539d2ece Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 14 Jun 2023 16:43:58 -0400 Subject: [PATCH 30/83] decimal too --- .../Column_Operations_Spec.enso | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) 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 fa8f9d1a2344..47df1c71b4fd 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 @@ -17,6 +17,7 @@ main = run_default_backend spec spec setup = prefix = setup.prefix table_builder = setup.table_builder + Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| t = table_builder [["X", [True, False, Nothing, True]]] @@ -446,17 +447,39 @@ spec setup = table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] table.at "x" . round . name . should_equal "round([x])" - Test.specify "should allow round on a float column (to >0 decimal places)" <| - table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] - result = table.at "x" . round 1 - result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false - - Test.specify "should allow round on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . round - result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] - result.value_type . is_integer . should_be_true + test_floatlike type = + Test.specify "should allow round on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . round + result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] + result.value_type . is_integer . should_be_true + + Test.specify "should allow round on a float column (to >0 decimal places)" <| + table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . cast type . round 1 + result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false + + Test.specify "should allow truncate on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . truncate + result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow ceil on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . ceil + result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow floor on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . floor + result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] + result.value_type . is_integer . should_be_true + + test_floatlike Value_Type.Float + test_floatlike Value_Type.Decimal Test.specify "should allow round on an int column" <| table = table_builder [["x", [1, 9, 31, 39, -1, -9, -31, -39]]] @@ -464,36 +487,18 @@ spec setup = result.to_vector.should_equal [0, 10, 30, 40, 0, -10, -30, -40] result.value_type . is_integer . should_be_true - Test.specify "should allow truncate on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . truncate - result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true - Test.specify "should allow truncate on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . truncate result.to_vector.should_equal [0, 3, -3, 1, -2] result.value_type . is_integer . should_be_true - Test.specify "should allow ceil on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . ceil - result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true - Test.specify "should allow ceil on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . ceil result.to_vector.should_equal [0, 3, -3, 1, -2] result.value_type . is_integer . should_be_true - Test.specify "should allow floor on a float column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . floor - result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] - result.value_type . is_integer . should_be_true - Test.specify "should allow floor on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . floor From 3fe8280198b3650898304e1e9f50f95f7505f89e Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 11:20:27 -0400 Subject: [PATCH 31/83] restore deleted test, restore integrity check --- .../Standard/Database/0.0.0-dev/src/Data/Column.enso | 3 +-- .../Column_Operations_Spec.enso | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 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 574c74ef36c2..ca829c1b6d74 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 @@ -132,8 +132,7 @@ type Column type_mapping = self.connection.dialect.get_type_mapping prepare_operand operand = case operand of other_column : Column -> - ## self and other_column do not have to be compatible if self is not going to be used as an argument to the op. - if include_self == False || Helpers.check_integrity self other_column then other_column.expression else + 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 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 47df1c71b4fd..5b92271b35ea 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 @@ -1108,6 +1108,16 @@ spec setup = t.at "a" . zip (t.at "b") [_, _] . name . should_equal "[a] x [b]" Test.group prefix+"Column.rename" <| + Test.specify "should not allow illegal names" <| + t = table_builder [["a", [1, 2, 3]]] + c = t.at "a" + + c.rename Nothing . should_fail_with Illegal_Argument + c.rename '' . should_fail_with Illegal_Argument + c.rename 'a\0b' . should_fail_with Illegal_Argument + c.rename '\0' . should_fail_with Illegal_Argument + + Test.group prefix+"Column.const" <| Test.specify "Should allow the creation of constant columns" <| t = table_builder [["x", ["1", "2", "3"]]] t.at "x" . const True . to_vector . should_equal [True, True, True] From 31874a8960ff4bd744fc30bab1ca59496cbd48a8 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 11:51:21 -0400 Subject: [PATCH 32/83] const not an op, using make_constant --- .../Database/0.0.0-dev/src/Data/Column.enso | 17 ++++--------- .../Database/0.0.0-dev/src/Data/Table.enso | 24 ++++++++++++------- .../src/Internal/Base_Generator.enso | 17 ++++--------- .../Internal/SQLite/SQLite_Type_Mapping.enso | 10 +------- .../Column_Operations_Spec.enso | 15 ++++++++---- .../src/Database/Transaction_Spec.enso | 1 - 6 files changed, 37 insertions(+), 47 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 ca829c1b6d74..2a5830cef20d 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 @@ -560,30 +560,23 @@ type Column ## PRIVATE - Creates a column from a single constant value. The value must be numeric, - text, or boolean. + Creates a column from a single constant value. The value must be of a + type supported by the database back-end. This method is not static; it must be called on an existing column. That existing column is only used for its internal state; the contents of the column are not used. Arguments: - - value: the constant value to use for the entire column. Must be - numeric, text, or boolean. + - value: the constant value to use for the entire column. The value must + be of a type supported by the database back-end. > Example Create a column of the value 42 column.const 42 const : Any -> Column - const self value = - case value.is_a Number || value.is_a Boolean || value.is_a Text of - True -> - new_name = "Constant " + value.to_text - self.make_binary_op "CONST" value new_name include_self=False - False -> - msg = "Cannot create a constant column of " + value.to_text - Error.throw (Illegal_Argument.Error msg) + const self value = self.to_table.make_constant_column value ## Round to a specified number of decimal places. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index e2e5d8cdd96c..7fe8e7717031 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -698,21 +698,27 @@ type Table evaluate_expression : Text -> Problem_Behavior -> Column ! No_Such_Column | Invalid_Value_Type | Expression_Error evaluate_expression self expression on_problems=Report_Warning = get_column name = self.at name - type_mapping = self.connection.dialect.get_type_mapping - make_constant value = - argument_value_type = Value_Type_Helpers.find_argument_type value - sql_type = case argument_value_type of - Nothing -> SQL_Type.null - _ -> type_mapping.value_type_to_sql argument_value_type Problem_Behavior.Ignore - expr = SQL_Expression.Constant value - new_type_ref = SQL_Type_Reference.from_constant sql_type - Column.Value ("Constant_" + UUID.randomUUID.to_text) self.connection new_type_ref expr self.context + make_constant value = self.make_constant_column value new_column = Expression.evaluate expression get_column make_constant "Standard.Database.Data.Column" "Column" Column.var_args_functions problems = Warning.get_all new_column . map .value result = new_column.rename (self.connection.dialect.get_naming_helpers.sanitize_name expression) on_problems.attach_problems_before problems <| Warning.set result [] + ## PRIVATE + + Create a constant column from a value. + make_constant_column : Any -> Column + make_constant_column self value = + type_mapping = self.connection.dialect.get_type_mapping + argument_value_type = Value_Type_Helpers.find_argument_type value + sql_type = case argument_value_type of + Nothing -> SQL_Type.null + _ -> type_mapping.value_type_to_sql argument_value_type Problem_Behavior.Ignore + expr = SQL_Expression.Constant value + new_type_ref = SQL_Type_Reference.from_constant sql_type + Column.Value ("Constant_" + UUID.randomUUID.to_text) self.connection new_type_ref expr self.context + ## UNSTABLE Returns the vector of columns contained in this table. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 4227cd2f1f7f..830c4b805e69 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -137,20 +137,14 @@ make_function name = arguments -> (Builder.code name) ++ (Builder.join ", " arguments . paren) -## PRIVATE -make_const : Vector Builder -> Builder -make_const = - arguments -> - arguments.at 0 . paren - ## PRIVATE A helper function to create an operation that takes no arguments. Arguments: - - sql_code: The SQL code for the literal. -make_literal : Text -> (Vector Builder -> Builder) -make_literal sql_code = + - sql_code: The SQL code for the constant. +make_constant : Text -> (Vector Builder -> Builder) +make_constant sql_code = arguments -> if arguments.not_empty then Error.throw <| Illegal_State.Error "No arguments were expected" else Builder.code sql_code @@ -188,13 +182,12 @@ base_dialect = compare = [eq, neq, bin "<", bin ">", bin "<=", bin ">=", ["BETWEEN", make_between]] functions = [["COALESCE", make_function "COALESCE"], ["ROW_MIN", make_function "MIN"], ["ROW_MAX", make_function "MAX"]] agg = [fun "MAX", fun "MIN", fun "AVG", fun "SUM"] - counts = [fun "COUNT", ["COUNT_ROWS", make_literal "COUNT(*)"]] - constants = [["CONST", make_const]] + counts = [fun "COUNT", ["COUNT_ROWS", make_constant "COUNT(*)"]] text = [is_empty, bin "LIKE", simple_equals_ignore_case, fold_case, make_case_sensitive] nulls = [["IS_NULL", make_right_unary_op "IS NULL"], ["FILL_NULL", make_function "COALESCE"]] contains = [["IS_IN", make_is_in], ["IS_IN_COLUMN", make_is_in_column]] types = [simple_cast] - base_map = Map.from_vector (arith + logic + compare + functions + agg + counts + constants + text + nulls + contains + types) + base_map = Map.from_vector (arith + logic + compare + functions + agg + counts + text + nulls + contains + types) Internal_Dialect.Value base_map wrap_in_quotes ## PRIVATE 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 e0d003c08a0c..e2294eb0d6bc 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 @@ -160,14 +160,6 @@ 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) - handle_const arguments = - v = arguments.at 0 - if v.is_a Decimal then SQLite_Types.real else - if v.is_a Integer then SQLite_Types.integer else - if v.is_a Text then SQLite_Types.text else - if v.is_a Boolean then SQLite_Types.boolean else - Error.throw <| Illegal_Argument.Error <| "An unsupported SQL constant type: " + v.to_text - handle_case arguments = fallback = arguments.last cases = arguments.drop (Last 1) @@ -188,7 +180,7 @@ operations_map = always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE", "CEIL", "FLOOR"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] - others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["CONST", handle_const]] + others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] Map.from_vector <| v1 = always_boolean_ops.map [_, const SQLite_Types.boolean] v2 = always_floating_ops.map [_, const SQLite_Types.real] 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 5b92271b35ea..87abda4b3762 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 @@ -18,6 +18,15 @@ spec setup = prefix = setup.prefix table_builder = setup.table_builder + Test.group "gmt" <| + Test.specify "Should create the correct column name" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const 12 . name . take 9 . should_equal "Constant_" + + Test.specify "Should not allow the creation of constant columns of an invalid type" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const [[]] . should_fail_with Unsupported_Database_Operation + Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| t = table_builder [["X", [True, False, Nothing, True]]] @@ -121,7 +130,6 @@ spec setup = t2 = table_builder [["x", [1, 4, 5, Nothing]], ["y", [2.0, 3.25, 5.0, Nothing]]] x = t2.at "x" y = t2.at "y" - Test.group prefix+"Column Operations - Equality & Null Handling" <| Test.specify "should provide basic == and != comparisons" pending="TODO figure out proper null handling" <| (x == y).to_vector . should_equal [False, False, True, Nothing] @@ -1127,9 +1135,8 @@ spec setup = Test.specify "Should create the correct column name" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const 12 . name . should_equal "Constant 12" + t.at "x" . const 12 . name . take 9 . should_equal "Constant_" Test.specify "Should not allow the creation of constant columns of an invalid type" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const (Date.new 1997) . should_fail_with Illegal_Argument - + t.at "x" . const [[]] . should_fail_with Unsupported_Database_Operation diff --git a/test/Table_Tests/src/Database/Transaction_Spec.enso b/test/Table_Tests/src/Database/Transaction_Spec.enso index 259520fb514f..3203aa02ff1a 100644 --- a/test/Table_Tests/src/Database/Transaction_Spec.enso +++ b/test/Table_Tests/src/Database/Transaction_Spec.enso @@ -4,7 +4,6 @@ from Standard.Table import Value_Type from Standard.Database import all from Standard.Database.Errors import all -import Standard.Database.Data.Column_Description.Column_Description from Standard.Test import Test, Test_Suite, Problems import Standard.Test.Extensions From 92569308c1feed2aa2d425a348ae4c1a38a4bb06 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 12:03:08 -0400 Subject: [PATCH 33/83] wip --- .../lib/Standard/Database/0.0.0-dev/src/Data/Table.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index 7fe8e7717031..64c10bc92c3f 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -698,7 +698,7 @@ type Table evaluate_expression : Text -> Problem_Behavior -> Column ! No_Such_Column | Invalid_Value_Type | Expression_Error evaluate_expression self expression on_problems=Report_Warning = get_column name = self.at name - make_constant value = self.make_constant_column value + make_constant = self.make_constant_column new_column = Expression.evaluate expression get_column make_constant "Standard.Database.Data.Column" "Column" Column.var_args_functions problems = Warning.get_all new_column . map .value result = new_column.rename (self.connection.dialect.get_naming_helpers.sanitize_name expression) From 03f3e059e7a285d14dbe20c2adcae11672ee0821 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 13:37:43 -0400 Subject: [PATCH 34/83] in-mem const --- .../Table/0.0.0-dev/src/Data/Column.enso | 21 +++++++++++++++++++ .../Column_Operations_Spec.enso | 15 +++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index ab022ee08302..75b2ade4ebdb 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -659,6 +659,27 @@ type Column rs = s.iif true_val false_val storage_type Column.Value (Java_Column.new new_name rs) + ## PRIVATE + + Creates a column from a single constant value. + + This method is not static; it must be called on an existing column. That + existing column is only used for its internal state; the contents of the + column are not used. + + Arguments: + - value: the constant value to use for the entire column. + + > Example + Create a column of the value 42 + + column.const 42 + const : Any -> Column + const self value = + name = "Constant_" + value.to_text + values = Vector.new (self.length) (_-> value) + Column.from_vector name values + ## Round the values in a numeric column to a specified number of decimal places. 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 87abda4b3762..130c9d5ef9fd 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 @@ -19,13 +19,16 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - Test.specify "Should create the correct column name" <| + Test.specify "Should allow the creation of constant columns" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const 12 . name . take 9 . should_equal "Constant_" + t.at "x" . const True . to_vector . should_equal [True, True, True] + t.at "x" . const 12 . to_vector . should_equal [12, 12, 12] + t.at "x" . const 12.3 . to_vector . should_equal [12.3, 12.3, 12.3] + t.at "x" . const "asdf" . to_vector . should_equal ["asdf", "asdf", "asdf"] - Test.specify "Should not allow the creation of constant columns of an invalid type" <| + Test.specify "Should create the correct column name" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const [[]] . should_fail_with Unsupported_Database_Operation + t.at "x" . const 12 . name . take 9 . should_equal "Constant_" Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -1136,7 +1139,3 @@ spec setup = Test.specify "Should create the correct column name" <| t = table_builder [["x", ["1", "2", "3"]]] t.at "x" . const 12 . name . take 9 . should_equal "Constant_" - - Test.specify "Should not allow the creation of constant columns of an invalid type" <| - t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const [[]] . should_fail_with Unsupported_Database_Operation From 78aa29f4705387c74ebaec31ea5e8b2fe1373bec Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 13:45:21 -0400 Subject: [PATCH 35/83] share with eval exp --- .../Standard/Database/0.0.0-dev/src/Data/Table.enso | 3 +-- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 5 +---- .../lib/Standard/Table/0.0.0-dev/src/Data/Table.enso | 10 ++++++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index 64c10bc92c3f..7e9dc05b367e 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -698,8 +698,7 @@ type Table evaluate_expression : Text -> Problem_Behavior -> Column ! No_Such_Column | Invalid_Value_Type | Expression_Error evaluate_expression self expression on_problems=Report_Warning = get_column name = self.at name - make_constant = self.make_constant_column - new_column = Expression.evaluate expression get_column make_constant "Standard.Database.Data.Column" "Column" Column.var_args_functions + new_column = Expression.evaluate expression get_column self.make_constant_column "Standard.Database.Data.Column" "Column" Column.var_args_functions problems = Warning.get_all new_column . map .value result = new_column.rename (self.connection.dialect.get_naming_helpers.sanitize_name expression) on_problems.attach_problems_before problems <| diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 75b2ade4ebdb..0682397bdf97 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -675,10 +675,7 @@ type Column column.const 42 const : Any -> Column - const self value = - name = "Constant_" + value.to_text - values = Vector.new (self.length) (_-> value) - Column.from_vector name values + const self value = self.to_table.make_constant_column value ## Round the values in a numeric column to a specified number of decimal places. diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso index e2148f021f35..590029083b5e 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso @@ -1299,13 +1299,19 @@ type Table evaluate_expression : Text -> Problem_Behavior -> Column ! No_Such_Column | Invalid_Value_Type | Expression_Error evaluate_expression self expression on_problems=Report_Warning = get_column name = self.at name - make_constant value = Column.from_vector_repeated (UUID.randomUUID.to_text) [value] self.row_count - new_column = Expression.evaluate expression get_column make_constant "Standard.Table.Data.Column" "Column" Column.var_args_functions + new_column = Expression.evaluate expression get_column self.make_constant_column "Standard.Table.Data.Column" "Column" Column.var_args_functions problems = Warning.get_all new_column . map .value result = new_column.rename (Naming_Helpers.sanitize_name expression) on_problems.attach_problems_before problems <| Warning.set result [] + ## PRIVATE + + Create a constant column from a value. + make_constant_column : Any -> Column + make_constant_column self value = + Column.from_vector_repeated ("Constant_" + UUID.randomUUID.to_text) [value] self.row_count + ## Returns the vector of columns contained in this table. > Examples From 92b4d49934d24237c25c5c164376d24cae7b3f7e Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 14:09:39 -0400 Subject: [PATCH 36/83] factor out dp check --- .../src/Data/Decimal_Places_Helpers.enso | 22 ++++++++++++++++++ .../Base/0.0.0-dev/src/Data/Numbers.enso | 23 +++---------------- .../Database/0.0.0-dev/src/Data/Column.enso | 21 ++--------------- .../Column_Operations_Spec.enso | 17 +++++++------- 4 files changed, 35 insertions(+), 48 deletions(-) create mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso new file mode 100644 index 000000000000..e90e755523c3 --- /dev/null +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso @@ -0,0 +1,22 @@ +import project.Any.Any +import project.Data.Numbers.Integer +import project.Error.Error +import project.Errors.Illegal_Argument.Illegal_Argument + +## PRIVATE + The smallest allowed value for the `decimal_places` argument to `round` +round_min_decimal_places : Integer +round_min_decimal_places = -15 + +## PRIVATE + The largest allowed value for the `decimal_places` argument to `round` +round_max_decimal_places : Integer +round_max_decimal_places = 15 + +## PRIVATE + Restrict rounding decimal_places parameter. +check_decimal_places : Integer -> Any -> Any ! Illegal_Argument +check_decimal_places decimal_places ~action = + if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else + msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text + Error.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 2003eba9d3b4..89a5faddcb4a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -1,4 +1,5 @@ import project.Any.Any +import project.Data.Decimal_Places_Helpers import project.Data.Text.Text import project.Data.Locale.Locale import project.Errors.Common.Arithmetic_Error @@ -609,7 +610,7 @@ type Decimal 2.5 . round use_bankers=True == 2 round : Integer -> Boolean -> Integer | Decimal ! Illegal_Argument round self decimal_places=0 use_bankers=False = - check_decimal_places decimal_places <| + Decimal_Places_Helpers.check_decimal_places decimal_places <| case self.is_nan || self.is_infinite of True -> msg = "round cannot accept " + self.to_text @@ -936,7 +937,7 @@ type Integer ## It's already an integer so unless decimal_places is negative, the value is unchanged. if decimal_places >= 0 then self else - check_decimal_places decimal_places <| check_round_input self <| + Decimal_Places_Helpers.check_decimal_places decimal_places <| check_round_input self <| scale = 10 ^ -decimal_places halfway = scale.div 2 remainder = self % scale @@ -1122,16 +1123,6 @@ type Number_Parse_Error to_display_text = "Could not parse " + self.text.to_text + " as a double." -## PRIVATE - The smallest allowed value for the `decimal_places` argument to `round` -round_min_decimal_places : Integer -round_min_decimal_places = -15 - -## PRIVATE - The largest allowed value for the `decimal_places` argument to `round` -round_max_decimal_places : Integer -round_max_decimal_places = 15 - ## PRIVATE The largest smallInteger (Long) that integer round can handle. Above 14 digits, it is possible that the underlying long, converted to double in the @@ -1148,14 +1139,6 @@ round_max_long = 99999999999999 round_min_long : Integer round_min_long = -99999999999999 -## PRIVATE - Restrict rounding decimal_places parameter. -check_decimal_places : Integer -> Any -> Any ! Illegal_Argument -check_decimal_places decimal_places ~action = - if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else - msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text - Error.throw (Illegal_Argument.Error msg) - ## PRIVATE Restrict allowed range of input to rounding methods. check_round_input : Number -> Function -> Any ! Illegal_Argument 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 2a5830cef20d..3aeeb697f234 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 @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Base.Data.Decimal_Places_Helpers import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Errors.Illegal_State.Illegal_State @@ -622,7 +623,7 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - check_decimal_places decimal_places <| Value_Type.expect_numeric self <| + Decimal_Places_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "round" [self] scale = 10 ^ decimal_places scaled = self * scale @@ -1354,21 +1355,3 @@ adapt_unified_column column expected_type = SQL_Type_Reference.new column.connection column.context expression adapted = dialect.adapt_unified_column column.as_internal expected_type infer_return_type Column.Value name=column.name connection=column.connection sql_type_reference=adapted.sql_type_reference expression=adapted.expression context=column.context - -## PRIVATE - The smallest allowed value for the `decimal_places` argument to `round` -round_min_decimal_places : Integer -round_min_decimal_places = -15 - -## PRIVATE - The largest allowed value for the `decimal_places` argument to `round` -round_max_decimal_places : Integer -round_max_decimal_places = 15 - -## PRIVATE - Restrict rounding decimal_places parameter. -check_decimal_places : Integer -> Any -> Any ! Illegal_Argument -check_decimal_places decimal_places ~action = - if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else - msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text - Error.throw (Illegal_Argument.Error msg) 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 130c9d5ef9fd..6ac7f5d626c9 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 @@ -19,16 +19,15 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - Test.specify "Should allow the creation of constant columns" <| - t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const True . to_vector . should_equal [True, True, True] - t.at "x" . const 12 . to_vector . should_equal [12, 12, 12] - t.at "x" . const 12.3 . to_vector . should_equal [12.3, 12.3, 12.3] - t.at "x" . const "asdf" . to_vector . should_equal ["asdf", "asdf", "asdf"] + Test.specify "should name a rounding column correctly" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + table.at "x" . round . name . should_equal "round([x])" - Test.specify "Should create the correct column name" <| - t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const 12 . name . take 9 . should_equal "Constant_" + Test.specify "should allow round on a column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . round + result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] + result.value_type . is_integer . should_be_true Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| From 8d19564b25437ac3646c8f11fab26c809d58fb8a Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 14:29:31 -0400 Subject: [PATCH 37/83] only test decimal on pg-like --- .../Column_Operations_Spec.enso | 48 +++++++++++++++---- .../src/Common_Table_Operations/Main.enso | 2 +- .../src/Database/Postgres_Spec.enso | 2 +- .../src/Database/Redshift_Spec.enso | 2 +- 4 files changed, 42 insertions(+), 12 deletions(-) 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 6ac7f5d626c9..f157f8157ae3 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 @@ -19,15 +19,42 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - Test.specify "should name a rounding column correctly" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - table.at "x" . round . name . should_equal "round([x])" + test_floatlike type = + Test.specify "should allow round on a "+type.to_text+" column" <| + IO.println 'AAA ' + IO.println type.to_text + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . round + result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] + result.value_type . is_integer . should_be_true - Test.specify "should allow round on a column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . round - result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] - result.value_type . is_integer . should_be_true + Test.specify "should allow round on a float column (to >0 decimal places)" <| + table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] + result = table.at "x" . cast type . round 1 + result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] + result.value_type . is_integer . should_be_false + + Test.specify "should allow truncate on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . truncate + result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow ceil on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . ceil + result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] + result.value_type . is_integer . should_be_true + + Test.specify "should allow floor on a "+type.to_text+" column" <| + table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] + result = table.at "x" . cast type . floor + result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] + result.value_type . is_integer . should_be_true + + test_floatlike Value_Type.Float + if setup.test_selection.supports_decimal_type then + test_floatlike Value_Type.Decimal Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -459,6 +486,8 @@ spec setup = test_floatlike type = Test.specify "should allow round on a "+type.to_text+" column" <| + IO.println 'AAA ' + IO.println type.to_text table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . round result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] @@ -489,7 +518,8 @@ spec setup = result.value_type . is_integer . should_be_true test_floatlike Value_Type.Float - test_floatlike Value_Type.Decimal + if setup.test_selection.supports_decimal_type then + test_floatlike Value_Type.Decimal Test.specify "should allow round on an int column" <| table = table_builder [["x", [1, 9, 31, 39, -1, -9, -31, -39]]] diff --git a/test/Table_Tests/src/Common_Table_Operations/Main.enso b/test/Table_Tests/src/Common_Table_Operations/Main.enso index 1211f6d7e941..ee31e82213ac 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Main.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Main.enso @@ -88,7 +88,7 @@ type Test_Selection - date_time: Specifies if the backend supports date/time operations. - fixed_length_text_columns: Specifies if the backend supports fixed length text columns. - Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False + Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False supports_decimal_type=False spec setup = Core_Spec.spec setup diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 5606124fe9b3..df9137345176 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -219,7 +219,7 @@ run_tests connection db_name = Common_Spec.spec prefix connection - common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=True order_by_unicode_normalization_by_default=True take_drop=False allows_mixed_type_comparisons=False fixed_length_text_columns=True + common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=True order_by_unicode_normalization_by_default=True take_drop=False allows_mixed_type_comparisons=False fixed_length_text_columns=True supports_decimal_type=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 agg_table = agg_in_memory_table.select_into_database_table connection (Name_Generator.random_name "Agg1") primary_key=Nothing temporary=True diff --git a/test/Table_Tests/src/Database/Redshift_Spec.enso b/test/Table_Tests/src/Database/Redshift_Spec.enso index fa61748f7ab5..50d9216f2549 100644 --- a/test/Table_Tests/src/Database/Redshift_Spec.enso +++ b/test/Table_Tests/src/Database/Redshift_Spec.enso @@ -51,7 +51,7 @@ run_tests connection = Common_Spec.spec prefix connection redshift_specific_spec connection - common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=True order_by_unicode_normalization_by_default=True take_drop=False allows_mixed_type_comparisons=False + common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=True order_by_unicode_normalization_by_default=True take_drop=False allows_mixed_type_comparisons=False supports_decimal_type=True aggregate_selection = Common_Table_Operations.Aggregate_Spec.Test_Selection.Config first_last_row_order=False aggregation_problems=False date_support=False agg_in_memory_table = (enso_project.data / "data.csv") . read agg_table = agg_in_memory_table.select_into_database_table connection (Name_Generator.random_name "Agg1") primary_key=Nothing temporary=True From 91d58b22e67ffbc00d663a604a952365f263d4fe Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 14:51:05 -0400 Subject: [PATCH 38/83] column arithmetic for in-mem too --- .../Table/0.0.0-dev/src/Data/Column.enso | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 0682397bdf97..906b4aa4ca41 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -1,6 +1,7 @@ from Standard.Base import all import Standard.Base.Data.Array_Proxy.Array_Proxy +import Standard.Base.Data.Decimal_Places_Helpers import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Errors.Illegal_State.Illegal_State @@ -719,13 +720,25 @@ type Column Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) round : Integer -> Boolean -> Problem_Behavior -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False on_problems=Report_Warning = Value_Type.expect_numeric self <| - # If it's an integer column and decimal_places >=0 then it's a no-op. - if self.value_type.is_integer && decimal_places >= 0 then self else - returns_double = decimal_places > 0 - builder = if returns_double then make_double_builder else make_long_builder - fun = _.round decimal_places use_bankers - Column_Ops.map_over_storage self fun builder skip_nothing=True on_problems=on_problems + round self decimal_places=0 use_bankers=False on_problems=Report_Warning = + Decimal_Places_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| + # If it's an integer column and decimal_places >=0 then it's a no-op. + new_name = Naming_Helpers.function_name "round" [self] + if self.value_type.is_integer && decimal_places >= 0 then self.rename new_name else + scale = 10 ^ decimal_places + scaled = self * scale + round_base = scaled.floor + round_midpoint = (round_base + 0.5) / scale + even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) + half_goes_up = if use_bankers then even_is_up else (self.const True) + do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) + result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + + should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer + result = if should_cast_to_integer.not then result_float else + result_float.cast Value_Type.Integer + + result.rename new_name ## ALIAS int From ea10375a805dab2f1ccb5788f40e7e3598e80675 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 14:54:21 -0400 Subject: [PATCH 39/83] wip --- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 906b4aa4ca41..a96e37b70f15 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -719,8 +719,8 @@ type Column Round a column of `Decimal` values`. Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) - round : Integer -> Boolean -> Problem_Behavior -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False on_problems=Report_Warning = + round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type + round self decimal_places=0 use_bankers=False = Decimal_Places_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| # If it's an integer column and decimal_places >=0 then it's a no-op. new_name = Naming_Helpers.function_name "round" [self] From b7b6d3ed6b86f47113299735bc65a9a662688b83 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 15 Jun 2023 15:27:43 -0400 Subject: [PATCH 40/83] symmetric --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 6 ++-- test/Tests/src/Data/Numbers_Spec.enso | 34 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 89a5faddcb4a..9e6192544980 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -623,7 +623,7 @@ type Decimal round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale even_is_up = if self >= 0 then (scaled.truncate % 2) != 0 else (scaled.truncate % 2) == 0 - half_goes_up = if use_bankers then even_is_up else True + half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = if half_goes_up then self >= round_midpoint else self > round_midpoint if do_round_up then ((round_base + 1.0) / scale) else (round_base / scale) # Convert to integer if it's really an integer anyway. @@ -945,11 +945,11 @@ type Integer result_unnudged = scaled_down * scale case self >= 0 of True -> - half_goes_up = if use_bankers then (scaled_down % 2) != 0 else True + half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 round_up = if half_goes_up then remainder >= halfway else remainder > halfway if round_up then result_unnudged + scale else result_unnudged False -> - half_goes_up = if use_bankers then (scaled_down % 2) == 0 else True + half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 round_up = if half_goes_up then remainder < -halfway else remainder <= -halfway if round_up then result_unnudged - scale else result_unnudged diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 1663c79092b5..7964213cf720 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -487,7 +487,7 @@ spec = -3.00001 . round . should_equal -3 -3.3 . round . should_equal -3 -3.49999 . round . should_equal -3 - -3.5 . round . should_equal -3 + -3.5 . round . should_equal -4 -3.50001 . round . should_equal -4 -3.99999 . round . should_equal -4 @@ -540,7 +540,7 @@ spec = -1499.0 . round -2 . should_equal -1500 -1499.0 . round -3 . should_equal -1000 - -1495.0 . round -1 . should_equal -1490 + -1495.0 . round -1 . should_equal -1500 -1494.0 . round -1 . should_equal -1490 -1495.0 . round -2 . should_equal -1500 -1494.0 . round -2 . should_equal -1500 @@ -594,12 +594,12 @@ spec = 1.222222222222225 . round 14 . should_equal 1.22222222222223 1.2222222222222225 . round 15 . should_equal 1.222222222222223 - -1.22222222225 . round 10 . should_equal -1.2222222222 - -1.222222222225 . round 11 . should_equal -1.22222222222 - -1.2222222222225 . round 12 . should_equal -1.222222222222 - -1.22222222222225 . round 13 . should_equal -1.2222222222222 - -1.222222222222225 . round 14 . should_equal -1.22222222222222 - -1.2222222222222225 . round 15 . should_equal -1.222222222222222 + -1.22222222225 . round 10 . should_equal -1.2222222223 + -1.222222222225 . round 11 . should_equal -1.22222222223 + -1.2222222222225 . round 12 . should_equal -1.222222222223 + -1.22222222222225 . round 13 . should_equal -1.2222222222223 + -1.222222222222225 . round 14 . should_equal -1.22222222222223 + -1.2222222222222225 . round 15 . should_equal -1.222222222222223 1.22222222235 . round 10 . should_equal 1.2222222224 1.222222222235 . round 11 . should_equal 1.22222222224 @@ -608,12 +608,12 @@ spec = 1.222222222222235 . round 14 . should_equal 1.22222222222224 1.2222222222222235 . round 15 . should_equal 1.222222222222224 - -1.22222222235 . round 10 . should_equal -1.2222222223 - -1.222222222235 . round 11 . should_equal -1.22222222223 - -1.2222222222235 . round 12 . should_equal -1.222222222223 - -1.22222222222235 . round 13 . should_equal -1.2222222222223 - -1.222222222222235 . round 14 . should_equal -1.22222222222223 - -1.2222222222222235 . round 15 . should_equal -1.222222222222223 + -1.22222222235 . round 10 . should_equal -1.2222222224 + -1.222222222235 . round 11 . should_equal -1.22222222224 + -1.2222222222235 . round 12 . should_equal -1.222222222224 + -1.22222222222235 . round 13 . should_equal -1.2222222222224 + -1.222222222222235 . round 14 . should_equal -1.22222222222224 + -1.2222222222222235 . round 15 . should_equal -1.222222222222224 Test.specify "Can round correctly near the precision limit, using banker's rounding" <| 1.22222222225 . round 10 use_bankers=True . should_equal 1.2222222222 @@ -706,13 +706,13 @@ spec = Test.specify "Can round negative integers to a specified number of negative places correctly" -4 . round -1 . should_equal 0 - -5 . round -1 . should_equal 0 + -5 . round -1 . should_equal -10 -6 . round -1 . should_equal -10 -9 . round -1 . should_equal -10 -10 . round -1 . should_equal -10 -11 . round -1 . should_equal -10 -24 . round -1 . should_equal -20 - -25 . round -1 . should_equal -20 + -25 . round -1 . should_equal -30 -29 . round -1 . should_equal -30 -30 . round -1 . should_equal -30 -31 . round -1 . should_equal -30 @@ -721,7 +721,7 @@ spec = -2001 . round -3 . should_equal -2000 -2412 . round -3 . should_equal -2000 -2499 . round -3 . should_equal -2000 - -2500 . round -3 . should_equal -2000 + -2500 . round -3 . should_equal -3000 -2501 . round -3 . should_equal -3000 -2511 . round -3 . should_equal -3000 -2907 . round -3 . should_equal -3000 From e8dbf9fa6a5110a502f135af001cb38620524338 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 16 Jun 2023 12:12:55 -0400 Subject: [PATCH 41/83] symmetric for in-mem --- .../Table/0.0.0-dev/src/Data/Column.enso | 2 +- .../src/In_Memory/Column_Spec.enso | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index a96e37b70f15..c67af944d183 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -730,7 +730,7 @@ type Column round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else (self.const True) + half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index f2b2eeeeba94..0105514263ae 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -98,26 +98,31 @@ spec = Test.group "Rounding" <| Test.specify "should be able to round a column of decimals" <| - Column.from_vector "foo" [1.2, 2.3, 3.6] . round . should_equal (Column.from_vector "foo" [1, 2, 4]) - Column.from_vector "foo" [1.25, 2.33, 3.57] . round 1 . should_equal <| Column.from_vector "foo" [1.3, 2.3, 3.6] - Column.from_vector "foo" [12.0, 24.0, 25.0, 29.0] . round -1 . should_equal <| Column.from_vector "foo" [10, 20, 30, 30] - Column.from_vector "foo" [1.5, 2.5, 3.5] . round use_bankers=True . should_equal <| Column.from_vector "foo" [2, 2, 4] + Column.from_vector "foo" [1.2, 2.3, 2.5, 3.6] . round . should_equal (Column.from_vector "round([foo])" [1, 2, 3, 4]) + Column.from_vector "foo" [1.25, 2.33, 3.57] . round 1 . should_equal <| Column.from_vector "round([foo])" [1.3, 2.3, 3.6] + Column.from_vector "foo" [12.0, 24.0, 25.0, 29.0] . round -1 . should_equal <| Column.from_vector "round([foo])" [10, 20, 30, 30] + Column.from_vector "foo" [1.5, 2.5, 3.5] . round use_bankers=True . should_equal <| Column.from_vector "round([foo])" [2, 2, 4] + + Column.from_vector "foo" [-1.2, -2.3, -2.5, -3.6] . round . should_equal (Column.from_vector "round([foo])" [-1, -2, -3, -4]) + Column.from_vector "foo" [-1.25, -2.33, -2.45, -3.57] . round 1 . should_equal <| Column.from_vector "round([foo])" [-1.3, -2.3, -2.5, -3.6] + Column.from_vector "foo" [-12.0, -24.0, -25.0, -29.0] . round -1 . should_equal <| Column.from_vector "round([foo])" [-10, -20, -30, -30] Test.specify "decimal rounding should return the correct column type" <| Column.from_vector "foo" [1.2, 2.3, 3.6] . round . value_type . should_equal Value_Type.Integer Column.from_vector "foo" [1.2, 2.3, 3.6] . round 1 . value_type . should_equal Value_Type.Float Test.specify "should be able to round a column of integers" <| - Column.from_vector "foo" [12, 24, 25, 29] . round . should_equal <| Column.from_vector "foo" [12, 24, 25, 29] - Column.from_vector "foo" [12, 24, 25, 29] . round -1 . should_equal <| Column.from_vector "foo" [10, 20, 30, 30] - Column.from_vector "foo" [15, 25, 35] . round -1 use_bankers=True . should_equal <| Column.from_vector "foo" [20, 20, 40] + Column.from_vector "foo" [12, 24, 25, 29] . round . should_equal <| Column.from_vector "round([foo])" [12, 24, 25, 29] + Column.from_vector "foo" [12, 24, 25, 29] . round -1 . should_equal <| Column.from_vector "round([foo])" [10, 20, 30, 30] + Column.from_vector "foo" [15, 25, 35] . round -1 use_bankers=True . should_equal <| Column.from_vector "round([foo])" [20, 20, 40] Test.specify "integer rounding should return the correct column type" <| Column.from_vector "foo" [12, 24, 25, 29] . round -1 . value_type . should_equal Value_Type.Integer Test.specify "should report out-of-range values as problems" <| col = Column.from_vector "foo" [12, 23, 99999999999999999] - expected = Column.from_vector "foo" [10, 20, Nothing] + expected = Column.from_vector "round([foo])" [10, 20, Nothing] + col.round -1 . should_equal expected action = col.round -1 on_problems=_ problems = [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive), but was 99999999999999999"] tester = _.should_equal expected @@ -125,7 +130,7 @@ spec = Test.specify "should throw an error on decimal places out of range" <| col = Column.from_vector "foo" [12, 23, 99999999999999999] - expected = Column.from_vector "foo" [Nothing, Nothing, Nothing] + expected = Column.from_vector "round([foo])" [Nothing, Nothing, Nothing] action = col.round decimal_places=-1200 on_problems=_ problems = [Illegal_Argument.Error "round: decimal_places must be between -15 and 15 (inclusive), but was -1200"] tester = _.should_equal expected From 218f2e2b51d2a2c956d308e851fcb4dc0016275a Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 16 Jun 2023 13:50:45 -0400 Subject: [PATCH 42/83] symmetric for db --- .../Database/0.0.0-dev/src/Data/Column.enso | 2 +- .../Column_Operations_Spec.enso | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 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 3aeeb697f234..71f68e195522 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 @@ -630,7 +630,7 @@ type Column round_base = scaled.floor round_midpoint = (round_base + 0.5) / scale even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else (self.const True) + half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) 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 f157f8157ae3..266b9b94f2bb 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 @@ -575,7 +575,7 @@ spec setup = do_round -3.00001 . should_equal -3 do_round -3.3 . should_equal -3 do_round -3.49999 . should_equal -3 - do_round -3.5 . should_equal -3 + do_round -3.5 . should_equal -4 do_round -3.50001 . should_equal -4 do_round -3.99999 . should_equal -4 @@ -628,7 +628,7 @@ spec setup = do_round -1499.0 -2 . should_equal -1500 do_round -1499.0 -3 . should_equal -1000 - do_round -1495.0 -1 . should_equal -1490 + do_round -1495.0 -1 . should_equal -1500 do_round -1494.0 -1 . should_equal -1490 do_round -1495.0 -2 . should_equal -1500 do_round -1494.0 -2 . should_equal -1500 @@ -682,12 +682,12 @@ spec setup = do_round 1.222222222222225 14 . should_equal 1.22222222222223 do_round 1.2222222222222225 15 . should_equal 1.222222222222223 - do_round -1.22222222225 10 . should_equal -1.2222222222 - do_round -1.222222222225 11 . should_equal -1.22222222222 - do_round -1.2222222222225 12 . should_equal -1.222222222222 - do_round -1.22222222222225 13 . should_equal -1.2222222222222 - do_round -1.222222222222225 14 . should_equal -1.22222222222222 - do_round -1.2222222222222225 15 . should_equal -1.222222222222222 + do_round -1.22222222225 10 . should_equal -1.2222222223 + do_round -1.222222222225 11 . should_equal -1.22222222223 + do_round -1.2222222222225 12 . should_equal -1.222222222223 + do_round -1.22222222222225 13 . should_equal -1.2222222222223 + do_round -1.222222222222225 14 . should_equal -1.22222222222223 + do_round -1.2222222222222225 15 . should_equal -1.222222222222223 do_round 1.22222222235 10 . should_equal 1.2222222224 do_round 1.222222222235 11 . should_equal 1.22222222224 @@ -696,12 +696,12 @@ spec setup = do_round 1.222222222222235 14 . should_equal 1.22222222222224 do_round 1.2222222222222235 15 . should_equal 1.222222222222224 - do_round -1.22222222235 10 . should_equal -1.2222222223 - do_round -1.222222222235 11 . should_equal -1.22222222223 - do_round -1.2222222222235 12 . should_equal -1.222222222223 - do_round -1.22222222222235 13 . should_equal -1.2222222222223 - do_round -1.222222222222235 14 . should_equal -1.22222222222223 - do_round -1.2222222222222235 15 . should_equal -1.222222222222223 + do_round -1.22222222235 10 . should_equal -1.2222222224 + do_round -1.222222222235 11 . should_equal -1.22222222224 + do_round -1.2222222222235 12 . should_equal -1.222222222224 + do_round -1.22222222222235 13 . should_equal -1.2222222222224 + do_round -1.222222222222235 14 . should_equal -1.22222222222224 + do_round -1.2222222222222235 15 . should_equal -1.222222222222224 Test.specify "Can round correctly near the precision limit, using banker's rounding" <| do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 @@ -779,13 +779,13 @@ spec setup = Test.specify "Can round negative integers to a specified number of negative places correctly" do_round -4 -1 . should_equal 0 - do_round -5 -1 . should_equal 0 + do_round -5 -1 . should_equal -10 do_round -6 -1 . should_equal -10 do_round -9 -1 . should_equal -10 do_round -10 -1 . should_equal -10 do_round -11 -1 . should_equal -10 do_round -24 -1 . should_equal -20 - do_round -25 -1 . should_equal -20 + do_round -25 -1 . should_equal -30 do_round -29 -1 . should_equal -30 do_round -30 -1 . should_equal -30 do_round -31 -1 . should_equal -30 @@ -794,7 +794,7 @@ spec setup = do_round -2001 -3 . should_equal -2000 do_round -2412 -3 . should_equal -2000 do_round -2499 -3 . should_equal -2000 - do_round -2500 -3 . should_equal -2000 + do_round -2500 -3 . should_equal -3000 do_round -2501 -3 . should_equal -3000 do_round -2511 -3 . should_equal -3000 do_round -2907 -3 . should_equal -3000 From 570756db483226b7bc21a8bf4859fad3f85b3aa6 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 16 Jun 2023 16:44:48 -0400 Subject: [PATCH 43/83] wip --- .../Database/0.0.0-dev/src/Data/Column.enso | 87 +++++++++++++++---- .../Column_Operations_Spec.enso | 51 ++++------- 2 files changed, 87 insertions(+), 51 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 71f68e195522..9f62c138b35c 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 @@ -581,6 +581,9 @@ type Column ## Round to a specified number of decimal places. + `round` is only valid for `Value_Type.Decimal`, `Value_Type.Float`, and + `Value_Type.Integer` columns. + By default, rounding uses "symmetric round-half-up", also known as "round towards 0." If use_bankers=True, then it uses "round-half-even", also known as "banker's rounding". @@ -623,22 +626,74 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Decimal_Places_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| - new_name = self.naming_helpers.function_name "round" [self] - scale = 10 ^ decimal_places - scaled = self * scale - round_base = scaled.floor - round_midpoint = (round_base + 0.5) / scale - even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else self >= 0 - do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - - should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer - result = if should_cast_to_integer.not then result_float else - result_float.cast Value_Type.Integer - - result.rename new_name + Decimal_Places_Helpers.check_decimal_places decimal_places <| + k = case self.value_type of + Value_Type.Integer _ -> self.round_integer decimal_places use_bankers + Value_Type.Float _ -> self.round_float decimal_places use_bankers + Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers + _ -> + expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" + Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) + #IO.println 'k' + #IO.println k + k + + ## PRIVATE + Round a float-like column. + round_float : Integer -> Boolean -> Column + round_float self decimal_places use_bankers = + IO.println 'round_float' + new_name = self.naming_helpers.function_name "round" [self] + scale = 10 ^ decimal_places + scaled = self * scale + round_base = scaled.floor + round_midpoint = (round_base + 0.5) / scale + even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) + half_goes_up = if use_bankers then even_is_up else self >= 0 + do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) + result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + + should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer + result = if should_cast_to_integer.not then result_float else + result_float.cast Value_Type.Integer + + result.rename new_name + + ## PRIVATE + Round an integer column. + round_integer : Integer -> Boolean -> Column + round_integer self decimal_places use_bankers = + IO.println 'round_integer' + new_name = self.naming_helpers.function_name "round" [self] + scale = 10 ^ -decimal_places + halfway = scale.div 2 + remainder = self % scale + #scaled_down_float = (self / scale) + scaled_down = (self / scale).truncate . cast Value_Type.Integer + result_unnudged = scaled_down * scale + + ## + IO.println 'halfway' + IO.println halfway + result_unnudged + + if_non_neg = + half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 + round_up = half_goes_up.iif (remainder >= halfway) (remainder > halfway) + round_up.iif (result_unnudged + scale) result_unnudged + if_neg = + half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 + round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) + round_up.iif (result_unnudged - scale) result_unnudged + + result_float = (self >= 0).iif if_non_neg if_neg + should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer + result = if should_cast_to_integer.not then result_float else + result_float.cast Value_Type.Integer + + result.rename new_name + #result_unnudged + #scaled_down_float ## ALIAS int UNSTABLE 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 266b9b94f2bb..1ff6265f8d2a 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 @@ -19,42 +19,19 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - test_floatlike type = - Test.specify "should allow round on a "+type.to_text+" column" <| - IO.println 'AAA ' - IO.println type.to_text - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . cast type . round - result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] - result.value_type . is_integer . should_be_true - - Test.specify "should allow round on a float column (to >0 decimal places)" <| - table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] - result = table.at "x" . cast type . round 1 - result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false - - Test.specify "should allow truncate on a "+type.to_text+" column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . cast type . truncate - result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true - - Test.specify "should allow ceil on a "+type.to_text+" column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . cast type . ceil - result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true - - Test.specify "should allow floor on a "+type.to_text+" column" <| - table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] - result = table.at "x" . cast type . floor - result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] - result.value_type . is_integer . should_be_true + do_round n dp=0 use_bankers=False = + table = table_builder [["x", [n]]] + result = table.at "x" . round dp use_bankers + #IO.println 'do_round' + #IO.println <| table.at 'x' . to_vector + #IO.println <| result . to_vector + result.to_vector.at 0 - test_floatlike Value_Type.Float - if setup.test_selection.supports_decimal_type then - test_floatlike Value_Type.Decimal + Test.specify "Can round positive decimals correctly" <| + #do_round 3.5 . should_equal 4 + #do_round 333 -1 . should_equal 330 + IO.println 'AAA' + IO.println <| do_round 9 -1 Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -555,6 +532,10 @@ spec setup = result.to_vector.should_equal [Nothing, 0.5, 0.6, 3.5, Nothing, 3.6, -0.5, -0.6, -3.5, -3.6] result.value_type . is_integer . should_be_false + Test.specify "should fail on bad column type" <| + table = table_builder [["x", ["a", "b"]]] + table.at "x" . round . should_fail_with Invalid_Value_Type + Test.group prefix+"Rounding numeric tests" <| do_round n dp=0 use_bankers=False = table = table_builder [["x", [n]]] From afd21acf54c1411728296294451c5caf113869ba Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 19 Jun 2023 12:58:33 -0400 Subject: [PATCH 44/83] integer impl --- .../Database/0.0.0-dev/src/Data/Column.enso | 70 ++++++++----------- .../Column_Operations_Spec.enso | 7 +- 2 files changed, 31 insertions(+), 46 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 9f62c138b35c..dc5b98c414e2 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 @@ -627,22 +627,18 @@ type Column round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = Decimal_Places_Helpers.check_decimal_places decimal_places <| - k = case self.value_type of - Value_Type.Integer _ -> self.round_integer decimal_places use_bankers - Value_Type.Float _ -> self.round_float decimal_places use_bankers - Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers - _ -> - expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" - Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) - #IO.println 'k' - #IO.println k - k + case self.value_type of + Value_Type.Integer _ -> self.round_integer decimal_places use_bankers + Value_Type.Float _ -> self.round_float decimal_places use_bankers + Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers + _ -> + expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" + Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) ## PRIVATE Round a float-like column. round_float : Integer -> Boolean -> Column round_float self decimal_places use_bankers = - IO.println 'round_float' new_name = self.naming_helpers.function_name "round" [self] scale = 10 ^ decimal_places scaled = self * scale @@ -663,37 +659,29 @@ type Column Round an integer column. round_integer : Integer -> Boolean -> Column round_integer self decimal_places use_bankers = - IO.println 'round_integer' new_name = self.naming_helpers.function_name "round" [self] - scale = 10 ^ -decimal_places - halfway = scale.div 2 - remainder = self % scale - #scaled_down_float = (self / scale) - scaled_down = (self / scale).truncate . cast Value_Type.Integer - result_unnudged = scaled_down * scale - - ## - IO.println 'halfway' - IO.println halfway - result_unnudged - - if_non_neg = - half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 - round_up = half_goes_up.iif (remainder >= halfway) (remainder > halfway) - round_up.iif (result_unnudged + scale) result_unnudged - if_neg = - half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 - round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) - round_up.iif (result_unnudged - scale) result_unnudged - - result_float = (self >= 0).iif if_non_neg if_neg - should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer - result = if should_cast_to_integer.not then result_float else - result_float.cast Value_Type.Integer - - result.rename new_name - #result_unnudged - #scaled_down_float + if decimal_places >= 0 then self.rename new_name else + scale = 10 ^ -decimal_places + halfway = scale.div 2 + remainder = self % scale + scaled_down = (self / scale).truncate . cast Value_Type.Integer + result_unnudged = scaled_down * scale + + if_non_neg = + half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 + round_up = half_goes_up.iif (remainder >= halfway) (remainder > halfway) + round_up.iif (result_unnudged + scale) result_unnudged + if_neg = + half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 + round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) + round_up.iif (result_unnudged - scale) result_unnudged + + result_float = (self >= 0).iif if_non_neg if_neg + should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer + result = if should_cast_to_integer.not then result_float else + result_float.cast Value_Type.Integer + + result.rename new_name ## ALIAS int UNSTABLE 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 1ff6265f8d2a..e8ee065f43d8 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 @@ -27,11 +27,8 @@ spec setup = #IO.println <| result . to_vector result.to_vector.at 0 - Test.specify "Can round positive decimals correctly" <| - #do_round 3.5 . should_equal 4 - #do_round 333 -1 . should_equal 330 - IO.println 'AAA' - IO.println <| do_round 9 -1 + Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" + do_round 0 . should_equal 0 Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| From b0c771eee9cf3dda744795dfc4548378c514051e Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 19 Jun 2023 14:40:42 -0400 Subject: [PATCH 45/83] wip --- .../Column_Operations_Spec.enso | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) 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 e8ee065f43d8..9eac42bf3c22 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 @@ -27,8 +27,19 @@ spec setup = #IO.println <| result . to_vector result.to_vector.at 0 - Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" - do_round 0 . should_equal 0 + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + do_round Number.nan . should_equal nan_result + Test.specify "Can handle NaN/Infinity" <| + do_round Number.positive_infinity . should_equal Number.positive_infinity + Test.specify "Can handle NaN/Infinity" <| + do_round Number.negative_infinity . should_equal Number.negative_infinity + + Test.specify "should allow round on a float column (to <0 decimal places)" <| + table = table_builder [["x", [51.2, 59.3, 351.45, 359.11, -51.2, -59.3, -351.23, -359.69]]] + result = table.at "x" . round -1 + result.to_vector.should_equal [50.0, 60.0, 350.0, 360.0, -50.0, -60.0, -350.0, -360.0] + result.value_type . is_integer . should_be_true Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -473,6 +484,12 @@ spec setup = result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] result.value_type . is_integer . should_be_false + Test.specify "should allow round on a float column (to <0 decimal places)" <| + table = table_builder [["x", [51.2, 59.3, 351.45, 359.11, -51.2, -59.3, -351.23, -359.69]]] + result = table.at "x" . cast type . round -1 + result.to_vector.should_equal [50.0, 60.0, 350.0, 360.0, -50.0, -60.0, -350.0, -360.0] + result.value_type . is_integer . should_be_true + Test.specify "should allow truncate on a "+type.to_text+" column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . truncate @@ -718,6 +735,12 @@ spec setup = do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 do_round 37.785 2 . should_equal 37.79 + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + do_round Number.nan . should_equal nan_result + do_round Number.positive_infinity . should_equal Number.positive_infinity + do_round Number.negative_infinity . should_equal Number.negative_infinity + Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" do_round 0 . should_equal 0 do_round 3 . should_equal 3 From bbd7033df7f03536e2fb2134ddb011b4712c031d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 19 Jun 2023 15:09:26 -0400 Subject: [PATCH 46/83] col col --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 2 +- .../Decimal_Places_Helpers.enso | 0 .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 2 + .../Database/0.0.0-dev/src/Data/Column.enso | 1 - .../Database/0.0.0-dev/src/Data/Table.enso | 19 ++++----- .../Table/0.0.0-dev/src/Data/Column.enso | 1 - .../Column_Operations_Spec.enso | 39 +++++++------------ 7 files changed, 26 insertions(+), 38 deletions(-) rename distribution/lib/Standard/Base/0.0.0-dev/src/{Data => Internal}/Decimal_Places_Helpers.enso (100%) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 9e6192544980..33b961d9bee9 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -1,5 +1,5 @@ import project.Any.Any -import project.Data.Decimal_Places_Helpers +import project.Internal.Decimal_Places_Helpers import project.Data.Text.Text import project.Data.Locale.Locale import project.Errors.Common.Arithmetic_Error diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso similarity index 100% rename from distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal_Places_Helpers.enso rename to distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index f36f01c10cd9..0766436f50e1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -10,6 +10,7 @@ import project.Data.Vector.Vector import project.Error.Error import project.Errors import project.Function +import project.Internal.Decimal_Places_Helpers import project.IO import project.Math import project.Meta @@ -42,6 +43,7 @@ export project.Data.Text.Text export project.Data.Vector.Vector export project.Error.Error export project.Errors +export project.Internal.Decimal_Places_Helpers export project.IO export project.Math export project.Meta 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 dc5b98c414e2..fbd4242a2d1c 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 @@ -1,5 +1,4 @@ from Standard.Base import all -import Standard.Base.Data.Decimal_Places_Helpers import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Errors.Illegal_State.Illegal_State diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index 7e9dc05b367e..e6051a08dec1 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -707,16 +707,17 @@ type Table ## PRIVATE Create a constant column from a value. - make_constant_column : Any -> Column + make_constant_column : Any -> Column ! Illegal_Argument make_constant_column self value = - type_mapping = self.connection.dialect.get_type_mapping - argument_value_type = Value_Type_Helpers.find_argument_type value - sql_type = case argument_value_type of - Nothing -> SQL_Type.null - _ -> type_mapping.value_type_to_sql argument_value_type Problem_Behavior.Ignore - expr = SQL_Expression.Constant value - new_type_ref = SQL_Type_Reference.from_constant sql_type - Column.Value ("Constant_" + UUID.randomUUID.to_text) self.connection new_type_ref expr self.context + if Table_Helpers.is_column value then Error.throw (Illegal_Argument.Error "A constant value may only be created from a scalar, not a Column") else + type_mapping = self.connection.dialect.get_type_mapping + argument_value_type = Value_Type_Helpers.find_argument_type value + sql_type = case argument_value_type of + Nothing -> SQL_Type.null + _ -> type_mapping.value_type_to_sql argument_value_type Problem_Behavior.Ignore + expr = SQL_Expression.Constant value + new_type_ref = SQL_Type_Reference.from_constant sql_type + Column.Value ("Constant_" + UUID.randomUUID.to_text) self.connection new_type_ref expr self.context ## UNSTABLE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index c67af944d183..83468e5e9c78 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -1,7 +1,6 @@ from Standard.Base import all import Standard.Base.Data.Array_Proxy.Array_Proxy -import Standard.Base.Data.Decimal_Places_Helpers import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Errors.Illegal_State.Illegal_State 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 9eac42bf3c22..f30a083ebdda 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 @@ -19,27 +19,9 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - do_round n dp=0 use_bankers=False = - table = table_builder [["x", [n]]] - result = table.at "x" . round dp use_bankers - #IO.println 'do_round' - #IO.println <| table.at 'x' . to_vector - #IO.println <| result . to_vector - result.to_vector.at 0 - - Test.specify "Can handle NaN/Infinity" <| - nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing - do_round Number.nan . should_equal nan_result - Test.specify "Can handle NaN/Infinity" <| - do_round Number.positive_infinity . should_equal Number.positive_infinity - Test.specify "Can handle NaN/Infinity" <| - do_round Number.negative_infinity . should_equal Number.negative_infinity - - Test.specify "should allow round on a float column (to <0 decimal places)" <| - table = table_builder [["x", [51.2, 59.3, 351.45, 359.11, -51.2, -59.3, -351.23, -359.69]]] - result = table.at "x" . round -1 - result.to_vector.should_equal [50.0, 60.0, 350.0, 360.0, -50.0, -60.0, -350.0, -360.0] - result.value_type . is_integer . should_be_true + Test.specify "Should not allow the creation of a constant column of columns" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const (t.at "x") . should_fail_with Illegal_Argument Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -735,11 +717,12 @@ spec setup = do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 do_round 37.785 2 . should_equal 37.79 - Test.specify "Can handle NaN/Infinity" <| - nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing - do_round Number.nan . should_equal nan_result - do_round Number.positive_infinity . should_equal Number.positive_infinity - do_round Number.negative_infinity . should_equal Number.negative_infinity + ## + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + do_round Number.nan . should_equal nan_result + do_round Number.positive_infinity . should_equal Number.positive_infinity + do_round Number.negative_infinity . should_equal Number.negative_infinity Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" do_round 0 . should_equal 0 @@ -1169,3 +1152,7 @@ spec setup = Test.specify "Should create the correct column name" <| t = table_builder [["x", ["1", "2", "3"]]] t.at "x" . const 12 . name . take 9 . should_equal "Constant_" + + Test.specify "Should not allow the creation of a constant column of columns" <| + t = table_builder [["x", ["1", "2", "3"]]] + t.at "x" . const t . should_fail_with Illegal_Argument From e891c446bc6fee53b00a2be821f8594768120f28 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 19 Jun 2023 15:10:42 -0400 Subject: [PATCH 47/83] unstable --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 9 ++------- 1 file changed, 2 insertions(+), 7 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 fbd4242a2d1c..338d7e901629 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 @@ -683,7 +683,6 @@ type Column result.rename new_name ## ALIAS int - UNSTABLE Truncate the floating-point values to an integer by dropping the fractional part. This is equivalent to "round-toward-zero". @@ -700,9 +699,7 @@ type Column new_name = self.naming_helpers.function_name "truncate" [self] self.make_unary_op "TRUNCATE" new_name - ## UNSTABLE - - Takes the ceiling of floating-point values, returning integer values. + ## Takes the ceiling of floating-point values, returning integer values. > Example Take the ceiling of a column of floating-point values. @@ -716,9 +713,7 @@ type Column new_name = self.naming_helpers.function_name "ceil" [self] self.make_unary_op "CEIL" new_name - ## UNSTABLE - - Takes the floor of floating-point values, returning integer values. + ## Takes the floor of floating-point values, returning integer values. > Example Take the floor of a column of floating-point values. From 511a6274eed72ec6ac32a03f5a08551d94575782 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 19 Jun 2023 15:28:29 -0400 Subject: [PATCH 48/83] review --- .../Database/0.0.0-dev/src/Data/Column.enso | 21 +++++++------------ .../Column_Operations_Spec.enso | 2 +- 2 files changed, 8 insertions(+), 15 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 338d7e901629..74165efd6dc4 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 @@ -122,10 +122,8 @@ type Column - operands: A vector of additional operation arguments (the column itself is always passed as the first argument). - new_name: The name of the resulting column. - - include_self: If True, then self is used as the first argument; - otherwise, it is omitted. - make_op : Text -> Vector Text -> (Text | Nothing) -> Boolean -> Column - make_op self op_kind operands new_name include_self=True = + make_op : Text -> Vector Text -> (Text | Nothing) -> Column + make_op self op_kind operands new_name = 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 <| @@ -137,15 +135,12 @@ type Column constant -> SQL_Expression.Constant constant - self_maybe = if include_self then [self] else [] - self_expression_maybe = if include_self then [self.expression] else [] - expressions = operands.map prepare_operand - new_expr = SQL_Expression.Operation op_kind (self_expression_maybe + expressions) + 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_maybe+operands new_expr + 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 @@ -156,13 +151,11 @@ type Column - op_kind: The kind of binary operator. - operand: The right operand to the binary operator. - new_name: The name of the resulting column. - - include_self: If True, then self is used as the first argument; - otherwise, it is omitted. - make_binary_op : Text -> Text -> (Text | Nothing) -> Boolean -> Column - make_binary_op self op_kind operand new_name=Nothing include_self=True = + make_binary_op : Text -> Text -> (Text | Nothing) -> Column + make_binary_op self op_kind operand new_name=Nothing = effective_new_name = new_name.if_nothing <| self.naming_helpers.binary_operation_name op_kind self operand - self.make_op op_kind [operand] effective_new_name include_self + self.make_op op_kind [operand] effective_new_name ## PRIVATE 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 f30a083ebdda..bded34fe5745 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 @@ -1155,4 +1155,4 @@ spec setup = Test.specify "Should not allow the creation of a constant column of columns" <| t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const t . should_fail_with Illegal_Argument + t.at "x" . const (t.at "x") . should_fail_with Illegal_Argument From 9769faf118409820b4bb8da75d49939b3a7b8eae Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 20 Jun 2023 12:22:33 -0400 Subject: [PATCH 49/83] pg/sl return type --- .../Database/0.0.0-dev/src/Data/Column.enso | 36 ++++++----- .../Database/0.0.0-dev/src/Data/Table.enso | 2 + .../src/Internal/Base_Generator.enso | 6 +- .../Table/0.0.0-dev/src/Data/Column.enso | 21 ++++++- .../0.0.0-dev/src/Data/Type/Value_Type.enso | 7 +++ .../Column_Operations_Spec.enso | 63 ++++++++++++++----- 6 files changed, 98 insertions(+), 37 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 74165efd6dc4..3d54458c4843 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 @@ -619,13 +619,16 @@ type Column round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = Decimal_Places_Helpers.check_decimal_places decimal_places <| - case self.value_type of + round_maybe = if decimal_places <= 0 then roundlike_cast self.value_type else identity + result_uncast = case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers Value_Type.Float _ -> self.round_float decimal_places use_bankers Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers _ -> expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) + result_uncast.if_not_error <| + round_maybe result_uncast ## PRIVATE Round a float-like column. @@ -639,12 +642,7 @@ type Column even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - - should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer - result = if should_cast_to_integer.not then result_float else - result_float.cast Value_Type.Integer - + result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) result.rename new_name ## PRIVATE @@ -652,6 +650,7 @@ type Column round_integer : Integer -> Boolean -> Column round_integer self decimal_places use_bankers = new_name = self.naming_helpers.function_name "round" [self] + # If non-negative decimal places, there's nothing to do. if decimal_places >= 0 then self.rename new_name else scale = 10 ^ -decimal_places halfway = scale.div 2 @@ -668,11 +667,7 @@ type Column round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) round_up.iif (result_unnudged - scale) result_unnudged - result_float = (self >= 0).iif if_non_neg if_neg - should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer - result = if should_cast_to_integer.not then result_float else - result_float.cast Value_Type.Integer - + result = (self >= 0).iif if_non_neg if_neg result.rename new_name ## ALIAS int @@ -690,7 +685,8 @@ type Column truncate self = Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "truncate" [self] - self.make_unary_op "TRUNCATE" new_name + if self.value_type.is_integer then self.rename new_name else + roundlike_cast self.value_type <| self.make_unary_op "TRUNCATE" new_name ## Takes the ceiling of floating-point values, returning integer values. @@ -704,7 +700,8 @@ type Column ceil self = Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "ceil" [self] - self.make_unary_op "CEIL" new_name + if self.value_type.is_integer then self.rename new_name else + roundlike_cast self.value_type <| self.make_unary_op "CEIL" new_name ## Takes the floor of floating-point values, returning integer values. @@ -718,7 +715,8 @@ type Column floor self = Value_Type.expect_numeric self <| new_name = self.naming_helpers.function_name "floor" [self] - self.make_unary_op "FLOOR" new_name + if self.value_type.is_integer then self.rename new_name else + roundlike_cast self.value_type <| self.make_unary_op "FLOOR" new_name ## Returns a column of first non-`Nothing` value on each row of `self` and `values` list. @@ -1385,3 +1383,11 @@ adapt_unified_column column expected_type = SQL_Type_Reference.new column.connection column.context expression adapted = dialect.adapt_unified_column column.as_internal expected_type infer_return_type Column.Value name=column.name connection=column.connection sql_type_reference=adapted.sql_type_reference expression=adapted.expression context=column.context + +## PRIVATE + Cast the result of a round-like method to the correct type. +roundlike_cast : Value_Type -> Column -> Column +roundlike_cast value_type column = case value_type of + Value_Type.Integer _ -> column.cast value_type + Value_Type.Float _ -> column . cast Value_Type.Integer + Value_Type.Decimal _ _ -> column.cast value_type diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index e6051a08dec1..f95ad95bbbb7 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -1735,6 +1735,8 @@ type Table Error.throw (Illegal_Argument.Error "Cannot create a table with no columns.") False -> sql = preprocessed.to_sql + IO.println 'SQL' + IO.println <| sql.unsafe_to_raw_sql column_type_suggestions = preprocessed.internal_columns.map .sql_type_reference self.connection.read_statement sql column_type_suggestions diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 830c4b805e69..322dfa5d2099 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -350,15 +350,15 @@ make_not_equals a b = ## PRIVATE make_truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - Builder.code "CAST(TRUNC(" ++ arg ++ ") AS BIGINT)" + Builder.code "TRUNC(" ++ arg ++ ")" ## PRIVATE make_ceil = Base_Generator.lift_unary_op "CEIL" arg-> - Builder.code "CAST(CEIL(" ++ arg ++ ") AS BIGINT)" + Builder.code "CEIL(" ++ arg ++ ")" ## PRIVATE make_floor = Base_Generator.lift_unary_op "FLOOR" arg-> - Builder.code "CAST(FLOOR(" ++ arg ++ ") AS BIGINT)" + Builder.code "FLOOR(" ++ arg ++ ")" ## PRIVATE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 83468e5e9c78..e0c1434793fb 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -760,7 +760,12 @@ type Column truncate self = case self.value_type.is_numeric of True -> - simple_unary_op self "truncate" + case self.value_type.is_integer of + True -> + new_name = Naming_Helpers.function_name "truncate" [self] + self.rename new_name + False -> + simple_unary_op self "truncate" False -> case self.value_type == Value_Type.Date_Time of True -> fun = _.date @@ -778,7 +783,12 @@ type Column Column.from_vector "foo" [1.25, 2.33, 3.57] . ceil == (Column.from_vector "foo" [2, 3, 4]) ceil : Column ! Invalid_Value_Type ceil self = Value_Type.expect_numeric self <| - simple_unary_op self "ceil" + case self.value_type.is_integer of + True -> + new_name = Naming_Helpers.function_name "ceil" [self] + self.rename new_name + False -> + simple_unary_op self "ceil" ## Computes the nearest integer below this number for values in a numeric column. @@ -791,7 +801,12 @@ type Column Column.from_vector "foo" [1.25, 2.33, 3.57] . floor == (Column.from_vector "foo" [1, 2, 3]) floor : Column ! Invalid_Value_Type floor self = Value_Type.expect_numeric self <| - simple_unary_op self "floor" + case self.value_type.is_integer of + True -> + new_name = Naming_Helpers.function_name "floor" [self] + self.rename new_name + False -> + simple_unary_op self "floor" ## Returns a column of first non-`Nothing` value on each row of `self` and `values` list. diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso index 8b5bac0f7233..a028fff6f0b0 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso @@ -178,6 +178,13 @@ type Value_Type Value_Type.Integer _ -> True _ -> False + ## UNSTABLE + Checks if the `Value_Type` represents a decimal type. + is_decimal : Boolean + is_decimal self = case self of + Value_Type.Decimal _ _ -> True + _ -> False + ## UNSTABLE Checks if the `Value_Type` represents a type that holds a date. 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 bded34fe5745..76083e9c3160 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 @@ -3,6 +3,7 @@ import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument from Standard.Table import Value_Type +from Standard.Table.Data.Type.Value_Type import Bits from Standard.Table.Errors import all from Standard.Database.Errors import all @@ -19,9 +20,15 @@ spec setup = table_builder = setup.table_builder Test.group "gmt" <| - Test.specify "Should not allow the creation of a constant column of columns" <| - t = table_builder [["x", ["1", "2", "3"]]] - t.at "x" . const (t.at "x") . should_fail_with Illegal_Argument + if setup.test_selection.supports_decimal_type then + Test.specify "should return decimals when rounding decimals" <| + i1 = 9223372036854775807 - 1 + c = table_builder [["X", [i1]]] . at "X" + decimal_col = c.cast Value_Type.Decimal + decimal_col.value_type.is_decimal . should_be_true + decimal_col2 = decimal_col + decimal_col*decimal_col + [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> + op decimal_col2 . to_vector . should_equal [i1 + i1*i1] Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| @@ -453,42 +460,34 @@ spec setup = test_floatlike type = Test.specify "should allow round on a "+type.to_text+" column" <| - IO.println 'AAA ' - IO.println type.to_text table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . round result.to_vector.should_equal [0, 1, 3, 4, 0, -1, -3, -4] - result.value_type . is_integer . should_be_true Test.specify "should allow round on a float column (to >0 decimal places)" <| table = table_builder [["x", [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59]]] result = table.at "x" . cast type . round 1 result.to_vector.should_equal [0.5, 0.6, 3.5, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false Test.specify "should allow round on a float column (to <0 decimal places)" <| table = table_builder [["x", [51.2, 59.3, 351.45, 359.11, -51.2, -59.3, -351.23, -359.69]]] result = table.at "x" . cast type . round -1 result.to_vector.should_equal [50.0, 60.0, 350.0, 360.0, -50.0, -60.0, -350.0, -360.0] - result.value_type . is_integer . should_be_true Test.specify "should allow truncate on a "+type.to_text+" column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . truncate result.to_vector.should_equal [0, 0, 3, 3, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true Test.specify "should allow ceil on a "+type.to_text+" column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . ceil result.to_vector.should_equal [1, 1, 4, 4, 0, 0, -3, -3] - result.value_type . is_integer . should_be_true Test.specify "should allow floor on a "+type.to_text+" column" <| table = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] result = table.at "x" . cast type . floor result.to_vector.should_equal [0, 0, 3, 3, -1, -1, -4, -4] - result.value_type . is_integer . should_be_true test_floatlike Value_Type.Float if setup.test_selection.supports_decimal_type then @@ -498,35 +497,67 @@ spec setup = table = table_builder [["x", [1, 9, 31, 39, -1, -9, -31, -39]]] result = table.at "x" . round -1 result.to_vector.should_equal [0, 10, 30, 40, 0, -10, -30, -40] - result.value_type . is_integer . should_be_true Test.specify "should allow truncate on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . truncate result.to_vector.should_equal [0, 3, -3, 1, -2] - result.value_type . is_integer . should_be_true Test.specify "should allow ceil on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . ceil result.to_vector.should_equal [0, 3, -3, 1, -2] - result.value_type . is_integer . should_be_true Test.specify "should allow floor on an int column" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] result = table.at "x" . floor result.to_vector.should_equal [0, 3, -3, 1, -2] - result.value_type . is_integer . should_be_true Test.specify "should fail on decimal_places out of range" <| table = table_builder [["x", [0, 3, -3, 1, -2]]] table.at "x" . round 16 . should_fail_with Illegal_Argument + Test.group "return types" <| + roundlike_ops = [["floor", .floor], ["ceil", .ceil], ["truncate", .truncate], ["round", .round]] + + round_with pair = + op_name = pair.at 0 + op = pair.at 1 + + Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + op (col.cast Value_Type.Float) . value_type . should_equal Value_Type.Integer + if setup.test_selection.supports_decimal_type then + col.cast Value_Type.Decimal . round . value_type . should_equal Value_Type.Decimal + + Test.specify "rounding-like should return a column of the same type as the argument (integers): "+op_name <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + op (col.cast (Value_Type.Integer Bits.Bits_32)) . value_type . should_equal (Value_Type.Integer Bits.Bits_32) + op (col.cast (Value_Type.Integer Bits.Bits_64)) . value_type . should_equal (Value_Type.Integer Bits.Bits_64) + + roundlike_ops.each op-> round_with op + + Test.specify "should not lose precision rounding large integers" <| + i1 = 9223372036854775807 - 1 + i2 = i1 - 1 + t = table_builder [["X", [i1, i2]]] + [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> + op (t.at "X") . to_vector . should_equal [i1, i2] + + if setup.test_selection.supports_decimal_type pending="fails on postgres" then + Test.specify "should return decimals when rounding decimals" <| + i1 = 9223372036854775807 - 1 + c = table_builder [["X", [i1]]] . at "X" + decimal_col = c.cast Value_Type.Decimal + decimal_col.value_type.is_decimal . should_be_true + decimal_col2 = decimal_col + decimal_col*decimal_col + [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> + op decimal_col2 . to_vector . should_equal [i1 + i1*i1] + Test.specify "should allow Nothing/NULL" <| table = table_builder [["x", [Nothing, 0.51, 0.59, 3.51, Nothing, 3.59, -0.51, -0.59, -3.51, -3.59]]] result = table.at "x" . round 1 result.to_vector.should_equal [Nothing, 0.5, 0.6, 3.5, Nothing, 3.6, -0.5, -0.6, -3.5, -3.6] - result.value_type . is_integer . should_be_false Test.specify "should fail on bad column type" <| table = table_builder [["x", ["a", "b"]]] From 41bd4178aa51864f69a3b1dfac71b6831198b914 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 20 Jun 2023 12:32:44 -0400 Subject: [PATCH 50/83] more type checks for in-mem --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 3 +++ test/Table_Tests/src/In_Memory/Column_Spec.enso | 7 +++++-- 2 files changed, 8 insertions(+), 2 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 3d54458c4843..bf810107b018 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 @@ -1386,6 +1386,9 @@ adapt_unified_column column expected_type = ## PRIVATE Cast the result of a round-like method to the correct type. + - Integer types should retain their bit-width + - Floating point should return integer + - Decimal should stay Decimal (for precision reasons) roundlike_cast : Value_Type -> Column -> Column roundlike_cast value_type column = case value_type of Value_Type.Integer _ -> column.cast value_type diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 0105514263ae..4f6ab736709c 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -108,8 +108,9 @@ spec = Column.from_vector "foo" [-12.0, -24.0, -25.0, -29.0] . round -1 . should_equal <| Column.from_vector "round([foo])" [-10, -20, -30, -30] Test.specify "decimal rounding should return the correct column type" <| - Column.from_vector "foo" [1.2, 2.3, 3.6] . round . value_type . should_equal Value_Type.Integer - Column.from_vector "foo" [1.2, 2.3, 3.6] . round 1 . value_type . should_equal Value_Type.Float + Column.from_vector "foo" [1.21, 2.34, 3.68] . round -1 . value_type . should_equal Value_Type.Integer + Column.from_vector "foo" [1.21, 2.34, 3.68] . round . value_type . should_equal Value_Type.Integer + Column.from_vector "foo" [1.21, 2.34, 3.68] . round 1 . value_type . should_equal Value_Type.Float Test.specify "should be able to round a column of integers" <| Column.from_vector "foo" [12, 24, 25, 29] . round . should_equal <| Column.from_vector "round([foo])" [12, 24, 25, 29] @@ -117,6 +118,8 @@ spec = Column.from_vector "foo" [15, 25, 35] . round -1 use_bankers=True . should_equal <| Column.from_vector "round([foo])" [20, 20, 40] Test.specify "integer rounding should return the correct column type" <| + Column.from_vector "foo" [12, 24, 25, 29] . round 1 . value_type . should_equal Value_Type.Integer + Column.from_vector "foo" [12, 24, 25, 29] . round 0 . value_type . should_equal Value_Type.Integer Column.from_vector "foo" [12, 24, 25, 29] . round -1 . value_type . should_equal Value_Type.Integer Test.specify "should report out-of-range values as problems" <| From 4d0627b0134cc8187f38f98d856302626c3d6f42 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 21 Jun 2023 10:09:02 -0400 Subject: [PATCH 51/83] input check for in-mem round --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 32 ++----------- .../src/Internal/Decimal_Places_Helpers.enso | 22 --------- .../src/Internal/Rounding_Helpers.enso | 48 +++++++++++++++++++ .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 4 +- .../Database/0.0.0-dev/src/Data/Column.enso | 2 +- .../Table/0.0.0-dev/src/Data/Column.enso | 45 ++++++++++++----- .../src/In_Memory/Column_Spec.enso | 16 ++----- 7 files changed, 92 insertions(+), 77 deletions(-) delete mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso create mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Rounding_Helpers.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 33b961d9bee9..290732b169bb 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -1,5 +1,5 @@ import project.Any.Any -import project.Internal.Decimal_Places_Helpers +import project.Internal.Rounding_Helpers import project.Data.Text.Text import project.Data.Locale.Locale import project.Errors.Common.Arithmetic_Error @@ -610,12 +610,12 @@ type Decimal 2.5 . round use_bankers=True == 2 round : Integer -> Boolean -> Integer | Decimal ! Illegal_Argument round self decimal_places=0 use_bankers=False = - Decimal_Places_Helpers.check_decimal_places decimal_places <| + Rounding_Helpers.check_decimal_places decimal_places <| case self.is_nan || self.is_infinite of True -> msg = "round cannot accept " + self.to_text Error.throw (Arithmetic_Error.Error msg) - False -> check_round_input self <| + False -> Rounding_Helpers.check_round_input self <| decimal_result = # Algorithm taken from https://stackoverflow.com/a/7211688 scale = 10 ^ decimal_places @@ -937,7 +937,7 @@ type Integer ## It's already an integer so unless decimal_places is negative, the value is unchanged. if decimal_places >= 0 then self else - Decimal_Places_Helpers.check_decimal_places decimal_places <| check_round_input self <| + Rounding_Helpers.check_decimal_places decimal_places <| Rounding_Helpers.check_round_input self <| scale = 10 ^ -decimal_places halfway = scale.div 2 remainder = self % scale @@ -1122,27 +1122,3 @@ type Number_Parse_Error to_display_text : Text to_display_text = "Could not parse " + self.text.to_text + " as a double." - -## PRIVATE - The largest smallInteger (Long) that integer round can handle. Above 14 - digits, it is possible that the underlying long, converted to double in the - rounding process, would lose precision in the least significant bits. - (See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.) -round_max_long : Integer -round_max_long = 99999999999999 - -## PRIVATE - The largest smallInteger (Long) that integer round can handle. Above 14 - digits, it is possible that the underlying long, converted to double in the - rounding process, would lose precision in the least significant bits. - (See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.) -round_min_long : Integer -round_min_long = -99999999999999 - -## PRIVATE - Restrict allowed range of input to rounding methods. -check_round_input : Number -> Function -> Any ! Illegal_Argument -check_round_input n ~action = - if n >= round_min_long && n <= round_max_long then action else - msg = "Error: `round` can only accept values between " + round_min_long.to_text + " and " + round_max_long.to_text + " (inclusive), but was " + n.to_text - Error.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso deleted file mode 100644 index e90e755523c3..000000000000 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Decimal_Places_Helpers.enso +++ /dev/null @@ -1,22 +0,0 @@ -import project.Any.Any -import project.Data.Numbers.Integer -import project.Error.Error -import project.Errors.Illegal_Argument.Illegal_Argument - -## PRIVATE - The smallest allowed value for the `decimal_places` argument to `round` -round_min_decimal_places : Integer -round_min_decimal_places = -15 - -## PRIVATE - The largest allowed value for the `decimal_places` argument to `round` -round_max_decimal_places : Integer -round_max_decimal_places = 15 - -## PRIVATE - Restrict rounding decimal_places parameter. -check_decimal_places : Integer -> Any -> Any ! Illegal_Argument -check_decimal_places decimal_places ~action = - if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else - msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text - Error.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Rounding_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Rounding_Helpers.enso new file mode 100644 index 000000000000..691edd6960f2 --- /dev/null +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Rounding_Helpers.enso @@ -0,0 +1,48 @@ +import project.Any.Any +import project.Data.Numbers.Number +import project.Data.Numbers.Integer +import project.Error.Error +import project.Errors.Illegal_Argument.Illegal_Argument +import project.Function.Function + +## PRIVATE + The largest smallInteger (Long) that integer round can handle. Above 14 + digits, it is possible that the underlying long, converted to double in the + rounding process, would lose precision in the least significant bits. + (See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.) +round_max_long : Integer +round_max_long = 99999999999999 + +## PRIVATE + The largest smallInteger (Long) that integer round can handle. Above 14 + digits, it is possible that the underlying long, converted to double in the + rounding process, would lose precision in the least significant bits. + (See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.) +round_min_long : Integer +round_min_long = -99999999999999 + +## PRIVATE + Restrict allowed range of input to rounding methods. +check_round_input : Number -> Function -> Any ! Illegal_Argument +check_round_input n ~action = + if n >= round_min_long && n <= round_max_long then action else + msg = "Error: `round` can only accept values between " + round_min_long.to_text + " and " + round_max_long.to_text + " (inclusive), but was " + n.to_text + Error.throw (Illegal_Argument.Error msg) + +## PRIVATE + The smallest allowed value for the `decimal_places` argument to `round` +round_min_decimal_places : Integer +round_min_decimal_places = -15 + +## PRIVATE + The largest allowed value for the `decimal_places` argument to `round` +round_max_decimal_places : Integer +round_max_decimal_places = 15 + +## PRIVATE + Restrict rounding decimal_places parameter. +check_decimal_places : Integer -> Any -> Any ! Illegal_Argument +check_decimal_places decimal_places ~action = + if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else + msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text + Error.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index 0766436f50e1..4b6fd64f4b1c 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -10,7 +10,7 @@ import project.Data.Vector.Vector import project.Error.Error import project.Errors import project.Function -import project.Internal.Decimal_Places_Helpers +import project.Internal.Rounding_Helpers import project.IO import project.Math import project.Meta @@ -43,7 +43,7 @@ export project.Data.Text.Text export project.Data.Vector.Vector export project.Error.Error export project.Errors -export project.Internal.Decimal_Places_Helpers +export project.Internal.Rounding_Helpers export project.IO export project.Math export project.Meta 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 bf810107b018..210ddabb3216 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 @@ -618,7 +618,7 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Decimal_Places_Helpers.check_decimal_places decimal_places <| + Rounding_Helpers.check_decimal_places decimal_places <| round_maybe = if decimal_places <= 0 then roundlike_cast self.value_type else identity result_uncast = case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index e0c1434793fb..3926854e27a2 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -720,25 +720,39 @@ type Column Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Decimal_Places_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| + Rounding_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| # If it's an integer column and decimal_places >=0 then it's a no-op. new_name = Naming_Helpers.function_name "round" [self] if self.value_type.is_integer && decimal_places >= 0 then self.rename new_name else - scale = 10 ^ decimal_places - scaled = self * scale - round_base = scaled.floor - round_midpoint = (round_base + 0.5) / scale - even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else self >= 0 - do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - should_cast_to_integer = decimal_places <= 0 || self.value_type.is_integer - result = if should_cast_to_integer.not then result_float else - result_float.cast Value_Type.Integer - + target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type + result = self.check_round_input target_value_type <| + scale = 10 ^ decimal_places + scaled = self * scale + round_base = scaled.floor + round_midpoint = (round_base + 0.5) / scale + even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) + half_goes_up = if use_bankers then even_is_up else self >= 0 + do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) + result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + cast_if_not result_float target_value_type result.rename new_name + ## PRIVATE + Restrict allowed range of input to rounding methods. + check_round_input : Column -> Value_Type -> Any ! Illegal_Argument + check_round_input self target_value_type column = + min = Rounding_Helpers.round_min_long + max = Rounding_Helpers.round_max_long + within_range = self >= self.const min && self <= self.const max + warning = Illegal_Argument.Error <| "Error: `round` can only accept values between " + min.to_text + " and " + max.to_text + " (inclusive)" + nothing_with_problem = Warning.attach warning Nothing + ## TODO: This should be cast to NULL when that type is available, + see https://github.com/enso-org/enso/issues/6281. + nothings = self.const nothing_with_problem . cast target_value_type + result = within_range.iif column nothings + result.rename column.name + ## ALIAS int If the column is numeric, truncate the floating-point values to an @@ -2068,3 +2082,8 @@ wrap_text_argument_as_value_provider val = col : Column -> storage = col.java_column.getStorage i-> storage.getItemBoxed i + +## PRIVATE + Cast a column to a `Value_Type`, unless it already has that type. +cast_if_not : Column -> Value_Type -> Column +cast_if_not column value_type = if column.value_type == value_type then column else column.cast value_type diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 4f6ab736709c..aeff3ed9fda7 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -122,22 +122,16 @@ spec = Column.from_vector "foo" [12, 24, 25, 29] . round 0 . value_type . should_equal Value_Type.Integer Column.from_vector "foo" [12, 24, 25, 29] . round -1 . value_type . should_equal Value_Type.Integer - Test.specify "should report out-of-range values as problems" <| + Test.specify "should report out-of-range values as warnings" <| col = Column.from_vector "foo" [12, 23, 99999999999999999] expected = Column.from_vector "round([foo])" [10, 20, Nothing] - col.round -1 . should_equal expected - action = col.round -1 on_problems=_ - problems = [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive), but was 99999999999999999"] - tester = _.should_equal expected - Problems.test_problem_handling action problems tester + actual = col.round -1 + actual . should_equal expected + Warning.get_all actual . map .value . should_equal [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive)"] Test.specify "should throw an error on decimal places out of range" <| col = Column.from_vector "foo" [12, 23, 99999999999999999] - expected = Column.from_vector "round([foo])" [Nothing, Nothing, Nothing] - action = col.round decimal_places=-1200 on_problems=_ - problems = [Illegal_Argument.Error "round: decimal_places must be between -15 and 15 (inclusive), but was -1200"] - tester = _.should_equal expected - Problems.test_problem_handling action problems tester + col.round decimal_places=-1200 . should_fail_with Illegal_Argument Test.group "truncate" <| Test.specify "should be able to truncate a column of floats" <| From 0ad7ef43cd0f16f3f4c72cf0fc7f01e04fe7dc2b Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 21 Jun 2023 12:10:44 -0400 Subject: [PATCH 52/83] use make_function --- .../0.0.0-dev/src/Internal/Base_Generator.enso | 14 +------------- .../Column_Operations_Spec.enso | 4 ++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 322dfa5d2099..967c4c8a797a 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -175,7 +175,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], make_truncate, make_ceil, make_floor] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["TRUNCATE", make_function "TRUNC"], ["CEIL", make_function "CEIL"], ["FLOOR", make_function "FLOOR"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals @@ -348,18 +348,6 @@ make_equals a b = make_not_equals a b = a.paren ++ " != " ++ b.paren -## PRIVATE -make_truncate = Base_Generator.lift_unary_op "TRUNCATE" arg-> - Builder.code "TRUNC(" ++ arg ++ ")" - -## PRIVATE -make_ceil = Base_Generator.lift_unary_op "CEIL" arg-> - Builder.code "CEIL(" ++ arg ++ ")" - -## PRIVATE -make_floor = Base_Generator.lift_unary_op "FLOOR" arg-> - Builder.code "FLOOR(" ++ arg ++ ")" - ## PRIVATE Builds code for an ordering. 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 76083e9c3160..170f06926eec 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 @@ -544,8 +544,8 @@ spec setup = [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> op (t.at "X") . to_vector . should_equal [i1, i2] - if setup.test_selection.supports_decimal_type pending="fails on postgres" then - Test.specify "should return decimals when rounding decimals" <| + if setup.test_selection.supports_decimal_type then + Test.specify "should return decimals when rounding decimals" pending="fails on postgres" <| i1 = 9223372036854775807 - 1 c = table_builder [["X", [i1]]] . at "X" decimal_col = c.cast Value_Type.Decimal From 0a350c21e9d26e75a2f5e28a0e92e40f353f10b4 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 21 Jun 2023 15:47:39 -0400 Subject: [PATCH 53/83] wip --- .../Database/0.0.0-dev/src/Data/Column.enso | 14 ++++++++++++- .../Internal/Postgres/Postgres_Dialect.enso | 4 ++-- .../0.0.0-dev/src/Internal/Upload_Table.enso | 2 ++ .../Column_Operations_Spec.enso | 21 +++++++++++++++++-- 4 files changed, 36 insertions(+), 5 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 210ddabb3216..b079f4ba7e4f 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 @@ -635,14 +635,26 @@ type Column round_float : Integer -> Boolean -> Column round_float self decimal_places use_bankers = new_name = self.naming_helpers.function_name "round" [self] + #scale = (self.const (10 ^ decimal_places)) . cast Value_Type.Decimal scale = 10 ^ decimal_places scaled = self * scale - round_base = scaled.floor + round_base = scaled.floor . rename "rb" round_midpoint = (round_base + 0.5) / scale even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + #result = round_midpoint + + ## + rb5 = round_base + 0.5 + round_midpoint2 = rb5 / scale + IO.println 'HMMM' + #IO.println <| scale.to_vector + IO.println <| rb5.to_vector + IO.println <| round_midpoint.to_vector + IO.println <| round_midpoint2.to_vector + result.rename new_name ## PRIVATE diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index b7286b9d910f..43c4ef21336b 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -446,11 +446,11 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> ## PRIVATE decimal_div = Base_Generator.lift_binary_op "/" x-> y-> - Builder.code "CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)" + Builder.code "CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)" ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> - x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y + x ++ " - FLOOR(CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)) * " ++ y ## PRIVATE make_extract_as_int enso_name sql_name = diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso index d643300a4ab9..71d44497b1b7 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso @@ -35,6 +35,8 @@ create_table_structure connection table_name structure primary_key temporary all if aligned_structure.is_empty then Error.throw (Illegal_Argument.Error "An empty table cannot be created: the `structure` must consist of at list one column description.") else resolved_primary_key = resolve_primary_key aligned_structure primary_key create_table_statement = prepare_create_table_statement connection effective_table_name aligned_structure resolved_primary_key temporary on_problems + IO.println 'create_table_statement' + IO.println create_table_statement update_result = create_table_statement.if_not_error <| connection.execute_update create_table_statement update_result.if_not_error <| 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 170f06926eec..fc5d56ed8a16 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 @@ -24,11 +24,28 @@ spec setup = Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 c = table_builder [["X", [i1]]] . at "X" + IO.println "CTYPE" + IO.println c.value_type + IO.println c.to_vector decimal_col = c.cast Value_Type.Decimal decimal_col.value_type.is_decimal . should_be_true decimal_col2 = decimal_col + decimal_col*decimal_col - [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> - op decimal_col2 . to_vector . should_equal [i1 + i1*i1] + #[(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> + [(x-> x.round 0)].each op-> + k = op decimal_col2 . rename "foo" + df = (k - decimal_col2) . rename "foo" + eq = (k == decimal_col2) . rename "foo" + IO.println 'KAY' + IO.println <| c.value_type + IO.println <| c.to_vector + IO.println <| k.value_type + IO.println <| k.to_vector + IO.println <| df.value_type + IO.println <| df.to_vector + IO.println <| eq.value_type + IO.println <| eq.to_vector + #IO.println <| ((op decimal_col2) - decimal_col2) . rename "foo" . to_vector + #((op decimal_col2) == decimal_col2) . rename "foo" . to_vector . should_equal [True] Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| From d6fe5ce63b16ff775563106f129874697cc78143 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 22 Jun 2023 12:42:21 -0400 Subject: [PATCH 54/83] wrapper --- .../Database/0.0.0-dev/src/Data/Column.enso | 24 ++++++++++++------- .../Internal/Postgres/Postgres_Dialect.enso | 4 ++-- 2 files changed, 18 insertions(+), 10 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 b079f4ba7e4f..5d88dafff838 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 @@ -620,10 +620,11 @@ type Column round self decimal_places=0 use_bankers=False = Rounding_Helpers.check_decimal_places decimal_places <| round_maybe = if decimal_places <= 0 then roundlike_cast self.value_type else identity + decimal_const x = self.const x . cast Value_Type.Decimal result_uncast = case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers Value_Type.Float _ -> self.round_float decimal_places use_bankers - Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers + Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers decimal_const _ -> expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) @@ -632,18 +633,25 @@ type Column ## PRIVATE Round a float-like column. - round_float : Integer -> Boolean -> Column - round_float self decimal_places use_bankers = + round_float : Integer -> Boolean -> (Any -> Column) -> Column + round_float self decimal_places use_bankers const_wrapper=self.const = + k = const_wrapper new_name = self.naming_helpers.function_name "round" [self] #scale = (self.const (10 ^ decimal_places)) . cast Value_Type.Decimal - scale = 10 ^ decimal_places + ## + point_five = self.const 0.5 . cast Value_Type.Decimal + zero = self.const 0 . cast Value_Type.Decimal + one = self.const 1 . cast Value_Type.Decimal + two = self.const 2 . cast Value_Type.Decimal + ten = self.const 10 . cast Value_Type.Decimal + scale = k 10 ^ decimal_places scaled = self * scale round_base = scaled.floor . rename "rb" - round_midpoint = (round_base + 0.5) / scale - even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else self >= 0 + round_midpoint = (round_base + k 0.5) / scale + even_is_up = (self >= k 0).iif ((scaled.truncate % k 2) != k 0) ((scaled.truncate % k 2) == k 0) + half_goes_up = if use_bankers then even_is_up else self >= k 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + result = do_round_up.iif ((round_base + k 1) / scale) (round_base / scale) #result = round_midpoint ## diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index 43c4ef21336b..b7286b9d910f 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -446,11 +446,11 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> ## PRIVATE decimal_div = Base_Generator.lift_binary_op "/" x-> y-> - Builder.code "CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)" + Builder.code "CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)" ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> - x ++ " - FLOOR(CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)) * " ++ y + x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y ## PRIVATE make_extract_as_int enso_name sql_name = From 395a3b654349125bf1e0db34c1f0debcd754bebf Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 22 Jun 2023 13:21:22 -0400 Subject: [PATCH 55/83] round_decimal --- .../Database/0.0.0-dev/src/Data/Column.enso | 65 +++++++++++-------- .../src/Internal/Base_Generator.enso | 1 + .../Internal/Postgres/Postgres_Dialect.enso | 12 +++- .../src/Internal/SQLite/SQLite_Dialect.enso | 4 +- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- 5 files changed, 53 insertions(+), 31 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 5d88dafff838..158b3ea059f5 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 @@ -620,11 +620,10 @@ type Column round self decimal_places=0 use_bankers=False = Rounding_Helpers.check_decimal_places decimal_places <| round_maybe = if decimal_places <= 0 then roundlike_cast self.value_type else identity - decimal_const x = self.const x . cast Value_Type.Decimal result_uncast = case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers Value_Type.Float _ -> self.round_float decimal_places use_bankers - Value_Type.Decimal _ _ -> self.round_float decimal_places use_bankers decimal_const + Value_Type.Decimal _ _ -> self.round_decimal decimal_places use_bankers _ -> expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) @@ -633,36 +632,34 @@ type Column ## PRIVATE Round a float-like column. - round_float : Integer -> Boolean -> (Any -> Column) -> Column - round_float self decimal_places use_bankers const_wrapper=self.const = - k = const_wrapper + round_float : Integer -> Boolean -> Column + round_float self decimal_places use_bankers = + new_name = self.naming_helpers.function_name "round" [self] + scale = 10 ^ decimal_places + scaled = self * scale + round_base = scaled.floor . rename "rb" + round_midpoint = (round_base + 0.5) / scale + even_is_up = (self >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) + half_goes_up = if use_bankers then even_is_up else self >= 0 + do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) + result = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) + result.rename new_name + + ## PRIVATE + Round a float-like column. + round_decimal : Integer -> Boolean -> Column + round_decimal self decimal_places use_bankers = + # Construct a constant Decimal column. + k x = self.const x . cast Value_Type.Decimal new_name = self.naming_helpers.function_name "round" [self] - #scale = (self.const (10 ^ decimal_places)) . cast Value_Type.Decimal - ## - point_five = self.const 0.5 . cast Value_Type.Decimal - zero = self.const 0 . cast Value_Type.Decimal - one = self.const 1 . cast Value_Type.Decimal - two = self.const 2 . cast Value_Type.Decimal - ten = self.const 10 . cast Value_Type.Decimal scale = k 10 ^ decimal_places scaled = self * scale round_base = scaled.floor . rename "rb" - round_midpoint = (round_base + k 0.5) / scale - even_is_up = (self >= k 0).iif ((scaled.truncate % k 2) != k 0) ((scaled.truncate % k 2) == k 0) + round_midpoint = (round_base + k 0.5).decimal_div scale + even_is_up = (self >= k 0).iif ((scaled.truncate.decimal_mod (k 2)) != k 0) ((scaled.truncate.decimal_mod (k 2)) == k 0) half_goes_up = if use_bankers then even_is_up else self >= k 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) - result = do_round_up.iif ((round_base + k 1) / scale) (round_base / scale) - #result = round_midpoint - - ## - rb5 = round_base + 0.5 - round_midpoint2 = rb5 / scale - IO.println 'HMMM' - #IO.println <| scale.to_vector - IO.println <| rb5.to_vector - IO.println <| round_midpoint.to_vector - IO.println <| round_midpoint2.to_vector - + result = do_round_up.iif ((round_base + k 1).decimal_div scale) (round_base.decimal_div scale) result.rename new_name ## PRIVATE @@ -738,6 +735,22 @@ type Column if self.value_type.is_integer then self.rename new_name else roundlike_cast self.value_type <| self.make_unary_op "FLOOR" new_name + ## PRVIATE + `/` for decimals. + decimal_div : Column -> Column ! Invalid_Value_Type + decimal_div self other = + Value_Type.expect_numeric self <| Value_Type.expect_numeric other <| + new_name = self.naming_helpers.function_name "decimal_div" [self, other] + self.make_binary_op "DECIMAL_DIV" other new_name + + ## PRVIATE + `mod` for decimals. + decimal_mod : Column -> Column ! Invalid_Value_Type + decimal_mod self other = + Value_Type.expect_numeric self <| Value_Type.expect_numeric other <| + new_name = self.naming_helpers.function_name "decimal_mod" [self, other] + self.make_binary_op "DECIMAL_MOD" other new_name + ## Returns a column of first non-`Nothing` value on each row of `self` and `values` list. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 967c4c8a797a..dd81dfb2d367 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -175,6 +175,7 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] + # ["decimal_div", make_function "DECIMAL_DIV"], ["decimal_mod", make_function "DECIMAL_MOD"], arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["TRUNCATE", make_function "TRUNC"], ["CEIL", make_function "CEIL"], ["FLOOR", make_function "FLOOR"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index b7286b9d910f..c1ad9b7a79d6 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -216,7 +216,7 @@ make_internal_generator_dialect = cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]] text = [starts_with, contains, ends_with, agg_shortest, agg_longest, make_case_sensitive]+concat_ops+cases+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] - arith_extensions = [is_nan, decimal_div, mod_op, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] + arith_extensions = [is_nan, floating_point_div, mod_op, decimal_div, decimal_mod, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] bool = [bool_or] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] @@ -445,13 +445,21 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> Builder.code "bool_or(" ++ arg ++ ")" ## PRIVATE -decimal_div = Base_Generator.lift_binary_op "/" x-> y-> +floating_point_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)" ## PRIVATE mod_op = Base_Generator.lift_binary_op "mod" x-> y-> x ++ " - FLOOR(CAST(" ++ x ++ " AS double precision) / CAST(" ++ y ++ " AS double precision)) * " ++ y +## PRIVATE +decimal_div = Base_Generator.lift_binary_op "DECIMAL_DIV" x-> y-> + Builder.code "CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)" + +## PRIVATE +decimal_mod = Base_Generator.lift_binary_op "DECIMAL_MOD" x-> y-> + x ++ " - FLOOR(CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)) * " ++ y + ## PRIVATE make_extract_as_int enso_name sql_name = Base_Generator.lift_unary_op enso_name arg-> diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 6ac22bbd2b07..3c5fb10c81fc 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [decimal_div, mod_op] + arith_extensions = [floating_point_div, mod_op] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -358,7 +358,7 @@ bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> Builder.code "max(" ++ arg ++ ")" ## PRIVATE -decimal_div = Base_Generator.lift_binary_op "/" x-> y-> +floating_point_div = Base_Generator.lift_binary_op "/" x-> y-> Builder.code "CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)" ## PRIVATE 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 e2294eb0d6bc..d3c93a84bb4c 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 @@ -177,7 +177,7 @@ operations_map = 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", "TRUNCATE", "CEIL", "FLOOR"] + always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE", "CEIL", "FLOOR", "DECIMAL_DIV", "DECIMAL_MOD"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] From 10fa61cc49c8afe59b37797ce543444d795920ef Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 22 Jun 2023 16:26:24 -0400 Subject: [PATCH 56/83] sl --- .../Database/0.0.0-dev/src/Data/Table.enso | 2 - .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- .../0.0.0-dev/src/Internal/Upload_Table.enso | 2 - .../Column_Operations_Spec.enso | 37 +------------------ .../src/Common_Table_Operations/Main.enso | 5 ++- .../Table_Tests/src/Database/SQLite_Spec.enso | 2 +- 6 files changed, 8 insertions(+), 42 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index f95ad95bbbb7..e6051a08dec1 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -1735,8 +1735,6 @@ type Table Error.throw (Illegal_Argument.Error "Cannot create a table with no columns.") False -> sql = preprocessed.to_sql - IO.println 'SQL' - IO.println <| sql.unsafe_to_raw_sql column_type_suggestions = preprocessed.internal_columns.map .sql_type_reference self.connection.read_statement sql column_type_suggestions 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 d3c93a84bb4c..e2294eb0d6bc 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 @@ -177,7 +177,7 @@ operations_map = 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", "TRUNCATE", "CEIL", "FLOOR", "DECIMAL_DIV", "DECIMAL_MOD"] + always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS", "TRUNCATE", "CEIL", "FLOOR"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso index 71d44497b1b7..d643300a4ab9 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso @@ -35,8 +35,6 @@ create_table_structure connection table_name structure primary_key temporary all if aligned_structure.is_empty then Error.throw (Illegal_Argument.Error "An empty table cannot be created: the `structure` must consist of at list one column description.") else resolved_primary_key = resolve_primary_key aligned_structure primary_key create_table_statement = prepare_create_table_statement connection effective_table_name aligned_structure resolved_primary_key temporary on_problems - IO.println 'create_table_statement' - IO.println create_table_statement update_result = create_table_statement.if_not_error <| connection.execute_update create_table_statement update_result.if_not_error <| 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 fc5d56ed8a16..6d534afaf095 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 @@ -19,34 +19,6 @@ spec setup = prefix = setup.prefix table_builder = setup.table_builder - Test.group "gmt" <| - if setup.test_selection.supports_decimal_type then - Test.specify "should return decimals when rounding decimals" <| - i1 = 9223372036854775807 - 1 - c = table_builder [["X", [i1]]] . at "X" - IO.println "CTYPE" - IO.println c.value_type - IO.println c.to_vector - decimal_col = c.cast Value_Type.Decimal - decimal_col.value_type.is_decimal . should_be_true - decimal_col2 = decimal_col + decimal_col*decimal_col - #[(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> - [(x-> x.round 0)].each op-> - k = op decimal_col2 . rename "foo" - df = (k - decimal_col2) . rename "foo" - eq = (k == decimal_col2) . rename "foo" - IO.println 'KAY' - IO.println <| c.value_type - IO.println <| c.to_vector - IO.println <| k.value_type - IO.println <| k.to_vector - IO.println <| df.value_type - IO.println <| df.to_vector - IO.println <| eq.value_type - IO.println <| eq.to_vector - #IO.println <| ((op decimal_col2) - decimal_col2) . rename "foo" . to_vector - #((op decimal_col2) == decimal_col2) . rename "foo" . to_vector . should_equal [True] - Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| t = table_builder [["X", [True, False, Nothing, True]]] @@ -541,15 +513,10 @@ spec setup = op_name = pair.at 0 op = pair.at 1 - Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| - col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" - op (col.cast Value_Type.Float) . value_type . should_equal Value_Type.Integer - if setup.test_selection.supports_decimal_type then - col.cast Value_Type.Decimal . round . value_type . should_equal Value_Type.Decimal - Test.specify "rounding-like should return a column of the same type as the argument (integers): "+op_name <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" - op (col.cast (Value_Type.Integer Bits.Bits_32)) . value_type . should_equal (Value_Type.Integer Bits.Bits_32) + bits_32_expected = if setup.test_selection.all_integers_64_bit then Value_Type.Integer Bits.Bits_64 else Value_Type.Integer Bits.Bits_32 + op (col.cast (Value_Type.Integer Bits.Bits_32)) . value_type . should_equal bits_32_expected op (col.cast (Value_Type.Integer Bits.Bits_64)) . value_type . should_equal (Value_Type.Integer Bits.Bits_64) roundlike_ops.each op-> round_with op diff --git a/test/Table_Tests/src/Common_Table_Operations/Main.enso b/test/Table_Tests/src/Common_Table_Operations/Main.enso index ee31e82213ac..c0b2c1f3948c 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Main.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Main.enso @@ -88,7 +88,10 @@ type Test_Selection - date_time: Specifies if the backend supports date/time operations. - fixed_length_text_columns: Specifies if the backend supports fixed length text columns. - Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False supports_decimal_type=False + - supports_decimal_type: Specifies if the backend supports the `Decimal` + high-precision type. + - all_integers_64_bit: True if the backend only has 64-bit integers. + Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False supports_decimal_type=False all_integers_64_bit=False spec setup = Core_Spec.spec setup diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 425b3327a9ff..8c57f3d6491c 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -143,7 +143,7 @@ sqlite_spec connection prefix = Common_Spec.spec prefix connection - common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False + common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False all_integers_64_bit=True ## For now `advanced_stats`, `first_last`, `text_shortest_longest` and `multi_distinct` remain disabled, because SQLite does not provide the From 086fb18ec0938c0614a5096962b373b359e93e6c Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 11:10:05 -0400 Subject: [PATCH 57/83] round etc return types --- .../Database/0.0.0-dev/src/Data/Column.enso | 50 +++++++++------- .../Internal/Postgres/Postgres_Dialect.enso | 6 +- .../src/Internal/SQLite/SQLite_Dialect.enso | 10 +++- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- .../0.0.0-dev/src/Data/Type/Value_Type.enso | 4 ++ .../Column_Operations_Spec.enso | 60 +++++++++++-------- .../src/Database/Postgres_Spec.enso | 12 ---- .../Table_Tests/src/Database/SQLite_Spec.enso | 16 +---- 8 files changed, 85 insertions(+), 75 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 158b3ea059f5..0c103632d341 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 @@ -618,17 +618,14 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Rounding_Helpers.check_decimal_places decimal_places <| - round_maybe = if decimal_places <= 0 then roundlike_cast self.value_type else identity - result_uncast = case self.value_type of + Rounding_Helpers.check_decimal_places decimal_places <| short_circuit_special_floating_point self <| + case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers Value_Type.Float _ -> self.round_float decimal_places use_bankers Value_Type.Decimal _ _ -> self.round_decimal decimal_places use_bankers _ -> expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) - result_uncast.if_not_error <| - round_maybe result_uncast ## PRIVATE Round a float-like column. @@ -700,10 +697,10 @@ type Column example_truncate = Examples.decimal_column.truncate truncate : Column ! Invalid_Value_Type truncate self = - Value_Type.expect_numeric self <| + Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| new_name = self.naming_helpers.function_name "truncate" [self] if self.value_type.is_integer then self.rename new_name else - roundlike_cast self.value_type <| self.make_unary_op "TRUNCATE" new_name + self.make_unary_op "TRUNCATE" new_name ## Takes the ceiling of floating-point values, returning integer values. @@ -715,10 +712,10 @@ type Column example_truncate = Examples.decimal_column.ceil ceil : Column ! Invalid_Value_Type ceil self = - Value_Type.expect_numeric self <| + Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| new_name = self.naming_helpers.function_name "ceil" [self] if self.value_type.is_integer then self.rename new_name else - roundlike_cast self.value_type <| self.make_unary_op "CEIL" new_name + self.make_unary_op "CEIL" new_name ## Takes the floor of floating-point values, returning integer values. @@ -730,10 +727,10 @@ type Column example_truncate = Examples.decimal_column.floor floor : Column ! Invalid_Value_Type floor self = - Value_Type.expect_numeric self <| + Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| new_name = self.naming_helpers.function_name "floor" [self] if self.value_type.is_integer then self.rename new_name else - roundlike_cast self.value_type <| self.make_unary_op "FLOOR" new_name + self.make_unary_op "FLOOR" new_name ## PRVIATE `/` for decimals. @@ -824,10 +821,21 @@ type Column Returns a column of booleans, with `True` items at the positions where this column contains a NaN. This is only applicable to double columns. is_nan : Column - is_nan self = Value_Type.expect_floating_point self <| + is_nan self = + IO.println 'IS_NAN' + Value_Type.expect_floating_point self <| new_name = self.naming_helpers.function_name "is_nan" [self] self.make_unary_op "IS_NAN" new_name + ## UNSTABLE + Returns a column of booleans, with `True` items at the positions where + this column contains a +Inf/-Inf. This is only applicable to double + columns. + is_infinite : Column + is_infinite self = Value_Type.expect_floating_point self <| + new_name = self.naming_helpers.function_name "is_infinite" [self] + self.make_unary_op "IS_INF" new_name + ## PRIVATE Returns a column of booleans, with `True` items at the positions where this column contains an empty string or `Nothing`. @@ -1417,13 +1425,11 @@ adapt_unified_column column expected_type = adapted = dialect.adapt_unified_column column.as_internal expected_type infer_return_type Column.Value name=column.name connection=column.connection sql_type_reference=adapted.sql_type_reference expression=adapted.expression context=column.context -## PRIVATE - Cast the result of a round-like method to the correct type. - - Integer types should retain their bit-width - - Floating point should return integer - - Decimal should stay Decimal (for precision reasons) -roundlike_cast : Value_Type -> Column -> Column -roundlike_cast value_type column = case value_type of - Value_Type.Integer _ -> column.cast value_type - Value_Type.Float _ -> column . cast Value_Type.Integer - Value_Type.Decimal _ _ -> column.cast value_type +## PRVIATE + If the input is NaN/Inf, return it immediately as the value; otherwise + return the expression. If returning immediately, rename to the expression's + name. If the input is not floating point, just return the expression. +short_circuit_special_floating_point : Column -> Column -> Column +short_circuit_special_floating_point input exp = + if input.value_type.is_floating_point.not then exp else + ((input.is_nan || input.is_infinite).iif input exp).rename exp.name diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index c1ad9b7a79d6..ab72f20dc169 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -216,7 +216,7 @@ make_internal_generator_dialect = cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]] text = [starts_with, contains, ends_with, agg_shortest, agg_longest, make_case_sensitive]+concat_ops+cases+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] - arith_extensions = [is_nan, floating_point_div, mod_op, decimal_div, decimal_mod, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] + arith_extensions = [is_nan, is_inf, floating_point_div, mod_op, decimal_div, decimal_mod, ["ROW_MIN", Base_Generator.make_function "LEAST"], ["ROW_MAX", Base_Generator.make_function "GREATEST"]] bool = [bool_or] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] @@ -440,6 +440,10 @@ make_order_descriptor internal_column sort_direction text_ordering = is_nan = Base_Generator.lift_unary_op "IS_NAN" arg-> (arg ++ " = double precision 'NaN'").paren +## PRIVATE +is_inf = Base_Generator.lift_unary_op "IS_INF" arg-> + (arg ++ " in (double precision 'Infinity', double precision '-Infinity')").paren + ## PRIVATE bool_or = Base_Generator.lift_unary_op "BOOL_OR" arg-> Builder.code "bool_or(" ++ arg ++ ")" diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 3c5fb10c81fc..99017f3c3e95 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [floating_point_div, mod_op] + arith_extensions = [is_nan, is_inf, floating_point_div, mod_op] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -318,6 +318,14 @@ trim_ops = Builder.code "CASE WHEN " ++ chars ++ " IS NULL OR " ++ chars ++ " == '' THEN " ++ fn_name ++ "(" ++ input ++ ") ELSE " ++ fn_name ++ "(" ++ input ++ ", " ++ chars ++ ") END" [make_fn "TRIM", make_fn "LTRIM", make_fn "RTRIM"] +## PRIVATE +is_nan = Base_Generator.lift_unary_op "IS_NAN" arg-> + (arg ++ " IS NULL").paren + +## PRIVATE +is_inf = Base_Generator.lift_unary_op "IS_INF" arg-> + (arg ++ " in (9e999, -9e999)").paren + ## PRIVATE agg_count_distinct args = case args.length == 1 of True -> Builder.code "COUNT(DISTINCT (" ++ args.first ++ "))" 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 e2294eb0d6bc..3548f3a0f3f3 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 @@ -174,7 +174,7 @@ operations_map = 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_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", "IS_NAN", "IS_INF"] 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", "TRUNCATE", "CEIL", "FLOOR"] diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso index a028fff6f0b0..e12ebb4df147 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso @@ -315,6 +315,10 @@ type Value_Type error. expect_floating_point : Any -> Any -> Any ! Invalid_Value_Type expect_floating_point argument ~action = + ## + IO.println 'WWW' + IO.println argument + IO.println argument.value_type expect_type argument .is_floating_point "Float" action ## PRIVATE 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 6d534afaf095..216e026c452e 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 @@ -19,6 +19,12 @@ spec setup = prefix = setup.prefix table_builder = setup.table_builder + do_op n op = + table = table_builder [["x", [n]]] + result = table.at "x" |> op + result.to_vector.at 0 + do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + Test.group prefix+"Boolean Column Operations" <| Test.specify "iif" <| t = table_builder [["X", [True, False, Nothing, True]]] @@ -330,14 +336,15 @@ spec setup = (y ^ "a").should_fail_with Invalid_Value_Type (y ^ 42).should_fail_with Invalid_Value_Type - 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 + ## + 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]]] @@ -506,6 +513,14 @@ spec setup = table = table_builder [["x", [0, 3, -3, 1, -2]]] table.at "x" . round 16 . should_fail_with Illegal_Argument + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + ops = [.round, .truncate, .ceil, .floor] + ops.each op-> + do_op Number.nan op . should_equal nan_result + do_op Number.positive_infinity op . should_equal Number.positive_infinity + do_op Number.negative_infinity op . should_equal Number.negative_infinity + Test.group "return types" <| roundlike_ops = [["floor", .floor], ["ceil", .ceil], ["truncate", .truncate], ["round", .round]] @@ -513,12 +528,22 @@ spec setup = op_name = pair.at 0 op = pair.at 1 + Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + op (col.cast Value_Type.Float) . value_type . should_equal Value_Type.Float + + Test.specify "rounding-like should return a column of the same type as the argument (integers): "+op_name <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" bits_32_expected = if setup.test_selection.all_integers_64_bit then Value_Type.Integer Bits.Bits_64 else Value_Type.Integer Bits.Bits_32 op (col.cast (Value_Type.Integer Bits.Bits_32)) . value_type . should_equal bits_32_expected op (col.cast (Value_Type.Integer Bits.Bits_64)) . value_type . should_equal (Value_Type.Integer Bits.Bits_64) + if setup.test_selection.supports_decimal_type then + Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + op (col.cast Value_Type.Decimal) . value_type . should_equal Value_Type.Decimal + roundlike_ops.each op-> round_with op Test.specify "should not lose precision rounding large integers" <| @@ -528,7 +553,6 @@ spec setup = [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> op (t.at "X") . to_vector . should_equal [i1, i2] - if setup.test_selection.supports_decimal_type then Test.specify "should return decimals when rounding decimals" pending="fails on postgres" <| i1 = 9223372036854775807 - 1 c = table_builder [["X", [i1]]] . at "X" @@ -548,11 +572,6 @@ spec setup = table.at "x" . round . should_fail_with Invalid_Value_Type Test.group prefix+"Rounding numeric tests" <| - do_round n dp=0 use_bankers=False = - table = table_builder [["x", [n]]] - result = table.at "x" . round dp use_bankers - result.to_vector.at 0 - Test.specify "Can round positive decimals correctly" <| do_round 3.0 . should_equal 3 do_round 3.00001 . should_equal 3 @@ -662,9 +681,9 @@ spec setup = Test.specify "Returns the correct type" <| do_round 231.2 1 . should_be_a Decimal - do_round 231.2 0 . should_be_a Integer - do_round 231.2 . should_be_a Integer - do_round 231.2 -1 . should_be_a Integer + do_round 231.2 0 . should_be_a Decimal + do_round 231.2 . should_be_a Decimal + do_round 231.2 -1 . should_be_a Decimal Test.specify "Can round correctly near the precision limit" <| do_round 1.22222222225 10 . should_equal 1.2222222223 @@ -732,13 +751,6 @@ spec setup = do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 do_round 37.785 2 . should_equal 37.79 - ## - Test.specify "Can handle NaN/Infinity" <| - nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing - do_round Number.nan . should_equal nan_result - do_round Number.positive_infinity . should_equal Number.positive_infinity - do_round Number.negative_infinity . should_equal Number.negative_infinity - Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" do_round 0 . should_equal 0 do_round 3 . should_equal 3 diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index df9137345176..b1c6fcb2cc3f 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -192,18 +192,6 @@ postgres_specific_spec connection db_name setup = Test.with_clue "d.value_type="+d.value_type.to_display_text+": " <| d.value_type.variable_length.should_be_true - Test.group "[PostgreSQL] Math" <| - table_builder = setup.table_builder - do_round n dp=0 use_bankers=False = - table = table_builder [["x", [n]]] - result = table.at "x" . round dp use_bankers - result.to_vector.at 0 - - Test.specify "NaN/Inf" <| - do_round Number.nan . should_fail_with SQL_Error - do_round Number.positive_infinity . should_fail_with SQL_Error - do_round Number.negative_infinity . should_fail_with SQL_Error - run_tests connection db_name = prefix = "[PostgreSQL] " name_counter = Ref.new 0 diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 8c57f3d6491c..21c482dff57b 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -18,7 +18,7 @@ import project.Database.Types.SQLite_Type_Mapping_Spec import project.Database.Helpers.Name_Generator import project.Common_Table_Operations -sqlite_specific_spec prefix connection setup = +sqlite_specific_spec prefix connection = Test.group prefix+"Schemas and Databases" <| Test.specify "should be able to get current database and list databases" <| connection.database . should_equal Nothing @@ -118,18 +118,6 @@ sqlite_specific_spec prefix connection setup = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] - Test.group prefix+"Math" <| - table_builder = setup.table_builder - do_round n dp=0 use_bankers=False = - table = table_builder [["x", [n]]] - result = table.at "x" . round dp use_bankers - result.to_vector.at 0 - - Test.specify "NaN/Inf" <| - do_round Number.nan . should_equal Nothing - do_round Number.positive_infinity . should_equal 9223372036854775807 - do_round Number.negative_infinity . should_equal -9223372036854775808 - sqlite_spec connection prefix = name_counter = Ref.new 0 table_builder columns = @@ -142,6 +130,7 @@ sqlite_spec connection prefix = materialize = .read Common_Spec.spec prefix connection + sqlite_specific_spec prefix connection common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False all_integers_64_bit=True @@ -159,7 +148,6 @@ sqlite_spec connection prefix = empty_agg_table = (agg_in_memory_table.take (First 0)).select_into_database_table connection (Name_Generator.random_name "Agg_Empty") primary_key=Nothing temporary=True setup = Common_Table_Operations.Main.Test_Setup.Config prefix agg_table empty_agg_table table_builder materialize is_database=True test_selection=common_selection aggregate_test_selection=aggregate_selection - sqlite_specific_spec prefix connection setup Common_Table_Operations.Main.spec setup connection.close From c6fed258aad69517f65e05b2acda84ef2bea89ab Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 11:45:08 -0400 Subject: [PATCH 58/83] rename round_with --- .../src/Common_Table_Operations/Column_Operations_Spec.enso | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 216e026c452e..905b9454f6eb 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 @@ -524,7 +524,7 @@ spec setup = Test.group "return types" <| roundlike_ops = [["floor", .floor], ["ceil", .ceil], ["truncate", .truncate], ["round", .round]] - round_with pair = + test_op pair = op_name = pair.at 0 op = pair.at 1 @@ -544,7 +544,7 @@ spec setup = col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" op (col.cast Value_Type.Decimal) . value_type . should_equal Value_Type.Decimal - roundlike_ops.each op-> round_with op + roundlike_ops.each op-> test_op op Test.specify "should not lose precision rounding large integers" <| i1 = 9223372036854775807 - 1 From 6351dce5e152b42307c5625ef197e496e5a7c359 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 12:20:00 -0400 Subject: [PATCH 59/83] cleanup --- .../Database/0.0.0-dev/src/Data/Column.enso | 31 +++++++++---------- .../src/Internal/SQLite/SQLite_Dialect.enso | 5 +-- .../Internal/SQLite/SQLite_Type_Mapping.enso | 2 +- .../0.0.0-dev/src/Data/Type/Value_Type.enso | 4 --- .../Column_Operations_Spec.enso | 17 +++++----- 5 files changed, 25 insertions(+), 34 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 0c103632d341..dfbbb605ddac 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 @@ -618,7 +618,7 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Rounding_Helpers.check_decimal_places decimal_places <| short_circuit_special_floating_point self <| + Rounding_Helpers.check_decimal_places decimal_places <| self.short_circuit_special_floating_point <| case self.value_type of Value_Type.Integer _ -> self.round_integer decimal_places use_bankers Value_Type.Float _ -> self.round_float decimal_places use_bankers @@ -697,7 +697,7 @@ type Column example_truncate = Examples.decimal_column.truncate truncate : Column ! Invalid_Value_Type truncate self = - Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| + Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "truncate" [self] if self.value_type.is_integer then self.rename new_name else self.make_unary_op "TRUNCATE" new_name @@ -712,7 +712,7 @@ type Column example_truncate = Examples.decimal_column.ceil ceil : Column ! Invalid_Value_Type ceil self = - Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| + Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "ceil" [self] if self.value_type.is_integer then self.rename new_name else self.make_unary_op "CEIL" new_name @@ -727,7 +727,7 @@ type Column example_truncate = Examples.decimal_column.floor floor : Column ! Invalid_Value_Type floor self = - Value_Type.expect_numeric self <| short_circuit_special_floating_point self <| + Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "floor" [self] if self.value_type.is_integer then self.rename new_name else self.make_unary_op "FLOOR" new_name @@ -821,9 +821,7 @@ type Column Returns a column of booleans, with `True` items at the positions where this column contains a NaN. This is only applicable to double columns. is_nan : Column - is_nan self = - IO.println 'IS_NAN' - Value_Type.expect_floating_point self <| + is_nan self = Value_Type.expect_floating_point self <| new_name = self.naming_helpers.function_name "is_nan" [self] self.make_unary_op "IS_NAN" new_name @@ -1379,6 +1377,16 @@ type Column var_args_functions : Array var_args_functions = ['is_in', 'coalesce', 'min', 'max'] + + ## PRVIATE + If the column is NaN/Inf, return it immediately as the value; otherwise + return the expression. If returning immediately, rename to the expression's + name. If the column is not floating point, just return the expression. + short_circuit_special_floating_point : Column -> Column + short_circuit_special_floating_point self exp = + self_is_nan = if self.connection.dialect.supports_separate_nan then self.is_nan else self.is_nothing + if self.value_type.is_floating_point.not then exp else + ((self_is_nan || self.is_infinite).iif self exp).rename exp.name ## PRIVATE Helper for case case_sensitivity based text operations make_text_case_op left op other case_sensitivity new_name = @@ -1424,12 +1432,3 @@ adapt_unified_column column expected_type = SQL_Type_Reference.new column.connection column.context expression adapted = dialect.adapt_unified_column column.as_internal expected_type infer_return_type Column.Value name=column.name connection=column.connection sql_type_reference=adapted.sql_type_reference expression=adapted.expression context=column.context - -## PRVIATE - If the input is NaN/Inf, return it immediately as the value; otherwise - return the expression. If returning immediately, rename to the expression's - name. If the input is not floating point, just return the expression. -short_circuit_special_floating_point : Column -> Column -> Column -short_circuit_special_floating_point input exp = - if input.value_type.is_floating_point.not then exp else - ((input.is_nan || input.is_infinite).iif input exp).rename exp.name diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 99017f3c3e95..21a7c041cea5 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -235,7 +235,7 @@ make_internal_generator_dialect = text = [starts_with, contains, ends_with, make_case_sensitive]+concat_ops+trim_ops counts = [agg_count_is_null, agg_count_empty, agg_count_not_empty, ["COUNT_DISTINCT", agg_count_distinct], ["COUNT_DISTINCT_INCLUDE_NULL", agg_count_distinct_include_null]] stats = [agg_stddev_pop, agg_stddev_samp] - arith_extensions = [is_nan, is_inf, floating_point_div, mod_op] + arith_extensions = [is_inf, floating_point_div, mod_op] bool = [bool_or] my_mappings = text + counts + stats + arith_extensions + bool @@ -318,9 +318,6 @@ trim_ops = Builder.code "CASE WHEN " ++ chars ++ " IS NULL OR " ++ chars ++ " == '' THEN " ++ fn_name ++ "(" ++ input ++ ") ELSE " ++ fn_name ++ "(" ++ input ++ ", " ++ chars ++ ") END" [make_fn "TRIM", make_fn "LTRIM", make_fn "RTRIM"] -## PRIVATE -is_nan = Base_Generator.lift_unary_op "IS_NAN" arg-> - (arg ++ " IS NULL").paren ## PRIVATE is_inf = Base_Generator.lift_unary_op "IS_INF" arg-> 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 3548f3a0f3f3..f391922d5571 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 @@ -174,7 +174,7 @@ operations_map = 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", "IS_NAN", "IS_INF"] + 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", "IS_INF"] 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", "TRUNCATE", "CEIL", "FLOOR"] diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso index e12ebb4df147..a028fff6f0b0 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso @@ -315,10 +315,6 @@ type Value_Type error. expect_floating_point : Any -> Any -> Any ! Invalid_Value_Type expect_floating_point argument ~action = - ## - IO.println 'WWW' - IO.println argument - IO.println argument.value_type expect_type argument .is_floating_point "Float" action ## PRIVATE 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 905b9454f6eb..843a222b7e46 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 @@ -336,15 +336,14 @@ spec setup = (y ^ "a").should_fail_with Invalid_Value_Type (y ^ 42).should_fail_with Invalid_Value_Type - ## - 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 + 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]]] From c27b52d70b6fe271253767cd991ebfd8319b8d44 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 12:21:59 -0400 Subject: [PATCH 60/83] docs --- .../Database/0.0.0-dev/src/Data/Column.enso | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 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 dfbbb605ddac..099de39b5d48 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 @@ -590,15 +590,8 @@ type Column - use_bankers: Rounds mid-point to nearest even number. ! NaN/Inf - `round` behaves differently on different database engines. - - Postgres: - - NaN: Error - - +Inf: Error - - -Inf: Error - - SQLite: - - NaN: Returns Nothing - - +Inf: Returns Java `Long.MAX_VALUE` - - -Inf: Returns Java `Long.MIN_VALUE` + If a `NaN` or `Inf` value is passed to `round`, it immediately returns + the same value. ! Error Conditions @@ -689,6 +682,10 @@ type Column Truncate the floating-point values to an integer by dropping the fractional part. This is equivalent to "round-toward-zero". + ! NaN/Inf + If a `NaN` or `Inf` value is passed to `truncate`, it immediately returns + the same value. + > Example Truncate a column of floating-point values. @@ -704,6 +701,10 @@ type Column ## Takes the ceiling of floating-point values, returning integer values. + ! NaN/Inf + If a `NaN` or `Inf` value is passed to `ceil`, it immediately returns + the same value. + > Example Take the ceiling of a column of floating-point values. @@ -719,6 +720,10 @@ type Column ## Takes the floor of floating-point values, returning integer values. + ! NaN/Inf + If a `NaN` or `Inf` value is passed to `floor`, it immediately returns + the same value. + > Example Take the floor of a column of floating-point values. From 314546d3c06794d7807673423357d139cf0a1448 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 12:26:54 -0400 Subject: [PATCH 61/83] decimal test --- .../src/Common_Table_Operations/Column_Operations_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 843a222b7e46..d3217178fbb9 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 @@ -552,7 +552,7 @@ spec setup = [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> op (t.at "X") . to_vector . should_equal [i1, i2] - Test.specify "should return decimals when rounding decimals" pending="fails on postgres" <| + Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 c = table_builder [["X", [i1]]] . at "X" decimal_col = c.cast Value_Type.Decimal From 7fcb5c7682ffb39d7745b9d1e685a0526c2140d7 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 15:00:26 -0400 Subject: [PATCH 62/83] specialize --- .../Redshift/Internal/Redshift_Dialect.enso | 4 ++ .../Database/0.0.0-dev/src/Data/Column.enso | 38 ++++++++++++++----- .../Database/0.0.0-dev/src/Data/Dialect.enso | 7 ++++ .../src/Internal/Base_Generator.enso | 2 +- .../Internal/Postgres/Postgres_Dialect.enso | 4 ++ .../src/Internal/SQLite/SQLite_Dialect.enso | 4 ++ .../Internal/SQLite/SQLite_Type_Mapping.enso | 12 ++++-- .../0.0.0-dev/src/Data/Type/Value_Type.enso | 7 ++++ .../Column_Operations_Spec.enso | 21 +++++----- 9 files changed, 75 insertions(+), 24 deletions(-) diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso index 20b5ad7fcff5..37789d4cbe62 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso @@ -127,6 +127,10 @@ type Redshift_Dialect supports_separate_nan : Boolean supports_separate_nan self = True + ## PRIVATE + supports_separate_nan : Boolean + supports_separate_nan self = True + ## PRIVATE adapt_unified_column : Internal_Column -> Value_Type -> (SQL_Expression -> SQL_Type_Reference) -> Internal_Column adapt_unified_column self column approximate_result_type infer_result_type_from_database_callback = 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 099de39b5d48..59fae363c3de 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 @@ -611,14 +611,33 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = - Rounding_Helpers.check_decimal_places decimal_places <| self.short_circuit_special_floating_point <| - case self.value_type of - Value_Type.Integer _ -> self.round_integer decimal_places use_bankers - Value_Type.Float _ -> self.round_float decimal_places use_bankers - Value_Type.Decimal _ _ -> self.round_decimal decimal_places use_bankers - _ -> - expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" - Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) + Rounding_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| + if self.should_use_builtin_round decimal_places use_bankers then self.round_builtin decimal_places else + self.short_circuit_special_floating_point <| + case self.value_type of + Value_Type.Integer _ -> self.round_integer decimal_places use_bankers + Value_Type.Float _ -> self.round_float decimal_places use_bankers + Value_Type.Decimal _ _ -> self.round_decimal decimal_places use_bankers + _ -> + expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" + Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) + + ## PRIVATE + Round a float-like column using the backend's builtin ROUND function. + should_use_builtin_round : Integer -> Boolean -> Boolean + should_use_builtin_round self decimal_places use_bankers = + use_bankers.not && (decimal_places >= 0 || self.connection.dialect.supports_negative_round_decimal_places) && (self.value_type.is_floating.not || decimal_places == 0) + + ## PRIVATE + Round a float-like column using the backend's builtin ROUND function. + round_builtin : Integer -> Boolean -> Column + round_builtin self decimal_places = + new_name = self.naming_helpers.function_name "round" [self] + case decimal_places == 0 of + True -> + self.make_unary_op "ROUND" new_name + False -> + self.make_binary_op "ROUND" decimal_places new_name ## PRIVATE Round a float-like column. @@ -675,7 +694,7 @@ type Column round_up.iif (result_unnudged - scale) result_unnudged result = (self >= 0).iif if_non_neg if_neg - result.rename new_name + result.cast Value_Type.Float . rename new_name ## ALIAS int @@ -1382,7 +1401,6 @@ type Column var_args_functions : Array var_args_functions = ['is_in', 'coalesce', 'min', 'max'] - ## PRVIATE If the column is NaN/Inf, return it immediately as the value; otherwise return the expression. If returning immediately, rename to the expression's 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 9b166cbc9407..71ac29e2a979 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 @@ -145,6 +145,13 @@ type Dialect supports_separate_nan self = Unimplemented.throw "This is an interface only." + ## PRIVATE + Specifies whether the Database ROUND() function supports negative + decimal places. + supports_negative_round_decimal_places : Boolean + supports_negative_round_decimal_places self = + Unimplemented.throw "This is an interface only." + ## PRIVATE Performs any transformations on a column resulting from unifying other columns. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index dd81dfb2d367..0a6e7bba3553 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -176,7 +176,7 @@ base_dialect = fun = name -> [name, make_function name] # ["decimal_div", make_function "DECIMAL_DIV"], ["decimal_mod", make_function "DECIMAL_MOD"], - arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["TRUNCATE", make_function "TRUNC"], ["CEIL", make_function "CEIL"], ["FLOOR", make_function "FLOOR"]] + arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["ROUND", make_function "ROUND"], ["TRUNCATE", make_function "TRUNC"], ["CEIL", make_function "CEIL"], ["FLOOR", make_function "FLOOR"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals neq = lift_binary_op "!=" make_not_equals diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index ab72f20dc169..71841d0c4099 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -156,6 +156,10 @@ type Postgres_Dialect supports_separate_nan : Boolean supports_separate_nan self = True + ## PRIVATE + supports_negative_round_decimal_places : Boolean + supports_negative_round_decimal_places self = True + ## PRIVATE There is a bug in Postgres type inference, where if we unify two fixed-length char columns of length N and M, the result type is said to diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 21a7c041cea5..6088c723c013 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -167,6 +167,10 @@ type SQLite_Dialect supports_separate_nan : Boolean supports_separate_nan self = False + ## PRIVATE + supports_negative_round_decimal_places : Boolean + supports_negative_round_decimal_places self = False + ## PRIVATE SQLite allows mixed type columns, but we want our columns to be uniform. So after unifying columns with mixed types, we add a cast to ensure that. 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 f391922d5571..4accf01a39f0 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 @@ -155,6 +155,10 @@ operations_map = result = if unified == Value_Type.Mixed then default_text else unified SQLite_Type_Mapping.value_type_to_sql result Problem_Behavior.Ignore + same_as_first_argument arguments = + typ = find_type (arguments.at 0) + SQLite_Type_Mapping.value_type_to_sql typ Problem_Behavior.Ignore + handle_iif arguments = if arguments.length != 3 then Panic.throw (Illegal_State.Error "Impossible: IIF must have 3 arguments. This is a bug in the Database library.") @@ -175,9 +179,10 @@ operations_map = 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", "IS_INF"] - always_floating_ops = ["/", "mod", "AVG", "STDDEV_POP", "STDDEV_SAMP"] + always_floating_ops = ["/", "mod", "AVG", "STDDEV_POP", "STDDEV_SAMP", "ROUND"] 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", "TRUNCATE", "CEIL", "FLOOR"] + always_integer_ops = ["COUNT", "COUNT_IS_NULL", "COUNT_DISTINCT", "COUNT_DISTINCT_INCLUDE_NULL", "COUNT_EMPTY", "COUNT_NOT_EMPTY", "COUNT_ROWS"] + same_as_first = ["TRUNCATE", "CEIL", "FLOOR"] arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"] merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"] others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case]] @@ -188,7 +193,8 @@ operations_map = v4 = always_text_ops.map [_, const SQLite_Types.text] v5 = arithmetic_ops.map [_, find_a_common_type] v6 = merge_input_types_ops.map [_, find_a_common_type] - v1 + v2 + v3 + v4 + v5 + v6 + others + v7 = same_as_first.map [_, same_as_first_argument] + v1 + v2 + v3 + v4 + v5 + v6 + v7 + others ## PRIVATE type SQLite_Types diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso index a028fff6f0b0..efd429cc3212 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso @@ -178,6 +178,13 @@ type Value_Type Value_Type.Integer _ -> True _ -> False + ## UNSTABLE + Checks if the `Value_Type` represents a floating-point type. + is_floating : Boolean + is_floating self = case self of + Value_Type.Float _ -> True + _ -> False + ## UNSTABLE Checks if the `Value_Type` represents a decimal type. is_decimal : Boolean 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 d3217178fbb9..bcd90d385d4c 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 @@ -545,12 +545,13 @@ spec setup = roundlike_ops.each op-> test_op op - Test.specify "should not lose precision rounding large integers" <| - i1 = 9223372036854775807 - 1 - i2 = i1 - 1 - t = table_builder [["X", [i1, i2]]] - [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> - op (t.at "X") . to_vector . should_equal [i1, i2] + if setup.test_selection.supports_decimal_type then + Test.specify "should not lose precision rounding large integers" <| + i1 = 9223372036854775807 - 1 + i2 = i1 - 1 + t = table_builder [["X", [i1, i2]]] + [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> + op (t.at "X") . to_vector . should_equal [i1, i2] Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 @@ -844,10 +845,10 @@ spec setup = do_round -12251 -2 use_bankers=True . should_equal -12300 Test.specify "Returns the correct type" <| - do_round 231 1 . should_be_a Integer - do_round 231 0 . should_be_a Integer - do_round 231 . should_be_a Integer - do_round 231 -1 . should_be_a Integer + do_round 231 1 . should_be_a Decimal + do_round 231 0 . should_be_a Decimal + do_round 231 . should_be_a Decimal + do_round 231 -1 . should_be_a Decimal Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] From 3bac3133d05585ad4b5771661db04ad2d0afad0d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 15:10:51 -0400 Subject: [PATCH 63/83] remove max_long test --- .../Common_Table_Operations/Column_Operations_Spec.enso | 7 ------- 1 file changed, 7 deletions(-) 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 bcd90d385d4c..942b3f7a3eaa 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 @@ -546,13 +546,6 @@ spec setup = roundlike_ops.each op-> test_op op if setup.test_selection.supports_decimal_type then - Test.specify "should not lose precision rounding large integers" <| - i1 = 9223372036854775807 - 1 - i2 = i1 - 1 - t = table_builder [["X", [i1, i2]]] - [(.floor), (.ceil), (.truncate), (x-> x.round 0), (x-> x.round 2)].each op-> - op (t.at "X") . to_vector . should_equal [i1, i2] - Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 c = table_builder [["X", [i1]]] . at "X" From 05620e4e49a50c608a3bc2c8a3bbca48a9d7a292 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 15:33:00 -0400 Subject: [PATCH 64/83] p flag for dp restriction --- .../Database/0.0.0-dev/src/Data/Column.enso | 14 ++++++++++++-- .../Database/0.0.0-dev/src/Data/Dialect.enso | 6 ++++++ .../src/Internal/Postgres/Postgres_Dialect.enso | 4 ++++ .../src/Internal/SQLite/SQLite_Dialect.enso | 4 ++++ 4 files changed, 26 insertions(+), 2 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 59fae363c3de..efd00e6ca4b8 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 @@ -623,10 +623,20 @@ type Column Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) ## PRIVATE - Round a float-like column using the backend's builtin ROUND function. + Determine whether to use built-in rounding. We use built-in rounding if: + - It's not banker's rounding, and + - If decimal_places is negative, the builtin rounding supports that, and + - We are not passing a floating point value to round with a non-zero decimal_places param (prohibited by postgres) + (Note that Postgres is fine with a `numeric` value, just not `double precision`.) should_use_builtin_round : Integer -> Boolean -> Boolean should_use_builtin_round self decimal_places use_bankers = - use_bankers.not && (decimal_places >= 0 || self.connection.dialect.supports_negative_round_decimal_places) && (self.value_type.is_floating.not || decimal_places == 0) + # Don't use for banker's rounding + not_bankers = use_bankers.not + # Don't use for negative decimal places, if the backend doesn't support it + negative_dp_ok = decimal_places >= 0 || self.connection.dialect.supports_negative_round_decimal_places + # Don't use for floating-point inputs if deciaml_places != 0, for Postgres + dp_param_ok = (self.connection.dialect.rounding_decimal_places_not_allowed_for_floats && self.value_type.is_floating && decimal_places != 0).not + not_bankers && negative_dp_ok && dp_param_ok ## PRIVATE Round a float-like column using the backend's builtin ROUND function. 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 71ac29e2a979..5c2c71416bd3 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 @@ -152,6 +152,12 @@ type Dialect supports_negative_round_decimal_places self = Unimplemented.throw "This is an interface only." + ## PRIVATE + Specifies whether round() can take a decimal_places argument for floating point values. + rounding_decimal_places_not_allowed_for_floats : Boolean + rounding_decimal_places_not_allowed_for_floats self = + Unimplemented.throw "This is an interface only." + ## PRIVATE Performs any transformations on a column resulting from unifying other columns. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index 71841d0c4099..dcc25d8ae8b4 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -160,6 +160,10 @@ type Postgres_Dialect supports_negative_round_decimal_places : Boolean supports_negative_round_decimal_places self = True + ## PRIVATE + rounding_decimal_places_not_allowed_for_floats : Boolean + rounding_decimal_places_not_allowed_for_floats self = False + ## PRIVATE There is a bug in Postgres type inference, where if we unify two fixed-length char columns of length N and M, the result type is said to diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index 6088c723c013..b6aa641f45ca 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -171,6 +171,10 @@ type SQLite_Dialect supports_negative_round_decimal_places : Boolean supports_negative_round_decimal_places self = False + ## PRIVATE + rounding_decimal_places_not_allowed_for_floats : Boolean + rounding_decimal_places_not_allowed_for_floats self = True + ## PRIVATE SQLite allows mixed type columns, but we want our columns to be uniform. So after unifying columns with mixed types, we add a cast to ensure that. From 307f9f0831acab490497fbc6f597366d13a6d803 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 15:39:42 -0400 Subject: [PATCH 65/83] rs too --- .../src/Database/Redshift/Internal/Redshift_Dialect.enso | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso index 37789d4cbe62..4090c0081f3e 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso @@ -131,6 +131,10 @@ type Redshift_Dialect supports_separate_nan : Boolean supports_separate_nan self = True + ## PRIVATE + rounding_decimal_places_not_allowed_for_floats : Boolean + rounding_decimal_places_not_allowed_for_floats self = False + ## PRIVATE adapt_unified_column : Internal_Column -> Value_Type -> (SQL_Expression -> SQL_Type_Reference) -> Internal_Column adapt_unified_column self column approximate_result_type infer_result_type_from_database_callback = From 021a2f491bbce60c5752280df7c3e6c0168b1659 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 16:53:28 -0400 Subject: [PATCH 66/83] return type tests --- .../Database/0.0.0-dev/src/Data/Column.enso | 9 ++---- .../Column_Operations_Spec.enso | 25 ---------------- .../src/Database/Postgres_Spec.enso | 25 ++++++++++++++++ .../Table_Tests/src/Database/SQLite_Spec.enso | 30 +++++++++++++++++-- 4 files changed, 56 insertions(+), 33 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 efd00e6ca4b8..c67fcf8d4a0b 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 @@ -725,8 +725,7 @@ type Column truncate self = Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "truncate" [self] - if self.value_type.is_integer then self.rename new_name else - self.make_unary_op "TRUNCATE" new_name + self.make_unary_op "TRUNCATE" new_name ## Takes the ceiling of floating-point values, returning integer values. @@ -744,8 +743,7 @@ type Column ceil self = Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "ceil" [self] - if self.value_type.is_integer then self.rename new_name else - self.make_unary_op "CEIL" new_name + self.make_unary_op "CEIL" new_name ## Takes the floor of floating-point values, returning integer values. @@ -763,8 +761,7 @@ type Column floor self = Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <| new_name = self.naming_helpers.function_name "floor" [self] - if self.value_type.is_integer then self.rename new_name else - self.make_unary_op "FLOOR" new_name + self.make_unary_op "FLOOR" new_name ## PRVIATE `/` for decimals. 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 942b3f7a3eaa..4ea47914b23d 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 @@ -520,31 +520,6 @@ spec setup = do_op Number.positive_infinity op . should_equal Number.positive_infinity do_op Number.negative_infinity op . should_equal Number.negative_infinity - Test.group "return types" <| - roundlike_ops = [["floor", .floor], ["ceil", .ceil], ["truncate", .truncate], ["round", .round]] - - test_op pair = - op_name = pair.at 0 - op = pair.at 1 - - Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| - col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" - op (col.cast Value_Type.Float) . value_type . should_equal Value_Type.Float - - - Test.specify "rounding-like should return a column of the same type as the argument (integers): "+op_name <| - col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" - bits_32_expected = if setup.test_selection.all_integers_64_bit then Value_Type.Integer Bits.Bits_64 else Value_Type.Integer Bits.Bits_32 - op (col.cast (Value_Type.Integer Bits.Bits_32)) . value_type . should_equal bits_32_expected - op (col.cast (Value_Type.Integer Bits.Bits_64)) . value_type . should_equal (Value_Type.Integer Bits.Bits_64) - - if setup.test_selection.supports_decimal_type then - Test.specify "rounding-like should return a column of the same type as the argument (floats): "+op_name <| - col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" - op (col.cast Value_Type.Decimal) . value_type . should_equal Value_Type.Decimal - - roundlike_ops.each op-> test_op op - if setup.test_selection.supports_decimal_type then Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index b1c6fcb2cc3f..040838012f97 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -192,6 +192,31 @@ postgres_specific_spec connection db_name setup = Test.with_clue "d.value_type="+d.value_type.to_display_text+": " <| d.value_type.variable_length.should_be_true + Test.group "[PostgreSQL] math functions" <| + Test.specify "round, trunc, ceil, floor" <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Float + + col . cast Value_Type.Float . round . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round . value_type . should_equal Value_Type.Decimal + + col . cast Value_Type.Float . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round 1 . value_type . should_equal Value_Type.Decimal + + col . cast Value_Type.Float . ceil . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . ceil . value_type . should_equal Value_Type.Decimal + + col . cast Value_Type.Float . floor . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . floor . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . floor . value_type . should_equal Value_Type.Decimal + + col . cast Value_Type.Float . truncate . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . truncate . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . truncate . value_type . should_equal Value_Type.Decimal + run_tests connection db_name = prefix = "[PostgreSQL] " name_counter = Ref.new 0 diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 21c482dff57b..a58c70b822c0 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -18,7 +18,9 @@ import project.Database.Types.SQLite_Type_Mapping_Spec import project.Database.Helpers.Name_Generator import project.Common_Table_Operations -sqlite_specific_spec prefix connection = +sqlite_specific_spec prefix connection setup = + table_builder = setup.table_builder + Test.group prefix+"Schemas and Databases" <| Test.specify "should be able to get current database and list databases" <| connection.database . should_equal Nothing @@ -118,6 +120,30 @@ sqlite_specific_spec prefix connection = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] + Test.group "[PostgreSQL] math functions" <| + Test.specify "round, trunc, ceil, floor" <| + col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" + + col . cast Value_Type.Float . round . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round . value_type . should_equal Value_Type.Float + + col . cast Value_Type.Float . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round 1 . value_type . should_equal Value_Type.Float + + col . cast Value_Type.Float . ceil . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Integer + col . cast Value_Type.Decimal . ceil . value_type . should_equal Value_Type.Float + + col . cast Value_Type.Float . floor . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . floor . value_type . should_equal Value_Type.Integer + col . cast Value_Type.Decimal . floor . value_type . should_equal Value_Type.Float + + col . cast Value_Type.Float . truncate . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . truncate . value_type . should_equal Value_Type.Integer + col . cast Value_Type.Decimal . truncate . value_type . should_equal Value_Type.Float + sqlite_spec connection prefix = name_counter = Ref.new 0 table_builder columns = @@ -130,7 +156,6 @@ sqlite_spec connection prefix = materialize = .read Common_Spec.spec prefix connection - sqlite_specific_spec prefix connection common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False all_integers_64_bit=True @@ -148,6 +173,7 @@ sqlite_spec connection prefix = empty_agg_table = (agg_in_memory_table.take (First 0)).select_into_database_table connection (Name_Generator.random_name "Agg_Empty") primary_key=Nothing temporary=True setup = Common_Table_Operations.Main.Test_Setup.Config prefix agg_table empty_agg_table table_builder materialize is_database=True test_selection=common_selection aggregate_test_selection=aggregate_selection + sqlite_specific_spec prefix connection setup Common_Table_Operations.Main.spec setup connection.close From 6ed5154296746f80f94136ec3130e89960924851 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 16:59:50 -0400 Subject: [PATCH 67/83] bankers --- .../Database/0.0.0-dev/src/Data/Column.enso | 35 +++++++++---------- .../src/Database/Postgres_Spec.enso | 4 +++ .../Table_Tests/src/Database/SQLite_Spec.enso | 4 +++ 3 files changed, 25 insertions(+), 18 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 c67fcf8d4a0b..b62e5c4bf003 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 @@ -687,24 +687,23 @@ type Column round_integer self decimal_places use_bankers = new_name = self.naming_helpers.function_name "round" [self] # If non-negative decimal places, there's nothing to do. - if decimal_places >= 0 then self.rename new_name else - scale = 10 ^ -decimal_places - halfway = scale.div 2 - remainder = self % scale - scaled_down = (self / scale).truncate . cast Value_Type.Integer - result_unnudged = scaled_down * scale - - if_non_neg = - half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 - round_up = half_goes_up.iif (remainder >= halfway) (remainder > halfway) - round_up.iif (result_unnudged + scale) result_unnudged - if_neg = - half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 - round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) - round_up.iif (result_unnudged - scale) result_unnudged - - result = (self >= 0).iif if_non_neg if_neg - result.cast Value_Type.Float . rename new_name + scale = 10 ^ -decimal_places + halfway = scale.div 2 + remainder = self % scale + scaled_down = (self / scale).truncate . cast Value_Type.Integer + result_unnudged = scaled_down * scale + + if_non_neg = + half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0 + round_up = half_goes_up.iif (remainder >= halfway) (remainder > halfway) + round_up.iif (result_unnudged + scale) result_unnudged + if_neg = + half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0 + round_up = half_goes_up.iif (remainder < -halfway) (remainder <= -halfway) + round_up.iif (result_unnudged - scale) result_unnudged + + result = (self >= 0).iif if_non_neg if_neg + result.cast Value_Type.Float . rename new_name ## ALIAS int diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 040838012f97..a2801f0ac66d 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -205,6 +205,10 @@ postgres_specific_spec connection db_name setup = col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Float col . cast Value_Type.Decimal . round 1 . value_type . should_equal Value_Type.Decimal + col . cast Value_Type.Float . round use_bankers=True . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round use_bankers=True . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round use_bankers=True . value_type . should_equal Value_Type.Decimal + col . cast Value_Type.Float . ceil . value_type . should_equal Value_Type.Float col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Float col . cast Value_Type.Decimal . ceil . value_type . should_equal Value_Type.Decimal diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index a58c70b822c0..0324422793f6 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -132,6 +132,10 @@ sqlite_specific_spec prefix connection setup = col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Float col . cast Value_Type.Decimal . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Float . round use_bankers=True . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round use_bankers=True . value_type . should_equal Value_Type.Float + col . cast Value_Type.Decimal . round use_bankers=True . value_type . should_equal Value_Type.Float + col . cast Value_Type.Float . ceil . value_type . should_equal Value_Type.Float col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Integer col . cast Value_Type.Decimal . ceil . value_type . should_equal Value_Type.Float From 519f3297e167c41c8f46439389c3d307a392328d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 26 Jun 2023 17:15:56 -0400 Subject: [PATCH 68/83] cleanup --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 8 +++----- .../Database/0.0.0-dev/src/Internal/Base_Generator.enso | 1 - test/Table_Tests/src/Common_Table_Operations/Main.enso | 3 +-- test/Table_Tests/src/Database/SQLite_Spec.enso | 2 +- 4 files changed, 5 insertions(+), 9 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 b62e5c4bf003..cd2aa57972c3 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 @@ -580,8 +580,7 @@ type Column "round towards 0." If use_bankers=True, then it uses "round-half-even", also known as "banker's rounding". - If `decimal_places` > 0, `round` returns a column of the same type as the - input. Otherwise, it returns an `Integer` column. + The return type depends on the backend. Arguments: - decimal_places: The number of decimal places to round to. Can be @@ -625,9 +624,9 @@ type Column ## PRIVATE Determine whether to use built-in rounding. We use built-in rounding if: - It's not banker's rounding, and - - If decimal_places is negative, the builtin rounding supports that, and + - If decimal_places is negative, the builtin `ROUND` supports that, and - We are not passing a floating point value to round with a non-zero decimal_places param (prohibited by postgres) - (Note that Postgres is fine with a `numeric` value, just not `double precision`.) + (Note that Postgres is fine with a `numeric` value here, just not `double precision`.) should_use_builtin_round : Integer -> Boolean -> Boolean should_use_builtin_round self decimal_places use_bankers = # Don't use for banker's rounding @@ -686,7 +685,6 @@ type Column round_integer : Integer -> Boolean -> Column round_integer self decimal_places use_bankers = new_name = self.naming_helpers.function_name "round" [self] - # If non-negative decimal places, there's nothing to do. scale = 10 ^ -decimal_places halfway = scale.div 2 remainder = self % scale diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso index 0a6e7bba3553..8b97782841dd 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso @@ -175,7 +175,6 @@ base_dialect = unary = name -> [name, make_unary_op name] fun = name -> [name, make_function name] - # ["decimal_div", make_function "DECIMAL_DIV"], ["decimal_mod", make_function "DECIMAL_MOD"], arith = [["ADD_NUMBER", make_binary_op "+"], ["ADD_TEXT", make_binary_op "||"], bin "-", bin "*", bin "/", bin "%", ["mod", make_function "MOD"], ["^", make_function "POWER"], ["ROUND", make_function "ROUND"], ["TRUNCATE", make_function "TRUNC"], ["CEIL", make_function "CEIL"], ["FLOOR", make_function "FLOOR"]] logic = [bin "AND", bin "OR", unary "NOT", ["IIF", make_iif], ["CASE", case_when]] eq = lift_binary_op "==" make_equals diff --git a/test/Table_Tests/src/Common_Table_Operations/Main.enso b/test/Table_Tests/src/Common_Table_Operations/Main.enso index c0b2c1f3948c..dcd7add5c0bc 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Main.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Main.enso @@ -90,8 +90,7 @@ type Test_Selection length text columns. - supports_decimal_type: Specifies if the backend supports the `Decimal` high-precision type. - - all_integers_64_bit: True if the backend only has 64-bit integers. - Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False supports_decimal_type=False all_integers_64_bit=False + Config supports_case_sensitive_columns=True order_by=True natural_ordering=False case_insensitive_ordering=True order_by_unicode_normalization_by_default=False case_insensitive_ascii_only=False take_drop=True allows_mixed_type_comparisons=True supports_unicode_normalization=False is_nan_and_nothing_distinct=True distinct_returns_first_row_from_group_if_ordered=True date_time=True fixed_length_text_columns=False supports_decimal_type=False spec setup = Core_Spec.spec setup diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 0324422793f6..bb4bc3e68967 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -161,7 +161,7 @@ sqlite_spec connection prefix = Common_Spec.spec prefix connection - common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False all_integers_64_bit=True + common_selection = Common_Table_Operations.Main.Test_Selection.Config supports_case_sensitive_columns=False order_by=True natural_ordering=False case_insensitive_ordering=True case_insensitive_ascii_only=True take_drop=False is_nan_and_nothing_distinct=False date_time=False ## For now `advanced_stats`, `first_last`, `text_shortest_longest` and `multi_distinct` remain disabled, because SQLite does not provide the From 35fe5b14ee1f2bd71c8a1a32f89d3e8099803167 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 27 Jun 2023 12:03:29 -0400 Subject: [PATCH 69/83] literal for dp --- build.sbt | 2 +- .../src/Database/Redshift/Internal/Redshift_Dialect.enso | 6 +++--- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 4 +++- .../0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso | 2 +- .../0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso | 2 +- test/Table_Tests/src/Database/Postgres_Spec.enso | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index a33a26036593..53dde575d927 100644 --- a/build.sbt +++ b/build.sbt @@ -1289,7 +1289,7 @@ lazy val runtime = (project in file("engine/runtime")) "org.typelevel" %% "cats-core" % catsVersion, "junit" % "junit" % junitVersion % Test, "com.novocode" % "junit-interface" % junitIfVersion % Test exclude ("junit", "junit-dep"), - "com.lihaoyi" %% "fansi" % fansiVersion % "provided" + "com.lihaoyi" %% "fansi" % fansiVersion ), Compile / compile / compileInputs := (Compile / compile / compileInputs) .dependsOn(CopyTruffleJAR.preCompileTask) diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso index 4090c0081f3e..014c323e7615 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso @@ -128,12 +128,12 @@ type Redshift_Dialect supports_separate_nan self = True ## PRIVATE - supports_separate_nan : Boolean - supports_separate_nan self = True + supports_negative_round_decimal_places : Boolean + supports_negative_round_decimal_places self = True ## PRIVATE rounding_decimal_places_not_allowed_for_floats : Boolean - rounding_decimal_places_not_allowed_for_floats self = False + rounding_decimal_places_not_allowed_for_floats self = True ## PRIVATE adapt_unified_column : Internal_Column -> Value_Type -> (SQL_Expression -> SQL_Type_Reference) -> Internal_Column 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 f98541721f5b..0bdcadb73281 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 @@ -143,6 +143,8 @@ type Column 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." + sql_expression : SQL_Expression -> + sql_expression constant -> SQL_Expression.Constant constant @@ -649,7 +651,7 @@ type Column True -> self.make_unary_op "ROUND" new_name False -> - self.make_binary_op "ROUND" decimal_places new_name + self.make_binary_op "ROUND" (SQL_Expression.Literal decimal_places.to_text) new_name ## PRIVATE Round a float-like column. diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso index dcc25d8ae8b4..403264b62013 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso @@ -162,7 +162,7 @@ type Postgres_Dialect ## PRIVATE rounding_decimal_places_not_allowed_for_floats : Boolean - rounding_decimal_places_not_allowed_for_floats self = False + rounding_decimal_places_not_allowed_for_floats self = True ## PRIVATE There is a bug in Postgres type inference, where if we unify two diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso index b6aa641f45ca..4016c8fc35f0 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso @@ -173,7 +173,7 @@ type SQLite_Dialect ## PRIVATE rounding_decimal_places_not_allowed_for_floats : Boolean - rounding_decimal_places_not_allowed_for_floats self = True + rounding_decimal_places_not_allowed_for_floats self = False ## PRIVATE SQLite allows mixed type columns, but we want our columns to be uniform. diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index a2801f0ac66d..af50f82a71fc 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -202,7 +202,7 @@ postgres_specific_spec connection db_name setup = col . cast Value_Type.Decimal . round . value_type . should_equal Value_Type.Decimal col . cast Value_Type.Float . round 1 . value_type . should_equal Value_Type.Float - col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Float + col . cast Value_Type.Integer . round 1 . value_type . should_equal Value_Type.Decimal col . cast Value_Type.Decimal . round 1 . value_type . should_equal Value_Type.Decimal col . cast Value_Type.Float . round use_bankers=True . value_type . should_equal Value_Type.Float From 076172a006f92c964713a778b945c0b82eb2a9f9 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 27 Jun 2023 12:06:08 -0400 Subject: [PATCH 70/83] changelog chronological --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f1a906c3f67..9a5fd8d18b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -496,11 +496,11 @@ - [Speed improvements to `Column` `.truncate`, `.ceil`, and `.floor`.][6941] - [Implemented addition and subtraction for `Date_Period` and `Time_Period`.][6956] +- [Implemented `Table.update_database_table`.][7035] - [Added AWS credential support and initial S3 list buckets API.][6973] +- [Removed `module` argument from `enso_project` and other minor tweaks.][7052] - [Added `round`, `ceil`, `floor`, `truncate` to the In-Database Column type] [6988] -- [Implemented `Table.update_database_table`.][7035] -- [Removed `module` argument from `enso_project` and other minor tweaks.][7052] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug From 3a9b1d75050f357c963d9afdb80ac28c8522429a Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 27 Jun 2023 12:10:16 -0400 Subject: [PATCH 71/83] review --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 4 ++-- .../Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso | 6 ------ 2 files changed, 2 insertions(+), 8 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 0bdcadb73281..33558297637c 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 @@ -623,7 +623,7 @@ type Column Value_Type.Float _ -> self.round_float decimal_places use_bankers Value_Type.Decimal _ _ -> self.round_decimal decimal_places use_bankers _ -> - expected = "Value_Type.Decimal, Value_Type.Float, or Value_Type.Integer" + expected = "Decimal, Float, or Integer" Error.throw (Invalid_Value_Type.Column expected self.value_type self.name) ## PRIVATE @@ -639,7 +639,7 @@ type Column # Don't use for negative decimal places, if the backend doesn't support it negative_dp_ok = decimal_places >= 0 || self.connection.dialect.supports_negative_round_decimal_places # Don't use for floating-point inputs if deciaml_places != 0, for Postgres - dp_param_ok = (self.connection.dialect.rounding_decimal_places_not_allowed_for_floats && self.value_type.is_floating && decimal_places != 0).not + dp_param_ok = (self.connection.dialect.rounding_decimal_places_not_allowed_for_floats && self.value_type.is_floating_point && decimal_places != 0).not not_bankers && negative_dp_ok && dp_param_ok ## PRIVATE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso index 01eb8fa2b424..b1ba4fd2a4f8 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type.enso @@ -173,12 +173,6 @@ type Value_Type Value_Type.Integer _ -> True _ -> False - ## Checks if the `Value_Type` represents a floating-point type. - is_floating : Boolean - is_floating self = case self of - Value_Type.Float _ -> True - _ -> False - ## Checks if the `Value_Type` represents a decimal type. is_decimal : Boolean is_decimal self = case self of From 819e75f141653457852a42d147fc52adce71612c Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 27 Jun 2023 12:14:00 -0400 Subject: [PATCH 72/83] undo changelog --- CHANGELOG.md | 4 ++-- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a5fd8d18b0c..2f1a906c3f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -496,11 +496,11 @@ - [Speed improvements to `Column` `.truncate`, `.ceil`, and `.floor`.][6941] - [Implemented addition and subtraction for `Date_Period` and `Time_Period`.][6956] -- [Implemented `Table.update_database_table`.][7035] - [Added AWS credential support and initial S3 list buckets API.][6973] -- [Removed `module` argument from `enso_project` and other minor tweaks.][7052] - [Added `round`, `ceil`, `floor`, `truncate` to the In-Database Column type] [6988] +- [Implemented `Table.update_database_table`.][7035] +- [Removed `module` argument from `enso_project` and other minor tweaks.][7052] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 63a6b754b8c1..b1a849fc3479 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -733,7 +733,7 @@ type Column half_goes_up = if use_bankers then even_is_up else self >= 0 do_round_up = half_goes_up.iif (self >= round_midpoint) (self > round_midpoint) result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - cast_if_not result_float target_value_type + cast_if_needed result_float target_value_type result.rename new_name ## PRIVATE @@ -2092,5 +2092,5 @@ wrap_text_argument_as_value_provider val = ## PRIVATE Cast a column to a `Value_Type`, unless it already has that type. -cast_if_not : Column -> Value_Type -> Column -cast_if_not column value_type = if column.value_type == value_type then column else column.cast value_type +cast_if_needed : Column -> Value_Type -> Column +cast_if_needed column value_type = if column.value_type == value_type then column else column.cast value_type From bfce3b70543b688298b1ea7a0dbca7c40e79ed2c Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 27 Jun 2023 12:37:03 -0400 Subject: [PATCH 73/83] precision too high --- .../Common_Table_Operations/Column_Operations_Spec.enso | 8 -------- 1 file changed, 8 deletions(-) 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 4ea47914b23d..d0dac2c24077 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 @@ -659,28 +659,24 @@ spec setup = do_round 1.2222222222225 12 . should_equal 1.222222222223 do_round 1.22222222222225 13 . should_equal 1.2222222222223 do_round 1.222222222222225 14 . should_equal 1.22222222222223 - do_round 1.2222222222222225 15 . should_equal 1.222222222222223 do_round -1.22222222225 10 . should_equal -1.2222222223 do_round -1.222222222225 11 . should_equal -1.22222222223 do_round -1.2222222222225 12 . should_equal -1.222222222223 do_round -1.22222222222225 13 . should_equal -1.2222222222223 do_round -1.222222222222225 14 . should_equal -1.22222222222223 - do_round -1.2222222222222225 15 . should_equal -1.222222222222223 do_round 1.22222222235 10 . should_equal 1.2222222224 do_round 1.222222222235 11 . should_equal 1.22222222224 do_round 1.2222222222235 12 . should_equal 1.222222222224 do_round 1.22222222222235 13 . should_equal 1.2222222222224 do_round 1.222222222222235 14 . should_equal 1.22222222222224 - do_round 1.2222222222222235 15 . should_equal 1.222222222222224 do_round -1.22222222235 10 . should_equal -1.2222222224 do_round -1.222222222235 11 . should_equal -1.22222222224 do_round -1.2222222222235 12 . should_equal -1.222222222224 do_round -1.22222222222235 13 . should_equal -1.2222222222224 do_round -1.222222222222235 14 . should_equal -1.22222222222224 - do_round -1.2222222222222235 15 . should_equal -1.222222222222224 Test.specify "Can round correctly near the precision limit, using banker's rounding" <| do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 @@ -688,28 +684,24 @@ spec setup = do_round 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 do_round 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 do_round 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 - do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 do_round -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 do_round -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 do_round -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 do_round -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 do_round -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 - do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 do_round 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 do_round 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 do_round 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 do_round 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 do_round 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 - do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 do_round -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 do_round -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 do_round -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 do_round -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 do_round -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 - do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 Test.specify "Decimal places out of range" <| do_round 3.1 16 . should_fail_with Illegal_Argument From 9d3f5489b833b66a77eba5747fd6dbe508c09c61 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 28 Jun 2023 12:20:41 -0400 Subject: [PATCH 74/83] wip --- .../Table/0.0.0-dev/src/Data/Table.enso | 3 +- .../Column_Operations_Spec.enso | 20 ------------- .../src/Database/Postgres_Spec.enso | 26 +++++++++++++++++ .../Table_Tests/src/Database/SQLite_Spec.enso | 26 +++++++++++++++++ .../src/In_Memory/Column_Spec.enso | 29 +++++++++++++++++++ 5 files changed, 83 insertions(+), 21 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso index 8725721ad5d4..9e611b1528f0 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso @@ -1311,7 +1311,8 @@ type Table Create a constant column from a value. make_constant_column : Any -> Column make_constant_column self value = - Column.from_vector_repeated ("Constant_" + UUID.randomUUID.to_text) [value] self.row_count + if Table_Helpers.is_column value then Error.throw (Illegal_Argument.Error "A constant value may only be created from a scalar, not a Column") else + Column.from_vector_repeated ("Constant_" + UUID.randomUUID.to_text) [value] self.row_count ## Returns the vector of columns contained in this table. 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 d0dac2c24077..95c7be241e40 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 @@ -512,14 +512,6 @@ spec setup = table = table_builder [["x", [0, 3, -3, 1, -2]]] table.at "x" . round 16 . should_fail_with Illegal_Argument - Test.specify "Can handle NaN/Infinity" <| - nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing - ops = [.round, .truncate, .ceil, .floor] - ops.each op-> - do_op Number.nan op . should_equal nan_result - do_op Number.positive_infinity op . should_equal Number.positive_infinity - do_op Number.negative_infinity op . should_equal Number.negative_infinity - if setup.test_selection.supports_decimal_type then Test.specify "should return decimals when rounding decimals" <| i1 = 9223372036854775807 - 1 @@ -647,12 +639,6 @@ spec setup = do_round -3.50001 . should_equal -4 do_round -3.99999 . should_equal -4 - Test.specify "Returns the correct type" <| - do_round 231.2 1 . should_be_a Decimal - do_round 231.2 0 . should_be_a Decimal - do_round 231.2 . should_be_a Decimal - do_round 231.2 -1 . should_be_a Decimal - Test.specify "Can round correctly near the precision limit" <| do_round 1.22222222225 10 . should_equal 1.2222222223 do_round 1.222222222225 11 . should_equal 1.22222222223 @@ -804,12 +790,6 @@ spec setup = do_round -12250 -2 use_bankers=True . should_equal -12200 do_round -12251 -2 use_bankers=True . should_equal -12300 - Test.specify "Returns the correct type" <| - do_round 231 1 . should_be_a Decimal - do_round 231 0 . should_be_a Decimal - do_round 231 . should_be_a Decimal - do_round 231 -1 . should_be_a Decimal - Test.group prefix+"Text Column Operations" <| t3 = table_builder [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] s1 = t3.at "s1" diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index afaefd9b1baa..018926828723 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -223,6 +223,32 @@ postgres_specific_spec connection db_name setup = col . cast Value_Type.Integer . truncate . value_type . should_equal Value_Type.Float col . cast Value_Type.Decimal . truncate . value_type . should_equal Value_Type.Decimal + do_op n op = + table = table_builder [["x", [n]]] + result = table.at "x" |> op + result.to_vector.at 0 + do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + ops = [.round, .truncate, .ceil, .floor] + ops.each op-> + do_op Number.nan op . should_equal nan_result + do_op Number.positive_infinity op . should_equal Number.positive_infinity + do_op Number.negative_infinity op . should_equal Number.negative_infinity + + Test.specify "round returns the correct type" <| + do_round 231.2 1 . should_be_a Decimal + do_round 231.2 0 . should_be_a Decimal + do_round 231.2 . should_be_a Decimal + do_round 231.2 -1 . should_be_a Decimal + + Test.specify "round returns the correct type" <| + do_round 231 1 . should_be_a Decimal + do_round 231 0 . should_be_a Decimal + do_round 231 . should_be_a Decimal + do_round 231 -1 . should_be_a Decimal + run_tests connection db_name = prefix = "[PostgreSQL] " name_counter = Ref.new 0 diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 58896ad82eb5..cc12e7281e82 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -151,6 +151,32 @@ sqlite_specific_spec prefix connection setup = col . cast Value_Type.Integer . truncate . value_type . should_equal Value_Type.Integer col . cast Value_Type.Decimal . truncate . value_type . should_equal Value_Type.Float + do_op n op = + table = table_builder [["x", [n]]] + result = table.at "x" |> op + result.to_vector.at 0 + do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + + Test.specify "Can handle NaN/Infinity" <| + nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing + ops = [.round, .truncate, .ceil, .floor] + ops.each op-> + do_op Number.nan op . should_equal nan_result + do_op Number.positive_infinity op . should_equal Number.positive_infinity + do_op Number.negative_infinity op . should_equal Number.negative_infinity + + Test.specify "round returns the correct type" <| + do_round 231.2 1 . should_be_a Decimal + do_round 231.2 0 . should_be_a Decimal + do_round 231.2 . should_be_a Decimal + do_round 231.2 -1 . should_be_a Decimal + + Test.specify "round returns the correct type" <| + do_round 231 1 . should_be_a Decimal + do_round 231 0 . should_be_a Decimal + do_round 231 . should_be_a Decimal + do_round 231 -1 . should_be_a Decimal + sqlite_spec connection prefix = name_counter = Ref.new 0 table_builder columns = diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index aeff3ed9fda7..57b9c1cd5740 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -169,6 +169,35 @@ spec = Test.specify "Should error on input of the wrong type" <| Column.from_vector "foo" ["asdf", "zxcv", "qwer"] . floor . should_fail_with Invalid_Value_Type + Test.group "round/truncate/ceil/floor return types" <| + do_op n op = + col = Column.from_vector "x" [n] + result = op col + result.to_vector.at 0 + do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + + Test.specify "Can handle NaN/Infinity" <| + ops = [.round, .truncate, .ceil, .floor] + ops.each op-> + IO.println 'AAA' + IO.println op + IO.println <| do_op Number.nan op + do_op Number.nan op . should_equal Nothing + do_op Number.positive_infinity op . should_equal Nothing + do_op Number.negative_infinity op . should_equal Nothing + + Test.specify "round returns the correct type" <| + do_round 231.2 1 . should_be_a Decimal + do_round 231.2 0 . should_be_a Integer + do_round 231.2 . should_be_a Integer + do_round 231.2 -1 . should_be_a Integer + + Test.specify "round returns the correct type" <| + do_round 231 1 . should_be_a Integer + do_round 231 0 . should_be_a Integer + do_round 231 . should_be_a Integer + do_round 231 -1 . should_be_a Integer + Test.group "Date_Time truncate" <| Test.specify "should be able to truncate a column of Date_Times" <| Column.from_vector "foo" [Date_Time.new 2020 10 24 1 2 3, Date_Time.new 2020 10 24 1 2 3] . truncate . should_equal <| Column.from_vector "foo" [Date.new 2020 10 24, Date.new 2020 10 24] From 48953663110c94c22839c6d209af6e093f113588 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 28 Jun 2023 15:04:15 -0400 Subject: [PATCH 75/83] trunc --- .../UnaryMapOperationWithProblemBuilder.java | 26 +++++++++++++++++++ .../column/storage/numeric/DoubleStorage.java | 25 +++++++++++++++--- .../src/In_Memory/Column_Spec.enso | 21 +++++++-------- 3 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java new file mode 100644 index 000000000000..8cd679129efe --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java @@ -0,0 +1,26 @@ +package org.enso.table.data.column.operation.map; + +import org.enso.table.data.column.storage.Storage; + +/** + * A map-like operation that ignores its second argument + * + * @param the supported storage type + */ +public abstract class UnaryMapOperationWithProblemBuilder> extends MapOperation { + public UnaryMapOperationWithProblemBuilder(String name) { + super(name); + } + + protected abstract Storage run(I storage, Object arg, MapOperationProblemBuilder problemBuilder); + + @Override + public Storage runMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) { + return run(storage, arg, problemBuilder); + } + + @Override + public Storage runZip(I storage, Storage arg, MapOperationProblemBuilder problemBuilder) { + return run(storage, arg, problemBuilder); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index f0eb17a5daf5..5c617690f84b 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -8,6 +8,7 @@ import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; import org.enso.table.data.column.operation.map.UnaryDoubleToLongOp; import org.enso.table.data.column.operation.map.UnaryMapOperation; +import org.enso.table.data.column.operation.map.UnaryMapOperationWithProblemBuilder; import org.enso.table.data.column.operation.map.numeric.DoubleBooleanOp; import org.enso.table.data.column.operation.map.numeric.DoubleIsInOp; import org.enso.table.data.column.operation.map.numeric.DoubleNumericOp; @@ -245,10 +246,28 @@ protected double doDouble( } }) .add( - new UnaryDoubleToLongOp(Maps.TRUNCATE) { + new UnaryMapOperationWithProblemBuilder<>(Maps.TRUNCATE) { @Override - protected long doOperation(double a) { - return (long) a; + public LongStorage run(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) { + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + + for (int i = 0; i < storage.size; i++) { + if (!storage.isNa(i)) { + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = (long) item; + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); + } + } else { + isMissing.set(i); + } + } + return new LongStorage(out, storage.size(), isMissing); } }) .add( diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 57b9c1cd5740..23393cf8c6e9 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -2,6 +2,7 @@ from Standard.Base import all import project.Util +import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Examples @@ -169,23 +170,13 @@ spec = Test.specify "Should error on input of the wrong type" <| Column.from_vector "foo" ["asdf", "zxcv", "qwer"] . floor . should_fail_with Invalid_Value_Type - Test.group "round/truncate/ceil/floor return types" <| + Test.group "round/truncate/ceil/floor" <| do_op n op = col = Column.from_vector "x" [n] result = op col result.to_vector.at 0 do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) - Test.specify "Can handle NaN/Infinity" <| - ops = [.round, .truncate, .ceil, .floor] - ops.each op-> - IO.println 'AAA' - IO.println op - IO.println <| do_op Number.nan op - do_op Number.nan op . should_equal Nothing - do_op Number.positive_infinity op . should_equal Nothing - do_op Number.negative_infinity op . should_equal Nothing - Test.specify "round returns the correct type" <| do_round 231.2 1 . should_be_a Decimal do_round 231.2 0 . should_be_a Integer @@ -198,6 +189,14 @@ spec = do_round 231 . should_be_a Integer do_round 231 -1 . should_be_a Integer + Test.specify "nan/inf" <| + ops = [.truncate] # [.round, .truncate, .ceil, .floor] + ops.map op-> + col = Column.from_vector "x" [2.1, 0.0, Number.nan, Number.positive_infinity, Number.negative_infinity, Nothing, 12.1] + result = op col + result.to_vector . drop 2 . take 4 . should_equal [Nothing, Nothing, Nothing, Nothing] + Warning.get_all result . map .value . should_equal [(Arithmetic_Error.Error 'Value is -Infinity (at rows [4]).'), (Arithmetic_Error.Error 'Value is Infinity (at rows [3]).'), (Arithmetic_Error.Error 'Value is NaN (at rows [2]).')] + Test.group "Date_Time truncate" <| Test.specify "should be able to truncate a column of Date_Times" <| Column.from_vector "foo" [Date_Time.new 2020 10 24 1 2 3, Date_Time.new 2020 10 24 1 2 3] . truncate . should_equal <| Column.from_vector "foo" [Date.new 2020 10 24, Date.new 2020 10 24] From 634223891c799899efb363cf87a33fe38b0b7508 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 28 Jun 2023 15:22:17 -0400 Subject: [PATCH 76/83] DoubleLongMapOpWithSpecialNumericHandling --- ...leLongMapOpWithSpecialNumericHandling.java | 36 +++++++++++++++++++ .../column/storage/numeric/DoubleStorage.java | 26 +++----------- .../src/In_Memory/Column_Spec.enso | 1 + 3 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java new file mode 100644 index 000000000000..7e1cde9cb0d0 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java @@ -0,0 +1,36 @@ +package org.enso.table.data.column.operation.map; + +import java.util.BitSet; +import org.enso.table.data.column.storage.numeric.DoubleStorage; +import org.enso.table.data.column.storage.numeric.LongStorage; + +public abstract class DoubleLongMapOpWithSpecialNumericHandling extends UnaryMapOperationWithProblemBuilder { + public DoubleLongMapOpWithSpecialNumericHandling(String name) { + super(name); + } + + protected abstract long doOperation(double a); + + @Override + public LongStorage run(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) { + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + + for (int i = 0; i < storage.size(); i++) { + if (!storage.isNa(i)) { + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = runOne(item); + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); + } + } else { + isMissing.set(i); + } + } + return new LongStorage(out, storage.size(), isMissing); + } +} \ No newline at end of file diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 5c617690f84b..f636923afd27 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -4,6 +4,7 @@ import java.util.List; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.NumericBuilder; +import org.enso.table.data.column.operation.map.DoubleLongMapOpWithSpecialNumericHandling; import org.enso.table.data.column.operation.map.MapOpStorage; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; import org.enso.table.data.column.operation.map.UnaryDoubleToLongOp; @@ -246,28 +247,9 @@ protected double doDouble( } }) .add( - new UnaryMapOperationWithProblemBuilder<>(Maps.TRUNCATE) { - @Override - public LongStorage run(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) { - long[] out = new long[storage.size()]; - BitSet isMissing = new BitSet(); - - for (int i = 0; i < storage.size; i++) { - if (!storage.isNa(i)) { - double item = storage.getItem(i); - boolean special = Double.isNaN(item) || Double.isInfinite(item); - if (!special) { - out[i] = (long) item; - } else { - String msg = "Value is " + item; - problemBuilder.reportArithmeticError(msg, i); - isMissing.set(i); - } - } else { - isMissing.set(i); - } - } - return new LongStorage(out, storage.size(), isMissing); + new DoubleLongMapOpWithSpecialNumericHandling(Maps.TRUNCATE) { + protected long doOperation(double a) { + return (long) a; } }) .add( diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 23393cf8c6e9..cc32e658056a 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -189,6 +189,7 @@ spec = do_round 231 . should_be_a Integer do_round 231 -1 . should_be_a Integer + Test.group "asdfasdf" <| Test.specify "nan/inf" <| ops = [.truncate] # [.round, .truncate, .ceil, .floor] ops.map op-> From c22364e24c26cad7980dcdaec0a3e55d0dd06461 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 28 Jun 2023 15:37:55 -0400 Subject: [PATCH 77/83] wip --- .../map/DoubleLongMapOpWithSpecialNumericHandling.java | 2 +- .../table/data/column/storage/numeric/DoubleStorage.java | 5 +++-- test/Table_Tests/src/In_Memory/Column_Spec.enso | 8 +++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java index 7e1cde9cb0d0..e01c21b1b24c 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java @@ -21,7 +21,7 @@ public LongStorage run(DoubleStorage storage, Object arg, MapOperationProblemBui double item = storage.getItem(i); boolean special = Double.isNaN(item) || Double.isInfinite(item); if (!special) { - out[i] = runOne(item); + out[i] = doOperation(item); } else { String msg = "Value is " + item; problemBuilder.reportArithmeticError(msg, i); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index f636923afd27..79c07cf16e97 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -248,19 +248,20 @@ protected double doDouble( }) .add( new DoubleLongMapOpWithSpecialNumericHandling(Maps.TRUNCATE) { + @Override protected long doOperation(double a) { return (long) a; } }) .add( - new UnaryDoubleToLongOp(Maps.CEIL) { + new DoubleLongMapOpWithSpecialNumericHandling(Maps.CEIL) { @Override protected long doOperation(double a) { return (long) Math.ceil(a); } }) .add( - new UnaryDoubleToLongOp(Maps.FLOOR) { + new DoubleLongMapOpWithSpecialNumericHandling(Maps.FLOOR) { @Override protected long doOperation(double a) { return (long) Math.floor(a); diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index cc32e658056a..c74b9a5392da 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -189,14 +189,16 @@ spec = do_round 231 . should_be_a Integer do_round 231 -1 . should_be_a Integer - Test.group "asdfasdf" <| Test.specify "nan/inf" <| - ops = [.truncate] # [.round, .truncate, .ceil, .floor] + ops = [.truncate, .ceil, .floor, .round] ops.map op-> col = Column.from_vector "x" [2.1, 0.0, Number.nan, Number.positive_infinity, Number.negative_infinity, Nothing, 12.1] result = op col result.to_vector . drop 2 . take 4 . should_equal [Nothing, Nothing, Nothing, Nothing] - Warning.get_all result . map .value . should_equal [(Arithmetic_Error.Error 'Value is -Infinity (at rows [4]).'), (Arithmetic_Error.Error 'Value is Infinity (at rows [3]).'), (Arithmetic_Error.Error 'Value is NaN (at rows [2]).')] + warnings = Warning.get_all result . map .value + warnings . should_contain <| Arithmetic_Error.Error 'Value is -Infinity (at rows [4]).' + warnings . should_contain <| Arithmetic_Error.Error 'Value is Infinity (at rows [3]).' + warnings . should_contain <| Arithmetic_Error.Error 'Value is NaN (at rows [2]).' Test.group "Date_Time truncate" <| Test.specify "should be able to truncate a column of Date_Times" <| From 7b165648b20cf4e1513a2a94d58c94dbdd258ecf Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 28 Jun 2023 15:49:28 -0400 Subject: [PATCH 78/83] fmt --- ...leLongMapOpWithSpecialNumericHandling.java | 52 ++++++++++--------- .../UnaryMapOperationWithProblemBuilder.java | 28 +++++----- .../column/storage/numeric/DoubleStorage.java | 2 - 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java index e01c21b1b24c..28545ba8b558 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/DoubleLongMapOpWithSpecialNumericHandling.java @@ -4,33 +4,35 @@ import org.enso.table.data.column.storage.numeric.DoubleStorage; import org.enso.table.data.column.storage.numeric.LongStorage; -public abstract class DoubleLongMapOpWithSpecialNumericHandling extends UnaryMapOperationWithProblemBuilder { - public DoubleLongMapOpWithSpecialNumericHandling(String name) { - super(name); - } +public abstract class DoubleLongMapOpWithSpecialNumericHandling + extends UnaryMapOperationWithProblemBuilder { + public DoubleLongMapOpWithSpecialNumericHandling(String name) { + super(name); + } - protected abstract long doOperation(double a); + protected abstract long doOperation(double a); - @Override - public LongStorage run(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) { - long[] out = new long[storage.size()]; - BitSet isMissing = new BitSet(); + @Override + public LongStorage run( + DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) { + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); - for (int i = 0; i < storage.size(); i++) { - if (!storage.isNa(i)) { - double item = storage.getItem(i); - boolean special = Double.isNaN(item) || Double.isInfinite(item); - if (!special) { - out[i] = doOperation(item); - } else { - String msg = "Value is " + item; - problemBuilder.reportArithmeticError(msg, i); - isMissing.set(i); - } - } else { - isMissing.set(i); - } + for (int i = 0; i < storage.size(); i++) { + if (!storage.isNa(i)) { + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = doOperation(item); + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); } - return new LongStorage(out, storage.size(), isMissing); + } else { + isMissing.set(i); + } } -} \ No newline at end of file + return new LongStorage(out, storage.size(), isMissing); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java index 8cd679129efe..291d8641a84f 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/UnaryMapOperationWithProblemBuilder.java @@ -7,20 +7,22 @@ * * @param the supported storage type */ -public abstract class UnaryMapOperationWithProblemBuilder> extends MapOperation { - public UnaryMapOperationWithProblemBuilder(String name) { - super(name); - } +public abstract class UnaryMapOperationWithProblemBuilder> + extends MapOperation { + public UnaryMapOperationWithProblemBuilder(String name) { + super(name); + } - protected abstract Storage run(I storage, Object arg, MapOperationProblemBuilder problemBuilder); + protected abstract Storage run( + I storage, Object arg, MapOperationProblemBuilder problemBuilder); - @Override - public Storage runMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) { - return run(storage, arg, problemBuilder); - } + @Override + public Storage runMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) { + return run(storage, arg, problemBuilder); + } - @Override - public Storage runZip(I storage, Storage arg, MapOperationProblemBuilder problemBuilder) { - return run(storage, arg, problemBuilder); - } + @Override + public Storage runZip(I storage, Storage arg, MapOperationProblemBuilder problemBuilder) { + return run(storage, arg, problemBuilder); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 79c07cf16e97..174b019852a3 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -7,9 +7,7 @@ import org.enso.table.data.column.operation.map.DoubleLongMapOpWithSpecialNumericHandling; import org.enso.table.data.column.operation.map.MapOpStorage; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; -import org.enso.table.data.column.operation.map.UnaryDoubleToLongOp; import org.enso.table.data.column.operation.map.UnaryMapOperation; -import org.enso.table.data.column.operation.map.UnaryMapOperationWithProblemBuilder; import org.enso.table.data.column.operation.map.numeric.DoubleBooleanOp; import org.enso.table.data.column.operation.map.numeric.DoubleIsInOp; import org.enso.table.data.column.operation.map.numeric.DoubleNumericOp; From bc6f8fab1366a17f1db2a971e986117f2c308607 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 29 Jun 2023 09:33:16 -0400 Subject: [PATCH 79/83] precision limit tests --- .../Standard/Table/0.0.0-dev/src/Data/Column.enso | 10 ++++++++++ test/Table_Tests/src/Database/Postgres_Spec.enso | 12 ++++++++++++ test/Table_Tests/src/Database/SQLite_Spec.enso | 12 ++++++++++++ 3 files changed, 34 insertions(+) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 4b0d61488b0b..d1c6d750f55d 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -707,6 +707,16 @@ type Column If `decimal_places` is outside the range -15..15 (inclusive), an `Illegal_Argument` error is thrown. + ! Precision + As floating-point numbers are inexact, rounding can have unexpected + results near the precision limit (about 15 decimal places), especially + when rounding mid-point values. For example: + + Postgres: rounding 1.2222222222222235 to 15 decimal places returns: + 1.222222222222223 + SQLite: rounding 1.2222222222222235 to 15 decimal places returns: + 1.222222222222224 + ? Negative decimal place counts Rounding to `n` digits can be thought of as "rounding to the nearest multiple of 10^(-n)". For negative decimal counts, this results in diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 018926828723..19d642c3a9b8 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -229,6 +229,18 @@ postgres_specific_spec connection db_name setup = result.to_vector.at 0 do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + Test.specify "Can round correctly near the precision limit" <| + do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + do_round -1.2222222222222225 15 . should_equal -1.222222222222223 + do_round 1.2222222222222235 15 . should_equal 1.222222222222224 + do_round -1.2222222222222235 15 . should_equal -1.222222222222224 + + Test.specify "Can round correctly near the precision limit, using banker's rounding" <| + do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 + do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 + do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 + do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 + Test.specify "Can handle NaN/Infinity" <| nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing ops = [.round, .truncate, .ceil, .floor] diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index cc12e7281e82..009d086d1dac 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -157,6 +157,18 @@ sqlite_specific_spec prefix connection setup = result.to_vector.at 0 do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + Test.specify "Can round correctly near the precision limit" <| + do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + do_round -1.2222222222222225 15 . should_equal -1.222222222222223 + do_round 1.2222222222222235 15 . should_equal 1.222222222222223 + do_round -1.2222222222222235 15 . should_equal -1.222222222222224 + + Test.specify "Can round correctly near the precision limit, using banker's rounding" <| + do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 + do_round -1.2222222222222225 15 use_bankers=True . should_equal -1.222222222222222 + do_round 1.2222222222222235 15 use_bankers=True . should_equal 1.222222222222224 + do_round -1.2222222222222235 15 use_bankers=True . should_equal -1.222222222222224 + Test.specify "Can handle NaN/Infinity" <| nan_result = if setup.test_selection.is_nan_and_nothing_distinct then Number.nan else Nothing ops = [.round, .truncate, .ceil, .floor] From 9905ac3301cc923ea990fdbb33428407b208573d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 29 Jun 2023 09:42:19 -0400 Subject: [PATCH 80/83] fixed --- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 4 ++-- test/Table_Tests/src/Database/Postgres_Spec.enso | 2 +- test/Table_Tests/src/Database/SQLite_Spec.enso | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index d1c6d750f55d..71a553bb68d5 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -713,9 +713,9 @@ type Column when rounding mid-point values. For example: Postgres: rounding 1.2222222222222235 to 15 decimal places returns: - 1.222222222222223 - SQLite: rounding 1.2222222222222235 to 15 decimal places returns: 1.222222222222224 + SQLite: rounding 1.2222222222222235 to 15 decimal places returns: + 1.222222222222223 ? Negative decimal place counts Rounding to `n` digits can be thought of as "rounding to the nearest diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 19d642c3a9b8..8b532c1bc5e5 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -194,7 +194,7 @@ postgres_specific_spec connection db_name setup = Test.with_clue "d.value_type="+d.value_type.to_display_text+": " <| d.value_type.variable_length.should_be_true - Test.group "[PostgreSQL] math functions" <| + Test.group "[PostgreSQL] math functionsasdfasdf" <| Test.specify "round, trunc, ceil, floor" <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Float diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 009d086d1dac..c55bff500cc1 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -123,7 +123,7 @@ sqlite_specific_spec prefix connection setup = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] - Test.group "[PostgreSQL] math functions" <| + Test.group "[PostgreSQL] math functionsasdfasdf" <| Test.specify "round, trunc, ceil, floor" <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" @@ -161,7 +161,7 @@ sqlite_specific_spec prefix connection setup = do_round 1.2222222222222225 15 . should_equal 1.222222222222223 do_round -1.2222222222222225 15 . should_equal -1.222222222222223 do_round 1.2222222222222235 15 . should_equal 1.222222222222223 - do_round -1.2222222222222235 15 . should_equal -1.222222222222224 + do_round -1.2222222222222235 15 . should_equal -1.222222222222223 Test.specify "Can round correctly near the precision limit, using banker's rounding" <| do_round 1.2222222222222225 15 use_bankers=True . should_equal 1.222222222222222 From 74f00d34470d9af7d8789ab247188b8ddbc3e9c8 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 29 Jun 2023 09:55:25 -0400 Subject: [PATCH 81/83] cleanup --- test/Table_Tests/src/Database/Postgres_Spec.enso | 2 +- test/Table_Tests/src/Database/SQLite_Spec.enso | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Table_Tests/src/Database/Postgres_Spec.enso b/test/Table_Tests/src/Database/Postgres_Spec.enso index 8b532c1bc5e5..19d642c3a9b8 100644 --- a/test/Table_Tests/src/Database/Postgres_Spec.enso +++ b/test/Table_Tests/src/Database/Postgres_Spec.enso @@ -194,7 +194,7 @@ postgres_specific_spec connection db_name setup = Test.with_clue "d.value_type="+d.value_type.to_display_text+": " <| d.value_type.variable_length.should_be_true - Test.group "[PostgreSQL] math functionsasdfasdf" <| + Test.group "[PostgreSQL] math functions" <| Test.specify "round, trunc, ceil, floor" <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" col . cast Value_Type.Integer . ceil . value_type . should_equal Value_Type.Float diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index c55bff500cc1..fc5967cb3eaf 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -123,7 +123,7 @@ sqlite_specific_spec prefix connection setup = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] - Test.group "[PostgreSQL] math functionsasdfasdf" <| + Test.group "[PostgreSQL] math functions" <| Test.specify "round, trunc, ceil, floor" <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" From 40d23762125f75f3b7cb66e5914a923a8421c339 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 29 Jun 2023 14:44:59 -0400 Subject: [PATCH 82/83] epsilon --- test/Table_Tests/src/Database/SQLite_Spec.enso | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index fc5967cb3eaf..c2231a13d053 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -123,7 +123,7 @@ sqlite_specific_spec prefix connection setup = expected_code = code_template.replace "{Tinfo}" tinfo t.distinct ["strs"] . to_sql . prepare . should_equal [expected_code, []] - Test.group "[PostgreSQL] math functions" <| + Test.group prefix+"math functions" <| Test.specify "round, trunc, ceil, floor" <| col = table_builder [["x", [0.1, 0.9, 3.1, 3.9, -0.1, -0.9, -3.1, -3.9]]] . at "x" @@ -158,7 +158,8 @@ sqlite_specific_spec prefix connection setup = do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) Test.specify "Can round correctly near the precision limit" <| - do_round 1.2222222222222225 15 . should_equal 1.222222222222223 + # This value varies depending on the version of SQLite. + do_round 1.2222222222222225 15 . should_equal 1.222222222222223 0.000000000000002 do_round -1.2222222222222225 15 . should_equal -1.222222222222223 do_round 1.2222222222222235 15 . should_equal 1.222222222222223 do_round -1.2222222222222235 15 . should_equal -1.222222222222223 From 9ff2ef6701e4a09e7d24d0f03b7d7703fb090340 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 30 Jun 2023 11:33:18 -0400 Subject: [PATCH 83/83] another epsilon --- test/Table_Tests/src/Database/SQLite_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index c2231a13d053..f866488e0cf0 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -160,7 +160,7 @@ sqlite_specific_spec prefix connection setup = Test.specify "Can round correctly near the precision limit" <| # This value varies depending on the version of SQLite. do_round 1.2222222222222225 15 . should_equal 1.222222222222223 0.000000000000002 - do_round -1.2222222222222225 15 . should_equal -1.222222222222223 + do_round -1.2222222222222225 15 . should_equal -1.222222222222223 0.000000000000002 do_round 1.2222222222222235 15 . should_equal 1.222222222222223 do_round -1.2222222222222235 15 . should_equal -1.222222222222223