From 061e53a315b6930d367880a8804172e56f4c735c Mon Sep 17 00:00:00 2001 From: Elliot Chance Date: Sat, 30 Dec 2023 01:04:32 +0100 Subject: [PATCH] numbers: Swap NUMERIC and DECIMAL (#187) I made a mistake, confusing the names of NUMERIC vs DECIMAL. NUMERIC is actually the exact type and DECIMAL is the more relaxed type, rather than the other way around. For prosperity, the *only* difference between these two types in the SQL standard is: ISO/IEC 9075-2:2016(E), 6.1, , Syntax Rules: > 26) NUMERIC specifies the data type exact numeric, with the decimal > precision and scale specified by the and . > > 27) DECIMAL specifies the data type exact numeric, with the decimal > scale specified by the and the implementation-defined decimal > precision equal to or greater than the value of the specified > . I have seen this interpreted as: DECIMAL can use more precision (than specified) if it's more storage efficient or otherwise favorable for the engine to do so. This is the reasoning behind vsql's DECIMAL implementation of storing the raw fraction instead of scaling the number to the exact NUMERIC value. This also fixes: 1. All exact numeric types (DECIMAL, NUMERIC, SMALLINT, INTEGER and BIGINT) were being converted to approximate types for comparison (=, >, etc). They are now compared as exact values. 2. Nowhere does it say say in the standard that exact values should be zero padded to the precision (ie. 1.200 for NUMERIC(4, 3)). In fact, it actually specifies that when converting an exact type to a variable-length character string it should not do that: ISO/IEC 9075-2:2016(E), 6.13, , General Rules: > 12) a) i) Let YP be the shortest character string that conforms to the > definition of in Subclause 5.3, "", > whose scale is the same as the scale of SD and whose interpreted value > is the absolute value of SV. It could be argued that the string/display values do not need to follow this same specification as the situation is different. However, I think it's going to be less confusing long term if we treat these conversions the same everywhere. 3. Fixed a bug where casting a DECIMAL to DECIMAL of a higher precision would not result in more actual precision as expected. --- docs/numbers.rst | 72 ++++++++++++++--------------- tests/comparison.sql | 18 ++++++++ tests/decimal.sql | 56 +++++++++++++--------- tests/numeric.sql | 40 ++++++++-------- vsql/numeric.v | 22 +++++++-- vsql/operators.v | 4 ++ vsql/std_comparison_predicate.v | 24 ++++++++-- vsql/std_numeric_value_expression.v | 5 -- vsql/std_store_assignment.v | 2 +- vsql/type.v | 13 ++++-- vsql/value.v | 10 +--- 11 files changed, 164 insertions(+), 102 deletions(-) diff --git a/docs/numbers.rst b/docs/numbers.rst index c24ecb1..b9da824 100644 --- a/docs/numbers.rst +++ b/docs/numbers.rst @@ -211,8 +211,8 @@ and precision. Both store their respective values as fractions. For example, ``1.23`` could be represented as ``123/100``. The main difference between these two types comes down to the allowed -denominators. In short, a ``NUMERIC`` may have any denominator, whereas a -``DECIMAL`` must have a denominator of exactly 10^scale. This can also be +denominators. In short, a ``DECIMAL`` may have any denominator, whereas a +``NUMERIC`` must have a denominator of exactly 10^scale. This can also be expressed as: .. list-table:: @@ -222,31 +222,31 @@ expressed as: - Numerator - Denominator - * - ``NUMERIC(scale, precision)`` + * - ``DECIMAL(scale, precision)`` - ± 10^scale (exclusive) - ± 10^scale (exclusive) - * - ``DECIMAL(scale, precision)`` + * - ``NUMERIC(scale, precision)`` - ± 10^scale (exclusive) - 10^scale -When calculations are performed on a ``DECIMAL``, the result from each operation +When calculations are performed on a ``NUMERIC``, the result from each operation will be normalized to always satisfy this constraint. -This means that a ``DECIMAL`` is always exact at the scale and precision +This means that a ``NUMERIC`` is always exact at the scale and precision specified and casting to a higher precision will not alter the value. In -contrast, a ``NUMERIC`` promises to have *at least* the precision specified but +contrast, a ``DECIMAL`` promises to have *at least* the precision specified but the value may change as to be more exact if the precision is increased. This is best understood with some examples: .. code-block:: sql - VALUES CAST(1.23 AS DECIMAL(3,2)) / CAST(5 AS DECIMAL) * CAST(5 AS DECIMAL); - -- 1.20 + VALUES CAST(1.23 AS NUMERIC(3,2)) / CAST(5 AS NUMERIC) * CAST(5 AS NUMERIC); + -- 1.2 Because: -1. ``1.23 AS DECIMAL(3,2)`` -> ``123/100`` +1. ``1.23 AS NUMERIC(3,2)`` -> ``123/100`` 2. Normalize denominator -> ``123/100`` 3. Divide by ``5`` -> ``123/500`` 4. Normalize denominator -> ``24/100`` @@ -257,63 +257,63 @@ Whereas, .. code-block:: sql - VALUES CAST(1.23 AS NUMERIC(3,2)) / 5 * 5; + VALUES CAST(1.23 AS DECIMAL(3,2)) / 5 * 5; -- 1.23 Because: -1. ``1.23 AS NUMERIC(3,2)`` -> ``123/100`` +1. ``1.23 AS DECIMAL(3,2)`` -> ``123/100`` 2. Divide by ``5`` -> ``123/500`` 3. Multiply by ``5`` -> ``615/500`` -This may seem like the only difference is that ``NUMERIC`` does not normalize +This may seem like the only difference is that ``DECIMAL`` does not normalize the denominator, but actually they both need to normalize a denominator that would be out of bounds. Consider the example: .. code-block:: sql - VALUES CAST(1.23 AS NUMERIC(3,2)) / 11; + VALUES CAST(1.23 AS DECIMAL(3,2)) / 11; -- 0.11 -1. ``1.23 AS NUMERIC(3,2)`` -> ``123/100`` +1. ``1.23 AS DECIMAL(3,2)`` -> ``123/100`` 2. Divide by ``11`` -> ``123/1100`` 3. Denominator is out of bounds as it cannot be larger than 100. Highest precision equivalent would be -> ``11/100`` -This the same process and result that a ``DECIMAL`` that the equivalent decimal +This the same process and result that a ``NUMERIC`` that the equivalent decimal operation. Casting to higher precision might result in a different value for -``NUMERIC`` values, for example: +``DECIMAL`` values, for example: .. code-block:: sql - VALUES CAST(CAST(5 AS NUMERIC(3,2)) / CAST(7 AS NUMERIC(5,4)) AS NUMERIC(5,4)); + VALUES CAST(CAST(5 AS DECIMAL(3,2)) / CAST(7 AS DECIMAL(5,4)) AS DECIMAL(5,4)); -- 0.7142 Because: -1. ``5 AS NUMERIC(3,2)`` -> ``5/1`` +1. ``5 AS DECIMAL(3,2)`` -> ``5/1`` 2. Divide by ``7`` -> ``5/7`` -3. Cast to ``NUMERIC(5,4)`` -> ``5/7`` +3. Cast to ``DECIMAL(5,4)`` -> ``5/7`` 4. Formatted result based on 4 precision -> ``0.7142`` .. code-block:: sql - VALUES CAST(CAST(5 AS DECIMAL(3,2)) / CAST(7 AS DECIMAL) AS DECIMAL(5,4)); + VALUES CAST(CAST(5 AS NUMERIC(3,2)) / CAST(7 AS NUMERIC) AS NUMERIC(5,4)); -- 0.7100 Because: -1. ``5 AS DECIMAL(3,2)`` -> ``500/100`` +1. ``5 AS NUMERIC(3,2)`` -> ``500/100`` 2. Divide by ``7`` -> ``500/700`` 3. Normalize denominator -> ``71/100`` -4. Cast to ``DECIMAL(5,4)`` -> ``7100/10000`` +4. Cast to ``NUMERIC(5,4)`` -> ``7100/10000`` 5. Formatted result based on 4 precision -> ``0.7100`` Operations Between Exact Types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Arithmetic operations can only be performed when both operates are the same -fundamental types, ``NUMERIC`` or ``DECIMAL``, although they do not need to +fundamental types, ``DECIMAL`` or ``NUMERIC``, although they do not need to share the same scale or precision. .. list-table:: @@ -322,27 +322,27 @@ share the same scale or precision. * - Operation - Result Type - * - ``NUMERIC(s1, s2) + NUMERIC(s2, p2)`` - - ``NUMERIC(MAX(s1, s2), MIN(p1, p2))`` + * - ``T(s1, s2) + T(s2, p2)`` + - ``T(MAX(s1, s2), MIN(p1, p2))`` - * - ``NUMERIC(s1, s2) - NUMERIC(s2, p2)`` - - ``NUMERIC(MAX(s1, s2), MIN(p1, p2))`` + * - ``T(s1, s2) - T(s2, p2)`` + - ``T(MAX(s1, s2), MIN(p1, p2))`` - * - ``NUMERIC(s1, s2) * NUMERIC(s2, p2)`` - - ``NUMERIC(s1 * s2, p1 + p2)`` + * - ``T(s1, s2) * T(s2, p2)`` + - ``T(s1 * s2, p1 + p2)`` - * - ``NUMERIC(s1, s2) / NUMERIC(s2, p2)`` - - ``NUMERIC(s1 * s2, p1 + p2)`` + * - ``T(s1, s2) / T(s2, p2)`` + - ``T(s1 * s2, p1 + p2)`` Examples: .. code-block:: sql - VALUES CAST(10.24 AS NUMERIC(4,2)) + CAST(12.123 AS NUMERIC(8,3)); - -- 22.36 as NUMERIC(32, 3) + VALUES CAST(10.24 AS DECIMAL(4,2)) + CAST(12.123 AS DECIMAL(8,3)); + -- 22.36 as DECIMAL(32, 3) - VALUES CAST(10.24 AS NUMERIC(4,2)) * CAST(12.123 AS NUMERIC(8,3)); - -- 124.13952 as NUMERIC(32, 5) + VALUES CAST(10.24 AS DECIMAL(4,2)) * CAST(12.123 AS DECIMAL(8,3)); + -- 124.13952 as DECIMAL(32, 5) Casting ------- diff --git a/tests/comparison.sql b/tests/comparison.sql index 8e978bd..6b5081a 100644 --- a/tests/comparison.sql +++ b/tests/comparison.sql @@ -33,3 +33,21 @@ VALUES 1 <= 2; VALUES 1 <= 1; -- COL1: TRUE + +VALUES 1.0 = 1; +-- COL1: TRUE + +VALUES CAST(1.0 AS NUMERIC(5, 3)) = CAST(1 AS NUMERIC(5, 4)); +-- COL1: TRUE + +VALUES CAST(1.0 AS DECIMAL(5, 3)) = CAST(1 AS DECIMAL(5, 4)); +-- COL1: TRUE + +VALUES CAST(1.0 AS DECIMAL) = CAST(1 AS DECIMAL); +-- COL1: TRUE + +VALUES CAST(1.0 AS DECIMAL) = 1; +-- COL1: TRUE + +VALUES CAST(1 AS DECIMAL) = 1.0; +-- COL1: TRUE diff --git a/tests/decimal.sql b/tests/decimal.sql index 4f22906..f00d52c 100644 --- a/tests/decimal.sql +++ b/tests/decimal.sql @@ -17,7 +17,7 @@ SELECT -bar FROM foo; -- COL1: 1.24 VALUES CAST(1.23 AS DECIMAL(10, 6)); --- COL1: 1.230000 +-- COL1: 1.23 VALUES CAST(1.23 AS DECIMAL(10, 2)); -- COL1: 1.23 @@ -44,19 +44,19 @@ VALUES CAST(-12.34 AS DECIMAL(3, 2)); -- error 22003: numeric value out of range VALUES CAST(1.23 AS DECIMAL(6, 2)) + CAST(1.5 AS DECIMAL(6, 3)); --- COL1: 2.730 +-- COL1: 2.73 VALUES CAST(1.23 AS DECIMAL(6, 2)) - CAST(1.5 AS DECIMAL(6, 3)); --- COL1: -0.270 +-- COL1: -0.27 VALUES CAST(1.23 AS DECIMAL(6, 2)) - CAST(-1.5 AS DECIMAL(6, 3)); --- COL1: 2.730 +-- COL1: 2.73 VALUES CAST(1.23 AS DECIMAL(6, 2)) * CAST(1.5 AS DECIMAL(6, 3)); --- COL1: 1.84500 +-- COL1: 1.845 VALUES CAST(CAST(1.23 AS DECIMAL(6, 2)) * CAST(1.5 AS DECIMAL(6, 3)) AS DECIMAL(6, 4)); --- COL1: 1.8450 +-- COL1: 1.845 VALUES CAST(1.24 AS DECIMAL(6, 2)) / CAST(1.5 AS DECIMAL(6, 3)); -- COL1: 0.82666 @@ -67,29 +67,39 @@ VALUES CAST(1.24 AS DECIMAL(6, 3)) / CAST(1.5 AS DECIMAL(6, 2)); VALUES CAST(CAST(1.24 AS DECIMAL(6, 2)) / CAST(1.5 AS DECIMAL(6, 3)) AS DECIMAL(6, 4)); -- COL1: 0.8266 +/* types */ VALUES CAST(1.23 AS DECIMAL(3,2)) / 5; --- error 42883: operator does not exist: DECIMAL / SMALLINT +-- COL1: 0.24 (DECIMAL(3, 2)) + +/* types */ +VALUES CAST(CAST(1.23 AS DECIMAL(3,2)) / 5 AS DECIMAL(4, 3)); +-- COL1: 0.246 (DECIMAL(4, 3)) + +-- # This is an important case because it's described in detail in the docs for +-- # DECIMAL vs DECIMAL. +VALUES CAST(1.23 AS DECIMAL(3,2)) / 5 * 5; +-- COL1: 1.23 -- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(CAST(5 AS DECIMAL(3,2)) / CAST(7 AS DECIMAL) AS DECIMAL(5,4)); --- COL1: 0.7100 +-- # DECIMAL vs DECIMAL. +VALUES CAST(1.23 AS DECIMAL(3,2)) / 11; +-- COL1: 0.11 +-- # This is an important case because it's described in detail in the docs for +-- # DECIMAL vs DECIMAL. +VALUES CAST(CAST(5 AS DECIMAL(3,2)) / CAST(7 AS DECIMAL(5,4)) AS DECIMAL(5,4)); +-- COL1: 0.7142 + +/* types */ VALUES CAST(10.24 AS DECIMAL(4,2)) + CAST(12.123 AS DECIMAL(8,3)); --- COL1: 22.360 +-- COL1: 22.36 (DECIMAL(8, 3)) +/* types */ VALUES CAST(10.24 AS DECIMAL(4,2)) * CAST(12.123 AS DECIMAL(8,3)); --- COL1: 124.13952 +-- COL1: 124.13952 (DECIMAL(32, 5)) --- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(1.23 AS DECIMAL(3,2)) / CAST(5 AS DECIMAL) * CAST(5 AS DECIMAL); --- COL1: 1.20 +VALUES CAST(1 AS DECIMAL(2,1)) / CAST(3 AS DECIMAL(2,1)); +-- COL1: 0.33 -VALUES CAST(5 AS DECIMAL(3,2)) / CAST(7 AS DECIMAL(5,4)); --- COL1: 0.714285 - --- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(1.23 AS DECIMAL(3,2)) / CAST(5 AS DECIMAL) * CAST(5 AS DECIMAL); --- COL1: 1.20 +VALUES CAST(CAST(1 AS DECIMAL(2,1)) / CAST(3 AS DECIMAL(2,1)) AS DECIMAL(10, 8)); +-- COL1: 0.33333333 diff --git a/tests/numeric.sql b/tests/numeric.sql index 9b05074..661f8ea 100644 --- a/tests/numeric.sql +++ b/tests/numeric.sql @@ -77,28 +77,32 @@ VALUES CAST(CAST(1.24 AS NUMERIC(6, 2)) / CAST(1.5 AS NUMERIC(6, 3)) AS NUMERIC( VALUES CAST(1.23 AS NUMERIC(3,2)) / 5; -- COL1: 0.24 -VALUES CAST(CAST(1.23 AS NUMERIC(3,2)) / 5 AS NUMERIC(4, 3)); --- COL1: 0.246 - -- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(1.23 AS NUMERIC(3,2)) / 5 * 5; --- COL1: 1.23 +-- # NUMERIC vs NUMERIC. +VALUES CAST(CAST(5 AS NUMERIC(3,2)) / CAST(7 AS NUMERIC) AS NUMERIC(5,4)); +-- COL1: 0.71 + +VALUES CAST(10.24 AS NUMERIC(4,2)) + CAST(12.123 AS NUMERIC(8,3)); +-- COL1: 22.36 + +VALUES CAST(10.24 AS NUMERIC(4,2)) * CAST(12.123 AS NUMERIC(8,3)); +-- COL1: 124.13952 -- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(1.23 AS NUMERIC(3,2)) / 11; --- COL1: 0.11 +-- # NUMERIC vs NUMERIC. +VALUES CAST(1.23 AS NUMERIC(3,2)) / CAST(5 AS NUMERIC) * CAST(5 AS NUMERIC); +-- COL1: 1.2 + +VALUES CAST(5 AS NUMERIC(3,2)) / CAST(7 AS NUMERIC(5,4)); +-- COL1: 0.714285 -- # This is an important case because it's described in detail in the docs for --- # NUMERIC vs DECIMAL. -VALUES CAST(CAST(5 AS NUMERIC(3,2)) / CAST(7 AS NUMERIC(5,4)) AS NUMERIC(5,4)); --- COL1: 0.7142 +-- # NUMERIC vs NUMERIC. +VALUES CAST(1.23 AS NUMERIC(3,2)) / CAST(5 AS NUMERIC) * CAST(5 AS NUMERIC); +-- COL1: 1.2 -/* types */ -VALUES CAST(10.24 AS NUMERIC(4,2)) + CAST(12.123 AS NUMERIC(8,3)); --- COL1: 22.36 (NUMERIC(8, 3)) +VALUES CAST(1 AS NUMERIC(2,1)) / CAST(3 AS NUMERIC(2,1)); +-- COL1: 0.33 -/* types */ -VALUES CAST(10.24 AS NUMERIC(4,2)) * CAST(12.123 AS NUMERIC(8,3)); --- COL1: 124.13952 (NUMERIC(32, 5)) +VALUES CAST(CAST(1 AS NUMERIC(2,1)) / CAST(3 AS NUMERIC(2,1)) AS NUMERIC(10, 8)); +-- COL1: 0.33 diff --git a/vsql/numeric.v b/vsql/numeric.v index 765906c..498d353 100644 --- a/vsql/numeric.v +++ b/vsql/numeric.v @@ -298,13 +298,13 @@ fn (n Numeric) normalize_denominator(typ Type) Numeric { denominator := big.integer_from_int(10).pow(u32(typ.scale)) max_denominator := big.integer_from_int(10).pow(u32(typ.scale + 1)) - big.one_int - // NUMERICAL only need to scale when the denominator goes beyond the bounds. - if typ.typ == .is_numeric && n.denominator > max_denominator { + // DECIMAL only need to scale when the denominator goes beyond the bounds. + if typ.typ == .is_decimal && n.denominator > max_denominator { return n.scale_numerator(denominator) } - // DECIMAL always needs to have a fixed denominator. - if typ.typ == .is_decimal && n.denominator != denominator { + // NUMERIC always needs to have a fixed denominator. + if typ.typ == .is_numeric && n.denominator != denominator { return n.scale_numerator(denominator) } @@ -326,6 +326,20 @@ fn common_denominator(n1 Numeric, n2 Numeric) (Numeric, Numeric) { return n3, n4 } +fn (n Numeric) compare(n2 Numeric) CompareResult { + n3, n4 := common_denominator(n, n2) + + if n3.numerator < n4.numerator { + return .is_less + } + + if n3.numerator > n4.numerator { + return .is_greater + } + + return .is_equal +} + fn (n Numeric) equals(n2 Numeric) bool { n3, n4 := common_denominator(n, n2) diff --git a/vsql/operators.v b/vsql/operators.v index 3abb464..1967a7b 100644 --- a/vsql/operators.v +++ b/vsql/operators.v @@ -277,5 +277,9 @@ fn binary_decimal_multiply_decimal(conn &Connection, a Value, b Value) !Value { } fn binary_decimal_divide_decimal(conn &Connection, a Value, b Value) !Value { + if b.as_f64()! == 0 { + return sqlstate_22012() // division by zero + } + return new_decimal_value_from_numeric(a.numeric_value().divide(b.numeric_value())!) } diff --git a/vsql/std_comparison_predicate.v b/vsql/std_comparison_predicate.v index ffdd402..4143719 100644 --- a/vsql/std_comparison_predicate.v +++ b/vsql/std_comparison_predicate.v @@ -385,9 +385,27 @@ fn compare_datetimes(xv Value, yv Value) CompareResult { fn compare_numbers(xv Value, yv Value) !CompareResult { // 2) Numbers are compared with respect to their algebraic value. - // - // TODO(elliotchance): as_f64() will not be safe when NUMERIC and DECIMAL are - // supported, so special handling will be required. + if (xv.typ.typ == .is_decimal || xv.typ.typ == .is_numeric) + && (yv.typ.typ == .is_decimal || yv.typ.typ == .is_numeric) { + return xv.numeric_value().compare(yv.numeric_value()) + } + + if (xv.typ.typ == .is_smallint || xv.typ.typ == .is_integer || xv.typ.typ == .is_bigint) + && (yv.typ.typ == .is_smallint || yv.typ.typ == .is_integer || yv.typ.typ == .is_bigint) { + x := xv.int_value() + y := yv.int_value() + + if x < y { + return .is_less + } + + if x > y { + return .is_greater + } + + return .is_equal + } + x := xv.as_f64()! y := yv.as_f64()! diff --git a/vsql/std_numeric_value_expression.v b/vsql/std_numeric_value_expression.v index 6ac3cb9..80cd83a 100644 --- a/vsql/std_numeric_value_expression.v +++ b/vsql/std_numeric_value_expression.v @@ -214,11 +214,6 @@ fn eval_binary(mut conn Connection, data Row, x Value, op string, y Value, param mut left := x mut right := y - // There is a special case we need to deal with when using literals against - // other approximate types. - // - // TODO(elliotchance): This won't be needed when we properly implement - // 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_type(left.typ, right.typ) or { diff --git a/vsql/std_store_assignment.v b/vsql/std_store_assignment.v index ed3262c..096bdc2 100644 --- a/vsql/std_store_assignment.v +++ b/vsql/std_store_assignment.v @@ -394,7 +394,7 @@ fn cast_numeric(mut conn Connection, v Value, t Type) !Value { // There is a special case where we should pass through an already // NUMERIC/DECIMAL value if there is no specified destination precision. // This will prevent as_numeric() from truncating the extra NUMERIC - // precision. Technically this is not required for DECIMAL, but it's just + // precision. Technically this is not required for NUMERIC, but it's just // more efficient to avoid the reparsing below. if t.size == 0 && (v.typ.typ == .is_numeric || v.typ.typ == .is_decimal) { return v diff --git a/vsql/type.v b/vsql/type.v index 9c10b7d..b75ccb1 100644 --- a/vsql/type.v +++ b/vsql/type.v @@ -57,8 +57,13 @@ fn (t SQLType) str() string { fn (t SQLType) is_number() bool { return match t { - .is_bigint, .is_double_precision, .is_integer, .is_real, .is_smallint, .is_numeric { true } - else { false } + .is_bigint, .is_double_precision, .is_integer, .is_real, .is_smallint, .is_decimal, + .is_numeric { + true + } + else { + false + } } } @@ -106,10 +111,10 @@ fn (t SQLType) supertype() (i16, i16) { .is_bigint { 2, 2 } - .is_decimal { + .is_numeric { 2, 3 } - .is_numeric { + .is_decimal { 2, 4 } // Approximate numeric types. diff --git a/vsql/value.v b/vsql/value.v index 57825e2..0295d9f 100644 --- a/vsql/value.v +++ b/vsql/value.v @@ -196,9 +196,6 @@ pub fn new_numeric_value(x string) Value { // 12.00 -> DECIMAL(4, 2) // pub fn new_decimal_value(x string) Value { - // All the same rules for determining NUMERIC can be used for DECIMAL, - // including denoninator being a power of 10. We just need to change it to a - // DECIMAL type. n := new_numeric_from_string(x) typ := new_type('DECIMAL', n.typ.size, n.typ.scale) @@ -339,7 +336,7 @@ fn (v Value) as_numeric() !Numeric { return sqlstate_22003() } - if v.typ.typ == .is_numeric { + if v.typ.typ == .is_numeric || v.typ.typ == .is_decimal { return v.numeric_value() } @@ -387,10 +384,7 @@ pub fn (v Value) str() string { .is_timestamp_with_time_zone, .is_timestamp_without_time_zone { v.time_value().str() } - .is_decimal { - v.numeric_value().str() - } - .is_numeric { + .is_decimal, .is_numeric { s := v.numeric_value().str() if s.contains('.') { return s.trim_right('0').trim_right('.')