Adding support for creating Atoms in expressions (#10820)

- Enables the `..` autoscoping style for creating Atoms in expressions.
- Add type checking to methods in columns.
- Auto wrap returns from method in expressions into a column as needed.
- Remove `Time_Period.Day` to remove confusion..
This commit is contained in:
James Dunkerley 2024-08-15 16:52:30 +01:00 committed by GitHub
parent a168baddb3
commit 422fa8c16b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 147 additions and 207 deletions

View File

@ -16,12 +16,14 @@
- [Support for reading from Tableau Hyper files.][10733]
- [Mixed Decimal/Float arithmetic now throws an error; mixed comparisons now
attach warnings.][10725]
- [Support for creating Atoms in expressions.][10820]
[10614]: https://github.com/enso-org/enso/pull/10614
[10660]: https://github.com/enso-org/enso/pull/10660
[10761]: https://github.com/enso-org/enso/pull/10761
[10733]: https://github.com/enso-org/enso/pull/10733
[10725]: https://github.com/enso-org/enso/pull/10725
[10820]: https://github.com/enso-org/enso/pull/10820
# Enso 2023.3

View File

@ -25,8 +25,8 @@ import Standard.Database.Internal.Statement_Setter.Statement_Setter
import Standard.Database.SQL.SQL_Builder
import Standard.Database.SQL_Statement.SQL_Statement
import Standard.Database.SQL_Type.SQL_Type
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
from Standard.Database.Dialect import Temp_Table_Style
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
import project.Database.Redshift.Internal.Redshift_Error_Mapper.Redshift_Error_Mapper

View File

@ -470,7 +470,7 @@ type Decimal
b = Decimal.new "20.33"
a.add b (Math_Context.new 3)
# => Decimal.new 30.5
# TODO: restore checked return type (here and elsewhere) after https://github.com/enso-org/enso/issues/10736
TODO: restore checked return type (here and elsewhere) after https://github.com/enso-org/enso/issues/10736
add : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
add self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
error_if_from_float self that <| handle_java_exception <|

View File

@ -3,9 +3,9 @@ private
import project.Data.Decimal.Decimal
import project.Data.Numbers.Number
import project.Data.Text.Text
from project.Errors.Common import Loss_Of_Numeric_Precision
import project.Warning.Warning
from project.Data.Ordering import Comparable, Ordering
from project.Errors.Common import Loss_Of_Numeric_Precision
polyglot java import org.enso.base.numeric.Decimal_Utils

View File

@ -46,12 +46,6 @@ type Date_Period
Week (first_day:Day_Of_Week = Day_Of_Week.Monday)
## Represents a time period of a single calendar day.
? Daylight Saving Time
Note that due to DST changes, some days may be slightly longer or
shorter. This date period will reflect that and still count such days
as one day. For a measure of exactly 24 hours, use `Time_Period.Day`.
Day
## PRIVATE
@ -67,7 +61,7 @@ type Date_Period
## PRIVATE
adjust_end : (Date | Date_Time) -> (Date | Date_Time)
adjust_end self date = if self == Date_Period.Day then date else
adjust_end self date =
adjuster = case self of
Date_Period.Year -> TemporalAdjusters.lastDayOfYear
Date_Period.Quarter -> Date_Period_Utils.quarter_end

View File

@ -605,7 +605,7 @@ type Date_Time
start_of self period=Date_Period.Month =
adjusted = period.adjust_start self
case period of
_ : Date_Period -> Time_Period.Day.adjust_start adjusted
_ : Date_Period -> Date_Period.Day.adjust_start adjusted
_ : Time_Period -> adjusted
## GROUP DateTime
@ -616,7 +616,7 @@ type Date_Time
end_of self period=Date_Period.Month =
adjusted = period.adjust_end self
case period of
_ : Date_Period -> Time_Period.Day.adjust_end adjusted
_ : Date_Period -> Date_Period.Day.adjust_end adjusted
_ : Time_Period -> adjusted
## ALIAS time to date
@ -668,7 +668,6 @@ type Date_Time
Date_Period.Month -> self.month
Date_Period.Week _ -> self.week_of_year locale=Nothing
Date_Period.Day -> self.day
Time_Period.Day -> self.day
Time_Period.Hour -> self.hour
Time_Period.Minute -> self.minute
Time_Period.Second -> self.second

View File

@ -272,13 +272,13 @@ type Time_Of_Day
ICON time
Returns the first time within the `Time_Period` containing self.
start_of : Time_Period -> Time_Of_Day
start_of self period=Time_Period.Day = period.adjust_start self
start_of self period=Time_Period.Hour = period.adjust_start self
## GROUP DateTime
ICON time
Returns the last time within the `Time_Period` containing self.
end_of : Time_Period -> Time_Of_Day
end_of self period=Time_Period.Day = period.adjust_end self
end_of self period=Time_Period.Hour = period.adjust_end self
## GROUP Conversions
ICON convert
@ -318,7 +318,6 @@ type Time_Of_Day
date_part : Time_Period -> Integer
date_part self (period : Time_Period) =
case period of
Time_Period.Day -> Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part.")
Time_Period.Hour -> self.hour
Time_Period.Minute -> self.minute
Time_Period.Second -> self.second
@ -339,11 +338,8 @@ type Time_Of_Day
- end: A time of day to compute the difference from.
- period: The period to compute the difference in.
date_diff : Time_Of_Day -> Time_Period -> Integer
date_diff self (end : Time_Of_Day) (period : Time_Period) = case period of
Time_Period.Day ->
Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part to compute a difference in days.")
_ ->
Time_Utils.unit_time_difference period.to_java_unit self end
date_diff self (end : Time_Of_Day) (period : Time_Period) =
Time_Utils.unit_time_difference period.to_java_unit self end
## GROUP DateTime
ICON time
@ -353,11 +349,8 @@ type Time_Of_Day
- amount: An integer specifying by how many periods to shift the time.
- period: The period by which to shift.
date_add : Integer -> Time_Period -> Time_Of_Day
date_add self (amount : Integer) (period : Time_Period) = case period of
Time_Period.Day ->
Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part to add days to.")
_ ->
Time_Utils.unit_time_add period.to_java_unit self amount
date_add self (amount : Integer) (period : Time_Period) =
Time_Utils.unit_time_add period.to_java_unit self amount
## ALIAS add duration
GROUP Operators

View File

@ -10,15 +10,6 @@ polyglot java import org.enso.base.Time_Utils
## Represents a unit of time of a day or shorter.
type Time_Period
## Represents a time period of a single day, measured as 24 hours.
? Daylight Saving Time
Note that due to DST changes, some days may be slightly longer or
shorter. This is not reflected in the duration of this time period. For
a calendar-oriented day period, use `Date_Period.Day` instead.
Day
## Represents a time period of an hour.
Hour
@ -38,10 +29,8 @@ type Time_Period
Nanosecond
## PRIVATE
We treat the `Time_Period.Day` as a period of 24 hours, not a calendar day.
to_java_unit : TemporalUnit
to_java_unit self = case self of
Time_Period.Day -> CustomTemporalUnits.DAY_AS_24_HOURS
Time_Period.Hour -> ChronoUnit.HOURS
Time_Period.Minute -> ChronoUnit.MINUTES
Time_Period.Second -> ChronoUnit.SECONDS
@ -49,30 +38,19 @@ type Time_Period
Time_Period.Microsecond -> ChronoUnit.MICROS
Time_Period.Nanosecond -> ChronoUnit.NANOS
## PRIVATE
A special case for `adjust_start` and `adjust_end` methods.
In this particular case, it seems better to treat `Time_Period.Day` as a
calendar day. Otherwise, the behaviour of `start_of` and `end_of` methods
near DST would become unintuitive.
to_java_unit_for_adjust : TemporalUnit
to_java_unit_for_adjust self = case self of
Time_Period.Day -> ChronoUnit.DAYS
_ -> self.to_java_unit
## PRIVATE
adjust_start : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time)
adjust_start self date =
(Time_Utils.utils_for date).start_of_time_period date self.to_java_unit_for_adjust
(Time_Utils.utils_for date).start_of_time_period date self.to_java_unit
## PRIVATE
adjust_end : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time)
adjust_end self date =
(Time_Utils.utils_for date).end_of_time_period date self.to_java_unit_for_adjust
(Time_Utils.utils_for date).end_of_time_period date self.to_java_unit
## PRIVATE
to_duration : Duration
to_duration self = case self of
Time_Period.Day -> Duration.new hours=24
Time_Period.Hour -> Duration.new hours=1
Time_Period.Minute -> Duration.new minutes=1
Time_Period.Second -> Duration.new seconds=1

