Skip to content

Commit

Permalink
Implement truncate Date_Time for database backend (#8235)
Browse files Browse the repository at this point in the history
Also adds some checks for column names generated for floor, ceil, truncate, round.
  • Loading branch information
GregoryTravis authored Nov 8, 2023
1 parent 5b4716e commit 6be94a8
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@
- [Added XML support for `.to Table` and `.expand_column`.][8083]
- [Added `Previous_Value` option to `fill_nothing` and `fill_empty`.][8105]
- [Added `Table.format` for the in-memory backend.][8150]
- [Implemented truncate `Date_Time` for database backend (Postgres only).][8235]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -844,6 +845,7 @@
[8083]: https://github.com/enso-org/enso/pull/8083
[8105]: https://github.com/enso-org/enso/pull/8105
[8150]: https://github.com/enso-org/enso/pull/8150
[8235]: https://github.com/enso-org/enso/pull/8235

#### Enso Compiler

Expand Down
22 changes: 17 additions & 5 deletions distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso
Original file line number Diff line number Diff line change
Expand Up @@ -810,9 +810,21 @@ type Column
example_truncate = Examples.decimal_column.truncate
truncate : Column ! Invalid_Value_Type
truncate self =
Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <|
new_name = self.naming_helper.function_name "truncate" [self]
self.make_unary_op "TRUNCATE" new_name
new_name = self.naming_helper.function_name "truncate" [self]
precise_value_type = self.inferred_precise_value_type
case precise_value_type.is_numeric of
True ->
self.short_circuit_special_floating_point <|
self.make_unary_op "TRUNCATE" new_name
False -> case precise_value_type == Value_Type.Date_Time of
True ->
op = "date_trunc_to_day"
case self.connection.dialect.is_supported op of
True ->
self.make_unary_op op new_name
False ->
Error.throw (Unsupported_Database_Operation.Error ("`Column.truncate` for `Date_Time` is not supported by this connection."))
False -> Error.throw <| Invalid_Value_Type.Column "Numeric or Date_Time" self.value_type self.name

## GROUP Standard.Base.Rounding
Takes the ceiling of floating-point values, returning integer values.
Expand All @@ -826,7 +838,7 @@ type Column

import Standard.Examples

example_truncate = Examples.decimal_column.ceil
example_ceil = Examples.decimal_column.ceil
ceil : Column ! Invalid_Value_Type
ceil self =
Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <|
Expand All @@ -845,7 +857,7 @@ type Column

import Standard.Examples

example_truncate = Examples.decimal_column.floor
example_floor = Examples.decimal_column.floor
floor : Column ! Invalid_Value_Type
floor self =
Value_Type.expect_numeric self <| self.short_circuit_special_floating_point <|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ make_internal_generator_dialect =
stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"]
stddev_samp = ["STDDEV_SAMP", Base_Generator.make_function "stddev_samp"]
stats = [agg_median, agg_mode, agg_percentile, stddev_pop, stddev_samp]
date_ops = [make_extract_as_int "year", make_extract_as_int "quarter", make_extract_as_int "month", make_extract_as_int "week", make_extract_as_int "day", make_extract_as_int "hour", make_extract_as_int "minute", make_extract_fractional_as_int "second", make_extract_fractional_as_int "millisecond" modulus=1000, make_extract_fractional_as_int "microsecond" modulus=1000, ["date_add", make_date_add], ["date_diff", make_date_diff]]
date_ops = [make_extract_as_int "year", make_extract_as_int "quarter", make_extract_as_int "month", make_extract_as_int "week", make_extract_as_int "day", make_extract_as_int "hour", make_extract_as_int "minute", make_extract_fractional_as_int "second", make_extract_fractional_as_int "millisecond" modulus=1000, make_extract_fractional_as_int "microsecond" modulus=1000, ["date_add", make_date_add], ["date_diff", make_date_diff], ["date_trunc_to_day", make_date_trunc_to_day]]
special_overrides = [is_null, is_empty]
other = [["RUNTIME_ERROR", make_runtime_error_op]]
my_mappings = text + counts + stats + first_last_aggregators + arith_extensions + bool + date_ops + special_overrides + other
Expand Down Expand Up @@ -716,6 +716,11 @@ make_date_diff arguments (metadata : Date_Period_Metadata) =
as_int64 <|
((extract_seconds ++ " * 1000000").paren ++ " + " ++ (micros ++ " % 1000000").paren).paren

make_date_trunc_to_day arguments =
if arguments.length != 1 then Error.throw (Illegal_State.Error "date_trunc_to_day expects exactly one sub expression. This is a bug in Database library.") else
expr = arguments.at 0
Builder.code "(DATE_TRUNC('day'," ++ expr ++ ") :: DATE)"

## PRIVATE
Alters the expression casting the value to a 64-bit integer.
as_int64 expr =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,19 +889,19 @@ type Column
col.truncate == dates
truncate : Column ! Invalid_Value_Type
truncate self =
new_name = naming_helper.function_name "truncate" [self]
precise_value_type = self.inferred_precise_value_type
case precise_value_type.is_numeric of
True ->
case precise_value_type.is_integer of
True ->
new_name = naming_helper.function_name "truncate" [self]
self.rename new_name
False ->
simple_unary_op self Java_Storage.Maps.TRUNCATE
False -> case precise_value_type == Value_Type.Date_Time of
True ->
fun = _.date
Column_Ops.map_over_storage self fun make_date_builder_adapter skip_nothing=True
Column_Ops.map_over_storage self fun make_date_builder_adapter skip_nothing=True . rename new_name
False -> Error.throw <| Invalid_Value_Type.Column "Numeric or Date_Time" self.value_type self.name

## GROUP Standard.Base.Rounding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ main = run_default_backend spec
spec setup =
prefix = setup.prefix
table_builder = setup.table_builder
pending_datetime = if setup.test_selection.date_time.not then "Date/Time operations are not supported by this backend."

do_op n op =
table = table_builder [["x", [n]]]
Expand Down Expand Up @@ -690,31 +691,37 @@ spec setup =
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.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" . 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.name . should_equal "round([x])"

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.name . should_equal "round([x])"

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.name . should_equal "truncate([x])"

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.name . should_equal "ceil([x])"

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.name . should_equal "floor([x])"

test_floatlike Value_Type.Float
if setup.test_selection.supports_decimal_type then
Expand All @@ -724,21 +731,25 @@ 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.name . should_equal "round([x])"

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.name . should_equal "truncate([x])"

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.name . should_equal "ceil([x])"

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.name . should_equal "floor([x])"

Test.specify "should fail on decimal_places out of range" <|
table = table_builder [["x", [0, 3, -3, 1, -2]]]
Expand Down Expand Up @@ -779,6 +790,16 @@ spec setup =
x.round . to_vector . should_equal [1, -2, 4]
x.truncate . to_vector . should_equal [1, -2, 3]

Test.group prefix+"Date truncation" pending=pending_datetime <|
Test.specify "should be able to truncate a column of Date_Times" <|
dates = [Date_Time.new 2020 10 24 1 2 3, Date_Time.new 2020 10 24 1 2 3]
table = table_builder [["foo", dates]]
table.at "foo" . value_type . should_equal (Value_Type.Date_Time with_timezone=True)
truncated = table.at "foo" . truncate
truncated . to_vector . should_equal [Date.new 2020 10 24, Date.new 2020 10 24]
truncated . value_type . should_equal Value_Type.Date
truncated.name . should_equal "truncate([foo])"

Test.group prefix+"Text Column Operations" <|
Test.specify "should handle operations like starts_with, ends_with, contains" <|
with_mixed_columns_if_supported [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] t3->
Expand Down
5 changes: 5 additions & 0 deletions test/Table_Tests/src/Database/SQLite_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ sqlite_specific_spec prefix connection setup =
do_round 231 . should_be_a Float
do_round 231 -1 . should_be_a Float

Test.group prefix+"Column.const" <|
Test.specify "Does not support making a constant column from a Date" <|
t = table_builder [["x", ["1", "2", "3"]]]
t.at "x" . const (Date.new 12 4 12) . should_fail_with Unsupported_Database_Operation

sqlite_spec connection prefix =
name_counter = Ref.new 0
table_builder columns =
Expand Down
6 changes: 4 additions & 2 deletions test/Table_Tests/src/In_Memory/Column_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -361,5 +361,7 @@ spec =

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]
Column.from_vector "foo" [Date_Time.new 2020 10 24 1 2 3, Date_Time.new 2020 10 24 1 2 3] . truncate . value_type . should_equal Value_Type.Date
c = Column.from_vector "foo" [Date_Time.new 2020 10 24 1 2 3, Date_Time.new 2020 10 24 1 2 3]
truncated = c.truncate
truncated . should_equal <| Column.from_vector "truncate([foo])" [Date.new 2020 10 24, Date.new 2020 10 24]
truncated . value_type . should_equal Value_Type.Date

0 comments on commit 6be94a8

Please sign in to comment.