Changeset: 97d3eed41b29 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/97d3eed41b29
Added Files:
        sql/test/BugTracker-2026/Tests/8037-alter-table-decimal.test
Modified Files:
        gdk/gdk_atoms.c
        gdk/gdk_calc_convert.c
        gdk/gdk_private.h
        sql/test/BugTracker-2026/Tests/All
Branch: Dec2025
Log Message:

Implement converting decimals to and from strings in BATconvert.
Fixes #8037.


diffs (truncated from 560 to 300 lines):

diff --git a/gdk/gdk_atoms.c b/gdk/gdk_atoms.c
--- a/gdk/gdk_atoms.c
+++ b/gdk/gdk_atoms.c
@@ -671,8 +671,9 @@ static const struct maxdiv maxdiv[] = {
 };
 static const int maxmod10 = 7; /* (int) (maxdiv[0].maxval % 10) */
 
-static ssize_t
-numFromStr(allocator *ma, const char *src, size_t *len, void **dst, int tp, 
bool external)
+ssize_t
+numFromStr(allocator *ma, const char *src, size_t *len, void **dst, int tp,
+          uint8_t scale, uint8_t precision, bool external)
 {
        const char *p = src;
        size_t sz = ATOMsize(tp);
@@ -682,6 +683,9 @@ numFromStr(allocator *ma, const char *sr
        lng base = 0;
 #endif
        int sign = 1;
+       bool dotseen = false;
+       uint8_t fracdigs = 0;
+       uint8_t digits = 0;
 
        /* a valid number has the following syntax:
         * [-+]?[0-9]+(_[0-9]+)*([eE][0-9]+(_[0-9]+)*)?(LL)? -- PCRE syntax, or 
in other words
@@ -721,22 +725,41 @@ numFromStr(allocator *ma, const char *sr
                        p++;
                        break;
                }
+               if (scale > 0 && *p == '.') {
+                       p++;
+                       dotseen = true;
+               }
                if (!GDKisdigit(*p)) {
                        GDKerror("'%s' not a number\n", src);
                        goto bailout;
                }
        }
        do {
-               int dig = base10(*p);
-               if (base > maxdiv[1].maxval ||
-                   (base == maxdiv[1].maxval && dig > maxmod10)) {
-                       /* overflow */
-                       goto overflow;
+               /* read but ignore superfluous digits after decimal point */
+               if (scale == 0 || fracdigs < scale) {
+                       int dig = base10(*p);
+                       if (base > maxdiv[1].maxval ||
+                           (base == maxdiv[1].maxval && dig > maxmod10)) {
+                               /* overflow */
+                               goto overflow;
+                       }
+                       base = 10 * base + dig;
+                       if (precision > 0 && base != 0) {
+                               if (++digits > precision) {
+                                       /* too many digits */
+                                       goto overflow;
+                               }
+                       }
                }
-               base = 10 * base + dig;
                p++;
                if (*p == '_' && GDKisdigit(p[1]))
                        p++;
+               if (dotseen) {
+                       fracdigs++;
+               } else if (*p == '.' && scale > 0) {
+                       dotseen = true;
+                       p++;
+               }
        } while (GDKisdigit(*p));
        if ((*p == 'e' || *p == 'E') && GDKisdigit(p[1])) {
                p++;
@@ -765,6 +788,14 @@ numFromStr(allocator *ma, const char *sr
                        base *= maxdiv[exp].scale;
                }
        }
+       while (fracdigs < scale) {
+               if (base > maxdiv[1].maxval) {
+                       /* overflow */
+                       goto overflow;
+               }
+               base *= 10;
+               fracdigs++;
+       }
        base *= sign;
        switch (sz) {
        case 1: {
@@ -820,8 +851,12 @@ numFromStr(allocator *ma, const char *sr
   overflow:
        while (GDKisdigit(*p))
                p++;
-       GDKerror("overflow: \"%.*s\" does not fit in %s\n",
-                (int) (p - src), src, ATOMname(tp));
+       if (precision > 0)
+               GDKerror("overflow: \"%.*s\" does not fit in 
DECIMAL(%"PRIu8",%"PRIu8")\n",
+                        (int) (p - src), src, precision, scale);
+       else
+               GDKerror("overflow: \"%.*s\" does not fit in %s\n",
+                        (int) (p - src), src, ATOMname(tp));
   bailout:
        memcpy(*dst, ATOMnilptr(tp), sz);
        return -1;
@@ -830,32 +865,37 @@ numFromStr(allocator *ma, const char *sr
 ssize_t
 bteFromStr(allocator *ma, const char *src, size_t *len, bte **dst, bool 
external)
 {
-       return numFromStr(ma, src, len, (void **) dst, TYPE_bte, external);
+       return numFromStr(ma, src, len, (void **) dst, TYPE_bte, 0, 0,
+                         external);
 }
 
 ssize_t
 shtFromStr(allocator *ma, const char *src, size_t *len, sht **dst, bool 
external)
 {
-       return numFromStr(ma, src, len, (void **) dst, TYPE_sht, external);
+       return numFromStr(ma, src, len, (void **) dst, TYPE_sht, 0, 0,
+                         external);
 }
 
 ssize_t
 intFromStr(allocator *ma, const char *src, size_t *len, int **dst, bool 
external)
 {
-       return numFromStr(ma, src, len, (void **) dst, TYPE_int, external);
+       return numFromStr(ma, src, len, (void **) dst, TYPE_int, 0, 0,
+                         external);
 }
 
 ssize_t
 lngFromStr(allocator *ma, const char *src, size_t *len, lng **dst, bool 
external)
 {
-       return numFromStr(ma, src, len, (void **) dst, TYPE_lng, external);
+       return numFromStr(ma, src, len, (void **) dst, TYPE_lng, 0, 0,
+                         external);
 }
 
 #ifdef HAVE_HGE
 ssize_t
 hgeFromStr(allocator *ma, const char *src, size_t *len, hge **dst, bool 
external)
 {
-       return numFromStr(ma, src, len, (void **) dst, TYPE_hge, external);
+       return numFromStr(ma, src, len, (void **) dst, TYPE_hge, 0, 0,
+                         external);
 }
 #endif
 
@@ -934,51 +974,110 @@ mskRead(allocator *ma, msk *A, size_t *d
 }
 
 atom_io(bit, Bte, bte)
-
-atomtostr(bte, "%hhd", )
 atom_io(bte, Bte, bte)
-
-atomtostr(sht, "%hd", )
 atom_io(sht, Sht, sht)
+atom_io(int, Int, int)
+atom_io(lng, Lng, lng)
+#ifdef HAVE_HGE
+atom_io(hge, Hge, hge)
+#endif
 
-atomtostr(int, "%d", )
-atom_io(int, Int, int)
+ssize_t
+numToStr(allocator *ma, char **dst, size_t *len, const void *src,
+        int tp, uint8_t scale, bool external)
+{
+       char buf[128];
+       char *p = buf + sizeof(buf);
+#ifdef HAVE_HGE
+       hge val;
+#else
+       lng val;
+#endif
+       bool isnil;
 
-atomtostr(lng, LLFMT, )
-atom_io(lng, Lng, lng)
+       switch (tp) {
+       case TYPE_bte:
+               isnil = is_bte_nil(*(bte *) src);
+               val = *(bte *) src;
+               break;
+       case TYPE_sht:
+               isnil = is_sht_nil(*(sht *) src);
+               val = *(sht *) src;
+               break;
+       case TYPE_int:
+               isnil = is_int_nil(*(int *) src);
+               val = *(int *) src;
+               break;
+       case TYPE_lng:
+               isnil = is_lng_nil(*(lng *) src);
+               val = *(lng *) src;
+               break;
+#ifdef HAVE_HGE
+       case TYPE_hge:
+               isnil = is_hge_nil(*(hge *) src);
+               val = *(hge *) src;
+               break;
+#endif
+       default:
+               MT_UNREACHABLE();
+       }
+       if (isnil) {
+               p = external ? "nil" : (char *) str_nil;
+       } else {
+               bool neg = val < 0;
+               if (neg)
+                       val = -val;
+               *--p = '\0';
+               if (scale > 0) {
+                       do {
+                               *--p = '0' + val % 10;
+                               val /= 10;
+                       } while (--scale > 0);
+                       *--p = '.';
+               }
+               do {
+                       *--p = '0' + val % 10;
+                       val /= 10;
+               } while (val > 0);
+               if (neg)
+                       *--p = '-';
+       }
+       size_t sz = strlen(p);
+       atommem(sz);
+       strcpy(*dst, p);
+       return (ssize_t) sz;
+}
+
+ssize_t
+bteToStr(allocator *ma, char **dst, size_t *len, const bte *src, bool external)
+{
+       return numToStr(ma, dst, len, src, TYPE_bte, 0, external);
+}
+
+ssize_t
+shtToStr(allocator *ma, char **dst, size_t *len, const sht *src, bool external)
+{
+       return numToStr(ma, dst, len, src, TYPE_sht, 0, external);
+}
+
+ssize_t
+intToStr(allocator *ma, char **dst, size_t *len, const int *src, bool external)
+{
+       return numToStr(ma, dst, len, src, TYPE_int, 0, external);
+}
+
+ssize_t
+lngToStr(allocator *ma, char **dst, size_t *len, const lng *src, bool external)
+{
+       return numToStr(ma, dst, len, src, TYPE_lng, 0, external);
+}
 
 #ifdef HAVE_HGE
-#define HGE_LL018FMT "%018" PRId64
-#define HGE_LL18DIGITS LL_CONSTANT(1000000000000000000)
-#define HGE_ABS(a) (((a) < 0) ? -(a) : (a))
 ssize_t
 hgeToStr(allocator *ma, char **dst, size_t *len, const hge *src, bool external)
 {
-       atommem(hgeStrlen);
-       if (is_hge_nil(*src)) {
-               if (external) {
-                       assert(*len >= strlen("nil") + 1);
-                       strcpy(*dst, "nil");
-                       return 3;
-               }
-               assert(*len >= strlen(str_nil) + 1);
-               strcpy(*dst, str_nil);
-               return 1;
-       }
-       if ((hge) GDK_lng_min <= *src && *src <= (hge) GDK_lng_max) {
-               lng s = (lng) *src;
-               return lngToStr(ma, dst, len, &s, external);
-       } else {
-               hge s = *src / HGE_LL18DIGITS;
-               ssize_t llen = hgeToStr(ma, dst, len, &s, external);
-               if (llen < 0)
-                       return llen;
-               snprintf(*dst + llen, *len - llen, HGE_LL018FMT,
-                        (lng) HGE_ABS(*src % HGE_LL18DIGITS));
-               return strlen(*dst);
-       }
+       return numToStr(ma, dst, len, src, TYPE_hge, 0, external);
 }
-atom_io(hge, Hge, hge)
 #endif
 
 ssize_t
diff --git a/gdk/gdk_calc_convert.c b/gdk/gdk_calc_convert.c
--- a/gdk/gdk_calc_convert.c
+++ b/gdk/gdk_calc_convert.c
@@ -656,7 +656,8 @@ convertimpl_msk(flt)
 convertimpl_msk(dbl)
 
 static BUN
-convert_any_str(BATiter *bi, BAT *bn, struct canditer *restrict ci)
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to