View File

@ -159,6 +159,22 @@ type DB_Column
if index < 0 then Error.throw (Unsupported_Database_Operation.Error "Reading backwards from end is not supported in-database. Use `read` to materialize the column.") else
self.read (..First index+1) . get index default
## GROUP Standard.Base.Selections
ICON parse3
Returns the first element in the column, if it exists.
If the column is empty, this method will return a dataflow error
containing an `Index_Out_Of_Bounds`.
> Example
Get the first element of a column.
import Standard.Examples
example_first = Examples.integer_column.first
first : Any ! Index_Out_Of_Bounds
first self = self.at 0
## GROUP Standard.Base.Metadata
ICON metadata
Returns the `Value_Type` associated with that column.
@ -323,7 +339,7 @@ type DB_Column
`other`.
@locale Locale.default_widget
equals_ignore_case : DB_Column | Any -> Locale -> DB_Column
equals_ignore_case self other locale=Locale.default =
equals_ignore_case self other locale:Locale=Locale.default =
Value_Type.expect_text self <|
Value_Type.expect_text other <|
Helpers.assume_default_locale locale <|
@ -1208,7 +1224,7 @@ type DB_Column
Sorting `column` in descending order.
column.sort Sort_Direction.Descending
sort : Sort_Direction -> DB_Column
sort self order=Sort_Direction.Ascending =
sort self order:Sort_Direction=..Ascending =
self.to_table.sort [..Index 0 order] . at 0
## ALIAS first, head, keep, last, limit, sample, slice, tail, top
@ -1249,7 +1265,7 @@ type DB_Column
missing value (a Nothing or a column with missing values), the behaviour
on these missing values is vendor specific.
starts_with : DB_Column | Text -> Case_Sensitivity -> DB_Column
starts_with self other case_sensitivity=Case_Sensitivity.Default =
starts_with self other case_sensitivity:Case_Sensitivity=..Default =
new_name = self.naming_helper.function_name "starts_with" [self, other]
make_text_case_op self "STARTS_WITH" other case_sensitivity new_name
@ -1267,7 +1283,7 @@ type DB_Column
missing value (a Nothing or a column with missing values), the behaviour
on these missing values is vendor specific.
ends_with : DB_Column | Text -> Case_Sensitivity -> DB_Column
ends_with self other case_sensitivity=Case_Sensitivity.Default =
ends_with self other case_sensitivity:Case_Sensitivity=..Default =
new_name = self.naming_helper.function_name "ends_with" [self, other]
make_text_case_op self "ENDS_WITH" other case_sensitivity new_name
@ -1347,7 +1363,7 @@ type DB_Column
missing value (a Nothing or a column with missing values), the behaviour
on these missing values is vendor specific.
contains : DB_Column | Text -> Case_Sensitivity -> DB_Column
contains self other case_sensitivity=Case_Sensitivity.Default =
contains self other case_sensitivity:Case_Sensitivity=..Default =
new_name = self.naming_helper.function_name "contains" [self, other]
make_text_case_op self "CONTAINS" other case_sensitivity new_name
@ -1461,7 +1477,7 @@ type DB_Column
column.text_replace '"(.*?)"'.to_regex '($1)'
@term make_regex_text_widget
text_replace : Text | Regex | DB_Column -> Text | DB_Column -> Case_Sensitivity -> Boolean -> DB_Column ! Unsupported_Database_Operation
text_replace self (term : Text | Regex | DB_Column = "") new_text="" case_sensitivity=Case_Sensitivity.Default only_first=False =
text_replace self (term : Text | Regex | DB_Column = "") new_text="" case_sensitivity:Case_Sensitivity=..Default only_first=False =
Value_Type.expect_text self <| case_sensitivity.disallow_non_default_locale <|
input_type = Meta.type_of term
params = Replace_Params.Value input_type case_sensitivity only_first
@ -1594,7 +1610,7 @@ type DB_Column
Returns a column of `Integer` type.
@period Date_Time_Helpers.make_period_selector_for_column
date_part : Date_Period | Time_Period -> DB_Column ! Invalid_Value_Type | Illegal_Argument
date_part self period =
date_part self period:(Date_Period | Time_Period) =
Date_Time_Helpers.make_date_part_function self period (column->op_name-> column.make_unary_op op_name) self.naming_helper
## GROUP Standard.Base.DateTime
@ -1763,7 +1779,7 @@ type DB_Column
@locale Locale.default_widget
@format (self-> Widget_Helpers.make_format_chooser_for_type self.value_type)
format : Text | Date_Time_Formatter | DB_Column -> Locale -> DB_Column ! Illegal_Argument
format self format:(Text | Date_Time_Formatter | DB_Column)="" locale=Locale.default =
format self format:(Text | Date_Time_Formatter | DB_Column)="" locale:Locale=Locale.default =
_ = [format, locale]
Error.throw <| Unsupported_Database_Operation.Error "`DB_Column.format` is not implemented yet for the Database backends."
@ -1816,7 +1832,7 @@ type DB_Column
types. Due to this, a Mixed column containing values `[2, "3"]` will
actually be converted into `[2, Nothing]` when casting to Integer type.
cast : Value_Type -> Problem_Behavior -> DB_Column ! Illegal_Argument | Inexact_Type_Coercion | Conversion_Failure
cast self value_type on_problems:Problem_Behavior=..Report_Warning =
cast self value_type:Value_Type on_problems:Problem_Behavior=..Report_Warning =
check_cast_compatibility self.value_type value_type <|
self.internal_do_cast value_type on_problems

View File

@ -1045,7 +1045,10 @@ type DB_Table
evaluate_expression : Text | Expression -> Problem_Behavior -> DB_Column ! No_Such_Column | Invalid_Value_Type | Expression_Error
evaluate_expression self expression:(Text | Expression) on_problems:Problem_Behavior=..Report_Warning = if expression.is_a Text then self.evaluate_expression (Expression.Value expression) on_problems else
get_column name = self.at name
new_column = Expression.evaluate expression get_column self.make_constant_column "Standard.Database.DB_Column" "DB_Column" DB_Column.var_args_functions
make_constant_column value = case value of
_ : DB_Column -> value
_ -> self.make_constant_column value
new_column = Expression.evaluate expression get_column make_constant_column "Standard.Database.DB_Column" "DB_Column" DB_Column.var_args_functions
problems = Warning.get_all new_column . map .value
result = new_column.rename (self.connection.base_connection.column_naming_helper.sanitize_name expression.expression)
on_problems.attach_problems_before problems <|

View File

@ -288,10 +288,10 @@ default_fetch_primary_key connection table_name =
## PRIVATE
type Temp_Table_Style
## PRIVATE
The temporary table is created using a create table statement.
Temporary_Table
## PRIVATE
The temporary table is created using a # table name.
Hash_Prefix
## PRIVATE
The temporary table is created using a create table statement.
Temporary_Table
## PRIVATE
The temporary table is created using a # table name.
Hash_Prefix

