diff --git a/docs/numbers.rst b/docs/numbers.rst index d93f65c..f1e72c2 100644 --- a/docs/numbers.rst +++ b/docs/numbers.rst @@ -3,58 +3,102 @@ Numbers .. contents:: -Literals --------- +There are two categories of numbers in vsql: **approximate** and **exact**. Each +category has its own distinct SQL types, literals, functions and pros/cons. -Different forms have implicit types: +Numbers are never implicitly cast from one category to another. Operations +containing mixed categories (such as arithmetic) are not allowed because the +result category would be ambiguous. This means that sometimes you will need to +explicitly cast between categories. -.. list-table:: - :header-rows: 1 +Approximate Numbers +------------------- - * - Description - - Examples - - Type +Approximate (inexact) numbers are by definition approximations and are stored in +a 32 or 64bit native floating-point type. While it's possible that these +representations can give very close approximations for most numbers we use +day-to-day the accuracy cannot be guaranteed in storage or string +representation. - * - Integers - - ``123``, ``0``, ``-1234`` - - ``BIGINT`` +Advantages and Disadvantages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - Floating-point (contains ``.``) - - ``12.3``, ``0.0001``, ``-12.0`` - - ``DOUBLE PRECISION`` +**Advantages** over exact numbers: -Exact Numeric Types -------------------- +1. Generally, they are very space efficient (4 or 8 bytes) regardless of +magnitude. -Exact numeric types will losslessly contain any value as long as it's within the -permitted range. If a value or an expression that produced a value is beyond the -possible range a ``SQLSTATE 22003 numeric value out of range`` is raised. +2. Can represent extremely large and extremely tiny values while maintaining a +certain amount of significant figures. -.. list-table:: - :header-rows: 1 +3. Operations (arithmetic, etc) are extremely fast because floating-point values +are implemented directly in the CPU. - * - Type - - Range (inclusive) - - Size +**Disadvantages** over exact numbers: - * - ``SMALLINT`` - - -32,768 to 32,767 - - 2 or 3 bytes [2]_ +1. They are not reliable to compare equality to exact values. For example, +``0.1 + 0.2 = 0.3`` is an operation that might return ``FALSE`` on some systems +where the left hand side is computed as ``0.30000000000000004``. **This point +cannot be stressed enough, especially since floating-point values are rendered +with a maximum of 6 places for ``REAL`` or 12 places for ``DOUBLE PRECISION`` +after the decimal.** See formatting notes below. - * - ``INTEGER`` or ``INT`` [1]_ - - -2,147,483,648 to 2,147,483,647 - - 4 or 5 bytes [2]_ +2. They are subject to rounding errors when values cannot be represented closely +enough, or the result of operations between approximate numbers. For the same +reason described in the previous point, but this can occur for individual +numbers that are converted from base 10 (decimal). - * - ``BIGINT`` - - -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - - 8 or 9 bytes [2]_ +3. The string representation may truncate part of the value. For example, +``0.30000000000000004`` may be shown as ``3e-1`` which is very close but not +exactly equal. + +Literals and Formatting +^^^^^^^^^^^^^^^^^^^^^^^ + +Literals for approximate numbers will always be ``DOUBLE PRECISION`` and must be +provided in scientific notation, in the form: + +.. code-block:: text + + [ + | - ] digit... { e | E } [ + | - ] digit... + +Examples: + +.. code-block:: sql + + 1e2 -- ~= 100.0 + 1.23456e4 -- ~= 12345.6 + 7E-5 -- ~= 0.00007 -Approximate Numeric Types -------------------------- +Since scientific notation isn't a very friendly format to use, you can append +``e0`` to any number to have it represented as scientific notation: -Approximate types store floating-point values with a precision that is relative -to scale of the number. These types should not be used when an exact value (such -as currency) needs to be retained. +.. code-block:: sql + + 1e0 -- ~= 1.0 + -1.23456e0 -- ~= -1.23456 + 0.000123e0 -- ~= 0.000123 + +When approximate numbers are displayed (formatted as strings) they are always +represented as scientific notation **with a maximum of 6 places for ``REAL`` or +12 places for ``DOUBLE PRECISION`` after the decimal.** This means that the +displayed value may not be the fully approximated value. This is partially to +combat encoding and rounding errors (such as ``0.30000000000000004``) but also +to reduce the string length as 6 or 12 places after the decimal is more than +enough for general use. + +Approximate numbers that are whole numbers will be have the ```.0`` trimmed for +readability and if the number isn't large or small enough to have an exponent, +``e0`` will be appended to ensure the formatted value is guaranteed to always be +scientific notation: + +.. code-block:: sql + + VALUES 100.0e0; + -- COL1: 100e0 + +Approximate Types +^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 @@ -71,68 +115,181 @@ as currency) needs to be retained. - -1.7e+308 to +1.7e+308 - 8 or 9 bytes [2]_ -Casting -------- +Exact Numbers +------------- + +Exact numbers retain all precision of a number. SQL types for exact numbers that +do not have predefined ranges need to explicitly specify the scale (the maximum +size) and the precision (the accuracy) that an exact number must conform to. + +Advantages and Disadvantages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Advantages** over approximate numbers: + +1. The value is always guaranteed to contain the scale and precision specified. + +2. They can be any arbitrary size of precision desired. + +3. Values can be bound to a maximum and minimum size (based on the scale). +Literals and operations that would result in an overflow will raise an error, +rather than implicitly truncating. + +**Disadvantages** over approximate numbers: + +1. Storage costs are higher, based on the scale of the number. Even if that +scale is not entirely used. + +2. Operations (arithmetic, etc) are significantly slower than approximate +numbers because the operations are not natively supported by the CPU. + +3. Can only represent numbers in the given precision, any extra precision will +be truncated by operations. + +Literals and Formatting +^^^^^^^^^^^^^^^^^^^^^^^ + +The SQL type of an exact number depends on it's form and size: + +.. code-block:: text + + [ + | - ] [ . ] digit... + [ + | - ] digit... [ . [ digit... ] ] + +Any number that contains a ``.`` will be treated as a ``NUMERIC``, even in the +case of whole numbers such as ``123.``. Otherwise, the smallest integer type +will be chosen that can contain the value. So ``100`` would be a ``SMALLINT``, +``-1000000`` would be an ``INTEGER``, etc. If the integer does not fit into the +range of ``BIGINT`` then it is treated as a ``NUMERIC`` with zero precision. + +The precision of a ``NUMERIC`` is taken directly from the literal, so ``1.0`` +and ``1.00`` are equal in value but have different types. + +Formatting integers (representing as a string) are always shown as integers (of +any size) and ``NUMERIC`` will always be shown with the precision specified, +even if that requires padding more zeros. + +Exact Types +^^^^^^^^^^^ + +Exact numeric types will contain any value as long as it's within the permitted +range. If a value or an expression that produced a value is beyond the possible +range a ``SQLSTATE 22003 numeric value out of range`` is raised. .. list-table:: + :header-rows: 1 - * - ↓ From / To → - - ``SMALLINT`` - - ``INTEGER`` - - ``BIGINT`` - - ``REAL`` - - ``DOUBLE PRECISION`` + * - Type + - Range (inclusive) + - Size * - ``SMALLINT`` - - ✅ - - ✅ - - ✅ - - ✅ - - ✅ - - * - ``INTEGER`` - - ✅ - - ✅ - - ✅ - - ✅ - - ✅ + - -32768 to 32767 + - 2 or 3 bytes [2]_ + + * - ``INTEGER`` or ``INT`` [1]_ + - -2147483648 to 2147483647 + - 4 or 5 bytes [2]_ * - ``BIGINT`` - - ✅ - - ✅ - - ✅ - - ✅ - - ✅ + - -9223372036854775808 to 9223372036854775807 + - 8 or 9 bytes [2]_ - * - ``REAL`` - - ✅ - - ✅ - - ✅ - - ✅ - - ✅ - - * - ``DOUBLE PRECISION`` - - ✅ - - ✅ - - ✅ - - ✅ - - ✅ - -Arithmetic Operations ---------------------- +Casting +------- + +Implicit Casting +^^^^^^^^^^^^^^^^ + +Implicit casting is when the value can be safely converted from one type to +another to satisfy an expression. Consider the example: + +.. code-block:: sql + + VALUES 123 + 456789; + -- 456912 + +This operation seems very straightforward, but the parser will read this as +``SMALLINT + INTEGER`` due to the size of the literals. However, arithmetic +operations must take in an produce the same result. Rather than forcing the user +to explicitly cast one type to another we can always safely convert a +``SMALLINT`` to an ``INTEGER`` (this is called a supertype in SQL terms). The +implicit cast results in an actual expression of ``INTEGER + INTEGER`` that also +produces an ``INTEGER``. + +It's important to know that the actual result is not taken into consideration, +so it's still possible to overflow: + +.. code-block:: sql + + VALUES 30000 + 30000; + -- error 22003: numeric value out of range + +Because ``SMALLINT + SMALLINT`` results in a ``SMALLINT``. If you think it will +be possible for the value to overflow you should explicitly cast any of the +values to a larger type: + +.. code-block:: sql + + VALUES CAST(30000 AS INTEGER) + 30000; + -- COL1: 60000 + +Implicit casting only happens in supertypes of the same category: + +* Approximate: ``REAL`` -> ``DOUBLE PRECISION`` + +* Exact: ``SMALLINT`` -> ``INTEGER`` -> ``BIGINT`` + +Explicit Casting +^^^^^^^^^^^^^^^^ + +Explicit casting is when you want to convert a value to a specific type. This is +done with the ``CAST`` function. The ``CAST`` function works for a variety of +types outside of numeric types but if a cast happens between numeric types the +value must be valid for the result or an error is returned: + +.. code-block:: sql + + VALUES CAST(30000 AS INTEGER); + -- Safe: 30000 + + VALUES CAST(60000 AS SMALLINT); + -- Error 22003: numeric value out of range + + VALUES CAST(12345 AS VARCHAR(10)); + -- Safe: "12345" + + VALUES CAST(12345 AS VARCHAR(3)); + -- Error 22001: string data right truncation for CHARACTER VARYING(3) + + VALUES CAST(123456789 AS DOUBLE PRECISION); + -- COL1: 1.23456789e+08 + +Arithmetic +---------- Arithmetic operations (sometimes called binary operations) require the same type for both operands and return this same type. For example ``INTEGER + INTEGER`` will result in an ``INTEGER``. When the type of the operands are different it will implicitly cast to the -supertype of both. For numbers all supertypes are and in order: ``SMALLINT``, -``INTEGER``, ``BIGINT``, ``REAL`` and ``DOUBLE PRECISION``. +supertype of both. See *Implicit Casting*. + +For example ``12 * 10.5`` will result in an error because +``SMALLINT * DOUBLE PRECISION`` because there is no supertype that satisfies +both operands (since they belong to different categories). Depending on what +category of result type you're looking for: + +.. code-block:: sql + + VALUES 12 * 10.5e0; + -- error 42883: operator does not exist: SMALLINT * DOUBLE PRECISION -For example ``12 * 10.5`` is evaluated as the expression -``BIGINT * DOUBLE PRECISION``. Since ``DOUBLE PRECISION`` is the only supertype -for both, ``12`` must be implicitly cast to a ``DOUBLE PRECISION`` and the -operation will yield as result as ``DOUBLE PRECISION``. + VALUES CAST(12 AS DOUBLE PRECISION) * 10.5e0; + -- COL1: 126e0 + + VALUES 12 * CAST(10.5e0 AS INTEGER); + -- COL1: 120 Notes ----- diff --git a/tests/arithmetic.sql b/tests/arithmetic.sql index f7cc355..91519ae 100644 --- a/tests/arithmetic.sql +++ b/tests/arithmetic.sql @@ -16,23 +16,23 @@ VALUES 6 / 2; /* types */ VALUES 1.2 + 2.4; --- COL1: 3.6 (DOUBLE PRECISION) +-- COL1: 3.6e0 (DOUBLE PRECISION) /* types */ VALUES 1.7 - 0.5; --- COL1: 1.2 (DOUBLE PRECISION) +-- COL1: 1.2e0 (DOUBLE PRECISION) /* types */ VALUES 2.2 * 3.3; --- COL1: 7.26 (DOUBLE PRECISION) +-- COL1: 7.26e0 (DOUBLE PRECISION) /* types */ VALUES 6.0 / 2.5; --- COL1: 2.4 (DOUBLE PRECISION) +-- COL1: 2.4e0 (DOUBLE PRECISION) /* types */ VALUES 0.0 / 2.5; --- COL1: 0 (DOUBLE PRECISION) +-- COL1: 0e0 (DOUBLE PRECISION) /* types */ VALUES 2.5 / 0.0; @@ -44,8 +44,23 @@ VALUES -123; /* types */ VALUES +1.23; --- COL1: 1.23 (NUMERIC) +-- COL1: 1.23e0 (DOUBLE PRECISION) /* types */ VALUES 1.5 + 2.4 * 7.0; --- COL1: 18.3 (DOUBLE PRECISION) +-- COL1: 18.3e0 (DOUBLE PRECISION) + +VALUES 30000 + 30000; +-- error 22003: numeric value out of range + +VALUES CAST(30000 AS INTEGER) + 30000; +-- COL1: 60000 + +VALUES 12 * 10.5e0; +-- error 42883: operator does not exist: SMALLINT * DOUBLE PRECISION + +VALUES CAST(12 AS DOUBLE PRECISION) * 10.5e0; +-- COL1: 126e0 + +VALUES 12 * CAST(10.5e0 AS INTEGER); +-- COL1: 120 diff --git a/tests/as.sql b/tests/as.sql index bad4b9a..4d13a24 100644 --- a/tests/as.sql +++ b/tests/as.sql @@ -4,14 +4,14 @@ INSERT INTO t1 (x) VALUES (0); EXPLAIN SELECT 1 AS bob FROM t1; -- EXPLAIN: TABLE ":memory:".PUBLIC.T1 (X INTEGER) --- EXPLAIN: EXPR (BOB NUMERIC) +-- EXPLAIN: EXPR (BOB SMALLINT) SELECT 1 AS bob FROM t1; -- BOB: 1 EXPLAIN SELECT 1 AS "Bob" FROM t1; -- EXPLAIN: TABLE ":memory:".PUBLIC.T1 (X INTEGER) --- EXPLAIN: EXPR ("Bob" NUMERIC) +-- EXPLAIN: EXPR ("Bob" SMALLINT) SELECT 1 AS "Bob" FROM t1; -- Bob: 1 diff --git a/tests/cast.sql b/tests/cast.sql index e2d155b..ae9a250 100644 --- a/tests/cast.sql +++ b/tests/cast.sql @@ -53,3 +53,15 @@ VALUES CAST(TRUE AS FLOAT); VALUES CAST(TRUE AS INT); -- error 22003: numeric value out of range + +VALUES CAST(60000 AS SMALLINT); +-- error 22003: numeric value out of range + +VALUES CAST(12345 AS VARCHAR(10)); +-- COL1: 12345 + +VALUES CAST(12345 AS VARCHAR(3)); +-- error 22001: string data right truncation for CHARACTER VARYING(3) + +VALUES CAST(123456789 AS DOUBLE PRECISION); +-- COL1: 1.23456789e+08 diff --git a/tests/delete.sql b/tests/delete.sql index 3022d1d..6b12440 100644 --- a/tests/delete.sql +++ b/tests/delete.sql @@ -24,7 +24,7 @@ SELECT * FROM foo; -- msg: INSERT 1 -- msg: DELETE 1 -- msg: DELETE 0 --- BAZ: 78 +-- BAZ: 78e0 DELETE FROM foo.bar; -- error 3F000: invalid schema name: FOO diff --git a/tests/double-precision.sql b/tests/double-precision.sql index ec8d1ea..4956432 100644 --- a/tests/double-precision.sql +++ b/tests/double-precision.sql @@ -38,89 +38,89 @@ INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS REAL) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 CREATE TABLE foo (x DOUBLE PRECISION); INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS DOUBLE PRECISION) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 /* types */ VALUES CAST(500000000.3 AS DOUBLE PRECISION) * 2000000000.7; --- COL1: 1e+18 (DOUBLE PRECISION) +-- COL1: 1.00000000095e+18 (DOUBLE PRECISION) /* types */ VALUES CAST(1.23 AS DOUBLE PRECISION) + 53.7; --- COL1: 54.93 (DOUBLE PRECISION) +-- COL1: 54.93e0 (DOUBLE PRECISION) /* types */ VALUES 53.7 + CAST(1.23 AS DOUBLE PRECISION); --- COL1: 54.93 (DOUBLE PRECISION) +-- COL1: 54.93e0 (DOUBLE PRECISION) /* types */ VALUES CAST(500000000 AS DOUBLE PRECISION) + 2000000000.7; --- COL1: 2.500000e+09 (DOUBLE PRECISION) +-- COL1: 2.5000000007e+09 (DOUBLE PRECISION) /* types */ VALUES 500000000.7 + CAST(2000000000 AS DOUBLE PRECISION); --- COL1: 2.500000e+09 (DOUBLE PRECISION) +-- COL1: 2.5000000007e+09 (DOUBLE PRECISION) /* types */ VALUES CAST(1.23 AS DOUBLE PRECISION) - 53.7; --- COL1: -52.47 (DOUBLE PRECISION) +-- COL1: -52.47e0 (DOUBLE PRECISION) /* types */ VALUES 53.7 - CAST(1.23 AS DOUBLE PRECISION); --- COL1: 52.47 (DOUBLE PRECISION) +-- COL1: 52.47e0 (DOUBLE PRECISION) /* types */ VALUES CAST(-2000000000.1 AS DOUBLE PRECISION) - 500000000.7; --- COL1: -2.500000e+09 (DOUBLE PRECISION) +-- COL1: -0.25000000008e+10 (DOUBLE PRECISION) /* types */ VALUES -500000000.7 - CAST(2000000000.1 AS DOUBLE PRECISION); --- COL1: -2.500000e+09 (DOUBLE PRECISION) +-- COL1: -0.25000000008e+10 (DOUBLE PRECISION) /* types */ VALUES CAST(12.3 AS DOUBLE PRECISION) * 53.7; --- COL1: 660.51 (DOUBLE PRECISION) +-- COL1: 660.51e0 (DOUBLE PRECISION) /* types */ VALUES -53.7 * CAST(12.3 AS DOUBLE PRECISION); --- COL1: -660.51 (DOUBLE PRECISION) +-- COL1: -660.51e0 (DOUBLE PRECISION) /* types */ VALUES CAST(-300000.1 AS DOUBLE PRECISION) * 200000.7; --- COL1: -6.000023e+10 (DOUBLE PRECISION) +-- COL1: -6.000023000007e+10 (DOUBLE PRECISION) /* types */ VALUES -300000.7 * CAST(200000.1 AS DOUBLE PRECISION); --- COL1: -6.000017e+10 (DOUBLE PRECISION) +-- COL1: -6.000017000007e+10 (DOUBLE PRECISION) /* types */ VALUES CAST(1.23 AS DOUBLE PRECISION) / 53.7; --- COL1: 0.022905 (DOUBLE PRECISION) +-- COL1: 0.022905027933e0 (DOUBLE PRECISION) /* types */ VALUES -123.7 / CAST(53.1 AS DOUBLE PRECISION); --- COL1: -2.329567 (DOUBLE PRECISION) +-- COL1: -2.329566854991e0 (DOUBLE PRECISION) /* types */ VALUES CAST(-300000000.5 AS DOUBLE PRECISION) / 0.02; --- COL1: -1.500000e+10 (DOUBLE PRECISION) +-- COL1: -1.5000000025e+10 (DOUBLE PRECISION) /* types */ VALUES -90000.7 / CAST(3.2 AS DOUBLE PRECISION); --- COL1: -28125.21875 (DOUBLE PRECISION) +-- COL1: -28125.218749999996e0 (DOUBLE PRECISION) -VALUES CAST(-30000.5 AS DOUBLE PRECISION) / 0; +VALUES CAST(-30000.5 AS DOUBLE PRECISION) / 0.0; -- error 22012: division by zero /* types */ VALUES -90000.5 / CAST(0.1 AS DOUBLE PRECISION); --- COL1: -900005 (DOUBLE PRECISION) +-- COL1: -900005e0 (DOUBLE PRECISION) CREATE TABLE foo (x DOUBLE PRECISION); INSERT INTO foo (x) VALUES (123456789123456789123456789); @@ -128,5 +128,5 @@ SELECT * FROM foo; SELECT CAST(x AS BIGINT) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- X: 0.1234568e+27 +-- X: 0.1234567891235e+27 -- error 22003: numeric value out of range diff --git a/tests/explain.sql b/tests/explain.sql index ea447dd..f423657 100644 --- a/tests/explain.sql +++ b/tests/explain.sql @@ -53,7 +53,7 @@ CREATE TABLE t1 (x INT); EXPLAIN SELECT * FROM t1 WHERE x = :foo; -- msg: CREATE TABLE 1 -- EXPLAIN: TABLE ":memory:".PUBLIC.T1 (X INTEGER) --- EXPLAIN: WHERE X = 2 +-- EXPLAIN: WHERE X = 2e0 -- EXPLAIN: EXPR (":memory:".PUBLIC.T1.X INTEGER) EXPLAIN CREATE TABLE t1 (x INT); diff --git a/tests/group-by-null.sql b/tests/group-by-null.sql index 23cc688..af7426e 100644 --- a/tests/group-by-null.sql +++ b/tests/group-by-null.sql @@ -7,18 +7,18 @@ INSERT INTO foo (x) VALUES (456); SELECT x FROM foo GROUP BY x; -- X: NULL --- X: 123 --- X: 456 +-- X: 123e0 +-- X: 456e0 SELECT x, count(*) FROM foo GROUP BY x; -- X: NULL COL2: 2 --- X: 123 COL2: 1 --- X: 456 COL2: 1 +-- X: 123e0 COL2: 1 +-- X: 456e0 COL2: 1 SELECT x, count(x) FROM foo GROUP BY x; -- X: NULL COL2: 0 --- X: 123 COL2: 1 --- X: 456 COL2: 1 +-- X: 123e0 COL2: 1 +-- X: 456e0 COL2: 1 SELECT count(*) FROM foo; -- COL1: 4 @@ -34,44 +34,44 @@ SELECT count(x) FROM foo WHERE x IS NOT NULL; SELECT x, min(x) FROM foo GROUP BY x; -- X: NULL COL2: NULL --- X: 123 COL2: 123 --- X: 456 COL2: 456 +-- X: 123e0 COL2: 123e0 +-- X: 456e0 COL2: 456e0 SELECT min(x) FROM foo; -- COL1: NULL SELECT min(x) FROM foo WHERE x IS NOT NULL; --- COL1: 123 +-- COL1: 123e0 SELECT x, max(x) FROM foo GROUP BY x; -- X: NULL COL2: NULL --- X: 123 COL2: 123 --- X: 456 COL2: 456 +-- X: 123e0 COL2: 123e0 +-- X: 456e0 COL2: 456e0 SELECT max(x) FROM foo; -- COL1: NULL SELECT max(x) FROM foo WHERE x IS NOT NULL; --- COL1: 456 +-- COL1: 456e0 SELECT x, avg(x) FROM foo GROUP BY x; -- X: NULL COL2: NULL --- X: 123 COL2: 123 --- X: 456 COL2: 456 +-- X: 123e0 COL2: 123e0 +-- X: 456e0 COL2: 456e0 SELECT avg(x) FROM foo; -- COL1: NULL SELECT avg(x) FROM foo WHERE x IS NOT NULL; --- COL1: 289.5 +-- COL1: 289.5e0 SELECT x, sum(x) FROM foo GROUP BY x; -- X: NULL COL2: NULL --- X: 123 COL2: 123 --- X: 456 COL2: 456 +-- X: 123e0 COL2: 123e0 +-- X: 456e0 COL2: 456e0 SELECT sum(x) FROM foo; -- COL1: NULL SELECT sum(x) FROM foo WHERE x IS NOT NULL; --- COL1: 579 +-- COL1: 579e0 diff --git a/tests/group-by.sql b/tests/group-by.sql index 8611412..1eeb39f 100644 --- a/tests/group-by.sql +++ b/tests/group-by.sql @@ -13,9 +13,9 @@ EXPLAIN SELECT x FROM foo GROUP BY x; -- EXPLAIN: EXPR (X DOUBLE PRECISION) SELECT x FROM foo GROUP BY x; --- X: 1.234 --- X: 5.6 --- X: 12.34 +-- X: 1.234e0 +-- X: 5.6e0 +-- X: 12.34e0 EXPLAIN SELECT x, count(*) FROM foo GROUP BY x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -24,9 +24,9 @@ EXPLAIN SELECT x, count(*) FROM foo GROUP BY x; -- EXPLAIN: EXPR (X DOUBLE PRECISION, COL2 INTEGER) SELECT x, count(*) FROM foo GROUP BY x; --- X: 1.234 COL2: 2 --- X: 5.6 COL2: 2 --- X: 12.34 COL2: 1 +-- X: 1.234e0 COL2: 2 +-- X: 5.6e0 COL2: 2 +-- X: 12.34e0 COL2: 1 EXPLAIN SELECT count(*), x FROM foo GROUP BY x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -35,9 +35,9 @@ EXPLAIN SELECT count(*), x FROM foo GROUP BY x; -- EXPLAIN: EXPR (COL1 INTEGER, X DOUBLE PRECISION) SELECT count(*), x FROM foo GROUP BY x; --- COL1: 2 X: 1.234 --- COL1: 2 X: 5.6 --- COL1: 1 X: 12.34 +-- COL1: 2 X: 1.234e0 +-- COL1: 2 X: 5.6e0 +-- COL1: 1 X: 12.34e0 EXPLAIN SELECT x, y FROM foo GROUP BY x, y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -46,10 +46,10 @@ EXPLAIN SELECT x, y FROM foo GROUP BY x, y; -- EXPLAIN: EXPR (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) SELECT x, y FROM foo GROUP BY x, y; --- X: 1.234 Y: hello --- X: 1.234 Y: world --- X: 5.6 Y: world --- X: 12.34 Y: hello +-- X: 1.234e0 Y: hello +-- X: 1.234e0 Y: world +-- X: 5.6e0 Y: world +-- X: 12.34e0 Y: hello EXPLAIN SELECT y, x FROM foo GROUP BY x, y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -58,10 +58,10 @@ EXPLAIN SELECT y, x FROM foo GROUP BY x, y; -- EXPLAIN: EXPR (Y CHARACTER VARYING(32), X DOUBLE PRECISION) SELECT y, x FROM foo GROUP BY x, y; --- Y: hello X: 1.234 --- Y: world X: 1.234 --- Y: world X: 5.6 --- Y: hello X: 12.34 +-- Y: hello X: 1.234e0 +-- Y: world X: 1.234e0 +-- Y: world X: 5.6e0 +-- Y: hello X: 12.34e0 EXPLAIN SELECT x, y FROM foo GROUP BY y, x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -70,10 +70,10 @@ EXPLAIN SELECT x, y FROM foo GROUP BY y, x; -- EXPLAIN: EXPR (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) SELECT x, y FROM foo GROUP BY y, x; --- X: 1.234 Y: hello --- X: 12.34 Y: hello --- X: 1.234 Y: world --- X: 5.6 Y: world +-- X: 1.234e0 Y: hello +-- X: 12.34e0 Y: hello +-- X: 1.234e0 Y: world +-- X: 5.6e0 Y: world EXPLAIN SELECT x + 1.0, x FROM foo GROUP BY x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -82,9 +82,9 @@ EXPLAIN SELECT x + 1.0, x FROM foo GROUP BY x; -- EXPLAIN: EXPR (COL1 DOUBLE PRECISION, X DOUBLE PRECISION) SELECT x + 1.0, x FROM foo GROUP BY x; --- COL1: 2.234 X: 1.234 --- COL1: 6.6 X: 5.6 --- COL1: 13.34 X: 12.34 +-- COL1: 2.234e0 X: 1.234e0 +-- COL1: 6.6e0 X: 5.6e0 +-- COL1: 13.34e0 X: 12.34e0 EXPLAIN SELECT x, count(x) FROM foo GROUP BY x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -93,9 +93,9 @@ EXPLAIN SELECT x, count(x) FROM foo GROUP BY x; -- EXPLAIN: EXPR (X DOUBLE PRECISION, COL2 INTEGER) SELECT x, count(x) FROM foo GROUP BY x; --- X: 1.234 COL2: 2 --- X: 5.6 COL2: 2 --- X: 12.34 COL2: 1 +-- X: 1.234e0 COL2: 2 +-- X: 5.6e0 COL2: 2 +-- X: 12.34e0 COL2: 1 EXPLAIN SELECT y, min(x) FROM foo GROUP BY y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -104,8 +104,8 @@ EXPLAIN SELECT y, min(x) FROM foo GROUP BY y; -- EXPLAIN: EXPR (Y CHARACTER VARYING(32), COL2 DOUBLE PRECISION) SELECT y, min(x) FROM foo GROUP BY y; --- Y: hello COL2: 1.234 --- Y: world COL2: 1.234 +-- Y: hello COL2: 1.234e0 +-- Y: world COL2: 1.234e0 EXPLAIN SELECT y, max(x) AS largest FROM foo GROUP BY y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -114,16 +114,16 @@ EXPLAIN SELECT y, max(x) AS largest FROM foo GROUP BY y; -- EXPLAIN: EXPR (Y CHARACTER VARYING(32), LARGEST DOUBLE PRECISION) SELECT y, max(x) AS largest FROM foo GROUP BY y; --- Y: hello LARGEST: 12.34 --- Y: world LARGEST: 5.6 +-- Y: hello LARGEST: 12.34e0 +-- Y: world LARGEST: 5.6e0 SELECT y, sum(x) FROM foo GROUP BY y; --- Y: hello COL2: 13.574 --- Y: world COL2: 12.434 +-- Y: hello COL2: 13.574e0 +-- Y: world COL2: 12.434e0 SELECT y, avg(x) FROM foo GROUP BY y; --- Y: hello COL2: 6.787 --- Y: world COL2: 4.144667 +-- Y: hello COL2: 6.787e0 +-- Y: world COL2: 4.144666666667e0 EXPLAIN SELECT y, avg(x) FROM foo WHERE y = 'hello' GROUP BY y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -133,7 +133,7 @@ EXPLAIN SELECT y, avg(x) FROM foo WHERE y = 'hello' GROUP BY y; -- EXPLAIN: EXPR (Y CHARACTER VARYING(32), COL2 DOUBLE PRECISION) SELECT y, avg(x) FROM foo WHERE y = 'hello' GROUP BY y; --- Y: hello COL2: 6.787 +-- Y: hello COL2: 6.787e0 EXPLAIN SELECT count(*) FROM foo; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -147,16 +147,16 @@ SELECT count(x) FROM foo; -- COL1: 5 SELECT min(x) FROM foo; --- COL1: 1.234 +-- COL1: 1.234e0 SELECT max(x) FROM foo; --- COL1: 12.34 +-- COL1: 12.34e0 SELECT avg(x) FROM foo; --- COL1: 5.2016 +-- COL1: 5.2016e0 SELECT sum(x) FROM foo; --- COL1: 26.008 +-- COL1: 26.008e0 EXPLAIN SELECT min(x), max(x) FROM foo; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -164,15 +164,15 @@ EXPLAIN SELECT min(x), max(x) FROM foo; -- EXPLAIN: EXPR (COL1 DOUBLE PRECISION, COL2 DOUBLE PRECISION) SELECT min(x), max(x) FROM foo; --- COL1: 1.234 COL2: 12.34 +-- COL1: 1.234e0 COL2: 12.34e0 EXPLAIN SELECT avg(x * 2.0) FROM foo; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) --- EXPLAIN: GROUP BY (AVG(X * 2.0) DOUBLE PRECISION) +-- EXPLAIN: GROUP BY (AVG(X * 2e0) DOUBLE PRECISION) -- EXPLAIN: EXPR (COL1 DOUBLE PRECISION) SELECT avg(x * 2.0) FROM foo; --- COL1: 10.4032 +-- COL1: 10.4032e0 EXPLAIN SELECT x FROM foo GROUP BY x FETCH FIRST 2 ROWS ONLY; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -182,8 +182,8 @@ EXPLAIN SELECT x FROM foo GROUP BY x FETCH FIRST 2 ROWS ONLY; -- EXPLAIN: EXPR (X DOUBLE PRECISION) SELECT x FROM foo GROUP BY x FETCH FIRST 2 ROWS ONLY; --- X: 1.234 --- X: 5.6 +-- X: 1.234e0 +-- X: 5.6e0 EXPLAIN SELECT x FROM foo GROUP BY x OFFSET 1 ROW @@ -197,8 +197,8 @@ FETCH FIRST 2 ROWS ONLY; SELECT x FROM foo GROUP BY x OFFSET 1 ROW FETCH FIRST 2 ROWS ONLY; --- X: 5.6 --- X: 12.34 +-- X: 5.6e0 +-- X: 12.34e0 EXPLAIN SELECT x FROM foo GROUP BY x ORDER BY x DESC FETCH FIRST 2 ROWS ONLY; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -209,8 +209,8 @@ EXPLAIN SELECT x FROM foo GROUP BY x ORDER BY x DESC FETCH FIRST 2 ROWS ONLY; -- EXPLAIN: EXPR (X DOUBLE PRECISION) SELECT x FROM foo GROUP BY x ORDER BY x DESC FETCH FIRST 2 ROWS ONLY; --- X: 12.34 --- X: 5.6 +-- X: 12.34e0 +-- X: 5.6e0 EXPLAIN SELECT x FROM foo GROUP BY x @@ -229,8 +229,8 @@ GROUP BY x ORDER BY x DESC OFFSET 1 ROW FETCH FIRST 2 ROWS ONLY; --- X: 5.6 --- X: 1.234 +-- X: 5.6e0 +-- X: 1.234e0 EXPLAIN SELECT y, min(x) * avg(x) FROM foo GROUP BY y; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) diff --git a/tests/insert.sql b/tests/insert.sql index 5067b45..dbd16d2 100644 --- a/tests/insert.sql +++ b/tests/insert.sql @@ -10,8 +10,8 @@ SELECT * FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: INSERT 1 --- X: 101 --- X: 102 +-- X: 101e0 +-- X: 102e0 CREATE TABLE foo (b BOOLEAN); INSERT INTO foo (b) VALUES (true); @@ -54,7 +54,7 @@ INSERT INTO t1 (f1, f2) VALUES ('a', 1.23); SELECT * FROM t1; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- F1: a F2: 1.23 +-- F1: a F2: 1.23e0 CREATE TABLE t1 (f1 CHARACTER VARYING(10), f2 FLOAT NOT NULL); INSERT INTO t1 (f1, f2) VALUES ('a', NULL); @@ -67,7 +67,7 @@ INSERT INTO t1 (f1, f2) VALUES (NULL, 1.23); SELECT * FROM t1; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- F1: NULL F2: 1.23 +-- F1: NULL F2: 1.23e0 CREATE TABLE t1 (f1 CHARACTER VARYING(10), f2 FLOAT); INSERT INTO t1 (f1, f2) VALUES (NULL, NULL); @@ -81,7 +81,7 @@ INSERT INTO t1 (f2) VALUES (1.23); SELECT * FROM t1; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- F1: NULL F2: 1.23 +-- F1: NULL F2: 1.23e0 CREATE TABLE t1 (f1 CHARACTER VARYING(10), f2 FLOAT NOT NULL); INSERT INTO t1 (f1) VALUES ('a'); @@ -94,7 +94,7 @@ INSERT INTO t1 (f1) VALUES (-123.0 * 4.2); SELECT * FROM t1; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- F1: -516.6 +-- F1: -516.6e0 INSERT INTO foo.bar (nothing) VALUES (123); -- error 3F000: invalid schema name: FOO diff --git a/tests/integer.sql b/tests/integer.sql index 71c9ef4..7823e82 100644 --- a/tests/integer.sql +++ b/tests/integer.sql @@ -45,14 +45,14 @@ INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS REAL) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 CREATE TABLE foo (x INTEGER); INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS DOUBLE PRECISION) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 /* types */ VALUES CAST(123 AS INTEGER) + 53; diff --git a/tests/literal.sql b/tests/literal.sql index 4ade5cb..d9135a7 100644 --- a/tests/literal.sql +++ b/tests/literal.sql @@ -4,20 +4,56 @@ VALUES 1; /* types */ VALUES 1.23; --- COL1: 1.23 (NUMERIC) +-- COL1: 1.23e0 (DOUBLE PRECISION) /* types */ VALUES 1.; --- COL1: 1 (NUMERIC) +-- COL1: 1e0 (DOUBLE PRECISION) /* types */ VALUES .23; --- COL1: 0.23 (NUMERIC) +-- COL1: 0.23e0 (DOUBLE PRECISION) /* types */ VALUES 789; -- COL1: 789 (SMALLINT) +/* types */ +VALUES 1 E 2; +-- COL1: 100e0 (DOUBLE PRECISION) + +/* types */ +VALUES -1.2E2; +-- COL1: -120e0 (DOUBLE PRECISION) + +/* types */ +VALUES +1.2e-2; +-- COL1: 0.012e0 (DOUBLE PRECISION) + +/* types */ +VALUES 1000000000000e0; +-- COL1: 1e+12 (DOUBLE PRECISION) + +/* types */ +VALUES 1.e+12; +-- COL1: 1e+12 (DOUBLE PRECISION) + +/* types */ +VALUES -1000000000000e0; +-- COL1: -1e+12 (DOUBLE PRECISION) + +/* types */ +VALUES -1.e+12; +-- COL1: -1e+12 (DOUBLE PRECISION) + +/* types */ +VALUES 0.000000000009e0; +-- COL1: 9e-12 (DOUBLE PRECISION) + +/* types */ +VALUES 9.e-12; +-- COL1: 9e-12 (DOUBLE PRECISION) + /* types */ VALUES 'hello'; -- COL1: hello (CHARACTER(5)) diff --git a/tests/math.sql b/tests/math.sql index 93bfe9f..b541f80 100644 --- a/tests/math.sql +++ b/tests/math.sql @@ -1,5 +1,5 @@ VALUES ROW(ABS(1.2), ABS(-1.23)); --- COL1: 1.2 COL2: 1.23 +-- COL1: 1.2e0 COL2: 1.23e0 VALUES ABS('hello'); -- error 42883: function does not exist: ABS(CHARACTER(5)) @@ -12,76 +12,76 @@ VALUES ABS(1, 2); /* types */ VALUES SIN(1.2); --- COL1: 0.932039 (DOUBLE PRECISION) +-- COL1: 0.932039085967e0 (DOUBLE PRECISION) /* types */ VALUES COS(1.2); --- COL1: 0.362358 (DOUBLE PRECISION) +-- COL1: 0.362357754477e0 (DOUBLE PRECISION) /* types */ VALUES TAN(1.2); --- COL1: 2.572152 (DOUBLE PRECISION) +-- COL1: 2.572151622126e0 (DOUBLE PRECISION) /* types */ VALUES SINH(1.2); --- COL1: 1.509461 (DOUBLE PRECISION) +-- COL1: 1.509461355412e0 (DOUBLE PRECISION) /* types */ VALUES COSH(1.2); --- COL1: 1.810656 (DOUBLE PRECISION) +-- COL1: 1.810655567324e0 (DOUBLE PRECISION) /* types */ VALUES TANH(1.2); --- COL1: 0.833655 (DOUBLE PRECISION) +-- COL1: 0.833654607012e0 (DOUBLE PRECISION) /* types */ VALUES ASIN(0.2); --- COL1: 0.201358 (DOUBLE PRECISION) +-- COL1: 0.20135792079e0 (DOUBLE PRECISION) /* types */ VALUES ACOS(0.2); --- COL1: 1.369438 (DOUBLE PRECISION) +-- COL1: 1.369438406005e0 (DOUBLE PRECISION) /* types */ VALUES ATAN(0.2); --- COL1: 0.197396 (DOUBLE PRECISION) +-- COL1: 0.19739555985e0 (DOUBLE PRECISION) /* types */ VALUES MOD(232.0, 3.0); --- COL1: 1 (DOUBLE PRECISION) +-- COL1: 1e0 (DOUBLE PRECISION) /* types */ VALUES MOD(10.7, 0.8); --- COL1: 0.3 (DOUBLE PRECISION) +-- COL1: 0.3e0 (DOUBLE PRECISION) /* types */ VALUES LOG10(13.7); --- COL1: 1.136721 (DOUBLE PRECISION) +-- COL1: 1.136720567156e0 (DOUBLE PRECISION) /* types */ VALUES LN(13.7); --- COL1: 2.617396 (DOUBLE PRECISION) +-- COL1: 2.617395832834e0 (DOUBLE PRECISION) /* types */ VALUES EXP(3.7); --- COL1: 40.447304 (DOUBLE PRECISION) +-- COL1: 40.447304360067e0 (DOUBLE PRECISION) /* types */ VALUES POWER(3.7, 2.5); --- COL1: 26.333241 (DOUBLE PRECISION) +-- COL1: 26.333240780428e0 (DOUBLE PRECISION) /* types */ VALUES SQRT(3.7); --- COL1: 1.923538 (DOUBLE PRECISION) +-- COL1: 1.923538406167e0 (DOUBLE PRECISION) /* types */ VALUES ROW(FLOOR(3.7), FLOOR(3.3), FLOOR(-3.7), FLOOR(-3.3)); --- COL1: 3 (DOUBLE PRECISION) COL2: 3 (DOUBLE PRECISION) COL3: -4 (DOUBLE PRECISION) COL4: -4 (DOUBLE PRECISION) +-- COL1: 3e0 (DOUBLE PRECISION) COL2: 3e0 (DOUBLE PRECISION) COL3: -4e0 (DOUBLE PRECISION) COL4: -4e0 (DOUBLE PRECISION) /* types */ VALUES ROW(CEIL(3.7), CEIL(3.3), CEIL(-3.7), CEIL(-3.3)); --- COL1: 4 (DOUBLE PRECISION) COL2: 4 (DOUBLE PRECISION) COL3: -3 (DOUBLE PRECISION) COL4: -3 (DOUBLE PRECISION) +-- COL1: 4e0 (DOUBLE PRECISION) COL2: 4e0 (DOUBLE PRECISION) COL3: -3e0 (DOUBLE PRECISION) COL4: -3e0 (DOUBLE PRECISION) /* types */ VALUES CEILING(3.7); --- COL1: 4 (DOUBLE PRECISION) +-- COL1: 4e0 (DOUBLE PRECISION) diff --git a/tests/null.sql b/tests/null.sql index ab1f0d3..cccbe9b 100644 --- a/tests/null.sql +++ b/tests/null.sql @@ -31,8 +31,8 @@ SELECT * FROM foo WHERE num IS NOT NULL; -- COL1: is null -- NUM: NULL -- COL1: is not null --- NUM: 13 --- NUM: 35 +-- NUM: 13e0 +-- NUM: 35e0 CREATE TABLE foo (x BOOLEAN); INSERT INTO foo (x) VALUES (NULL); diff --git a/tests/order-nulls.sql b/tests/order-nulls.sql index b24dba9..6c47379 100644 --- a/tests/order-nulls.sql +++ b/tests/order-nulls.sql @@ -8,11 +8,11 @@ INSERT INTO bar (x) VALUES (NULL); SELECT * FROM bar ORDER BY x; -- X: NULL -- X: NULL --- X: 1.234 --- X: 4.567 +-- X: 1.234e0 +-- X: 4.567e0 SELECT * FROM bar ORDER BY x DESC; --- X: 4.567 --- X: 1.234 +-- X: 4.567e0 +-- X: 1.234e0 -- X: NULL -- X: NULL diff --git a/tests/order.sql b/tests/order.sql index d7fa78e..112f51f 100644 --- a/tests/order.sql +++ b/tests/order.sql @@ -11,10 +11,10 @@ EXPLAIN SELECT * FROM foo ORDER BY x; -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.X DOUBLE PRECISION, ":memory:".PUBLIC.FOO.Y CHARACTER VARYING(32)) SELECT * FROM foo ORDER BY x; --- X: 0.1234 Y: hi --- X: 1.234 Y: hi --- X: 5.6 Y: bar --- X: 12.34 Y: there +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi +-- X: 5.6e0 Y: bar +-- X: 12.34e0 Y: there EXPLAIN SELECT * FROM foo ORDER BY x ASC; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -27,16 +27,16 @@ EXPLAIN SELECT * FROM foo ORDER BY x DESC; -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.X DOUBLE PRECISION, ":memory:".PUBLIC.FOO.Y CHARACTER VARYING(32)) SELECT * FROM foo ORDER BY x DESC; --- X: 12.34 Y: there --- X: 5.6 Y: bar --- X: 1.234 Y: hi --- X: 0.1234 Y: hi +-- X: 12.34e0 Y: there +-- X: 5.6e0 Y: bar +-- X: 1.234e0 Y: hi +-- X: 0.1234e0 Y: hi SELECT * FROM foo ORDER BY y; --- X: 5.6 Y: bar --- X: 1.234 Y: hi --- X: 0.1234 Y: hi --- X: 12.34 Y: there +-- X: 5.6e0 Y: bar +-- X: 1.234e0 Y: hi +-- X: 0.1234e0 Y: hi +-- X: 12.34e0 Y: there EXPLAIN SELECT * FROM foo ORDER BY y, x; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -44,22 +44,22 @@ EXPLAIN SELECT * FROM foo ORDER BY y, x; -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.X DOUBLE PRECISION, ":memory:".PUBLIC.FOO.Y CHARACTER VARYING(32)) SELECT * FROM foo ORDER BY y, x; --- X: 5.6 Y: bar --- X: 0.1234 Y: hi --- X: 1.234 Y: hi --- X: 12.34 Y: there +-- X: 5.6e0 Y: bar +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi +-- X: 12.34e0 Y: there SELECT * FROM foo ORDER BY y, x DESC; --- X: 5.6 Y: bar --- X: 1.234 Y: hi --- X: 0.1234 Y: hi --- X: 12.34 Y: there +-- X: 5.6e0 Y: bar +-- X: 1.234e0 Y: hi +-- X: 0.1234e0 Y: hi +-- X: 12.34e0 Y: there SELECT * FROM foo ORDER BY x DESC, y; --- X: 12.34 Y: there --- X: 5.6 Y: bar --- X: 1.234 Y: hi --- X: 0.1234 Y: hi +-- X: 12.34e0 Y: there +-- X: 5.6e0 Y: bar +-- X: 1.234e0 Y: hi +-- X: 0.1234e0 Y: hi EXPLAIN SELECT * FROM foo ORDER BY ABS(10 - x); -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (X DOUBLE PRECISION, Y CHARACTER VARYING(32)) @@ -67,24 +67,24 @@ EXPLAIN SELECT * FROM foo ORDER BY ABS(10 - x); -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.X DOUBLE PRECISION, ":memory:".PUBLIC.FOO.Y CHARACTER VARYING(32)) SELECT * FROM foo ORDER BY ABS(10.0 - x); --- X: 12.34 Y: there --- X: 5.6 Y: bar --- X: 1.234 Y: hi --- X: 0.1234 Y: hi +-- X: 12.34e0 Y: there +-- X: 5.6e0 Y: bar +-- X: 1.234e0 Y: hi +-- X: 0.1234e0 Y: hi SELECT * FROM foo ORDER BY ABS(10.0 - x) DESC; --- X: 0.1234 Y: hi --- X: 1.234 Y: hi --- X: 5.6 Y: bar --- X: 12.34 Y: there +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi +-- X: 5.6e0 Y: bar +-- X: 12.34e0 Y: there SELECT * FROM foo ORDER BY y, x OFFSET 0 ROWS; --- X: 5.6 Y: bar --- X: 0.1234 Y: hi --- X: 1.234 Y: hi --- X: 12.34 Y: there +-- X: 5.6e0 Y: bar +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi +-- X: 12.34e0 Y: there EXPLAIN SELECT * FROM foo ORDER BY y, x @@ -97,8 +97,8 @@ OFFSET 2 ROW; SELECT * FROM foo ORDER BY y, x OFFSET 2 ROW; --- X: 1.234 Y: hi --- X: 12.34 Y: there +-- X: 1.234e0 Y: hi +-- X: 12.34e0 Y: there SELECT * FROM foo ORDER BY y, x @@ -107,8 +107,8 @@ OFFSET 50 ROW; SELECT * FROM foo ORDER BY y, x FETCH FIRST 2 ROWS ONLY; --- X: 5.6 Y: bar --- X: 0.1234 Y: hi +-- X: 5.6e0 Y: bar +-- X: 0.1234e0 Y: hi EXPLAIN SELECT * FROM foo ORDER BY y, x @@ -123,8 +123,8 @@ SELECT * FROM foo ORDER BY y, x OFFSET 1 ROW FETCH FIRST 2 ROWS ONLY; --- X: 0.1234 Y: hi --- X: 1.234 Y: hi +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi SELECT * FROM foo ORDER BY y, x @@ -137,8 +137,8 @@ SELECT * FROM foo ORDER BY y, x OFFSET :offset_num ROW FETCH FIRST :row_num ROWS ONLY; --- X: 0.1234 Y: hi --- X: 1.234 Y: hi +-- X: 0.1234e0 Y: hi +-- X: 1.234e0 Y: hi CREATE TABLE bar (x FLOAT, y VARCHAR(32)); EXPLAIN SELECT * FROM bar ORDER BY x; diff --git a/tests/parameters.sql b/tests/parameters.sql index 7a0bedf..c23e1d5 100644 --- a/tests/parameters.sql +++ b/tests/parameters.sql @@ -8,12 +8,12 @@ INSERT INTO t1 (x) VALUES (:foo); INSERT INTO t1 (x) VALUES (:foo); SELECT * FROM t1; -- msg: INSERT 1 --- X: 2 +-- X: 2e0 /* set foo 2 */ /* types */ VALUES :foo; --- COL1: 2 (DOUBLE PRECISION) +-- COL1: 2e0 (DOUBLE PRECISION) /* set foo 'hello' */ INSERT INTO t1 (x) VALUES (:foo); @@ -44,7 +44,7 @@ UPDATE t1 SET x = :foo; SELECT * FROM t1; -- msg: INSERT 1 -- msg: UPDATE 1 --- X: 321 +-- X: 321e0 DELETE FROM t1 WHERE x = :foo; -- msg: DELETE 0 diff --git a/tests/real.sql b/tests/real.sql index 0c2e453..b3cbfaa 100644 --- a/tests/real.sql +++ b/tests/real.sql @@ -38,14 +38,14 @@ INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS REAL) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 CREATE TABLE foo (x REAL); INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS DOUBLE PRECISION) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 /* types */ VALUES CAST(500000000.3 AS REAL) * CAST(2000000000.7 AS REAL); @@ -53,43 +53,43 @@ VALUES CAST(500000000.3 AS REAL) * CAST(2000000000.7 AS REAL); /* types */ VALUES CAST(1.23 AS REAL) + CAST(53.7 AS REAL); --- COL1: 54.93 (REAL) +-- COL1: 54.93e0 (REAL) /* types */ VALUES CAST(53.7 AS REAL) + CAST(1.23 AS REAL); --- COL1: 54.93 (REAL) +-- COL1: 54.93e0 (REAL) /* types */ VALUES CAST(500000000 AS REAL) + CAST(2000000000.7 AS REAL); --- COL1: 2.500000e+09 (REAL) +-- COL1: 2.5e+09 (REAL) /* types */ VALUES CAST(500000000.7 AS REAL) + CAST(2000000000 AS REAL); --- COL1: 2.500000e+09 (REAL) +-- COL1: 2.5e+09 (REAL) /* types */ VALUES CAST(1.23 AS REAL) - CAST(53.7 AS REAL); --- COL1: -52.470001 (REAL) +-- COL1: -52.470001e0 (REAL) /* types */ VALUES CAST(53.7 AS REAL) - CAST(1.23 AS REAL); --- COL1: 52.470001 (REAL) +-- COL1: 52.470001e0 (REAL) /* types */ VALUES CAST(-2000000000.1 AS REAL) - CAST(500000000.7 AS REAL); --- COL1: -2.500000e+09 (REAL) +-- COL1: -2.5e+09 (REAL) /* types */ VALUES CAST(-500000000.7 AS REAL) - CAST(2000000000.1 AS REAL); --- COL1: -2.500000e+09 (REAL) +-- COL1: -2.5e+09 (REAL) /* types */ VALUES CAST(12.3 AS REAL) * CAST(53.7 AS REAL); --- COL1: 660.51001 (REAL) +-- COL1: 660.51001e0 (REAL) /* types */ VALUES CAST(-53.7 AS REAL) * CAST(12.3 AS REAL); --- COL1: -660.51001 (REAL) +-- COL1: -660.51001e0 (REAL) /* types */ VALUES CAST(-300000.1 AS REAL) * CAST(200000.7 AS REAL); @@ -101,19 +101,19 @@ VALUES CAST(-300000.7 AS REAL) * CAST(200000.1 AS REAL); /* types */ VALUES CAST(1.23 AS REAL) / CAST(53.7 AS REAL); --- COL1: 0.022905 (REAL) +-- COL1: 0.022905e0 (REAL) /* types */ VALUES CAST(-123.7 AS REAL) / CAST(53.1 AS REAL); --- COL1: -2.329567 (REAL) +-- COL1: -2.329567e0 (REAL) /* types */ VALUES CAST(-300000000.5 AS REAL) / CAST(0.02 AS REAL); --- COL1: -1.500000e+10 (REAL) +-- COL1: -1.5e+10 (REAL) /* types */ VALUES CAST(-90000.7 AS REAL) / CAST(3.2 AS REAL); --- COL1: -28125.21875 (REAL) +-- COL1: -28125.21875e0 (REAL) /* types */ VALUES CAST(-30000.5 AS REAL) / CAST(0 AS REAL); @@ -121,7 +121,7 @@ VALUES CAST(-30000.5 AS REAL) / CAST(0 AS REAL); /* types */ VALUES CAST(-90000.5 AS REAL) / CAST(0.1 AS REAL); --- COL1: -900005 (REAL) +-- COL1: -900005e0 (REAL) CREATE TABLE foo (x REAL); INSERT INTO foo (x) VALUES (123456789123456789123456789); diff --git a/tests/select-from.sql b/tests/select-from.sql index fc8909f..5bc8de7 100644 --- a/tests/select-from.sql +++ b/tests/select-from.sql @@ -6,14 +6,14 @@ INSERT INTO foo (x) VALUES (1.234); SELECT * FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- X: 1.234 +-- X: 1.234e0 CREATE TABLE "Foo" ("a" FLOAT); INSERT INTO "Foo" ("a") VALUES (4.56); SELECT * FROM "Foo"; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- a: 4.56 +-- a: 4.56e0 CREATE TABLE foo (x FLOAT); CREATE TABLE "Foo" ("a" FLOAT); @@ -25,8 +25,8 @@ SELECT * FROM "Foo"; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: INSERT 1 --- X: 1.234 --- a: 4.56 +-- X: 1.234e0 +-- a: 4.56e0 SELECT * FROM foo; @@ -45,7 +45,7 @@ INSERT INTO foo (x) VALUES (1.234); SELECT foo.* FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- X: 1.234 +-- X: 1.234e0 CREATE TABLE foo (x FLOAT); EXPLAIN SELECT bar.* FROM foo; @@ -62,4 +62,4 @@ SELECT * FROM foo.bar; -- msg: CREATE SCHEMA 1 -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- X: 1.234 +-- X: 1.234e0 diff --git a/tests/select-where.sql b/tests/select-where.sql index c294242..8d9f536 100644 --- a/tests/select-where.sql +++ b/tests/select-where.sql @@ -6,39 +6,39 @@ INSERT INTO foo (num) VALUES (35); EXPLAIN SELECT * FROM foo WHERE num = 27.0; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (NUM DOUBLE PRECISION) --- EXPLAIN: WHERE NUM = 27.0 +-- EXPLAIN: WHERE NUM = 27e0 -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.NUM DOUBLE PRECISION) SELECT * FROM foo WHERE num = 27.0; --- NUM: 27 +-- NUM: 27e0 SELECT * FROM foo WHERE num <> 13.0; --- NUM: 27 --- NUM: 35 +-- NUM: 27e0 +-- NUM: 35e0 SELECT * FROM foo WHERE num > 27.0; --- NUM: 35 +-- NUM: 35e0 SELECT * FROM foo WHERE num >= 27.0; --- NUM: 27 --- NUM: 35 +-- NUM: 27e0 +-- NUM: 35e0 SELECT * FROM foo WHERE num < 27.0; --- NUM: 13 +-- NUM: 13e0 EXPLAIN SELECT * FROM foo WHERE num <= 27.0; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (NUM DOUBLE PRECISION) --- EXPLAIN: WHERE NUM <= 27.0 +-- EXPLAIN: WHERE NUM <= 27e0 -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.NUM DOUBLE PRECISION) SELECT * FROM foo WHERE num <= 27.0; --- NUM: 13 --- NUM: 27 +-- NUM: 13e0 +-- NUM: 27e0 EXPLAIN SELECT * FROM foo WHERE foo.num = 27.0; -- EXPLAIN: TABLE ":memory:".PUBLIC.FOO (NUM DOUBLE PRECISION) --- EXPLAIN: WHERE FOO.NUM = 27.0 +-- EXPLAIN: WHERE FOO.NUM = 27e0 -- EXPLAIN: EXPR (":memory:".PUBLIC.FOO.NUM DOUBLE PRECISION) SELECT * FROM foo WHERE foo.num = 27.0; --- NUM: 27 +-- NUM: 27e0 diff --git a/tests/smallint.sql b/tests/smallint.sql index 1bd314a..9d7d5b7 100644 --- a/tests/smallint.sql +++ b/tests/smallint.sql @@ -38,14 +38,14 @@ INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS REAL) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 CREATE TABLE foo (x SMALLINT); INSERT INTO foo (x) VALUES (123); SELECT CAST(x AS DOUBLE PRECISION) FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 --- COL1: 123 +-- COL1: 123e0 /* types */ VALUES CAST(123 AS SMALLINT) + CAST(53 AS SMALLINT); diff --git a/tests/update.sql b/tests/update.sql index 9641e99..239796c 100644 --- a/tests/update.sql +++ b/tests/update.sql @@ -29,8 +29,8 @@ SELECT * FROM foo; -- msg: INSERT 1 -- msg: UPDATE 1 -- msg: UPDATE 0 --- BAZ: 100 --- BAZ: 78 +-- BAZ: 100e0 +-- BAZ: 78e0 CREATE TABLE foo (baz FLOAT); UPDATE foo SET baz = true; @@ -60,7 +60,7 @@ SELECT * FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: UPDATE 1 --- BAZ: -936.6 +-- BAZ: -936.6e0 CREATE TABLE foo (baz FLOAT); INSERT INTO foo (baz) VALUES (-123); @@ -69,7 +69,7 @@ SELECT * FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: UPDATE 1 --- BAZ: -516.6 +-- BAZ: -516.6e0 CREATE TABLE foo (baz FLOAT); INSERT INTO foo (baz) VALUES (-123); @@ -78,7 +78,7 @@ SELECT * FROM foo; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: UPDATE 1 --- BAZ: -516.6 +-- BAZ: -516.6e0 UPDATE foo.bar SET baz = baz * 4.2; -- error 3F000: invalid schema name: FOO @@ -92,7 +92,7 @@ SELECT * FROM foo.bar; -- msg: CREATE TABLE 1 -- msg: INSERT 1 -- msg: UPDATE 1 --- BAZ: -516.6 +-- BAZ: -516.6e0 CREATE TABLE foo (baz FLOAT NOT NULL); UPDATE foo SET baz = NULL; diff --git a/tests/values.sql b/tests/values.sql index a124ab5..1ac4600 100644 --- a/tests/values.sql +++ b/tests/values.sql @@ -3,18 +3,18 @@ SELECT * FROM (VALUES 1); EXPLAIN SELECT * FROM (VALUES 1); -- EXPLAIN: $1: --- EXPLAIN: VALUES (COL1 NUMERIC) = ROW(1) --- EXPLAIN: TABLE $1 (COL1 NUMERIC) --- EXPLAIN: EXPR ($1.COL1 NUMERIC) +-- EXPLAIN: VALUES (COL1 SMALLINT) = ROW(1) +-- EXPLAIN: TABLE $1 (COL1 SMALLINT) +-- EXPLAIN: EXPR ($1.COL1 SMALLINT) EXPLAIN SELECT * FROM (VALUES 1.23); -- EXPLAIN: $1: --- EXPLAIN: VALUES (COL1 NUMERIC) = ROW(1.23) --- EXPLAIN: TABLE $1 (COL1 NUMERIC) --- EXPLAIN: EXPR ($1.COL1 NUMERIC) +-- EXPLAIN: VALUES (COL1 DOUBLE PRECISION) = ROW(1.23e0) +-- EXPLAIN: TABLE $1 (COL1 DOUBLE PRECISION) +-- EXPLAIN: EXPR ($1.COL1 DOUBLE PRECISION) SELECT * FROM (VALUES 1.23); --- COL1: 1.23 +-- COL1: 1.23e0 SELECT * FROM (VALUES 1, 'foo', TRUE); -- COL1: 1 @@ -29,9 +29,9 @@ EXPLAIN SELECT * FROM (VALUES ROW(1, 'foo', TRUE)); EXPLAIN SELECT * FROM (VALUES 1, 'foo', TRUE) AS t1 (abc, col2, "f"); -- EXPLAIN: T1: --- EXPLAIN: VALUES (ABC NUMERIC, COL2 NUMERIC, "f" NUMERIC) = ROW(1), ROW('foo'), ROW(TRUE) --- EXPLAIN: TABLE T1 (ABC NUMERIC, COL2 NUMERIC, "f" NUMERIC) --- EXPLAIN: EXPR (T1.ABC NUMERIC, T1.COL2 NUMERIC, T1."f" NUMERIC) +-- EXPLAIN: VALUES (ABC SMALLINT, COL2 SMALLINT, "f" SMALLINT) = ROW(1), ROW('foo'), ROW(TRUE) +-- EXPLAIN: TABLE T1 (ABC SMALLINT, COL2 SMALLINT, "f" SMALLINT) +-- EXPLAIN: EXPR (T1.ABC SMALLINT, T1.COL2 SMALLINT, T1."f" SMALLINT) EXPLAIN SELECT * FROM (VALUES ROW(1, 'foo', TRUE)) AS t1 (abc, col2, "f"); -- EXPLAIN: T1: @@ -52,11 +52,11 @@ VALUES 'cool'; /* types */ VALUES 'cool', 12.3; -- COL1: cool (CHARACTER(4)) --- COL1: 12.3 (NUMERIC) +-- COL1: 12.3e0 (DOUBLE PRECISION) /* types */ VALUES ROW('cool', 12.3); --- COL1: cool (CHARACTER(4)) COL2: 12.3 (NUMERIC) +-- COL1: cool (CHARACTER(4)) COL2: 12.3e0 (DOUBLE PRECISION) /* types */ VALUES '12.3'; @@ -70,10 +70,10 @@ EXPLAIN VALUES 'hello'; -- EXPLAIN: VALUES (COL1 CHARACTER(5)) = ROW('hello') EXPLAIN VALUES 'hello', 1.22; --- EXPLAIN: VALUES (COL1 CHARACTER(5)) = ROW('hello'), ROW(1.22) +-- EXPLAIN: VALUES (COL1 CHARACTER(5)) = ROW('hello'), ROW(1.22e0) EXPLAIN VALUES ROW('hello', 1.22); --- EXPLAIN: VALUES (COL1 CHARACTER(5), COL2 NUMERIC) = ROW('hello', 1.22) +-- EXPLAIN: VALUES (COL1 CHARACTER(5), COL2 DOUBLE PRECISION) = ROW('hello', 1.22e0) SELECT * FROM (VALUES ROW(123), ROW(456)); -- COL1: 123 @@ -126,10 +126,10 @@ SELECT * FROM (VALUES ROW(1), ROW(3, 4)) AS t1 (foo, bar); EXPLAIN SELECT * FROM (VALUES ROW(1, 2), ROW(3, 4), ROW(5, 6)) AS t1 (foo, bar) FETCH FIRST 2 ROWS ONLY; -- EXPLAIN: T1: --- EXPLAIN: VALUES (FOO SMALLINT, BAR NUMERIC) = ROW(1, 2), ROW(3, 4), ROW(5, 6) --- EXPLAIN: TABLE T1 (FOO SMALLINT, BAR NUMERIC) +-- EXPLAIN: VALUES (FOO SMALLINT, BAR SMALLINT) = ROW(1, 2), ROW(3, 4), ROW(5, 6) +-- EXPLAIN: TABLE T1 (FOO SMALLINT, BAR SMALLINT) -- EXPLAIN: FETCH FIRST 2 ROWS ONLY --- EXPLAIN: EXPR (T1.FOO SMALLINT, T1.BAR NUMERIC) +-- EXPLAIN: EXPR (T1.FOO SMALLINT, T1.BAR SMALLINT) SELECT * FROM (VALUES ROW(1, 2), ROW(3, 4), ROW(5, 6)) AS t1 (foo, bar) FETCH FIRST 2 ROWS ONLY; @@ -139,10 +139,10 @@ FETCH FIRST 2 ROWS ONLY; EXPLAIN SELECT * FROM (VALUES ROW(1, 2), ROW(3, 4), ROW(5, 6)) AS t1 (foo, bar) OFFSET 1 ROW; -- EXPLAIN: T1: --- EXPLAIN: VALUES (FOO SMALLINT, BAR NUMERIC) = ROW(1, 2), ROW(3, 4), ROW(5, 6) --- EXPLAIN: TABLE T1 (FOO SMALLINT, BAR NUMERIC) +-- EXPLAIN: VALUES (FOO SMALLINT, BAR SMALLINT) = ROW(1, 2), ROW(3, 4), ROW(5, 6) +-- EXPLAIN: TABLE T1 (FOO SMALLINT, BAR SMALLINT) -- EXPLAIN: OFFSET 1 ROWS --- EXPLAIN: EXPR (T1.FOO SMALLINT, T1.BAR NUMERIC) +-- EXPLAIN: EXPR (T1.FOO SMALLINT, T1.BAR SMALLINT) SELECT * FROM (VALUES ROW(1, 2), ROW(3, 4), ROW(5, 6)) AS t1 (foo, bar) OFFSET 1 ROW; diff --git a/vsql/keywords.v b/vsql/keywords.v index 57f8449..eed6a43 100644 --- a/vsql/keywords.v +++ b/vsql/keywords.v @@ -104,6 +104,12 @@ fn is_non_reserved_word(word string) bool { return word.to_upper() in non_reserved_words } +fn is_syntax_word(word string) bool { + syntax_words := ['E'] + + return word.to_upper() in syntax_words +} + fn is_key_word(word string) bool { - return is_reserved_word(word) || is_non_reserved_word(word) + return is_reserved_word(word) || is_non_reserved_word(word) || is_syntax_word(word) } diff --git a/vsql/lexer.v b/vsql/lexer.v index adf7e4c..fc15be0 100644 --- a/vsql/lexer.v +++ b/vsql/lexer.v @@ -49,6 +49,16 @@ fn tokenize(sql_stmt string) []Token { i++ } tokens << Token{.literal_number, word} + + // There is a special case for approximate numbers where 'E' is considered + // a separate token in the SQL BNF. However, "e2" should not be treated as + // two tokens, but rather we need to catch this case only when with a + // number token. + if i < cs.len && (cs[i] == `e` || cs[i] == `E`) { + tokens << Token{.keyword, 'E'} + i++ + } + continue } diff --git a/vsql/operators.v b/vsql/operators.v index 7721773..9b196c4 100644 --- a/vsql/operators.v +++ b/vsql/operators.v @@ -101,10 +101,10 @@ fn binary_double_precision_plus_double_precision(conn &Connection, a Value, b Va } fn binary_integer_plus_integer(conn &Connection, a Value, b Value) !Value { - x := i64(a.int_value() + b.as_f64()!) + x := i64(a.int_value() + b.int_value()) check_integer_range(x, .is_integer)! - return new_integer_value(i16(x)) + return new_integer_value(int(x)) } fn binary_bigint_plus_bigint(conn &Connection, a Value, b Value) !Value { @@ -178,15 +178,15 @@ fn binary_bigint_divide_bigint(conn &Connection, a Value, b Value) !Value { } fn binary_numeric_plus_numeric(conn &Connection, a Value, b Value) !Value { - return new_numeric_value(f64_string(a.as_f64()! + b.as_f64()!)) + return new_numeric_value(f64_string(a.as_f64()! + b.as_f64()!, 64)) } fn binary_numeric_minus_numeric(conn &Connection, a Value, b Value) !Value { - return new_numeric_value(f64_string(a.as_f64()! - b.as_f64()!)) + return new_numeric_value(f64_string(a.as_f64()! - b.as_f64()!, 64)) } fn binary_numeric_multiply_numeric(conn &Connection, a Value, b Value) !Value { - return new_numeric_value(f64_string(a.as_f64()! * b.as_f64()!)) + return new_numeric_value(f64_string(a.as_f64()! * b.as_f64()!, 64)) } fn binary_numeric_divide_numeric(conn &Connection, a Value, b Value) !Value { @@ -194,7 +194,7 @@ fn binary_numeric_divide_numeric(conn &Connection, a Value, b Value) !Value { return sqlstate_22012() // division by zero } - return new_numeric_value(f64_string(a.as_f64()! / b.as_f64()!)) + return new_numeric_value(f64_string(a.as_f64()! / b.as_f64()!, 64)) } fn binary_smallint_plus_smallint(conn &Connection, a Value, b Value) !Value { diff --git a/vsql/query_cache.v b/vsql/query_cache.v index 084cd69..b26700d 100644 --- a/vsql/query_cache.v +++ b/vsql/query_cache.v @@ -71,7 +71,9 @@ fn (q QueryCache) prepare_stmt(tokens []Token) (string, map[string]Value, []Toke if !ignore { match token.kind { .literal_number { - v := new_numeric_value(token.value) + // This should never fail as the value is already well formed, but we + // have to satisfy the compiler with an "or". + v := numeric_literal(token.value) or { panic(err) } params['P${i}'] = v key += ':P${i} ' diff --git a/vsql/std_literal.v b/vsql/std_literal.v index 152d2e1..79b0c64 100644 --- a/vsql/std_literal.v +++ b/vsql/std_literal.v @@ -1,6 +1,7 @@ module vsql import math.big +import math // ISO/IEC 9075-2:2016(E), 5.3, // @@ -15,7 +16,7 @@ import math.big //~ | //~ //~ /* Value */ ::= -//~ -> signed_numeric_literal_1 +//~ //~ | //~ //~ /* Value */ ::= @@ -27,13 +28,14 @@ import math.big //~ ^string //~ //~ /* Value */ ::= -//~ -> signed_numeric_literal_1 +//~ //~ | -> signed_numeric_literal_2 //~ -//~ /* string */ ::= +//~ /* Value */ ::= //~ +//~ | //~ -//~ /* string */ ::= +//~ /* Value */ ::= //~ -> exact_numeric_literal_1 //~ | -> exact_numeric_literal_2 //~ | -> exact_numeric_literal_3 @@ -43,6 +45,19 @@ import math.big //~ //~ | //~ +//~ /* Value */ ::= +//~ E -> approximate_numeric_literal +//~ +//~ /* Value */ ::= +//~ +//~ +//~ /* Value */ ::= +//~ +//~ +//~ /* Value */ ::= +//~ -> signed_integer_1 +//~ | -> signed_integer_2 +//~ //~ /* string */ ::= //~ ^integer //~ @@ -74,20 +89,20 @@ import math.big //~ | FALSE -> false //~ | UNKNOWN -> unknown -fn parse_exact_numeric_literal_1(x string) !string { - return x +fn parse_exact_numeric_literal_1(x string) !Value { + return numeric_literal(x) } -fn parse_exact_numeric_literal_2(a string) !string { - return '${a}.' +fn parse_exact_numeric_literal_2(a string) !Value { + return numeric_literal('${a}.') } -fn parse_exact_numeric_literal_3(a string, b string) !string { - return '${a}.${b}' +fn parse_exact_numeric_literal_3(a string, b string) !Value { + return numeric_literal('${a}.${b}') } -fn parse_exact_numeric_literal_4(a string) !string { - return '0.${a}' +fn parse_exact_numeric_literal_4(a string) !Value { + return numeric_literal('0.${a}') } fn parse_date_literal(v Value) !Value { @@ -107,7 +122,9 @@ fn numeric_literal(x string) !Value { // treated as a NUMERIC. if x.contains('.') { // The trim handles cases of "123." which should be treated as "123". - return new_numeric_value(x.trim_right('.')) + // + // TODO(elliotchance): This needs to be new_numeric_value() once supported. + return new_double_precision_value(x.trim_right('.').f64()) } // Otherwise, we know this is an int but we have to choose the smallest type. @@ -133,10 +150,22 @@ fn numeric_literal(x string) !Value { return new_numeric_value(x) } -fn parse_signed_numeric_literal_1(v string) !Value { - return numeric_literal(v) +fn parse_signed_numeric_literal_2(sign string, v Value) !Value { + return numeric_literal(sign + v.str()) +} + +fn parse_signed_integer_1(v string) !Value { + return new_numeric_value(v) +} + +fn parse_signed_integer_2(sign string, v string) !Value { + if sign == '-' { + return new_numeric_value('-' + v) + } + + return new_numeric_value(v) } -fn parse_signed_numeric_literal_2(sign string, v string) !Value { - return numeric_literal(sign + v) +fn parse_approximate_numeric_literal(mantissa Value, exponent Value) !Value { + return new_double_precision_value(mantissa.as_f64()! * math.pow(10, exponent.as_f64()!)) } diff --git a/vsql/std_numeric_value_expression.v b/vsql/std_numeric_value_expression.v index b2ed44e..1219d7f 100644 --- a/vsql/std_numeric_value_expression.v +++ b/vsql/std_numeric_value_expression.v @@ -220,11 +220,11 @@ fn eval_binary(mut conn Connection, data Row, x Value, op string, y Value, param // ISO/IEC 9075-2:2016(E), 6.29, mut key := '${left.typ.typ} ${op} ${right.typ.typ}' if left.typ.typ.is_number() && right.typ.typ.is_number() { - supertype := most_specific_value(left, right) or { + supertype := most_specific_type(left.typ, right.typ) or { return sqlstate_42883('operator does not exist: ${key}') } - left = cast_numeric(mut conn, cast_approximate(left, supertype.typ)!, supertype)! - right = cast_numeric(mut conn, cast_approximate(right, supertype.typ)!, supertype)! + left = cast(mut conn, '', left, supertype)! + right = cast(mut conn, '', right, supertype)! key = '${supertype.typ} ${op} ${supertype.typ}' } @@ -235,30 +235,3 @@ fn eval_binary(mut conn Connection, data Row, x Value, op string, y Value, param return sqlstate_42883('operator does not exist: ${key}') } - -fn cast_approximate(v Value, want SQLType) !Value { - if v.typ.typ == .is_numeric && v.typ.size == 0 { - match want { - .is_double_precision { - return new_double_precision_value(v.as_f64()!) - } - .is_real { - return new_real_value(f32(v.as_f64()!)) - } - .is_bigint { - return new_bigint_value(i64(v.as_f64()!)) - } - .is_smallint { - return new_smallint_value(i16(v.as_f64()!)) - } - .is_integer { - return new_integer_value(int(v.as_f64()!)) - } - else { - // Let it fall through. - } - } - } - - return v -} diff --git a/vsql/type.v b/vsql/type.v index 659654b..3c699b6 100644 --- a/vsql/type.v +++ b/vsql/type.v @@ -82,64 +82,62 @@ fn (t SQLType) is_datetime() bool { } // A greater supertype means a literal value is allowed to be cast up to it. -// This number is only relevent when multiple values are os the same is_* is -// true (ie. a boolean cannot be case up to an integer). -fn (t SQLType) supertype() i16 { +// However, you cannot cast between different categories. The same category and +// supertype does not mean that they are the same type - casting may still be +// required, +fn (t SQLType) supertype() (i16, i16) { return match t { - .is_boolean { 0 } - .is_character { 0 } - .is_varchar { 1 } - .is_smallint { 0 } - .is_integer { 1 } - .is_bigint { 2 } - .is_real { 3 } - .is_double_precision { 4 } - .is_numeric { 5 } - // We don't cast date times in this way so setting all to 0 prevents any - // kind of casting. - .is_date { 0 } - .is_time_without_time_zone { 0 } - .is_time_with_time_zone { 0 } - .is_timestamp_without_time_zone { 0 } - .is_timestamp_with_time_zone { 0 } - } -} - -// TODO(elliotchance): This can be removed when we don't need special handling -// for literals. -fn most_specific_value(v1 Value, v2 Value) !Type { - mut t1 := v1.typ - mut t2 := v2.typ - - if v1.typ.is_numeric_literal() { - t1 = if v1.str().contains('.') { - new_type('DOUBLE PRECISION', 0, 0) - } else { - new_type('INTEGER', 0, 0) + // Booleans are on their own. + .is_boolean { + 0, 0 } - } - - if v2.typ.is_numeric_literal() { - t2 = if v2.str().contains('.') { - new_type('DOUBLE PRECISION', 0, 0) - } else { - new_type('INTEGER', 0, 0) + // Character types are all equal but would be subject to limitations of the + // allowed size. + .is_character, .is_varchar { + 1, 0 + } + // Exact numeric types. + .is_smallint { + 2, 0 + } + .is_integer { + 2, 1 + } + .is_bigint { + 2, 2 + } + // Approximate numeric types. + .is_real { + 3, 0 + } + .is_double_precision { + 3, 1 + } + // Dates and times cannot be implicitly cast. + .is_date, .is_time_without_time_zone, .is_time_with_time_zone, + .is_timestamp_without_time_zone, .is_timestamp_with_time_zone { + 4, 0 + } + // Numeric isn't a storage type (yet), otherwise it would be (2, 3). + .is_numeric { + 5, 0 } } - - return most_specific_type(t1, t2) } fn most_specific_type(t1 Type, t2 Type) !Type { - if t1.typ.is_number() && t2.typ.is_number() { - if t1.typ.supertype() > t2.typ.supertype() { + t1_category, t1_supertype := t1.typ.supertype() + t2_category, t2_supertype := t2.typ.supertype() + + if t1_category == t2_category { + if t1_supertype > t2_supertype { return t1 } return t2 } - // TODO(elliotchance): Is this it the correct SQLSTATE? + // TODO(elliotchance): Is this the correct SQLSTATE? return sqlstate_42601('no supertype of both ${t1} and ${t2}') } diff --git a/vsql/value.v b/vsql/value.v index 737378a..ddc0b3e 100644 --- a/vsql/value.v +++ b/vsql/value.v @@ -214,18 +214,22 @@ pub fn new_date_value(ts string) !Value { } } -fn f64_string(x f64) string { - mut n := '${x:.6}' - if n.contains('e') { - return n - } +fn f64_string(x f64, bits i16) string { + mut s := if bits == 32 { '${x:.6}' } else { '${x:.12}' } - s := n.trim('.').split('.') - if s.len == 1 { - return s[0] + if s.contains('e') { + if s.contains('.') { + parts := s.split('e') + s = parts[0].trim_right('0') + 'e' + parts[1] + } + } else { + if s.contains('.') { + s = s.trim_right('0') + } + s += 'e0' } - return '${s[0]}.${s[1].trim_right('0')}' + return s } // as_int() is not safe to use if the value is not numeric. It is used in cases @@ -315,8 +319,11 @@ pub fn (v Value) str() string { .is_boolean { v.bool_value().str() } - .is_double_precision, .is_real { - f64_string(v.f64_value()) + .is_double_precision { + f64_string(v.f64_value(), 64) + } + .is_real { + f64_string(v.f64_value(), 32) } .is_bigint, .is_integer, .is_smallint { v.int_value().str()