Changeset: 8d706ac54502 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=8d706ac54502
Modified Files:
        sql/server/rel_optimizer.c
Branch: Oct2020
Log Message:

Import decimal fixes from June.


diffs (truncated from 562 to 300 lines):

diff --git a/sql/backends/monet5/sql_result.c b/sql/backends/monet5/sql_result.c
--- a/sql/backends/monet5/sql_result.c
+++ b/sql/backends/monet5/sql_result.c
@@ -1888,6 +1888,8 @@ get_print_width(int mtype, sql_class ecl
                count = 1 + digits;
                if (scale > 0)
                        count += 1;
+               if (scale == digits) // for preceding 0, e.g. 0.
+                       count += 1;
                return count;
        } else if (eclass == EC_DATE) {
                return 10;
diff --git a/sql/backends/monet5/sql_round_impl.h 
b/sql/backends/monet5/sql_round_impl.h
--- a/sql/backends/monet5/sql_round_impl.h
+++ b/sql/backends/monet5/sql_round_impl.h
@@ -310,31 +310,23 @@ static inline str
 str_2dec_body(TYPE *res, const str val, const int d, const int sc)
 {
        char *s = val;
-       char *dot, *end;
        int digits;
        int scale;
        BIG value;
 
-       dot = strchr(s, '.');
-       if (dot != NULL) {
-               s = strip_extra_zeros(s);
-               digits = _strlen(s) - 1;
-               scale = _strlen(dot + 1);
-       } else {
-               digits = _strlen(s);
-               scale = 0;
-       }
-       end = NULL;
+       if (d < 0 || d >= (int) (sizeof(scales) / sizeof(scales[0])))
+               throw(SQL, STRING(TYPE), SQLSTATE(42000) "Decimal (%s) doesn't 
have format (%d.%d)", s, d, sc);
+
+       int has_errors;
        value = 0;
 
-       if (digits < 0)
-               throw(SQL, STRING(TYPE), SQLSTATE(42000) "Decimal (%s) doesn't 
have format (%d.%d)", s, d, sc);
-       if (d < 0 || (size_t) d >= sizeof(scales) / sizeof(scales[0]))
+       // s = strip_extra_zeros(s);
+
+       value = decimal_from_str(s, &digits, &scale, &has_errors);
+       if (has_errors)
                throw(SQL, STRING(TYPE), SQLSTATE(42000) "Decimal (%s) doesn't 
have format (%d.%d)", s, d, sc);
 
-       value = decimal_from_str(s, &end);
-       if (*s == '+' || *s == '-')
-               digits--;
+       // handle situations where the de facto scale is different from the 
formal scale.
        if (scale < sc) {
                /* the current scale is too small, increase it by adding 0's */
                int dff = sc - scale;   /* CANNOT be 0! */
@@ -364,7 +356,7 @@ str_2dec_body(TYPE *res, const str val, 
                if (value >= scales[d] || value <= -scales[d])
                        throw(SQL, STRING(TYPE), SQLSTATE(42000) "Rounding of 
decimal (%s) doesn't fit format (%d.%d)", s, d, sc);
        }
-       if (value <= -scales[d] || value >= scales[d]  || *end)
+       if (value <= -scales[d] || value >= scales[d])
                throw(SQL, STRING(TYPE), SQLSTATE(42000) "Decimal (%s) doesn't 
have format (%d.%d)", s, d, sc);
        *res = (TYPE) value;
        return MAL_SUCCEED;
diff --git a/sql/server/rel_optimizer.c b/sql/server/rel_optimizer.c
--- a/sql/server/rel_optimizer.c
+++ b/sql/server/rel_optimizer.c
@@ -6406,7 +6406,7 @@ rel_push_project_up(visitor *v, sql_rel 
                           Check if they can be pushed up, ie are they not
                           changing or introducing any columns used
                           by the upper operator. */
-
+ 
                        exps = new_exp_list(v->sql->sa);
                        for (n = l->exps->h; n; n = n->next) {
                                sql_exp *e = n->data;
diff --git a/sql/server/sql_decimal.c b/sql/server/sql_decimal.c
--- a/sql/server/sql_decimal.c
+++ b/sql/server/sql_decimal.c
@@ -10,46 +10,103 @@
 
 #include "sql_decimal.h"
 
-#ifdef HAVE_HGE
-hge
-#else
-lng
-#endif
-decimal_from_str(char *dec, char **end)
+
+DEC_TPE
+decimal_from_str(char *dec, int* digits, int* scale, int* has_errors)
 {
+
 #ifdef HAVE_HGE
-       hge res = 0;
-       const hge max0 = GDK_hge_max / 10, max1 = GDK_hge_max % 10;
+    const hge max0 = GDK_hge_max / 10, max1 = GDK_hge_max % 10;
 #else
-       lng res = 0;
-       const lng max0 = GDK_lng_max / 10, max1 = GDK_lng_max % 10;
+    const lng max0 = GDK_lng_max / 10, max1 = GDK_lng_max % 10;
 #endif
-       int neg = 0, seen_dot = 0;
+
+       assert(digits);
+       assert(scale);
+       assert(has_errors);
 
+       DEC_TPE res = 0;
+       *has_errors = 0;
+
+       int _digits     = 0;
+       int _scale      = 0;
+
+// preceding whitespace:
+       int neg = 0;
        while(isspace((unsigned char) *dec))
                dec++;
+
+// optional sign:
        if (*dec == '-') {
                neg = 1;
                dec++;
        } else if (*dec == '+') {
                dec++;
        }
-       for (; *dec && (isdigit((unsigned char) *dec) || *dec == '.'); dec++) {
-               if (*dec != '.') {
-                       if (res > max0 || (res == max0 && *dec - '0' > max1))
-                               break;
-                       res *= 10;
-                       res += *dec - '0';
-               } else if (seen_dot) {
-                       break; /* dot cannot appear twice */
-               } else {
-                       seen_dot = 1;
+
+// optional fractional separator first opportunity
+       if (*dec == '.') {  // case: (+|-).456
+fractional_sep_first_opp:
+               dec++;
+               goto trailing_digits;
+       }
+
+// preceding_digits:
+       if (!isdigit((unsigned char) *dec)) {
+               *has_errors = 1;
+               goto end_state;
+       }
+       while (*dec == '0'){
+               // skip leading zeros in preceding digits, e.g. '0004563.1234' 
=> '4563.1234'
+               dec++;
+               if (*dec == '.') {
+                       _digits = 1; // case: 0.xyz the zero. the single 
preceding zero counts for one digit by convention.
+                       goto fractional_sep_first_opp;
                }
        }
+       for (; *dec && (isdigit((unsigned char) *dec)); dec++) {
+               if (res > max0 || (res == max0 && *dec - '0' > max1)) {
+                       *has_errors = 1;
+                       return 0;
+               }
+               res *= 10;
+               res += *dec - '0';
+               _digits++;
+       }
+
+// optional fractional separator second opportunity
+       if (*dec == '.')        // case: (+|-)123.(456)
+               dec++;
+       else                                    // case:  (+|-)123
+               goto trailing_whitespace;
+
+trailing_digits:
+       if (!isdigit((unsigned char) *dec))
+               goto trailing_whitespace;
+       for (; *dec && (isdigit((unsigned char) *dec)); dec++) {
+               if (res > max0 || (res == max0 && *dec - '0' > max1)) {
+                       *has_errors = 1;
+                       return 0;
+               }
+               res *= 10;
+               res += *dec - '0';
+               _scale++;
+       }
+       _digits += _scale;
+
+trailing_whitespace:
        while(isspace((unsigned char) *dec))
                dec++;
-       if (end)
-               *end = dec;
+
+end_state:
+       /* When the string cannot be parsed up to and including the null 
terminator,
+        * the string is an invalid decimal representation. */
+       if (*dec != 0)
+               *has_errors = 1;
+
+       *digits = _digits;
+       *scale = _scale;
+
        if (neg)
                return -res;
        else
diff --git a/sql/server/sql_decimal.h b/sql/server/sql_decimal.h
--- a/sql/server/sql_decimal.h
+++ b/sql/server/sql_decimal.h
@@ -14,19 +14,14 @@
 #include "gdk.h"
 
 #ifdef HAVE_HGE
-extern hge decimal_from_str(char *dec, char **end);
-extern char * decimal_to_str(sql_allocator *sa, hge v, sql_subtype *t);
+#define DEC_TPE hge
 #else
-extern lng decimal_from_str(char *dec, char **end);
-extern char * decimal_to_str(sql_allocator *sa, lng v, sql_subtype *t);
+#define DEC_TPE lng
 #endif
 
-#ifdef HAVE_HGE
-extern hge
-#else
-extern lng
-#endif
-scale2value(int scale);
+extern DEC_TPE decimal_from_str(char *dec, int* digits, int* scale, int* 
has_errors);
+extern char * decimal_to_str(sql_allocator *sa, DEC_TPE v, sql_subtype *t);
+DEC_TPE scale2value(int scale);
 
 #endif /* _SQL_DECIMAL_H */
 
diff --git a/sql/server/sql_parser.y b/sql/server/sql_parser.y
--- a/sql/server/sql_parser.y
+++ b/sql/server/sql_parser.y
@@ -4710,37 +4710,36 @@ literal:
                }
  |  INTNUM
                { char *s = sa_strdup(SA, $1);
-                 char *dot = strchr(s, '.');
-                 int digits = _strlen(s) - 1;
-                 int scale = digits - (int) (dot-s);
-                 sql_subtype t;
-
-                 if (digits <= 0)
-                       digits = 1;
-                 if (digits <= MAX_DEC_DIGITS) {
-#ifdef HAVE_HGE
-                       hge value = decimal_from_str(s, NULL);
-#else
-                       lng value = decimal_from_str(s, NULL);
-#endif
-
-                       if (*s == '+' || *s == '-')
-                               digits --;
-                       sql_find_subtype(&t, "decimal", digits, scale );
-                       $$ = _newAtomNode( atom_dec(SA, &t, value));
-                  } else {
-                       char *p = $1;
-                       double val;
-
-                       errno = 0;
-                       val = strtod($1,&p);
-                       if (p == $1 || is_dbl_nil(val) || (errno == ERANGE && 
(val < -1 || val > 1))) {
-                               sqlformaterror(m, SQLSTATE(22003) "Double value 
too large or not a number (%s)", $1);
-                               $$ = NULL;
-                               YYABORT;
+                       int digits;
+                       int scale;
+                       int has_errors;
+                       sql_subtype t;
+
+                       DEC_TPE value = decimal_from_str(s, &digits, &scale, 
&has_errors);
+
+                       if (!has_errors && digits <= MAX_DEC_DIGITS) {
+                               // The float-like value seems to fit in decimal 
storage
+                               sql_find_subtype(&t, "decimal", digits, scale );
+                               $$ = _newAtomNode( atom_dec(SA, &t, value));
                        }
-                       sql_find_subtype(&t, "double", 51, 0 );
-                       $$ = _newAtomNode(atom_float(SA, &t, val));
+                       else {
+                               /*
+                               * The float-like value either doesn't fit in 
integer decimal storage
+                               * or it is not a valid float representation.
+                               */
+                               char *p = $1;
+                               double val;
+
+                               errno = 0;
+                               val = strtod($1,&p);
+                               if (p == $1 || is_dbl_nil(val) || (errno == 
ERANGE && (val < -1 || val > 1))) {
+                                       sqlformaterror(m, SQLSTATE(22003) 
"Double value too large or not a number (%s)", $1);
+                                       $$ = NULL;
+                                       YYABORT;
+                               } else {
+                                       sql_find_subtype(&t, "double", 51, 0 );
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to