View File

@ -11,8 +11,8 @@ import project.Internal.IR.Query.Query
import project.Internal.IR.SQL_Expression.SQL_Expression
import project.Internal.IR.SQL_Join_Kind.SQL_Join_Kind
import project.SQL.SQL_Builder
from project.Errors import Unsupported_Database_Operation
from project.Dialect import Temp_Table_Style
from project.Errors import Unsupported_Database_Operation
from project.Internal.IR.Operation_Metadata import Row_Number_Metadata
type Dialect_Operations

View File

@ -38,7 +38,9 @@ type Support_Level
if metadata.dataDefinitionCausesTransactionCommit then Support_Level.Causes_Commit else
Support_Level.Allowed
## PRIVATE
type Transactional_Table_Description
## PRIVATE
Value name:Text temporary:Boolean (structure : Vector Column_Description) (primary_key : Vector Text | Nothing) (remove_after_transaction:Boolean) (on_problems:Problem_Behavior)
## PRIVATE
@ -107,9 +109,11 @@ private create_tables_outside_transaction connection (tables : Vector Transactio
cleanup_transaction_scoped_tables connection tables
cleanup_errors.if_not_error result
## PRIVATE
private cleanup_tables_silently connection table_names = Context.Output.with_enabled <|
table_names.each (name-> connection.drop_table name if_exists=True . catch)
## PRIVATE
private cleanup_transaction_scoped_tables connection tables = Context.Output.with_enabled <|
tables.each (t-> if t.remove_after_transaction then connection.drop_table t.name if_exists=True)

View File

@ -37,8 +37,8 @@ import project.SQL.SQL_Builder
import project.SQL.SQL_Fragment
import project.SQL_Statement.SQL_Statement
import project.SQL_Type.SQL_Type
from project.Errors import SQL_Error, Unsupported_Database_Operation
from project.Dialect import Temp_Table_Style
from project.Errors import SQL_Error, Unsupported_Database_Operation
from project.Internal.IR.Operation_Metadata import Date_Period_Metadata
polyglot java import java.sql.Types
@ -667,8 +667,6 @@ make_date_add arguments (metadata : Date_Period_Metadata) =
"weeks=>1"
Date_Period.Day ->
"days=>1"
Time_Period.Day ->
"hours=>24"
Time_Period.Hour ->
"hours=>1"
Time_Period.Minute ->
@ -734,10 +732,6 @@ make_date_diff arguments (metadata : Date_Period_Metadata) =
Time_Period.Hour -> (extract_seconds ++ " / 3600").paren
Time_Period.Minute -> (extract_seconds ++ " / 60").paren
Time_Period.Second -> extract_seconds
Time_Period.Day -> case metadata.input_value_type of
Value_Type.Date -> extract_days
# Time_Period.Day is treated as 24 hours, so for types that support time we use the same algorithm like for hours, but divide by 24.
_ -> (extract_seconds ++ " / (3600 * 24)").paren
## The EPOCH gives back just the integer amount of seconds, without
the fractional part. So we get the fractional part using
MILLISECONDS - but that does not give the _total_ just the

View File

@ -31,8 +31,8 @@ import project.Internal.Statement_Setter.Statement_Setter
import project.SQL.SQL_Builder
import project.SQL_Statement.SQL_Statement
import project.SQL_Type.SQL_Type
from project.Errors import SQL_Error, Unsupported_Database_Operation
from project.Dialect import Temp_Table_Style
from project.Errors import SQL_Error, Unsupported_Database_Operation
## PRIVATE

View File

@ -7,8 +7,8 @@ import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Table.Internal.Problem_Builder.Problem_Builder
from Standard.Table import Table
import project.Connection.Connection.Connection
import project.Column_Description.Column_Description
import project.Connection.Connection.Connection
import project.DB_Table.DB_Table
## PRIVATE

View File

@ -14,7 +14,7 @@ from project.Errors import SQL_Error
from project.Internal.Upload.Helpers.Argument_Checks import check_delete_rows_arguments
from project.Internal.Upload.Helpers.Check_Queries import check_duplicate_key_matches_for_delete, check_for_null_keys
from project.Internal.Upload.Helpers.Constants import dry_run_row_limit
from project.Internal.Upload.Operations.Internal_Core import Table_Upload_Operation, internal_upload_in_memory_table, internal_upload_table
from project.Internal.Upload.Operations.Internal_Core import internal_upload_in_memory_table, internal_upload_table, Table_Upload_Operation
## PRIVATE
common_delete_rows (target_table : DB_Table) (key_values_to_delete : Table | DB_Table) (key_columns : Vector Text) (allow_duplicate_matches : Boolean) -> Integer =

View File

@ -13,8 +13,8 @@ import project.Internal.DDL_Transaction.Transactional_Table_Description
import project.Internal.In_Transaction.In_Transaction
import project.Internal.IR.Query.Query
import project.SQL_Query.SQL_Query
from project.Errors import SQL_Error, Table_Already_Exists, Unsupported_Database_Operation
from project.Dialect import Temp_Table_Style
from project.Errors import SQL_Error, Table_Already_Exists, Unsupported_Database_Operation
from project.Internal.Upload.Helpers.Argument_Checks import resolve_primary_key
from project.Internal.Upload.Helpers.Constants import default_batch_size
from project.Internal.Upload.Helpers.Error_Helpers import handle_upload_errors, internal_translate_known_upload_errors

View File

@ -13,9 +13,9 @@ import project.Internal.DDL_Transaction
import project.Internal.In_Transaction.In_Transaction
import project.Internal.IR.Query.Query
import project.Update_Action.Update_Action
from project.Errors import SQL_Error, Rows_Already_Present, Unmatched_Rows
from project.Errors import Rows_Already_Present, SQL_Error, Unmatched_Rows
from project.Internal.Upload.Helpers.Argument_Checks import all
from project.Internal.Upload.Helpers.Check_Queries import check_multiple_rows_match, check_for_null_keys_if_any_keys_set
from project.Internal.Upload.Helpers.Check_Queries import check_for_null_keys_if_any_keys_set, check_multiple_rows_match
from project.Internal.Upload.Helpers.Constants import dry_run_row_limit
from project.Internal.Upload.Helpers.Error_Helpers import handle_upload_errors
from project.Internal.Upload.Operations.Internal_Core import internal_upload_table

View File

@ -1,5 +1,5 @@
from Standard.Base import Text
import Standard.Base.Errors.Common.Missing_Argument
from Standard.Base import Text
type SQL_Query
## Query a whole table or view.

View File

@ -38,8 +38,8 @@ import Standard.Database.SQL.SQL_Builder
import Standard.Database.SQL.SQL_Fragment
import Standard.Database.SQL_Statement.SQL_Statement
import Standard.Database.SQL_Type.SQL_Type
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
from Standard.Database.Dialect import Temp_Table_Style
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
from Standard.Database.Internal.IR.Operation_Metadata import Date_Period_Metadata
from Standard.Database.Internal.Statement_Setter import fill_hole_default
@ -193,7 +193,7 @@ type SQLSever_Dialect
Specifies how the database creates temp tables.
temp_table_style : Temp_Table_Style
temp_table_style self = Temp_Table_Style.Hash_Prefix
## 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 =
@ -565,8 +565,6 @@ make_date_add arguments (metadata : Date_Period_Metadata) =
"weeks=>1"
Date_Period.Day ->
"days=>1"
Time_Period.Day ->
"hours=>24"
Time_Period.Hour ->
"hours=>1"
Time_Period.Minute ->
@ -630,10 +628,6 @@ make_date_diff arguments (metadata : Date_Period_Metadata) =
Time_Period.Hour -> (extract_seconds ++ " / 3600").paren
Time_Period.Minute -> (extract_seconds ++ " / 60").paren
Time_Period.Second -> extract_seconds
Time_Period.Day -> case metadata.input_value_type of
Value_Type.Date -> extract_days
# Time_Period.Day is treated as 24 hours, so for types that support time we use the same algorithm like for hours, but divide by 24.
_ -> (extract_seconds ++ " / (3600 * 24)").paren
## The EPOCH gives back just the integer amount of seconds, without
the fractional part. So we get the fractional part using
MILLISECONDS - but that does not give the _total_ just the

View File

@ -39,8 +39,8 @@ import Standard.Database.SQL.SQL_Builder
import Standard.Database.SQL.SQL_Fragment
import Standard.Database.SQL_Statement.SQL_Statement
import Standard.Database.SQL_Type.SQL_Type
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
from Standard.Database.Dialect import Temp_Table_Style
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
from Standard.Database.Internal.IR.Operation_Metadata import Date_Period_Metadata
from Standard.Database.Internal.Statement_Setter import fill_hole_default
@ -616,7 +616,6 @@ date_period_to_part_with_multiplier period =
Date_Period.Month -> ["month", 1]
Date_Period.Week _ -> ["week", 1]
Date_Period.Day -> ["day", 1]
Time_Period.Day -> ["hour", 24]
Time_Period.Hour -> ["hour", 1]
Time_Period.Minute -> ["minute", 1]
Time_Period.Second -> ["second", 1]

View File

@ -240,7 +240,7 @@ type Column
`other`.
@locale Locale.default_widget
equals_ignore_case : Column | Any -> Locale -> Column
equals_ignore_case self other locale=Locale.default =
equals_ignore_case self other locale:Locale=Locale.default =
## TODO currently this always runs the fallback which is slow due to the
cost of Java-to-Enso calls. We want to have a vectorized
implementation, but we need to extend the architecture to allow
@ -1244,7 +1244,7 @@ type Column
example_starts_with = Examples.text_column_1.starts_with "hell" Case_Sensitivity.Insensitive
starts_with : Column | Text -> Case_Sensitivity -> Column
starts_with self other case_sensitivity=Case_Sensitivity.Default =
starts_with self other case_sensitivity:Case_Sensitivity=..Default =
new_name = naming_helper.function_name "starts_with" [self, other]
run_vectorized_binary_case_text_op self Java_Storage.Maps.STARTS_WITH other case_sensitivity (a -> b -> a.starts_with b case_sensitivity) new_name
@ -1275,7 +1275,7 @@ type Column
example_ends_with = Examples.text_column_1.ends_with "hell"
ends_with : Column | Text -> Case_Sensitivity -> Column
ends_with self other case_sensitivity=Case_Sensitivity.Default =
ends_with self other case_sensitivity:Case_Sensitivity=..Default =
new_name = naming_helper.function_name "ends_with" [self, other]
run_vectorized_binary_case_text_op self Java_Storage.Maps.ENDS_WITH other case_sensitivity (a -> b -> a.ends_with b case_sensitivity) new_name
@ -1367,7 +1367,7 @@ type Column
example_contains = Examples.text_column_1.contains "hell"
contains : Column | Text -> Case_Sensitivity -> Column
contains self other case_sensitivity=Case_Sensitivity.Default =
contains self other case_sensitivity:Case_Sensitivity=..Default =
new_name = naming_helper.function_name "contains" [self, other]
run_vectorized_binary_case_text_op self Java_Storage.Maps.CONTAINS other case_sensitivity (a -> b -> a.contains b case_sensitivity) new_name
@ -1450,7 +1450,7 @@ type Column
column.text_replace '"(.*?)"'.to_regex '($1)'
@term make_regex_text_widget
text_replace : Text | Regex | Column -> Text | Column -> Case_Sensitivity -> Boolean -> Column
text_replace self (term : Text | Regex | Column = "") new_text="" case_sensitivity=Case_Sensitivity.Sensitive only_first=False =
text_replace self (term : Text | Regex | Column = "") new_text="" case_sensitivity:Case_Sensitivity=..Sensitive only_first=False =
Value_Type.expect_text self <|
term_fn = wrap_text_or_regex_argument_as_value_provider term
new_text_fn = wrap_text_argument_as_value_provider new_text
@ -1596,7 +1596,7 @@ type Column
Returns a column of `Integer` type.
@period Date_Time_Helpers.make_period_selector_for_column
date_part : Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_part self period =
date_part self period:(Date_Period | Time_Period)=..Day =
date_part_operation col name =
operation = case name of
DatePartOperation.YEAR -> DatePartOperation.YEAR_INSTANCE
@ -1639,7 +1639,7 @@ type Column
unusual events like DST.
@period Date_Time_Helpers.make_period_selector_for_column
date_diff : (Column | Date | Date_Time | Time_Of_Day) -> Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_diff self end (period : Date_Period | Time_Period = Date_Period.Day) =
date_diff self end (period : Date_Period | Time_Period = ..Day) =
Value_Type.expect_type self .is_date_or_time "date/time" <|
my_type = self.inferred_precise_value_type
Value_Type.expect_type end (== my_type) my_type.to_display_text <|
@ -1900,7 +1900,7 @@ type Column
@locale Locale.default_widget
@format (self-> Widget_Helpers.make_format_chooser_for_type self.value_type)
format : Text | Date_Time_Formatter | Column -> Locale -> Column ! Illegal_Argument
format self format:(Text | Date_Time_Formatter | Column)="" locale=Locale.default = case format of
format self format:(Text | Date_Time_Formatter | Column)="" locale:Locale=Locale.default = case format of
format_column : Column -> Value_Type.expect_text format_column <|
formatter = make_value_formatter_for_value_type self.value_type locale
formatter.if_not_error <|
@ -1959,7 +1959,7 @@ type Column
types. Due to this, a Mixed column containing values `[2, "3"]` will
actually be converted into `[2, Nothing]` when casting to Integer type.
cast : Value_Type -> Problem_Behavior -> Column ! Illegal_Argument | Inexact_Type_Coercion | Conversion_Failure
cast self value_type on_problems:Problem_Behavior=..Report_Warning =
cast self value_type:Value_Type on_problems:Problem_Behavior=..Report_Warning =
Cast_Helpers.check_cast_compatibility self.value_type value_type <|
target_storage_type = Storage.from_value_type value_type on_problems
Java_Problems.with_problem_aggregator on_problems java_problem_aggregator->
@ -2377,7 +2377,7 @@ type Column
my_compare a b = Ordering.compare a.abs b.abs
Examples.decimal_column.sort by=my_compare
sort : Sort_Direction -> Boolean -> (Any -> Any -> Ordering) | Nothing -> Column
sort self order=Sort_Direction.Ascending missing_last=True by=Nothing = case by of
sort self order:Sort_Direction=..Ascending missing_last:Boolean=True by=Nothing = case by of
Nothing ->
order_bool = case order of
Sort_Direction.Ascending -> True

View File

@ -12,18 +12,10 @@ import project.Value_Type.Value_Type
align_period_with_value_type value_type (period : Date_Period | Time_Period) = case value_type of
Value_Type.Date ->
if period.is_a Date_Period then period else
## We don't 'officially' allow `Time_Period` for Date, but since
`Time_Period.Day` and `Date_Period.Day` in this context can be interchangeable,
we allow it as an exception - we just swap it to be the right type.
if period == Time_Period.Day then Date_Period.Day else
Error.throw (Illegal_Argument.Error "`Time_Period` is not allowed for Date columns. Use `Date_Period`.")
Error.throw (Illegal_Argument.Error "`Time_Period` is not allowed for Date columns. Use `Date_Period`.")
Value_Type.Time ->
case period of
_ : Date_Period ->
Error.throw (Illegal_Argument.Error "`Date_Period` is not allowed for Time columns. Use `Time_Period`.")
Time_Period.Day ->
Error.throw (Illegal_Argument.Error "`Time_Period.Day` does not make sense for Time columns.")
_ -> period
if period.is_a Time_Period then period else
Error.throw (Illegal_Argument.Error "`Date_Period` is not allowed for Time columns. Use `Time_Period`.")
Value_Type.Date_Time _ ->
## Both kinds are allowed for `Date_Time` columns, return them as-is.
period
@ -41,7 +33,6 @@ make_date_part_function column period make_unary_op naming_helper =
Date_Period.Month -> make_unary_op column "month"
Date_Period.Week _ -> make_unary_op column "week"
Date_Period.Day -> make_unary_op column "day"
Time_Period.Day -> make_unary_op column "day"
Time_Period.Hour -> make_unary_op column "hour"
Time_Period.Minute -> make_unary_op column "minute"
Time_Period.Second -> make_unary_op column "second"

View File

@ -1894,7 +1894,10 @@ type Table
evaluate_expression : Text | Expression -> Problem_Behavior -> Column ! No_Such_Column | Invalid_Value_Type | Expression_Error
evaluate_expression self expression:(Text | Expression) on_problems:Problem_Behavior=..Report_Warning = if expression.is_a Text then self.evaluate_expression (Expression.Value expression) on_problems else
get_column name = self.at name
new_column = Expression.evaluate expression get_column self.make_constant_column "Standard.Table.Column" "Column" Column.var_args_functions
make_constant_column value = case value of
_ : Column -> value
_ -> self.make_constant_column value
new_column = Expression.evaluate expression get_column make_constant_column "Standard.Table.Column" "Column" Column.var_args_functions
problems = Warning.get_all new_column . map .value
result = new_column.rename (self.column_naming_helper.sanitize_name expression.expression)
on_problems.attach_problems_before problems <|

View File

@ -4,6 +4,7 @@ from Standard.Table import Value_Type
## An Enso representation of a Column in a Tableau Hyper Table.
type Hyper_Column
## PRIVATE
Value name:Text value_type:Value_Type nullable:Boolean
## PRIVATE

View File

@ -4,15 +4,17 @@ polyglot java import org.enso.tableau.HyperQueryError
polyglot java import org.enso.tableau.HyperTableNotFound
## Error when a Table is not found in a Hyper File.
type Table_Not_Found
type Hyper_Table_Not_Found
## PRIVATE
Error schema:Text name:Text
## Error when a query fails.
type Query_Failed
## PRIVATE
Error message:Text query:Text
## PRIVATE
private handle_java_exceptions ~action =
Panic.catch HyperTableNotFound handler=(c-> Error.throw (Table_Not_Found.Error c.payload.getSchema c.payload.getName)) <|
Panic.catch HyperTableNotFound handler=(c-> Error.throw (Hyper_Table_Not_Found.Error c.payload.getSchema c.payload.getName)) <|
Panic.catch HyperQueryError handler=(c-> Error.throw (Query_Failed.Error c.payload.getMessage c.payload.getQuery)) <|
action

View File

@ -4,11 +4,11 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
from Standard.Base.Metadata.Choice import Option
from Standard.Base.Metadata.Widget import Single_Choice
from Standard.Table import Table
import Standard.Table.Rows_To_Read.Rows_To_Read
from Standard.Table import Table
import project.Hyper_Errors.Hyper_Table_Not_Found
import project.Hyper_Table.Hyper_Table
import project.Hyper_Errors.Table_Not_Found
polyglot java import org.enso.tableau.HyperReader
@ -66,7 +66,7 @@ type Hyper_File
_ -> HyperReader.listTables self.file.path schema
array.map t-> Hyper_Table.Value self t.schema t.name
## ALIAS sheet, get
## ALIAS get, sheet
GROUP Standard.Base.Input
ICON data_input
Read a table from the Hyper_File into a Table.
@ -83,7 +83,7 @@ type Hyper_File
"" -> self.read table self.schema limit
"*" ->
table_to_read = self.tables.find if_missing=Nothing t-> t.table == table
if table_to_read.is_nothing then Error.throw (Table_Not_Found.Error "*" table) else
if table_to_read.is_nothing then Error.throw (Hyper_Table_Not_Found.Error "*" table) else
table_to_read.read limit
_ -> Hyper_Table.Value self table schema . read limit

View File

@ -1,13 +1,13 @@
from Standard.Base import all
import Standard.Base.Errors.File_Error.File_Error
from Standard.Table import Column, Table, Value_Type
import Standard.Table.Rows_To_Read.Rows_To_Read
import Standard.Table.Internal.Java_Problems
import Standard.Table.Rows_To_Read.Rows_To_Read
from Standard.Table import Column, Table, Value_Type
import project.Hyper_Column.Hyper_Column
import project.Hyper_File.Hyper_File
import project.Hyper_Errors
import project.Hyper_File.Hyper_File
polyglot java import java.sql.Types
polyglot java import org.enso.tableau.HyperReader

View File

@ -13,9 +13,9 @@ export project.Extensions.should_be_a
export project.Extensions.should_be_false
export project.Extensions.should_be_true
export project.Extensions.should_contain
export project.Extensions.should_equal_ignoring_order
export project.Extensions.should_end_with
export project.Extensions.should_equal
export project.Extensions.should_equal_ignoring_order
export project.Extensions.should_equal_type
export project.Extensions.should_fail_with
export project.Extensions.should_not_contain

View File

@ -507,7 +507,7 @@ public final class ValuesGenerator {
v(null, "import Standard.Base.Data.Time.Date_Period.Date_Period", "Date_Period.Year")
.type());
collect.add(
v(null, "import Standard.Base.Data.Time.Time_Period.Time_Period", "Time_Period.Day")
v(null, "import Standard.Base.Data.Time.Time_Period.Time_Period", "Time_Period.Hour")
.type());
collect.add(
v(null, "import Standard.Base.Data.Time.Period.Period", "Period.new 1 2 3").type());

View File

@ -17,6 +17,7 @@ expr: expr op=POWER expr # Power
| '(' expr ')' # Paren
| COLUMN_NAME # Column
| MINUS expr # UnaryMinus
| '..' IDENTIFIER # Atom
| value # Literal
;

View File

@ -82,6 +82,7 @@ public class ExpressionVisitorImpl extends ExpressionBaseVisitor<Value> {
private final Function<String, Value> getColumn;
private final Function<Object, Value> makeConstantColumn;
private final Function<String, Value> getMethod;
private final Function<String, Value> makeConstructor;
private final Set<String> variableArgumentFunctions;
private ExpressionVisitorImpl(
@ -93,10 +94,11 @@ public class ExpressionVisitorImpl extends ExpressionBaseVisitor<Value> {
this.getColumn = getColumn;
this.makeConstantColumn = makeConstantColumn;
final Value module =
Context.getCurrent().getBindings("enso").invokeMember("get_module", moduleName);
var context = Context.getCurrent().getBindings("enso");
final Value module = context.invokeMember("get_module", moduleName);
final Value type = module.invokeMember("get_type", typeName);
this.getMethod = name -> module.invokeMember("get_method", type, name);
getMethod = name -> module.invokeMember("get_method", type, name);
makeConstructor = name -> module.invokeMember("eval_expression", ".." + name);
this.variableArgumentFunctions = new HashSet<>(Arrays.asList(variableArgumentFunctions));
}
@ -135,7 +137,7 @@ public class ExpressionVisitorImpl extends ExpressionBaseVisitor<Value> {
if (result.canExecute()) {
throw new IllegalArgumentException("Insufficient arguments for method " + name + ".");
}
return result;
return makeConstantColumn.apply(result);
} catch (PolyglotException e) {
if (e.getMessage().startsWith("Type error: expected a function")) {
throw new IllegalArgumentException("Too many arguments for method " + name + ".");
@ -224,6 +226,29 @@ public class ExpressionVisitorImpl extends ExpressionBaseVisitor<Value> {
return executeMethod("*", visit(ctx.expr()), Value.asValue(-1));
}
@Override
public Value visitAtom(ExpressionParser.AtomContext ctx) {
var atomName = ctx.IDENTIFIER().getText();
return makeConstructor.apply(toCamelCase(atomName));
}
private static String toCamelCase(String name) {
var builder = new StringBuilder(name.length());
boolean convertNext = true;
for (var c : name.toCharArray()) {
if (convertNext) {
builder.append(Character.toUpperCase(c));
convertNext = c == '_';
} else if (c == '_') {
convertNext = true;
builder.append(c);
} else {
builder.append(Character.toLowerCase(c));
}
}
return builder.toString();
}
@Override
public Value visitNullOrNothing(ExpressionParser.NullOrNothingContext ctx) {
return Value.asValue(null);

View File

@ -476,7 +476,7 @@ spec_with suite_builder name create_new_date parse_date pending=Nothing =
d1.date_part Date_Period.Week . should_equal 52
d1.date_part Date_Period.Day . should_equal 30
Test.expect_panic_with (d1.date_part Time_Period.Day) Type_Error
Test.expect_panic_with (d1.date_part Time_Period.Hour) Type_Error
group_builder.specify "should allow computing date_diff" <|
d1 = create_new_date 2021 11 3
@ -505,7 +505,6 @@ spec_with suite_builder name create_new_date parse_date pending=Nothing =
d3.date_diff (Date.new 2021 03 01) Date_Period.Day . should_equal 58
d3.date_diff (Date.new 2021 03 01) Date_Period.Month . should_equal 1
Test.expect_panic_with (d1.date_diff d2 Time_Period.Day) Type_Error
Test.expect_panic_with (d1.date_diff d2 Time_Period.Hour) Type_Error
group_builder.specify "should allow shifting with date_add" <|
@ -521,7 +520,6 @@ spec_with suite_builder name create_new_date parse_date pending=Nothing =
d1.date_add 2 Date_Period.Quarter . should_equal (Date.new 2021 07 31)
Test.expect_panic_with (d1.date_add 1 Time_Period.Hour) Type_Error
Test.expect_panic_with (d1.date_add 1 Time_Period.Day) Type_Error
Test.expect_panic_with (d1.date_add 1.5 Date_Period.Day) Type_Error
Test.expect_panic_with (d1.date_add 1.0 Date_Period.Day) Type_Error

View File

@ -318,7 +318,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
group_builder.specify "should support addition of Time_Period" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc)
time+Time_Period.Day . should_equal <| create_new_datetime 1970 1 2 (zone = Time_Zone.utc)
time+Time_Period.Hour . should_equal <| create_new_datetime 1970 1 1 1 (zone = Time_Zone.utc)
time+Time_Period.Minute . should_equal <| create_new_datetime 1970 1 1 0 1 (zone = Time_Zone.utc)
time+Time_Period.Second . should_equal <| create_new_datetime 1970 1 1 0 0 1 (zone = Time_Zone.utc)
@ -329,7 +328,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
group_builder.specify "should support subtraction of Time_Period" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc)
time-Time_Period.Day . should_equal <| create_new_datetime 1969 12 31 (zone = Time_Zone.utc)
time-Time_Period.Hour . should_equal <| create_new_datetime 1969 12 31 23 (zone = Time_Zone.utc)
time-Time_Period.Minute . should_equal <| create_new_datetime 1969 12 31 23 59 (zone = Time_Zone.utc)
time-Time_Period.Second . should_equal <| create_new_datetime 1969 12 31 23 59 59 (zone = Time_Zone.utc)
@ -346,20 +344,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
time+Date_Period.Year-Time_Period.Minute . should_equal <| create_new_datetime 1970 12 31 23 59 (zone = Time_Zone.utc)
time+Time_Period.Minute+Time_Period.Minute-Time_Period.Minute . should_equal <| create_new_datetime 1970 1 1 0 1 (zone = Time_Zone.utc)
time+Date_Period.Quarter-Date_Period.Month . should_equal <| create_new_datetime 1970 3 1 (zone = Time_Zone.utc)
time+Date_Period.Day-Time_Period.Day . should_equal <| create_new_datetime 1970 (zone = Time_Zone.utc)
group_builder.specify "will reflect that Time_Period.Day does not reflect daylight saving" <|
tz = Time_Zone.parse "Europe/Warsaw"
dt = Date_Time.new 2023 03 26 01 20 zone=tz
dt1 = dt + Time_Period.Day
dt2 = dt + Date_Period.Day
# Time Period adds 24 hours which will be 1 hour later due to the DST hole.
dt1 . should_equal (Date_Time.new 2023 03 27 02 20 zone=tz)
# Date Period shifts by 1 calendar day.
dt2 . should_equal (Date_Time.new 2023 03 27 01 20 zone=tz)
group_builder.specify "should get the default comparator for datetimes" <|
assert_comparator value comparator =
@ -482,8 +466,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
group_builder.specify "should allow to find start/end of a Time_Period containing the current datetime" <|
d1 = create_new_datetime 2022 9 12 15 37 58 123456789
d1.start_of Time_Period.Day . should_equal (Date_Time.new 2022 9 12)
d1.end_of Time_Period.Day . should_equal (Date_Time.new 2022 9 12 23 59 59 nanosecond=max_nanos)
d1.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 9 12 15 0 0 0)
d1.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 9 12 15 59 59 nanosecond=max_nanos)
d1.start_of Time_Period.Minute . should_equal (Date_Time.new 2022 9 12 15 37 0 0)
@ -501,8 +483,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
d1.end_of Time_Period.Nanosecond . should_equal d1
d2 = create_new_datetime 1970 1 1 0 0 0
d2.start_of Time_Period.Day . should_equal (Date_Time.new 1970)
d2.end_of Time_Period.Day . should_equal (Date_Time.new 1970 1 1 23 59 59 nanosecond=max_nanos)
d2.start_of Time_Period.Hour . should_equal (Date_Time.new 1970 1 1 0 0 0 0)
d2.end_of Time_Period.Hour . should_equal (Date_Time.new 1970 1 1 0 59 59 nanosecond=max_nanos)
d2.start_of Time_Period.Minute . should_equal (Date_Time.new 1970 1 1 0 0 0 0)
@ -511,8 +491,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
d2.end_of Time_Period.Second . should_equal (Date_Time.new 1970 1 1 0 0 0 nanosecond=max_nanos)
d3 = create_new_datetime 2100 12 31 23 59 59 max_nanos
d3.start_of Time_Period.Day . should_equal (Date_Time.new 2100 12 31)
d3.end_of Time_Period.Day . should_equal (Date_Time.new 2100 12 31 23 59 59 nanosecond=max_nanos)
d3.start_of Time_Period.Hour . should_equal (Date_Time.new 2100 12 31 23 0 0 0)
d3.end_of Time_Period.Hour . should_equal (Date_Time.new 2100 12 31 23 59 59 nanosecond=max_nanos)
d3.start_of Time_Period.Minute . should_equal (Date_Time.new 2100 12 31 23 59 0 0)
@ -536,8 +514,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
check_dates_spring date =
date.start_of Date_Period.Day . should_equal (Date_Time.new 2022 3 27 zone=tz)
date.end_of Date_Period.Day . should_equal (Date_Time.new 2022 3 27 23 59 59 nanosecond=max_nanos zone=tz)
date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 3 27 zone=tz)
date.end_of Time_Period.Day . should_equal (Date_Time.new 2022 3 27 23 59 59 nanosecond=max_nanos zone=tz)
date.start_of Date_Period.Month . should_equal (Date_Time.new 2022 3 1 zone=tz)
date.end_of Date_Period.Month . should_equal (Date_Time.new 2022 3 31 23 59 59 nanosecond=max_nanos zone=tz)
@ -575,9 +551,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
Time_Utils.get_datetime_offset d4 . should_equal offset_1_h
check_dates_autumn date =
date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 10 30 zone=tz)
date.end_of Time_Period.Day . should_equal (Date_Time.new 2022 10 30 23 59 59 nanosecond=max_nanos zone=tz)
date.start_of Date_Period.Month . should_equal (Date_Time.new 2022 10 1 zone=tz)
date.end_of Date_Period.Month . should_equal (Date_Time.new 2022 10 31 23 59 59 nanosecond=max_nanos zone=tz)
@ -686,7 +659,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
d1.date_part Date_Period.Month . should_equal 12
d1.date_part Date_Period.Week . should_equal 52
d1.date_part Date_Period.Day . should_equal 30
d1.date_part Time_Period.Day . should_equal 30
d1.date_part Time_Period.Hour . should_equal 15
d1.date_part Time_Period.Minute . should_equal 37
d1.date_part Time_Period.Second . should_equal 58
@ -711,8 +683,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
t1.date_diff t2 Date_Period.Year . should_equal 0
t1.date_diff (Date_Time.new 2031 12 1 10 15 0) Date_Period.Year . should_equal 10
t1.date_diff t2 Time_Period.Day . should_equal 32
t1.date_diff t2 Time_Period.Hour . should_equal 770
t1.date_diff (Date_Time.new 2021 11 3 12 15 0 zone=Time_Zone.utc) Time_Period.Hour . should_equal 2
@ -734,7 +704,6 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
group_builder.specify "should allow shifting with date_add" <|
t1 = Date_Time.new 2021 01 01 12 30 0
t1.date_add 5 Date_Period.Day . should_equal (Date_Time.new 2021 01 06 12 30 0)
t1.date_add -1 Time_Period.Day . should_equal (Date_Time.new 2020 12 31 12 30 0)
t1.date_add 1 Date_Period.Month . should_equal (Date_Time.new 2021 02 01 12 30 0)
t1.date_add 10 Date_Period.Year . should_equal (Date_Time.new 2031 01 01 12 30 0)
@ -759,21 +728,16 @@ spec_with suite_builder name create_new_datetime parse_datetime nanoseconds_loss
# But 1 day date shift will shift 1 day, keeping the time, even if that particular day is only 23 hours.
dt1.date_add 1 Date_Period.Day . should_equal (Date_Time.new 2023 03 27 00 30 00 zone=zone)
# Time_Period.Day will shift by 24 hours.
dt1.date_add 1 Time_Period.Day . should_equal (Date_Time.new 2023 03 27 01 30 00 zone=zone)
dt2 = Date_Time.new 2023 03 27 00 30 00 zone=zone
dt1.date_diff dt2 Time_Period.Hour . should_equal 23
dt1.date_diff dt2 Date_Period.Day . should_equal 1
# But when counting in hours - 23 hours is not a full 24-hour day.
dt1.date_diff dt2 Time_Period.Day . should_equal 0
dt3 = Date_Time.new 2023 03 28 01 30 00 zone=zone
dt4 = Date_Time.new 2023 03 29 00 30 00 zone=zone
dt3.date_diff dt4 Date_Period.Day . should_equal 0
dt3.date_diff dt4 Time_Period.Day . should_equal 0
dt3.date_diff dt4 Time_Period.Hour . should_equal 23
Date_Part_Spec.add_specs suite_builder name create_new_datetime

