From 126fe94e2303dd62d8e9622b1a6b1f83899e1524 Mon Sep 17 00:00:00 2001 From: Elliot Chance Date: Sun, 3 Jul 2022 17:53:18 -0400 Subject: [PATCH] F051: Basic date and time (#109) In a nutshell, this adds storage support for date/time types and values. Although, we do not yet support other features such as constants, functions, comparisons and casting. They can come as a separate features in the near future. More specifically, this adds support for: 1. F051-01: `DATE` data type (including support of `DATE` literal). 2. F051-02: `TIME` data type (including support of `TIME` literal) with fractional seconds precision of at least 0. 3. F051-03: `TIMESTAMP` data type (including support of `TIMESTAMP` literal) with fractional seconds precision of at least 0 and 6. The five new distinct data types and their storage requirements are: 1. `DATE` (8 bytes) 2. `TIME(n) WITH TIME ZONE` (10 bytes) 3. `TIME(n) WITHOUT TIME ZONE` or `TIME(n)` (8 bytes) 4. `TIMESTAMP(n) WITH TIME ZONE` (10 bytes) 5. `TIMESTAMP(n) WITHOUT TIME ZONE` or `TIMESTAMP(n)` (8 bytes) --- docs/data-types.rst | 106 ++++++- docs/sql-compliance.rst | 34 ++- generate-grammar.py | 4 +- grammar.bnf | 81 +++++- tests/create-table.sql | 39 +++ tests/date.sql | 63 +++++ tests/time.sql | 124 +++++++++ tests/timestamp.sql | 111 ++++++++ vsql/ast.v | 4 +- vsql/bytes.v | 16 ++ vsql/cast.v | 88 +++--- vsql/grammar.v | 594 +++++++++++++++++++++++++++++++++++++++- vsql/parse.v | 82 +++++- vsql/query_cache.v | 8 + vsql/row.v | 43 +-- vsql/table.v | 6 +- vsql/time.v | 317 +++++++++++++++++++++ vsql/type.v | 131 +++++++-- vsql/value.v | 48 +++- 19 files changed, 1779 insertions(+), 120 deletions(-) create mode 100644 tests/date.sql create mode 100644 tests/time.sql create mode 100644 tests/timestamp.sql create mode 100644 vsql/time.v diff --git a/docs/data-types.rst b/docs/data-types.rst index 57bb3f4..f399833 100644 --- a/docs/data-types.rst +++ b/docs/data-types.rst @@ -82,6 +82,105 @@ is an alias for ``CHARACTER(1)``. 1. The *n* limit is not yet enforced. 2. Values are not actually space padded. +Date and Time Types +------------------- + +``DATE`` +^^^^^^^^ + +A ``DATE`` holds a year-month-day value, such as ``2010-10-25``. + +A ``DATE`` value can be created with the ``DATE '2010-10-25'`` literal +expression. + +Valid date ranges are between ``0000-01-01`` and ``9999-12-31``. + +A ``DATE`` is stored as 8 bytes. + +``TIME(n) WITH TIME ZONE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Holds a time as hour-minute-second-timezone (without respect to a date), +for example: ``15:12:47+05:30``. + +The ``(n)`` describes the sub-second resolution to be stored. It must be +inclusively between 0 (whole seconds) and 6 (microseconds). If omitted, 0 is +used. + +A ``TIME(n) WITH TIME ZONE`` value is created with the ``TIME 'VALUE'`` literal +expression. The ``VALUE`` itself will determine whether the time has a time zone +and its precision. For example: + +.. list-table:: + :header-rows: 1 + + * - Expr + - Type + + * - ``TIME '15:12:47'`` + - ``TIME(0) WITHOUT TIME ZONE`` + + * - ``TIME '15:12:47.123'`` + - ``TIME(3) WITHOUT TIME ZONE`` + + * - ``TIME '15:12:47+05:30'`` + - ``TIME(0) WITH TIME ZONE`` + + * - ``TIME '15:12:47.000000+05:30'`` + - ``TIME(6) WITH TIME ZONE`` + +A ``TIME WITH TIME ZONE`` (with any precision) is stored as 10 bytes. + +``TIME(n) WITHOUT TIME ZONE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This works the same way as ``TIME(n) WITH TIME ZONE`` except there is no +time zone component. + +A ``TIME WITHOUT TIME ZONE`` (with any precision) is stored as 8 bytes. + +``TIMESTAMP(n) WITH TIME ZONE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Holds a timestamp as year-month-day-hour-minute-second-timezone, for example: +``2010-10-25 15:12:47+05:30``. + +The ``(n)`` describes the sub-second resolution to be stored. It must be +inclusively between 0 (whole seconds) and 6 (microseconds). If omitted, 6 is +used. This is different from the behavior of ``TIME`` that uses 0 by default. + +A ``TIMESTAMP(n) WITH TIME ZONE`` value is created with the +``TIMESTAMP 'VALUE'`` literal expression. The ``VALUE`` itself will determine +whether the timestamp has a time zone and its precision. For example: + +.. list-table:: + :header-rows: 1 + + * - Expr + - Type + + * - ``TIMESTAMP '2010-10-25 15:12:47'`` + - ``TIMESTAMP(0) WITHOUT TIME ZONE`` + + * - ``TIMESTAMP '2010-10-25 15:12:47.123'`` + - ``TIMESTAMP(3) WITHOUT TIME ZONE`` + + * - ``TIMESTAMP '2010-10-25 15:12:47+05:30'`` + - ``TIMESTAMP(0) WITH TIME ZONE`` + + * - ``TIMESTAMP '2010-10-25 15:12:47.000000+05:30'`` + - ``TIMESTAMP(6) WITH TIME ZONE`` + +A ``TIMESTAMP WITH TIME ZONE`` (with any precision) is stored as 10 bytes. + +``TIMESTAMP(n) WITHOUT TIME ZONE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This works the same way as ``TIMESTAMP(n) WITH TIME ZONE`` except there is no +time zone component. + +A ``TIMESTAMP WITHOUT TIME ZONE`` (with any precision) is stored as 8 bytes. + Unsupported Data Types ---------------------- @@ -136,13 +235,6 @@ Some are supported, but the remaining ones that are not supported: 1. ``DECFLOAT`` - -^^^^^^^^^^^^^^^ - -1. ``DATE`` -2. ``TIME`` -3. ``TIMESTAMP`` - ^^^^^^^^^^^^^^^ diff --git a/docs/sql-compliance.rst b/docs/sql-compliance.rst index 0326f8e..7c8e283 100644 --- a/docs/sql-compliance.rst +++ b/docs/sql-compliance.rst @@ -210,9 +210,41 @@ The following table is **not complete** and will be filled in as time goes on. - All comparison operators are supported (rather than just =) * - **F051** - - **Unknown** + - **Partial** - **Basic date and time** + * - F051-01 + - Yes + - ``DATE`` data type (including support of ``DATE`` literal) + + * - F051-02 + - Yes + - ``TIME`` data type (including support of ``TIME`` literal) with fractional seconds precision of at least 0. + + * - F051-03 + - Yes + - ``TIMESTAMP`` data type (including support of ``TIMESTAMP`` literal) with fractional seconds precision of at least 0 and 6. + + * - F051-04 + - No + - Comparison predicate on ``DATE``, ``TIME``, and ``TIMESTAMP`` data types + + * - F051-05 + - No + - Explicit ``CAST`` between date-time types and character string types + + * - F051-06 + - No + - ``CURRENT_DATE`` + + * - F051-07 + - No + - ``LOCALTIME`` + + * - F051-08 + - No + - ``LOCALTIMESTAMP`` + * - **F081** - **Unknown** - **UNION and EXCEPT in views** diff --git a/generate-grammar.py b/generate-grammar.py index 73e9f90..cb5e46c 100644 --- a/generate-grammar.py +++ b/generate-grammar.py @@ -418,7 +418,7 @@ def var_name(name): if node.children.len == 0 { match node.value.name { '^integer' { - return [EarleyValue(new_integer_value(node.value.end_column.value.int()))] + return [EarleyValue(node.value.end_column.value.int())] } '^identifier' { return [EarleyValue(new_identifier(node.value.end_column.value))] @@ -513,6 +513,8 @@ def parse_tree(text): # parse_tree("SELECT * FROM ( VALUES ROW ( 123 ) , ROW ( 456 ) )") # parse_tree("SELECT x FROM ( SELECT y FROM t2 )") # parse_tree("SELECT x , y FROM ( SELECT x , y FROM t1 )") +# parse_tree("VALUES TIMESTAMP '2022-06-30'") +# parse_tree("INSERT INTO foo ( f1 ) VALUES ( TIMESTAMP '2022-06-30' )") for arg in sys.argv[1:]: print(arg) diff --git a/grammar.bnf b/grammar.bnf index 5c5b80e..5ca13f5 100644 --- a/grammar.bnf +++ b/grammar.bnf @@ -81,6 +81,7 @@ | | + | /* Type */ ::= CHARACTER -> character @@ -95,13 +96,13 @@ ::= ")" - /* Value */ ::= + /* int */ ::= - /* Value */ ::= + /* int */ ::= - /* Value */ ::= + /* int */ ::= ^integer ::= "," @@ -122,7 +123,7 @@ | REAL -> real | DOUBLE PRECISION -> double_precision - /* Value */ ::= + /* int */ ::= /* Type */ ::= @@ -182,8 +183,8 @@ /* Value */ ::= - - | -> value + -> int_value + | -> int_value | -> exact_numeric_literal1 | -> exact_numeric_literal2 @@ -245,6 +246,19 @@ /* Expr */ ::= | + | + + /* Expr */ ::= + + + /* Expr */ ::= + + + /* Expr */ ::= + + + /* Expr */ ::= + /* Expr */ ::= @@ -365,8 +379,32 @@ /* Value */ ::= + | | + /* Value */ ::= + + |