View File

@ -137,7 +137,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
group_builder.specify "should support addition of Time_Period" <|
time = create_new_time 0
time+Time_Period.Day . should_equal <| create_new_time 0
time+Time_Period.Hour . should_equal <| create_new_time 1
time+Time_Period.Minute . should_equal <| create_new_time 0 1
time+Time_Period.Second . should_equal <| create_new_time 0 0 1
@ -148,7 +147,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
group_builder.specify "should support subtraction of Time_Period" <|
time = create_new_time 12
time-Time_Period.Day . should_equal <| create_new_time 12
time-Time_Period.Hour . should_equal <| create_new_time 11
time-Time_Period.Minute . should_equal <| create_new_time 11 59
time-Time_Period.Second . should_equal <| create_new_time 11 59 59
@ -183,8 +181,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
max_nanos = 999999999
group_builder.specify "should allow to find start/end of a Time_Period containing the current time of day" <|
d1 = create_new_time 15 37 58 123456789
d1.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
d1.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 nanosecond=max_nanos)
d1.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 15 0 0 0)
d1.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 15 59 59 nanosecond=max_nanos)
d1.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 15 37 0 0)
@ -201,8 +197,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
d1.end_of Time_Period.Nanosecond . should_equal d1
d2 = create_new_time 0 0 0
d2.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
d2.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 nanosecond=max_nanos)
d2.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 0 0 0 0)
d2.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 0 59 59 nanosecond=max_nanos)
d2.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 0 0 0 0)
@ -211,8 +205,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
d2.end_of Time_Period.Second . should_equal (Time_Of_Day.new 0 0 0 nanosecond=max_nanos)
d3 = create_new_time 23 59 59 max_nanos
d3.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
d3.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 nanosecond=max_nanos)
d3.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 23 0 0 0)
d3.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 23 59 59 nanosecond=max_nanos)
d3.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 23 59 0 0)
@ -230,7 +222,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
d1.date_part Time_Period.Microsecond . should_equal 456
d1.date_part Time_Period.Nanosecond . should_equal 789
d1.date_part Time_Period.Day . should_fail_with Illegal_Argument
Test.expect_panic_with (d1.date_part Date_Period.Day) Type_Error
group_builder.specify "should allow computing a date_diff" <|
@ -240,7 +231,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
# There is no default period:
t1.date_diff t2 . should_be_a Function
Test.expect_panic_with (t1.date_diff t2 Date_Period.Month) Type_Error
t1.date_diff t2 Time_Period.Day . should_fail_with Illegal_Argument
t1.date_diff t2 Time_Period.Hour . should_equal 2
t1.date_diff (Time_Of_Day.new 9 15 0) Time_Period.Hour . should_equal -1
@ -275,7 +265,6 @@ specWith suite_builder name create_new_time parse_time nanoseconds_loss_in_preci
t1.date_add -2 Time_Period.Nanosecond . should_equal (Time_Of_Day.new 23 44 59 999 999 998)
# No sense to shift Time_Of_Day by days
t1.date_add 1 Time_Period.Day . should_fail_with Illegal_Argument
Test.expect_panic_with (t1.date_add 1 Date_Period.Month) Type_Error
# There is no default period.

View File

@ -319,8 +319,6 @@ add_specs suite_builder setup =
(t1.at "X1").date_diff (Date.new 2021 03 01) Date_Period.Day . to_vector . should_equal [58]
(t1.at "X1").date_diff (Date.new 2021 03 01) Date_Period.Month . to_vector . should_equal [1]
# We do allow the `Time_Period.Day` as a kind of alias for `Date_Period.Day` here.
(t1.at "X").date_diff (t1.at "Y") Time_Period.Day . to_vector . should_equal [32]
(t1.at "X").date_diff (t1.at "Y") Time_Period.Hour . should_fail_with Illegal_Argument
t2 = dates1.get.select_columns ["dt_X", "dt_Y"] reorder=True . rename_columns ["X", "Y"]
@ -335,8 +333,6 @@ add_specs suite_builder setup =
(t2.at "X").date_diff (t2.at "Y") Date_Period.Year . to_vector . should_equal [0]
(t2.at "X").date_diff (Date_Time.new 2031 12 1 10 15 0 zone=zone) Date_Period.Year . to_vector . should_equal [10]
(t2.at "X").date_diff (t2.at "Y") Time_Period.Day . to_vector . should_equal [32]
(t2.at "X").date_diff (t2.at "Y") Time_Period.Hour . to_vector . should_equal [770]
(t2.at "X").date_diff (Date_Time.new 2021 11 3 12 15 0 zone=zone) Time_Period.Hour . to_vector . should_equal [2]
@ -365,9 +361,6 @@ add_specs suite_builder setup =
# There is no default period:
(t3.at "X").date_diff (t3.at "Y") Date_Period.Month . should_fail_with Illegal_Argument
# This will always be 0, should it be allowed?
(t3.at "X").date_diff (t3.at "Y") Time_Period.Day . should_fail_with Illegal_Argument
(t3.at "X").date_diff (t3.at "Y") Time_Period.Hour . to_vector . should_equal [2]
(t3.at "X").date_diff (Time_Of_Day.new 9 15 0) Time_Period.Hour . to_vector . should_equal [-1]
@ -434,14 +427,9 @@ add_specs suite_builder setup =
(t1.at "X").date_add 1 Date_Period.Quarter . to_vector . should_equal [Date.new 2021 04 30, Date.new 2021 04 01, Date.new 2022 03 31]
(t1.at "X").date_add 1 Time_Period.Hour . should_fail_with Illegal_Argument
# Will accept Time_Period.Day as alias of Date_Period.Day
r1 = (t1.at "X").date_add 1 Time_Period.Day
r1.value_type . should_equal Value_Type.Date
r1.to_vector . should_equal [Date.new 2021 02 01, Date.new 2021 01 02, Date.new 2022 01 01]
t2 = table_builder [["X", [Date_Time.new 2021 01 31 12 30 0, Date_Time.new 2021 01 01 12 30 0, Date_Time.new 2021 12 31 12 30 0]], ["Y", [11, -1, 0]]]
(t2.at "X").date_add (t2.at "Y") Date_Period.Day . to_vector . should_equal_tz_agnostic [Date_Time.new 2021 02 11 12 30 0, Date_Time.new 2020 12 31 12 30 0, Date_Time.new 2021 12 31 12 30 0]
(t2.at "X").date_add -1 Time_Period.Day . to_vector . should_equal_tz_agnostic [Date_Time.new 2021 01 30 12 30 0, Date_Time.new 2020 12 31 12 30 0, Date_Time.new 2021 12 30 12 30 0]
(t2.at "X").date_add (t2.at "Y") Date_Period.Month . to_vector . should_equal_tz_agnostic [Date_Time.new 2021 12 31 12 30 0, Date_Time.new 2020 12 01 12 30 0, Date_Time.new 2021 12 31 12 30 0]
(t2.at "X").date_add 1 Date_Period.Month . to_vector . should_equal_tz_agnostic [Date_Time.new 2021 02 28 12 30 0, Date_Time.new 2021 02 01 12 30 0, Date_Time.new 2022 01 31 12 30 0]
(t2.at "X").date_add (t2.at "Y") Date_Period.Year . to_vector . should_equal_tz_agnostic [Date_Time.new 2032 01 31 12 30 0, Date_Time.new 2020 01 01 12 30 0, Date_Time.new 2021 12 31 12 30 0]
@ -478,7 +466,6 @@ add_specs suite_builder setup =
(t3.at "X").date_add 1 Time_Period.Nanosecond . should_fail_with Unsupported_Database_Operation
# No sense to shift Time_Of_Day by days either or by a Date_Period
(t3.at "X").date_add (t3.at "Y") Time_Period.Day . to_vector . should_fail_with Illegal_Argument
(t3.at "X").date_add 1 Date_Period.Month . to_vector . should_fail_with Illegal_Argument
# Date period defaults to Day for date/date-time
@ -509,9 +496,6 @@ add_specs suite_builder setup =
# instead let's compare as local timestamps:
plus_1_calendar_day.to_vector . map as_local_date_time_repr . should_equal ["2023-03-27 00:30:00.0"]
# Time_Period.Day will shift by 24 hours.
x.date_add 1 Time_Period.Day . to_vector . should_equal_tz_agnostic [Date_Time.new 2023 03 27 01 30 00 zone=zone]
# Once we add 3 months, we should get the same hour back.
plus_3_months = x.date_add 3 Date_Period.Month
plus_3_months.to_vector.map as_local_date_time_repr . should_equal ["2023-06-26 00:30:00.0"]
@ -528,15 +512,12 @@ add_specs suite_builder setup =
cannot 'guess' that it should be a day of a difference and
since it is 'just' 23 hours - there is 0 days.
[[0], [1]] . should_contain (x.date_diff dt2 Date_Period.Day . to_vector)
# Again consistent for both backends, when counting in hours - 23 hours is not a full 24-hour day.
x.date_diff dt2 Time_Period.Day . to_vector . should_equal [0]
dt4 = Date_Time.new 2023 03 29 00 30 00 zone=zone
y = t.at "Y"
# No DST switch here, so all backends agree that 0 days elapsed in the 23 hours.
# Snowflake returns 1...
y.date_diff dt4 Date_Period.Day . to_vector . should_equal [0]
y.date_diff dt4 Time_Period.Day . to_vector . should_equal [0]
y.date_diff dt4 Time_Period.Hour . to_vector . should_equal [23]
if setup.test_selection.date_time.not then

View File

@ -11,7 +11,6 @@ from Standard.Database.Errors import SQL_Error
from Standard.Test import all
from project.Util import all
from project.Common_Table_Operations.Util import run_default_backend, build_sorted_table
@ -218,10 +217,6 @@ add_specs suite_builder 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
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" group_builder expression_test->
expression_test "1+1*2+2" 5
@ -375,6 +370,20 @@ add_specs suite_builder detailed setup =
expression_test "min(10, 3, 8)" 3
expression_test "max([A], [B], 3)" [3, 3, 3, 4, 6]
specify_test "should be able to use functions with constants" group_builder expression_test->
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 use scalar functions" group_builder expression_test->
expression_test "first([C])" "Hello"
expression_test "at([C], 2)" "Hello World!"
specify_test "should be able to pass Atom constructors to functions" group_builder expression_test->
expression_test "cast([A], ..Char)" ["1", "2", "3", "4", "5"]
expression_test "starts_WITH([C], 'HELLO', ..INSENSITIVE)" [True, False, True, False, Nothing]
suite_builder.group prefix+"Expression Errors should be handled" group_builder->
expect_error ctor expr =
expr.should_fail_with Expression_Error

View File

@ -3,7 +3,7 @@ import Standard.Base.Errors.File_Error.File_Error
from Standard.Table import all
from Standard.Tableau import Hyper_File
import Standard.Tableau.Hyper_Errors.Table_Not_Found
import Standard.Tableau.Hyper_Errors.Hyper_Table_Not_Found
from Standard.Test import all
@ -60,7 +60,7 @@ add_specs suite_builder = suite_builder.group "Read Tables" group_builder->
group_builder.specify "should handle a missing table when reading a table" <|
r1 = Hyper_File.new names_file . read "NoTable"
r1.should_fail_with Table_Not_Found
r1.should_fail_with Hyper_Table_Not_Found
r2 = Hyper_File.new names_file . read "NoTable" schema="Extract"
r2.should_fail_with Table_Not_Found
r2.should_fail_with Hyper_Table_Not_Found