LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_result.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 792 1096 72.3 %
Date: 2024-10-07 21:21:43 Functions: 39 46 84.8 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * author N.J. Nes
      15             :  */
      16             : 
      17             : #include "monetdb_config.h"
      18             : #include "sql_result.h"
      19             : #include "str.h"
      20             : #include "tablet.h"
      21             : #include "gdk_time.h"
      22             : #include "bat/res_table.h"
      23             : #include "bat/bat_storage.h"
      24             : #include "rel_exp.h"
      25             : #include "sql_bincopyconvert.h"
      26             : 
      27             : #ifndef HAVE_LLABS
      28             : #define llabs(x)        ((x) < 0 ? -(x) : (x))
      29             : #endif
      30             : 
      31             : #ifdef _MSC_VER
      32             : /* use intrinsic functions on Windows */
      33             : #define short_int_SWAP(s)       ((short) _byteswap_ushort((unsigned short) (s)))
      34             : /* on Windows, long is the same size as int */
      35             : #define normal_int_SWAP(s)      ((int) _byteswap_ulong((unsigned long) (s)))
      36             : #define long_long_SWAP(s)       ((lng) _byteswap_uint64((unsigned __int64) (s)))
      37             : #else
      38             : #define short_int_SWAP(s) ((short)(((0x00ff&(s))<<8) | ((0xff00&(s))>>8)))
      39             : 
      40             : #define normal_int_SWAP(i) (((0x000000ff&(i))<<24) | ((0x0000ff00&(i))<<8) | \
      41             :                             ((0x00ff0000&(i))>>8)  | ((0xff000000&(i))>>24))
      42             : #define long_long_SWAP(l)                                               \
      43             :                 ((((lng)normal_int_SWAP(l))<<32) |                \
      44             :                  (0xffffffff&normal_int_SWAP(l>>32)))
      45             : #endif
      46             : 
      47             : #ifdef HAVE_HGE
      48             : #define huge_int_SWAP(h)                                                                \
      49             :                 ((((hge)long_long_SWAP(h))<<64) |                         \
      50             :                  (0xffffffffffffffff&long_long_SWAP(h>>64)))
      51             : #endif
      52             : #define DEC_TOSTR(TYPE)                                                         \
      53             :         do {                                                                                    \
      54             :                 char buf[64];                                                           \
      55             :                 TYPE v = *(const TYPE *) a;                                     \
      56             :                 int scale = (int) (ptrdiff_t) extra;            \
      57             :                 int cur = 63, i, done = 0;                                      \
      58             :                 int neg = v < 0;                                                     \
      59             :                 ssize_t l;                                                                      \
      60             :                 if (is_##TYPE##_nil(v)) {                                       \
      61             :                         if (*Buf == NULL || *len < 5){                       \
      62             :                                 GDKfree(*Buf);                                          \
      63             :                                 *len = 5;                                                       \
      64             :                                 *Buf = GDKzalloc(*len);                         \
      65             :                                 if (*Buf == NULL) {                                     \
      66             :                                         return -1;                                              \
      67             :                                 }                                                                       \
      68             :                         }                                                                               \
      69             :                         strcpy(*Buf, "NULL");                                 \
      70             :                         return 4;                                                               \
      71             :                 }                                                                                       \
      72             :                 if (v<0)                                                                     \
      73             :                         v = -v;                                                                 \
      74             :                 buf[cur--] = 0;                                                         \
      75             :                 if (scale){                                                                     \
      76             :                         for (i=0; i<scale; i++) {                            \
      77             :                                 buf[cur--] = (char) (v%10 + '0');       \
      78             :                                 v /= 10;                                                        \
      79             :                         }                                                                               \
      80             :                         buf[cur--] = '.';                                               \
      81             :                 }                                                                                       \
      82             :                 while (v) {                                                                     \
      83             :                         buf[cur--] = (char ) (v%10 + '0');              \
      84             :                         v /= 10;                                                                \
      85             :                         done = 1;                                                               \
      86             :                 }                                                                                       \
      87             :                 if (!done)                                                                      \
      88             :                         buf[cur--] = '0';                                               \
      89             :                 if (neg)                                                                        \
      90             :                         buf[cur--] = '-';                                               \
      91             :                 l = (64-cur-1);                                                         \
      92             :                 if (*Buf == NULL || (ssize_t) *len < l) {    \
      93             :                         GDKfree(*Buf);                                                  \
      94             :                         *len = (size_t) l+1;                                    \
      95             :                         *Buf = GDKzalloc(*len);                                 \
      96             :                         if (*Buf == NULL) {                                             \
      97             :                                 return -1;                                                      \
      98             :                         }                                                                               \
      99             :                 }                                                                                       \
     100             :                 strcpy(*Buf, buf+cur+1);                                        \
     101             :                 return l-1;                                                                     \
     102             :         } while (0)
     103             : 
     104             : static ssize_t
     105       21382 : dec_tostr(void *extra, char **Buf, size_t *len, int type, const void *a)
     106             : {
     107             :         /* support dec map to bte, sht, int and lng */
     108       21382 :         if (type == TYPE_bte) {
     109         377 :                 DEC_TOSTR(bte);
     110             :         } else if (type == TYPE_sht) {
     111        1385 :                 DEC_TOSTR(sht);
     112             :         } else if (type == TYPE_int) {
     113       20371 :                 DEC_TOSTR(int);
     114             :         } else if (type == TYPE_lng) {
     115       50782 :                 DEC_TOSTR(lng);
     116             : #ifdef HAVE_HGE
     117             :         } else if (type == TYPE_hge) {
     118       92797 :                 DEC_TOSTR(hge);
     119             : #endif
     120             :         } else {
     121           0 :                 GDKerror("Decimal cannot be mapped to %s\n", ATOMname(type));
     122             :         }
     123           0 :         return -1;
     124             : }
     125             : 
     126             : struct time_res {
     127             :         int fraction;
     128             :         int has_tz;
     129             :         lng timezone;
     130             : };
     131             : 
     132             : static ssize_t
     133        2617 : sql_time_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     134             : {
     135        2617 :         struct time_res *ts_res = TS_RES;
     136        2617 :         ssize_t len1;
     137        2617 :         size_t big = 128;
     138        2617 :         char buf1[128], *s1 = buf1, *s;
     139        2617 :         daytime tmp;
     140             : 
     141        2617 :         (void) type;
     142        2617 :         tmp = *(const daytime *) A;
     143        2617 :         if (ts_res->has_tz)
     144         200 :                 tmp = daytime_add_usec_modulo(tmp, ts_res->timezone * 1000);
     145             : 
     146        2617 :         len1 = daytime_precision_tostr(&s1, &big, tmp, ts_res->fraction, true);
     147        2617 :         if (len1 < 0)
     148             :                 return -1;
     149        2617 :         if (len1 == 3 && strcmp(s1, "nil") == 0) {
     150           0 :                 if (*len < 4 || *buf == NULL) {
     151           0 :                         GDKfree(*buf);
     152           0 :                         *buf = GDKzalloc(*len = 4);
     153           0 :                         if (*buf == NULL)
     154             :                                 return -1;
     155             :                 }
     156           0 :                 strcpy(*buf, "nil");
     157           0 :                 return len1;
     158             :         }
     159             : 
     160        2617 :         if (*buf == NULL || *len < (size_t) len1 + 8) {
     161           0 :                 GDKfree(*buf);
     162           0 :                 *buf = (str) GDKzalloc(*len = len1 + 8);
     163           0 :                 if (*buf == NULL) {
     164             :                         return -1;
     165             :                 }
     166             :         }
     167        2617 :         s = *buf;
     168        2617 :         strcpy(s, buf1);
     169        2617 :         s += len1;
     170             : 
     171        2617 :         if (ts_res->has_tz) {
     172         200 :                 lng timezone = llabs(ts_res->timezone / 60000);
     173         200 :                 s += sprintf(s, "%c%02d:%02d",
     174             :                              (ts_res->timezone >= 0) ? '+' : '-',
     175         200 :                              (int) (timezone / 60), (int) (timezone % 60));
     176             :         }
     177        2617 :         return (ssize_t) (s - *buf);
     178             : }
     179             : 
     180             : static ssize_t
     181        1606 : sql_timestamp_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     182             : {
     183        1606 :         struct time_res *ts_res = TS_RES;
     184        1606 :         ssize_t len1, len2;
     185        1606 :         size_t big = 128;
     186        1606 :         char buf1[128], buf2[128], *s, *s1 = buf1, *s2 = buf2;
     187        1606 :         timestamp tmp;
     188        1606 :         lng timezone = ts_res->timezone;
     189        1606 :         date days;
     190        1606 :         daytime usecs;
     191             : 
     192        1606 :         (void) type;
     193        1606 :         tmp = *(const timestamp *)A;
     194        1606 :         if (ts_res->has_tz) {
     195         526 :                 tmp = timestamp_add_usec(tmp, timezone * 1000);
     196             :         }
     197        1606 :         days = timestamp_date(tmp);
     198        1606 :         usecs = timestamp_daytime(tmp);
     199        1606 :         len1 = date_tostr(&s1, &big, &days, true);
     200        1606 :         len2 = daytime_precision_tostr(&s2, &big, usecs, ts_res->fraction, true);
     201        1606 :         if (len1 < 0 || len2 < 0) {
     202           0 :                 GDKfree(s1);
     203           0 :                 GDKfree(s2);
     204           0 :                 return -1;
     205             :         }
     206             : 
     207        1606 :         if ((len1 == 3 && strcmp(s1, "nil") == 0) ||
     208           0 :             (len2 == 3 && strcmp(s2, "nil") == 0)) {
     209           0 :                 if (*len < 4 || *buf == NULL) {
     210           0 :                         GDKfree(*buf);
     211           0 :                         *buf = GDKzalloc(*len = 4);
     212           0 :                         if (*buf == NULL)
     213             :                                 return -1;
     214             :                 }
     215           0 :                 strcpy(*buf, "nil");
     216           0 :                 return len1;
     217             :         }
     218             : 
     219        1606 :         if (*buf == NULL || *len < (size_t) len1 + (size_t) len2 + 8) {
     220           0 :                 GDKfree(*buf);
     221           0 :                 *buf = (str) GDKzalloc(*len = (size_t) (len1 + len2 + 8));
     222           0 :                 if (*buf == NULL) {
     223             :                         return -1;
     224             :                 }
     225             :         }
     226        1606 :         s = *buf;
     227        1606 :         strcpy(s, buf1);
     228        1606 :         s += len1;
     229        1606 :         *s++ = ' ';
     230        1606 :         strcpy(s, buf2);
     231        1606 :         s += len2;
     232        1606 :         s[0] = 0;
     233             : 
     234        1606 :         if (ts_res->has_tz) {
     235         526 :                 timezone = ts_res->timezone / 60000;
     236         526 :                 *s++ = (ts_res->timezone >= 0) ? '+' : '-';
     237         526 :                 sprintf(s, "%02d:%02d", (int) (llabs(timezone) / 60), (int) (llabs(timezone) % 60));
     238         526 :                 s += 5;
     239             :         }
     240        1606 :         return (ssize_t) (s - *buf);
     241             : }
     242             : 
     243             : static int
     244       97560 : bat_max_strlength(BAT *b)
     245             : {
     246       97560 :         BUN p, q;
     247       97560 :         int l = 0;
     248       97560 :         int max = 0;
     249       97560 :         BATiter bi = bat_iterator(b);
     250             : 
     251     4747698 :         BATloop(b, p, q) {
     252     4650138 :                 l = UTF8_strwidth((const char *) BUNtvar(bi, p));
     253             : 
     254     4650138 :                 if (is_int_nil(l))
     255      504986 :                         l = 0;
     256     4650138 :                 if (l > max)
     257             :                         max = l;
     258             :         }
     259       97560 :         bat_iterator_end(&bi);
     260       97560 :         return max;
     261             : }
     262             : 
     263             : #define bat_max_length(TPE, HIGH) \
     264             : static size_t \
     265             : bat_max_##TPE##length(BAT *b) \
     266             : { \
     267             :         BUN p, q; \
     268             :         HIGH max = 0, min = 0; \
     269             :         size_t ret = 0; \
     270             :         BATiter bi = bat_iterator(b); \
     271             :         const TPE *restrict vals = (const TPE *) bi.base; \
     272             :  \
     273             :         BATloop(b, p, q) { \
     274             :                 HIGH m = 0; \
     275             :                 TPE l = vals[p]; \
     276             :  \
     277             :                 if (!is_##TPE##_nil(l)) \
     278             :                         m = l; \
     279             :                 if (m > max) \
     280             :                         max = m; \
     281             :                 if (m < min) \
     282             :                         min = m; \
     283             :         } \
     284             :         bat_iterator_end(&bi); \
     285             :         if (-min > max / 10) { \
     286             :                 max = -min; \
     287             :                 ret++;          /* '-' */ \
     288             :         } \
     289             :         while (max /= 10) \
     290             :                 ret++; \
     291             :         ret++; \
     292             :         return ret; \
     293             : }
     294             : 
     295     1418348 : bat_max_length(bte, lng)
     296     1199205 : bat_max_length(sht, lng)
     297     5928971 : bat_max_length(int, lng)
     298      842360 : bat_max_length(lng, lng)
     299             : #ifdef HAVE_HGE
     300        5724 : bat_max_length(hge, hge)
     301             : #endif
     302             : 
     303             : #define DEC_FRSTR(X)                                                                                                    \
     304             :         do {                                                                                                                            \
     305             :                 sql_column *col = c->extra;                                                                          \
     306             :                 sql_subtype *t = &col->type;                                                                     \
     307             :                 unsigned int scale = t->scale;                                                                       \
     308             :                 unsigned int i;                                                                                                 \
     309             :                 bool neg = false;                                                                                               \
     310             :                 X *r;                                                                                                                   \
     311             :                 X res = 0;                                                                                                              \
     312             :                 while(isspace((unsigned char) *s))                                                              \
     313             :                         s++;                                                                                                            \
     314             :                 if (*s == '-'){                                                                                                 \
     315             :                         neg = true;                                                                                                     \
     316             :                         s++;                                                                                                            \
     317             :                 } else if (*s == '+'){                                                                                  \
     318             :                         s++;                                                                                                            \
     319             :                 }                                                                                                                               \
     320             :                 for (i = 0; *s && *s != c->decsep && ((res == 0 && *s == '0') || i < t->digits - t->scale); s++) { \
     321             :                         if (c->decskip && *s == c->decskip)                                                       \
     322             :                                 continue;                                                                                               \
     323             :                         if (!isdigit((unsigned char) *s))                                                       \
     324             :                                 break;                                                                                                  \
     325             :                         res *= 10;                                                                                                      \
     326             :                         res += (*s-'0');                                                                                        \
     327             :                         if (res)                                                                                                        \
     328             :                                 i++;                                                                                                    \
     329             :                 }                                                                                                                               \
     330             :                 if (*s == c->decsep) {                                                                                       \
     331             :                         s++;                                                                                                            \
     332             :                         while (*s && scale > 0) {                                                                    \
     333             :                                 if (isdigit((unsigned char) *s)) {                                              \
     334             :                                         res *= 10;                                                                                      \
     335             :                                         res += *s++ - '0';                                                                      \
     336             :                                         scale--;                                                                                        \
     337             :                                 } else if (c->decskip && *s == c->decskip) {                      \
     338             :                                         s++;                                                                                            \
     339             :                                 } else {                                                                                                \
     340             :                                         break;                                                                                          \
     341             :                                 }                                                                                                               \
     342             :                         }                                                                                                                       \
     343             :                 }                                                                                                                               \
     344             :                 while(*s && isspace((unsigned char) *s))                                                \
     345             :                         s++;                                                                                                            \
     346             :                 while (scale > 0) {                                                                                          \
     347             :                         res *= 10;                                                                                                      \
     348             :                         scale--;                                                                                                        \
     349             :                 }                                                                                                                               \
     350             :                 if (*s)                                                                                                                 \
     351             :                         return NULL;                                                                                            \
     352             :                 r = c->data;                                                                                                 \
     353             :                 if (r == NULL &&                                                                                                \
     354             :                     (r = GDKzalloc(sizeof(X))) == NULL)                                                 \
     355             :                         return NULL;                                                                                            \
     356             :                 c->data = r;                                                                                                 \
     357             :                 if (neg)                                                                                                                \
     358             :                         *r = -res;                                                                                                      \
     359             :                 else                                                                                                                    \
     360             :                         *r = res;                                                                                                       \
     361             :                 return (void *) r;                                                                                              \
     362             :         } while (0)
     363             : 
     364             : static void *
     365    63050165 : dec_frstr(Column *c, int type, const char *s)
     366             : {
     367    63050165 :         assert(c->decsep != '\0');
     368             : 
     369             :         /* support dec map to bte, sht, int and lng */
     370    63050165 :         if( strcmp(s,"nil")== 0)
     371             :                 return NULL;
     372    63050165 :         if (type == TYPE_bte) {
     373        8369 :                 DEC_FRSTR(bte);
     374             :         } else if (type == TYPE_sht) {
     375       21228 :                 DEC_FRSTR(sht);
     376             :         } else if (type == TYPE_int) {
     377   478119132 :                 DEC_FRSTR(int);
     378             :         } else if (type == TYPE_lng) {
     379     4295531 :                 DEC_FRSTR(lng);
     380             : #ifdef HAVE_HGE
     381             :         } else if (type == TYPE_hge) {
     382           0 :                 DEC_FRSTR(hge);
     383             : #endif
     384             :         }
     385             :         return NULL;
     386             : }
     387             : 
     388             : static void *
     389         183 : sec_frstr(Column *c, int type, const char *s)
     390             : {
     391             :         /* read a sec_interval value
     392             :          * this knows that the stored scale is always 3 */
     393         183 :         unsigned int i, neg = 0;
     394         183 :         lng *r;
     395         183 :         lng res = 0;
     396             : 
     397         183 :         (void) c;
     398         183 :         (void) type;
     399         183 :         assert(type == TYPE_lng);
     400             : 
     401         183 :         if (*s == '-') {
     402          10 :                 neg = 1;
     403          10 :                 s++;
     404         173 :         } else if (*s == '+') {
     405           0 :                 neg = 0;
     406           0 :                 s++;
     407             :         }
     408        1359 :         for (i = 0; i < (19 - 3) && *s && *s != c->decsep; i++, s++) {
     409        1176 :                 if (c->decskip && *s == c->decskip) {
     410           4 :                         i--;
     411           4 :                         continue;
     412             :                 }
     413        1172 :                 if (!isdigit((unsigned char) *s))
     414             :                         return NULL;
     415        1172 :                 res *= 10;
     416        1172 :                 res += (*s - '0');
     417             :         }
     418         183 :         i = 0;
     419         183 :         if (*s) {
     420         151 :                 if (*s != c->decsep)
     421             :                         return NULL;
     422         151 :                 s++;
     423         604 :                 for (; *s && i < 3; i++, s++) {
     424         453 :                         if (c->decskip && *s == c->decskip) {
     425           2 :                                 i--;
     426           2 :                                 continue;
     427             :                         }
     428         451 :                         if (!isdigit((unsigned char) *s))
     429             :                                 return NULL;
     430         451 :                         res *= 10;
     431         451 :                         res += (*s - '0');
     432             :                 }
     433             :         }
     434         183 :         if (*s)
     435             :                 return NULL;
     436         281 :         for (; i < 3; i++) {
     437          98 :                 res *= 10;
     438             :         }
     439         183 :         r = c->data;
     440         183 :         if (r == NULL && (r = (lng *) GDKzalloc(sizeof(lng))) == NULL)
     441             :                 return NULL;
     442         183 :         c->data = r;
     443         183 :         if (neg)
     444          10 :                 *r = -res;
     445             :         else
     446         173 :                 *r = res;
     447             :         return (void *) r;
     448             : }
     449             : 
     450             : static void *
     451     1913684 : fltdbl_frStr(Column *c, int type, const char *s)
     452             : {
     453             :         // The regular fltFromStr/dblFromStr functions do not take decimal commas
     454             :         // and thousands separators into account. When these are in use, this
     455             :         // function first converts them to decimal dots and empty strings,
     456             :         // respectively. We use a fixed size buffer so abnormally long floats such
     457             :         // as
     458             :         // +00000000000000000000000000000000000000000000000000000000000000000000001.5e1
     459             :         // will be rejected.
     460             : 
     461             :         // According to Stack Overflow https://stackoverflow.com/questions/1701055/what-is-the-maximum-length-in-chars-needed-to-represent-any-double-value
     462             :         // 24 bytes is a reasonable buffer but we'll make it a bit larger.
     463     1913684 :         char tmp[120];
     464     1913684 :         if (c->decskip || c->decsep != '.') {
     465          12 :                 char *p = &tmp[0];
     466             : 
     467          12 :                 while (GDKisspace(*s))
     468           8 :                         s++;
     469         102 :                 while (*s != '\0') {
     470          98 :                         if (p >= tmp + sizeof(tmp) - 1) {
     471             :                                 // If the input is this big it's probably an error.
     472             :                                 // Exception: only whitespace remains.
     473           0 :                                 while (GDKisspace(*s))
     474           0 :                                         s++;
     475           0 :                                 if (*s == '\0') {
     476             :                                         // there was only trailing whitespace
     477             :                                         break;
     478             :                                 } else {
     479             :                                         // not just trailing whitespace, abort!
     480             :                                         return NULL;
     481             :                                 }
     482             :                         }
     483          98 :                         char ch = *s++;
     484          98 :                         if (ch == c->decskip) {
     485           6 :                                 continue;
     486          92 :                         } else if (ch == c->decsep) {
     487             :                                 ch = '.';
     488          88 :                         } else if (ch == '.') {
     489             :                                 // We're mapping c->decsep to '.', if there are already
     490             :                                 // periods in the input we're losing information
     491             :                                 return NULL;
     492             :                         }
     493          92 :                         *p++ = ch;
     494             :                 }
     495             :                 // If we're here either we either encountered the end of s or the buffer is
     496             :                 // full. In the latter case we still need to write the NUL.
     497             :                 // We left room for it.
     498           4 :                 *p = '\0';
     499             : 
     500             :                 // now process the converted text rather than the original
     501           4 :                 s = &tmp[0];
     502             :         }
     503             : 
     504     1913684 :         ssize_t len = (*BATatoms[type].atomFromStr) (s, &c->len, &c->data, false);
     505     1916763 :         return (len > 0) ? c->data : NULL;
     506             : }
     507             : 
     508             : /* Literal parsing for SQL all pass through this routine */
     509             : static void *
     510   257639397 : _ASCIIadt_frStr(Column *c, int type, const char *s)
     511             : {
     512   257639397 :         ssize_t len;
     513             : 
     514   257639397 :         len = (*BATatoms[type].atomFromStr) (s, &c->len, &c->data, false);
     515   268858052 :         if (len < 0)
     516             :                 return NULL;
     517   268858039 :         switch (type) {
     518   238586558 :         case TYPE_bte:
     519             :         case TYPE_int:
     520             :         case TYPE_lng:
     521             :         case TYPE_sht:
     522             : #ifdef HAVE_HGE
     523             :         case TYPE_hge:
     524             : #endif
     525   238586558 :                 if (len == 0 || s[len]) {
     526             :                         /* decimals can be converted to integers when *.000 */
     527           8 :                         if (s[len++] == '.') {
     528          22 :                                 while (s[len] == '0')
     529          14 :                                         len++;
     530           8 :                                 if (s[len] == 0)
     531           4 :                                         return c->data;
     532             :                         }
     533             :                         return NULL;
     534             :                 }
     535             :                 break;
     536    29486717 :         case TYPE_str: {
     537    29486717 :                 sql_column *col = (sql_column *) c->extra;
     538             : 
     539    29486717 :                 s = c->data;
     540    51619163 :                 if (col->type.digits > 0 && len > 0 && !strNil(s) && UTF8_strlen(s) > (int) col->type.digits) {
     541             :                         return NULL;
     542             :                 }
     543             :                 break;
     544             :         }
     545             :         default:
     546             :                 break;
     547             :         }
     548   268822600 :         return c->data;
     549             : }
     550             : 
     551             : 
     552             : static ssize_t
     553    14624011 : _ASCIIadt_toStr(void *extra, char **buf, size_t *len, int type, const void *a)
     554             : {
     555    14624011 :         if (type == TYPE_str) {
     556     8505804 :                 Column *c = extra;
     557     8505804 :                 char *dst;
     558     8505804 :                 const char *src = a;
     559     8505804 :                 size_t l = escapedStrlen(src, c->sep, c->rsep, c->quote), l2 = 0;
     560             : 
     561     8505804 :                 if (c->quote)
     562     8505804 :                         l = escapedStrlen(src, NULL, NULL, c->quote);
     563             :                 else
     564           0 :                         l = escapedStrlen(src, c->sep, c->rsep, 0);
     565     8505804 :                 if (l + 3 > *len) {
     566          52 :                         GDKfree(*buf);
     567          52 :                         *len = 2 * l + 3;
     568          52 :                         *buf = GDKzalloc(*len);
     569          52 :                         if (*buf == NULL) {
     570             :                                 return -1;
     571             :                         }
     572             :                 }
     573     8505804 :                 dst = *buf;
     574     8505804 :                 if (c->quote) {
     575     8505804 :                         dst[0] = c->quote;
     576     8505804 :                         l2 = 1;
     577     8505804 :                         l = escapedStr(dst + l2, src, *len - l2, NULL, NULL, c->quote);
     578             :                 } else {
     579           0 :                         l = escapedStr(dst + l2, src, *len - l2, c->sep, c->rsep, 0);
     580             :                 }
     581           0 :                 if (l2) {
     582     8505804 :                         dst[l + l2] = c->quote;
     583     8505804 :                         l2++;
     584             :                 }
     585     8505804 :                 dst[l + l2] = 0;
     586     8505804 :                 return l + l2;
     587             :         } else {
     588     6118207 :                 return (*BATatoms[type].atomToStr) (buf, len, a, true);
     589             :         }
     590             : }
     591             : 
     592             : 
     593             : static int
     594       10334 : has_whitespace(const char *s)
     595             : {
     596       10334 :         if (*s == ' ' || *s == '\t')
     597             :                 return 1;
     598        7696 :         while (*s)
     599        3859 :                 s++;
     600        3837 :         s--;
     601        3837 :         if (*s == ' ' || *s == '\t')
     602           0 :                 return 1;
     603             :         return 0;
     604             : }
     605             : 
     606             : str
     607        1118 : mvc_import_table(Client cntxt, BAT ***bats, mvc *m, bstream *bs, sql_table *t, const char *sep, const char *rsep, const char *ssep, const char *ns, lng sz, lng offset, int best, bool from_stdin, bool escape, const char *decsep, const char *decskip)
     608             : {
     609        1118 :         int i = 0, j;
     610        1118 :         node *n;
     611        1118 :         Tablet as;
     612        1118 :         Column *fmt;
     613        1118 :         str msg = MAL_SUCCEED;
     614             : 
     615        1118 :         *bats =0;       // initialize the receiver
     616             : 
     617        1118 :         if (!bs)
     618           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "No stream (pointer) provided");
     619        1118 :         if (mnstr_errnr(bs->s) != MNSTR_NO__ERROR) {
     620           0 :                 mnstr_error_kind errnr = mnstr_errnr(bs->s);
     621           0 :                 const char *stream_msg = mnstr_peek_error(bs->s);
     622           0 :                 msg = createException(IO, "sql.copy_from", SQLSTATE(42000) "Stream not open %s: %s", mnstr_error_kind_name(errnr), stream_msg ? stream_msg : "unknown error");
     623           0 :                 return msg;
     624             :         }
     625        1118 :         if (offset < 0 || offset > (lng) BUN_MAX)
     626           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "Offset out of range");
     627             : 
     628        1118 :         if (offset > 0)
     629          28 :                 offset--;
     630        1118 :         if (ol_first_node(t->columns)) {
     631        1118 :                 stream *out = m->scanner.ws;
     632             : 
     633        2236 :                 as = (Tablet) {
     634        1118 :                         .nr_attrs = ol_length(t->columns),
     635        1118 :                         .nr = (sz < 1) ? BUN_NONE : (BUN) sz,
     636        1118 :                         .offset = (BUN) offset,
     637             :                         .error = NULL,
     638             :                         .tryall = 0,
     639             :                         .complaints = NULL,
     640        1118 :                         .filename = m->scanner.rs == bs ? NULL : "",
     641             :                 };
     642        1118 :                 fmt = GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
     643        1118 :                 if (fmt == NULL)
     644           0 :                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     645        1118 :                 as.format = fmt;
     646        1118 :                 if (!isa_block_stream(bs->s))
     647         559 :                         out = NULL;
     648             : 
     649       11452 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     650       10334 :                         sql_column *col = n->data;
     651             : 
     652       10334 :                         fmt[i].name = col->base.name;
     653       10334 :                         fmt[i].sep = (n->next) ? sep : rsep;
     654       10334 :                         fmt[i].rsep = rsep;
     655       10334 :                         fmt[i].seplen = _strlen(fmt[i].sep);
     656       10334 :                         fmt[i].decsep = decsep[0],
     657       10334 :                         fmt[i].decskip = decskip != NULL ? decskip[0] : '\0',
     658       10334 :                         fmt[i].type = sql_subtype_string(m->ta, &col->type);
     659       10334 :                         fmt[i].adt = ATOMindex(col->type.type->impl);
     660       10334 :                         fmt[i].tostr = &_ASCIIadt_toStr;
     661       10334 :                         fmt[i].frstr = &_ASCIIadt_frStr;
     662       10334 :                         fmt[i].extra = col;
     663       10334 :                         fmt[i].len = ATOMlen(fmt[i].adt, ATOMnilptr(fmt[i].adt));
     664       10334 :                         fmt[i].data = GDKzalloc(fmt[i].len);
     665       10334 :                         if(fmt[i].data == NULL || fmt[i].type == NULL) {
     666           0 :                                 for (j = 0; j < i; j++) {
     667           0 :                                         GDKfree(fmt[j].data);
     668           0 :                                         BBPunfix(fmt[j].c->batCacheid);
     669             :                                 }
     670           0 :                                 GDKfree(fmt[i].data);
     671           0 :                                 GDKfree(fmt);
     672           0 :                                 throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     673             :                         }
     674       10334 :                         fmt[i].c = NULL;
     675       10334 :                         fmt[i].ws = !has_whitespace(fmt[i].sep);
     676       10334 :                         fmt[i].quote = ssep ? ssep[0] : 0;
     677       10334 :                         fmt[i].nullstr = ns;
     678       10334 :                         fmt[i].null_length = strlen(ns);
     679       10334 :                         fmt[i].nildata = ATOMnilptr(fmt[i].adt);
     680       10334 :                         fmt[i].skip = (col->base.name[0] == '%');
     681       10334 :                         if (col->type.type->eclass == EC_DEC) {
     682         357 :                                 fmt[i].tostr = &dec_tostr;
     683         357 :                                 fmt[i].frstr = &dec_frstr;
     684        9977 :                         } else if (col->type.type->eclass == EC_SEC) {
     685          87 :                                 fmt[i].tostr = &dec_tostr;
     686          87 :                                 fmt[i].frstr = &sec_frstr;
     687        9890 :                         } else if (col->type.type->eclass == EC_FLT) {
     688             :                                 // no need to override .tostr, only .frstr
     689        3959 :                                 fmt[i].frstr = &fltdbl_frStr;
     690             :                         }
     691       10334 :                         fmt[i].size = ATOMsize(fmt[i].adt);
     692             :                 }
     693        2158 :                 if ((msg = TABLETcreate_bats(&as, (BUN) (sz < 0 ? 1000 : sz))) == MAL_SUCCEED){
     694        1118 :                         if (!sz || (SQLload_file(cntxt, &as, bs, out, sep, rsep, ssep ? ssep[0] : 0, offset, sz, best, from_stdin, t->base.name, escape) != BUN_NONE &&
     695        1081 :                                 (best || !as.error))) {
     696        1093 :                                 *bats = (BAT**) GDKzalloc(sizeof(BAT *) * as.nr_attrs);
     697        1093 :                                 if ( *bats == NULL){
     698           0 :                                         TABLETdestroy_format(&as);
     699           0 :                                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     700             :                                 }
     701        1093 :                                 msg = TABLETcollect(*bats,&as);
     702             :                         }
     703             :                 }
     704        1118 :                 if (as.error) {
     705          30 :                         if( !best) msg = createException(SQL, "sql.copy_from", SQLSTATE(42000) "Failed to import table '%s', %s", t->base.name, getExceptionMessage(as.error));
     706          30 :                         freeException(as.error);
     707          30 :                         as.error = NULL;
     708             :                 }
     709       11452 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     710       10334 :                         fmt[i].sep = NULL;
     711       10334 :                         fmt[i].rsep = NULL;
     712       10334 :                         fmt[i].nullstr = NULL;
     713             :                 }
     714        1118 :                 TABLETdestroy_format(&as);
     715             :         }
     716             :         return msg;
     717             : }
     718             : 
     719             : /*
     720             :  * mvc_export_result dumps the sql header information and the
     721             :  * first part (reply_size) of the result set. It should be produced in Monet format to
     722             :  * enable mapi to work with it.
     723             :  */
     724             : 
     725             : static int
     726      110946 : mvc_export_warning(stream *s, str w)
     727             : {
     728      110946 :         str tmp = NULL;
     729      111021 :         while (w != NULL && *w != '\0') {
     730           0 :                 if ((tmp = strchr(w, (int) '\n')) != NULL)
     731           0 :                         *tmp++ = '\0';
     732           0 :                 if (mnstr_printf(s, "#%s", w) < 0)
     733             :                         return -4;
     734             :                 w = tmp;
     735             :         }
     736             :         return 1;
     737             : }
     738             : 
     739             : static int
     740           2 : mvc_export_binary_bat(stream *s, BAT* bn, bstream *in)
     741             : {
     742           2 :         BATiter bni = bat_iterator(bn);
     743           2 :         bool sendtheap = bni.type != TYPE_void, sendtvheap = sendtheap && bni.vh;
     744             : 
     745           4 :         if (mnstr_printf(s, /*JSON*/"{"
     746             :                                          "\"version\":1,"
     747             :                                          "\"ttype\":%d,"
     748             :                                          "\"hseqbase\":" OIDFMT ","
     749             :                                          "\"tseqbase\":" OIDFMT ","
     750             :                                          "\"tsorted\":%d,"
     751             :                                          "\"trevsorted\":%d,"
     752             :                                          "\"tkey\":%d,"
     753             :                                          "\"tnonil\":%d,"
     754             :                                          "\"tdense\":%d,"
     755             :                                          "\"size\":" BUNFMT ","
     756             :                                          "\"tailsize\":%zu,"
     757             :                                          "\"theapsize\":%zu"
     758             :                                          "}\n",
     759             :                                          bni.type,
     760             :                                          bn->hseqbase, bn->tseqbase,
     761           2 :                                          bni.sorted, bni.revsorted,
     762           2 :                                          bni.key,
     763           2 :                                          bni.nonil,
     764           2 :                                          BATtdensebi(&bni),
     765             :                                          bn->batCount,
     766           2 :                                          sendtheap ? (size_t)bni.count << bni.shift : 0,
     767           1 :                                          sendtvheap && bni.count > 0 ? bni.vhfree : 0) < 0) {
     768           0 :                 bat_iterator_end(&bni);
     769           0 :                 return -4;
     770             :         }
     771             : 
     772           2 :         if (sendtheap && bni.count > 0) {
     773           2 :                 if (mnstr_write(s, /* tail */ bni.base, bni.count * bni.width, 1) < 1) {
     774           0 :                         bat_iterator_end(&bni);
     775           0 :                         return -4;
     776             :                 }
     777           2 :                 if (sendtvheap && mnstr_write(s, /* tvheap */ bni.vh->base, bni.vhfree, 1) < 1) {
     778           0 :                         bat_iterator_end(&bni);
     779           0 :                         return -4;
     780             :                 }
     781             :         }
     782           2 :         bat_iterator_end(&bni);
     783           2 :         if (bstream_getoob(in))
     784           0 :                 return -5;
     785             :         return 0;
     786             : }
     787             : 
     788             : static int
     789         335 : create_prepare_result(backend *b, cq *q, int nrows)
     790             : {
     791         335 :         int error = 0;
     792             : 
     793         335 :         BAT* btype              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     794         335 :         BAT* bimpl              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     795         335 :         BAT* bdigits    = COLnew(0, TYPE_int, nrows, TRANSIENT);
     796         335 :         BAT* bscale             = COLnew(0, TYPE_int, nrows, TRANSIENT);
     797         335 :         BAT* bschema    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     798         335 :         BAT* btable             = COLnew(0, TYPE_str, nrows, TRANSIENT);
     799         335 :         BAT* bcolumn    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     800         335 :         node *n;
     801             : 
     802         335 :         const int nr_columns = (b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) ? 7 : 6;
     803             : 
     804         335 :         int len1 = 0, len4 = 0, len5 = 0, len6 = 0, len7 =0;    /* column widths */
     805         335 :         int len2 = 1, len3 = 1;
     806         335 :         sql_arg *a;
     807         335 :         sql_subtype *t;
     808         335 :         sql_rel *r = q->rel;
     809             : 
     810         335 :         if (!btype || !bimpl || !bdigits || !bscale || !bschema || !btable || !bcolumn) {
     811           0 :                 error = -1;
     812           0 :                 goto wrapup;
     813             :         }
     814             : 
     815         335 :         if (r && (is_topn(r->op) || is_sample(r->op)))
     816           2 :                 r = r->l;
     817         335 :         if (r && is_project(r->op) && r->exps) {
     818         288 :                 unsigned int max2 = 10, max3 = 10;      /* to help calculate widths */
     819         288 :                 nrows += list_length(r->exps);
     820             : 
     821        1798 :                 for (n = r->exps->h; n; n = n->next) {
     822        1510 :                         const char *name = NULL, *rname = NULL, *schema = NULL;
     823        1510 :                         sql_exp *e = n->data;
     824        1510 :                         int slen;
     825             : 
     826        1510 :                         t = exp_subtype(e);
     827        1510 :                         slen = (int) strlen(t->type->base.name);
     828        1510 :                         if (slen > len1)
     829             :                                 len1 = slen;
     830        1597 :                         while (t->digits >= max2) {
     831          87 :                                 len2++;
     832          87 :                                 max2 *= 10;
     833             :                         }
     834        1510 :                         while (t->scale >= max3) {
     835           0 :                                 len3++;
     836           0 :                                 max3 *= 10;
     837             :                         }
     838        1510 :                         rname = exp_relname(e);
     839        1510 :                         if (!rname && e->type == e_column && e->l)
     840        1510 :                                 rname = e->l;
     841        1510 :                         slen = name ? (int) strlen(name) : 0;
     842        1510 :                         if (slen > len5)
     843             :                                 len5 = slen;
     844        1510 :                         name = exp_name(e);
     845        1510 :                         if (!name && e->type == e_column && e->r)
     846           0 :                                 name = e->r;
     847        1510 :                         slen = name ? (int) strlen(name) : 0;
     848        1510 :                         if (slen > len6)
     849             :                                 len6 = slen;
     850        1510 :                         slen = (int) strlen(t->type->impl);
     851        1510 :                         if (slen > len7)
     852             :                                 len7 = slen;
     853             : 
     854        1510 :                         if (!schema)
     855        1510 :                                 schema = "";
     856             : 
     857        1510 :                         if (!rname)
     858           7 :                                 rname = "";
     859             : 
     860        1510 :                         if (!name)
     861           0 :                                 name = "";
     862             : 
     863        3020 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     864        3020 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     865        3020 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     866        3020 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     867        3020 :                                         BUNappend(bschema,      schema                          , false) != GDK_SUCCEED ||
     868        3020 :                                         BUNappend(btable,       rname                           , false) != GDK_SUCCEED ||
     869        1510 :                                         BUNappend(bcolumn,      name                            , false) != GDK_SUCCEED) {
     870           0 :                                 error = -3;
     871           0 :                                 goto wrapup;
     872             :                         }
     873             :                 }
     874             :         }
     875             : 
     876         335 :         if (q->f->ops) {
     877         243 :                 int i;
     878             : 
     879        1796 :                 for (n = q->f->ops->h, i = 0; n; n = n->next, i++) {
     880        1553 :                         a = n->data;
     881        1553 :                         t = &a->type;
     882             : 
     883        3106 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     884        3106 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     885        3106 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     886        3106 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     887        3106 :                                         BUNappend(bschema,      str_nil                         , false) != GDK_SUCCEED ||
     888        3106 :                                         BUNappend(btable,       str_nil                         , false) != GDK_SUCCEED ||
     889        1553 :                                         BUNappend(bcolumn,      str_nil                         , false) != GDK_SUCCEED) {
     890           0 :                                 error = -3;
     891           0 :                                 goto wrapup;
     892             :                         }
     893             :                 }
     894             :         }
     895             : 
     896             :         // A little hack to inform the result receiver of the name of the compiled mal program.
     897         335 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
     898           0 :                 if (    BUNappend(btype,        str_nil         , false) != GDK_SUCCEED ||
     899           0 :                                 BUNappend(bimpl,        str_nil         , false) != GDK_SUCCEED ||
     900           0 :                                 BUNappend(bdigits,      &int_nil    , false) != GDK_SUCCEED ||
     901           0 :                                 BUNappend(bscale,       &int_nil    , false) != GDK_SUCCEED ||
     902           0 :                                 BUNappend(bschema,      str_nil         , false) != GDK_SUCCEED ||
     903           0 :                                 BUNappend(btable,       q->f->imp , false) != GDK_SUCCEED ||
     904           0 :                                 BUNappend(bcolumn,      str_nil         , false) != GDK_SUCCEED) {
     905           0 :                         error = -3;
     906           0 :                         goto wrapup;
     907             :                 }
     908             :         }
     909             : 
     910         148 :         b->results = res_table_create(
     911         335 :                                                         b->mvc->session->tr,
     912         335 :                                                         b->result_id++,
     913         335 :                                                         b->mb? b->mb->tag: 0 /*TODO check if this is sensible*/,
     914             :                                                         nr_columns,
     915             :                                                         Q_PREPARE,
     916             :                                                         b->results);
     917         335 :         if (!b->results) {
     918           0 :                 error = -1;
     919           0 :                 goto wrapup;
     920             :         }
     921             : 
     922         670 :         if (    mvc_result_column(b, ".prepare", "type"             , "varchar",  len1, 0, btype  ) ||
     923         670 :                         mvc_result_column(b, ".prepare", "digits"   , "int",              len2, 0, bdigits) ||
     924         670 :                         mvc_result_column(b, ".prepare", "scale"    , "int",              len3, 0, bscale ) ||
     925         670 :                         mvc_result_column(b, ".prepare", "schema"   , "varchar",  len4, 0, bschema) ||
     926         670 :                         mvc_result_column(b, ".prepare", "table"    , "varchar",  len5, 0, btable ) ||
     927         335 :                         mvc_result_column(b, ".prepare", "column"   , "varchar",  len6, 0, bcolumn)) {
     928           0 :                 error = -1;
     929           0 :                 goto wrapup;
     930             :         }
     931             : 
     932         335 :         if ((b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) && mvc_result_column(b, "prepare", "impl" , "varchar", len7, 0, bimpl))
     933           0 :                 error = -1;
     934             : 
     935         335 :         wrapup:
     936         335 :                 BBPreclaim(btype);
     937         335 :                 BBPreclaim(bdigits);
     938         335 :                 BBPreclaim(bimpl);
     939         335 :                 BBPreclaim(bscale);
     940         335 :                 BBPreclaim(bschema);
     941         335 :                 BBPreclaim(btable);
     942         335 :                 BBPreclaim(bcolumn);
     943         335 :                 if (error < 0 && b->results) {
     944           0 :                         res_table_destroy(b->results);
     945           0 :                         b->results = NULL;
     946             :                 }
     947         335 :                 return error;
     948             : }
     949             : 
     950             : int
     951         335 : mvc_export_prepare(backend *b, stream *out)
     952             : {
     953         335 :         cq *q = b->q;
     954         335 :         int nparam = q->f->ops ? list_length(q->f->ops) : 0;
     955         335 :         int nrows = nparam, res;
     956             : 
     957         335 :         if ((res = create_prepare_result(b, q, nrows)) < 0)
     958             :                 return res;
     959             : 
     960         335 :         return mvc_export_result(b, out, b->results->id /*TODO is this right?*/, true, 0 /*TODO*/, 0 /*TODO*/);
     961             : }
     962             : 
     963             : /*
     964             :  * improved formatting of positive integers
     965             :  */
     966             : 
     967             : static ssize_t
     968           0 : mvc_send_bte(stream *s, bte cnt)
     969             : {
     970           0 :         char buf[50], *b;
     971           0 :         int neg = cnt < 0;
     972           0 :         if (neg)
     973           0 :                 cnt = -cnt;
     974             :         b = buf + 49;
     975           0 :         do {
     976           0 :                 *b-- = (char) ('0' + (cnt % 10));
     977           0 :                 cnt /= 10;
     978           0 :         } while (cnt > 0);
     979           0 :         if (neg)
     980           0 :                 *b = '-';
     981             :         else
     982             :                 b++;
     983           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     984             : }
     985             : 
     986             : static ssize_t
     987           0 : mvc_send_sht(stream *s, sht cnt)
     988             : {
     989           0 :         char buf[50], *b;
     990           0 :         int neg = cnt < 0;
     991           0 :         if (neg)
     992           0 :                 cnt = -cnt;
     993             :         b = buf + 49;
     994           0 :         do {
     995           0 :                 *b-- = (char) ('0' + (cnt % 10));
     996           0 :                 cnt /= 10;
     997           0 :         } while (cnt > 0);
     998           0 :         if (neg)
     999           0 :                 *b = '-';
    1000             :         else
    1001             :                 b++;
    1002           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1003             : }
    1004             : 
    1005             : static ssize_t
    1006      490994 : mvc_send_int(stream *s, int cnt)
    1007             : {
    1008      490994 :         char buf[50], *b;
    1009      490994 :         int neg = cnt < 0;
    1010      490994 :         if (neg)
    1011             :                 cnt = -cnt;
    1012             :         b = buf + 49;
    1013      658966 :         do {
    1014      658966 :                 *b-- = (char) ('0' + (cnt % 10));
    1015      658966 :                 cnt /= 10;
    1016      658966 :         } while (cnt > 0);
    1017      490994 :         if (neg)
    1018           0 :                 *b = '-';
    1019             :         else
    1020             :                 b++;
    1021      490994 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1022             : }
    1023             : 
    1024             : static ssize_t
    1025     1498737 : mvc_send_lng(stream *s, lng cnt)
    1026             : {
    1027     1498737 :         char buf[50], *b;
    1028     1498737 :         int neg = cnt < 0;
    1029     1498737 :         if (neg)
    1030             :                 cnt = -cnt;
    1031             :         b = buf + 49;
    1032     3119534 :         do {
    1033     3119534 :                 *b-- = (char) ('0' + (cnt % 10));
    1034     3119534 :                 cnt /= 10;
    1035     3119534 :         } while (cnt > 0);
    1036     1498737 :         if (neg)
    1037       84070 :                 *b = '-';
    1038             :         else
    1039             :                 b++;
    1040     1498737 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1041             : }
    1042             : 
    1043             : #ifdef HAVE_HGE
    1044             : static ssize_t
    1045           0 : mvc_send_hge(stream *s, hge cnt)
    1046             : {
    1047           0 :         char buf[50], *b;
    1048           0 :         int neg = cnt <0;
    1049           0 :         if(neg) cnt = -cnt;
    1050             :         b= buf+49;
    1051           0 :         do{
    1052           0 :                 *b--= (char) ('0'+ (cnt % 10));
    1053           0 :                 cnt /=10;
    1054           0 :         } while(cnt>0);
    1055           0 :         if( neg)
    1056           0 :                 *b = '-';
    1057             :         else b++;
    1058           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1059             : }
    1060             : #endif
    1061             : 
    1062             : ssize_t
    1063    11041481 : convert2str(mvc *m, sql_class eclass, int d, int sc, int has_tz, const void *p, int mtype, char **buf, size_t *len)
    1064             : {
    1065    11041481 :         ssize_t l = 0;
    1066             : 
    1067    11041481 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
    1068       21570 :                 (*buf)[0] = '\200';
    1069       21570 :                 (*buf)[1] = 0;
    1070    11192118 :         } else if (eclass == EC_DEC) {
    1071         269 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
    1072             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1073          54 :                 struct time_res ts_res;
    1074          54 :                 ts_res.has_tz = has_tz;
    1075          54 :                 ts_res.fraction = d ? d - 1 : 0;
    1076          54 :                 ts_res.timezone = m->timezone;
    1077          54 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
    1078             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1079          97 :                 struct time_res ts_res;
    1080          97 :                 ts_res.has_tz = has_tz;
    1081          97 :                 ts_res.fraction = d ? d - 1 : 0;
    1082          97 :                 ts_res.timezone = m->timezone;
    1083          97 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1084             :         } else if (eclass == EC_SEC) {
    1085          24 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1086             :         } else if (eclass == EC_BIT) {
    1087        2361 :                 bit b = *(bit *) p;
    1088        2361 :                 if (*len == 0 || *len > 5) {
    1089        2346 :                         if (b) {
    1090        2239 :                                 strcpy(*buf, "true");
    1091        2239 :                                 l = 4;
    1092             :                         } else {
    1093         107 :                                 strcpy(*buf, "false");
    1094         107 :                                 l = 5;
    1095             :                         }
    1096             :                 } else {
    1097          15 :                         (*buf)[0] = b?'t':'f';
    1098          15 :                         (*buf)[1] = 0;
    1099          15 :                         l = 1;
    1100             :                 }
    1101             :         } else {
    1102    11189313 :                 l = (*BATatoms[mtype].atomToStr) (buf, len, p, false);
    1103             :         }
    1104    11136298 :         return l;
    1105             : }
    1106             : 
    1107             : static int
    1108           0 : export_value(mvc *m, stream *s, sql_class eclass, const char *sqlname, int d, int sc, ptr p, int mtype, char **buf, size_t *len, const char *ns)
    1109             : {
    1110           0 :         int ok = 0;
    1111           0 :         ssize_t l = 0;
    1112             : 
    1113           0 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
    1114           0 :                 if (mnstr_write(s, ns, strlen(ns), 1) < 1)
    1115           0 :                         ok = -4;
    1116           0 :         } else if (eclass == EC_DEC) {
    1117           0 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
    1118           0 :                 if (l > 0 && mnstr_write(s, *buf, l, 1) < 1)
    1119           0 :                         ok = -4;
    1120           0 :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1121           0 :                 struct time_res ts_res;
    1122           0 :                 ts_res.has_tz = (strcmp(sqlname, "timetz") == 0);
    1123           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1124           0 :                 ts_res.timezone = m->timezone;
    1125           0 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
    1126           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1127           0 :                         ok = -4;
    1128           0 :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1129           0 :                 struct time_res ts_res;
    1130           0 :                 ts_res.has_tz = (strcmp(sqlname, "timestamptz") == 0);
    1131           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1132           0 :                 ts_res.timezone = m->timezone;
    1133           0 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1134           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1135           0 :                         ok = -4;
    1136           0 :         } else if (eclass == EC_SEC) {
    1137           0 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1138           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1139           0 :                         ok = -4;
    1140             :         } else {
    1141           0 :                 switch (mtype) {
    1142           0 :                 case TYPE_bte:
    1143           0 :                         if (mvc_send_bte(s, *(bte *) p) < 1)
    1144           0 :                                 ok = -4;
    1145             :                         break;
    1146           0 :                 case TYPE_sht:
    1147           0 :                         if (mvc_send_sht(s, *(sht *) p) < 1)
    1148           0 :                                 ok = -4;
    1149             :                         break;
    1150           0 :                 case TYPE_int:
    1151           0 :                         if (mvc_send_int(s, *(int *) p) < 1)
    1152           0 :                                 ok = -4;
    1153             :                         break;
    1154           0 :                 case TYPE_lng:
    1155           0 :                         if (mvc_send_lng(s, *(lng *) p) < 1)
    1156           0 :                                 ok = -4;
    1157             :                         break;
    1158             : #ifdef HAVE_HGE
    1159           0 :                 case TYPE_hge:
    1160           0 :                         if (mvc_send_hge(s, *(hge *) p) < 1)
    1161           0 :                                 ok = -4;
    1162             :                         break;
    1163             : #endif
    1164           0 :                 default:
    1165           0 :                         l = (*BATatoms[mtype].atomToStr) (buf, len, p, true);
    1166           0 :                         if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1167           0 :                                 ok = -4;
    1168             :                 }
    1169             :         }
    1170           0 :         return ok;
    1171             : }
    1172             : 
    1173             : static int
    1174           0 : mvc_export_row(backend *b, stream *s, res_table *t, const char *btag, const char *sep, const char *rsep, const char *ssep, const char *ns)
    1175             : {
    1176           0 :         mvc *m = b->mvc;
    1177           0 :         size_t seplen = strlen(sep);
    1178           0 :         size_t rseplen = strlen(rsep);
    1179           0 :         char *buf = NULL;
    1180           0 :         size_t len = 0;
    1181           0 :         int i, ok = 1;
    1182           0 :         int csv = (b->output_format == OFMT_CSV);
    1183           0 :         int json = (b->output_format == OFMT_JSON);
    1184             : 
    1185           0 :         if (!s || !t)
    1186             :                 return 0;
    1187             : 
    1188           0 :         (void) ssep;
    1189           0 :         if (csv && btag[0] && mnstr_write(s, btag, strlen(btag), 1) < 1)
    1190           0 :                 ok = -4;
    1191           0 :         if (json) {
    1192           0 :                 sep = ", ";
    1193           0 :                 seplen = strlen(sep);
    1194             :         }
    1195           0 :         for (i = 0; i < t->nr_cols && ok > -1; i++) {
    1196           0 :                 res_col *c = t->cols + i;
    1197             : 
    1198           0 :                 if (i != 0 && mnstr_write(s, sep, seplen, 1) < 1) {
    1199             :                         ok = -4;
    1200             :                         break;
    1201             :                 }
    1202           0 :                 if (json && (mnstr_write(s, c->name, strlen(c->name), 1) < 1 || mnstr_write(s, ": ", 2, 1) < 1)) {
    1203             :                         ok = -4;
    1204             :                         break;
    1205             :                 }
    1206           0 :                 ok = export_value(m, s, c->type.type->eclass, c->type.type->base.name, c->type.digits, c->type.scale, c->p, c->mtype, &buf, &len, ns);
    1207             :         }
    1208           0 :         _DELETE(buf);
    1209           0 :         if (ok > -1 && mnstr_write(s, rsep, rseplen, 1) < 1)
    1210           0 :                 ok = -4;
    1211           0 :         b->results = res_tables_remove(b->results, t);
    1212           0 :         return ok;
    1213             : }
    1214             : 
    1215             : static int
    1216           1 : mvc_export_table_columnar(stream *s, res_table *t, bstream *in)
    1217             : {
    1218           1 :         int i, res = 0;
    1219             : 
    1220           1 :         if (!s || !t)
    1221             :                 return 0;
    1222             : 
    1223           3 :         for (i = 1; i <= t->nr_cols; i++) {
    1224           2 :                 res_col *c = t->cols + (i - 1);
    1225             : 
    1226           2 :                 if (!c->b)
    1227             :                         break;
    1228             : 
    1229           2 :                 BAT *b = BATdescriptor(c->b);
    1230           2 :                 if (b == NULL)
    1231             :                         return -2;
    1232             : 
    1233           2 :                 res = mvc_export_binary_bat(s, b, in);
    1234           2 :                 BBPunfix(b->batCacheid);
    1235           2 :                 if (res < 0)
    1236           0 :                         return res;
    1237             :         }
    1238             : 
    1239             :         return res;
    1240             : }
    1241             : 
    1242             : static int
    1243      122760 : mvc_export_table_(mvc *m, int output_format, stream *s, res_table *t, BUN offset, BUN nr, const char *btag, const char *sep, const char *rsep, const char *ssep, const char *ns)
    1244             : {
    1245      122760 :         Tablet as;
    1246      122760 :         Column *fmt;
    1247      122760 :         int i, ok = 0;
    1248      122760 :         struct time_res *tres;
    1249      122760 :         int csv = (output_format == OFMT_CSV);
    1250      122760 :         int json = (output_format == OFMT_JSON);
    1251      122760 :         char *bj;
    1252             : 
    1253      122760 :         if (!s || !t)
    1254             :                 return 0;
    1255             : 
    1256      122756 :         as.nr_attrs = t->nr_cols + 1;        /* for the leader */
    1257      122756 :         as.nr = nr;
    1258      122756 :         as.offset = offset;
    1259      122756 :         fmt = as.format = (Column *) GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
    1260      122807 :         tres = GDKzalloc(sizeof(struct time_res) * (as.nr_attrs));
    1261      122799 :         if (fmt == NULL || tres == NULL) {
    1262           0 :                 GDKfree(fmt);
    1263           0 :                 GDKfree(tres);
    1264           0 :                 return -4;
    1265             :         }
    1266             : 
    1267      122799 :         fmt[0].c = NULL;
    1268      122799 :         fmt[0].sep = (csv) ? btag : "";
    1269      122799 :         fmt[0].rsep = rsep;
    1270      122799 :         fmt[0].seplen = _strlen(fmt[0].sep);
    1271      122799 :         fmt[0].ws = 0;
    1272      122799 :         fmt[0].nullstr = NULL;
    1273             : 
    1274      457918 :         for (i = 1; i <= t->nr_cols; i++) {
    1275      335159 :                 res_col *c = t->cols + (i - 1);
    1276             : 
    1277      335159 :                 if (!c->b)
    1278             :                         break;
    1279             : 
    1280      335159 :                 fmt[i].c = BATdescriptor(c->b);
    1281      335155 :                 if (fmt[i].c == NULL) {
    1282           0 :                         while (--i >= 1) {
    1283           0 :                                 bat_iterator_end(&fmt[i].ci);
    1284           0 :                                 BBPunfix(fmt[i].c->batCacheid);
    1285             :                         }
    1286           0 :                         GDKfree(fmt);
    1287           0 :                         GDKfree(tres);
    1288           0 :                         return -2;
    1289             :                 }
    1290      335155 :                 fmt[i].ci = bat_iterator(fmt[i].c);
    1291      335153 :                 fmt[i].name = NULL;
    1292      335153 :                 if (csv) {
    1293      335138 :                         fmt[i].sep = ((i - 1) < (t->nr_cols - 1)) ? sep : rsep;
    1294      335138 :                         fmt[i].seplen = _strlen(fmt[i].sep);
    1295      335138 :                         fmt[i].rsep = rsep;
    1296             :                 }
    1297      335153 :                 if (json) {
    1298           0 :                         res_col *p = t->cols + (i - 1);
    1299             : 
    1300             :                         /*
    1301             :                          * We define the "proper" way of returning
    1302             :                          * a relational table in json format as a
    1303             :                          * json array of objects, where each row is
    1304             :                          * represented as a json object.
    1305             :                          */
    1306           0 :                         if (i == 1) {
    1307           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(btag));
    1308           0 :                                 snprintf(bj, strlen(p->name) + strlen(btag), btag, p->name);
    1309           0 :                                 fmt[i - 1].sep = bj;
    1310           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1311           0 :                                 fmt[i - 1].rsep = NULL;
    1312           0 :                         } else if (i <= t->nr_cols) {
    1313           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(sep));
    1314           0 :                                 snprintf(bj, strlen(p->name) + 10, sep, p->name);
    1315           0 :                                 fmt[i - 1].sep = bj;
    1316           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1317           0 :                                 fmt[i - 1].rsep = NULL;
    1318             :                         }
    1319           0 :                         if (i == t->nr_cols) {
    1320           0 :                                 fmt[i].sep = rsep;
    1321           0 :                                 fmt[i].seplen = _strlen(fmt[i].sep);
    1322           0 :                                 fmt[i].rsep = NULL;
    1323             :                         }
    1324             :                 }
    1325      335153 :                 fmt[i].type = ATOMname(fmt[i].c->ttype);
    1326      335119 :                 fmt[i].adt = fmt[i].c->ttype;
    1327      335119 :                 fmt[i].tostr = &_ASCIIadt_toStr;
    1328      335119 :                 fmt[i].frstr = &_ASCIIadt_frStr;
    1329      335119 :                 fmt[i].extra = fmt + i;
    1330      335119 :                 fmt[i].data = NULL;
    1331      335119 :                 fmt[i].len = 0;
    1332      335119 :                 fmt[i].ws = 0;
    1333      335119 :                 fmt[i].quote = ssep ? ssep[0] : 0;
    1334      335119 :                 fmt[i].nullstr = ns;
    1335      335119 :                 if (c->type.type->eclass == EC_DEC) {
    1336        1267 :                         fmt[i].tostr = &dec_tostr;
    1337        1267 :                         fmt[i].frstr = &dec_frstr;
    1338        1267 :                         fmt[i].extra = (void *) (ptrdiff_t) c->type.scale;
    1339      333852 :                 } else if (c->type.type->eclass == EC_TIMESTAMP || c->type.type->eclass == EC_TIMESTAMP_TZ) {
    1340         802 :                         struct time_res *ts_res = tres + (i - 1);
    1341         802 :                         ts_res->has_tz = EC_TEMP_TZ(c->type.type->eclass);
    1342         802 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1343         802 :                         ts_res->timezone = m->timezone;
    1344             : 
    1345         802 :                         fmt[i].tostr = &sql_timestamp_tostr;
    1346         802 :                         fmt[i].frstr = NULL;
    1347         802 :                         fmt[i].extra = ts_res;
    1348      333050 :                 } else if (c->type.type->eclass == EC_TIME || c->type.type->eclass == EC_TIME_TZ) {
    1349         287 :                         struct time_res *ts_res = tres + (i - 1);
    1350         287 :                         ts_res->has_tz = (strcmp(c->type.type->base.name, "timetz") == 0);
    1351         287 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1352         287 :                         ts_res->timezone = m->timezone;
    1353             : 
    1354         287 :                         fmt[i].tostr = &sql_time_tostr;
    1355         287 :                         fmt[i].frstr = NULL;
    1356         287 :                         fmt[i].extra = ts_res;
    1357      332763 :                 } else if (c->type.type->eclass == EC_SEC) {
    1358         479 :                         fmt[i].tostr = &dec_tostr;
    1359         479 :                         fmt[i].frstr = &sec_frstr;
    1360         479 :                         fmt[i].extra = (void *) (ptrdiff_t) 3;
    1361             :                 } else {
    1362             :                         fmt[i].extra = fmt + i;
    1363             :                 }
    1364             :         }
    1365      122759 :         if (i == t->nr_cols + 1)
    1366      122759 :                 ok = TABLEToutput_file(&as, NULL, s, m->scanner.rs);
    1367      580728 :         for (i = 0; i <= t->nr_cols; i++) {
    1368      457921 :                 fmt[i].sep = NULL;
    1369      457921 :                 fmt[i].rsep = NULL;
    1370      457921 :                 fmt[i].type = NULL;
    1371      457921 :                 fmt[i].nullstr = NULL;
    1372             :         }
    1373      457934 :         for (i = 1; i <= t->nr_cols; i++)
    1374      335143 :                 bat_iterator_end(&fmt[i].ci);
    1375      122791 :         TABLETdestroy_format(&as);
    1376      122807 :         GDKfree(tres);
    1377      122807 :         if (ok < 0)
    1378             :                 return ok;
    1379      122807 :         if (mnstr_errnr(s) != MNSTR_NO__ERROR)
    1380             :                 return -4;
    1381             :         return 0;
    1382             : }
    1383             : 
    1384             : static int
    1385      122781 : mvc_export_table(backend *b, stream *s, res_table *t, BUN offset, BUN nr, const char *btag, const char *sep, const char *rsep, const char *ssep, const char *ns)
    1386             : {
    1387      122781 :         return mvc_export_table_(b->mvc, b->output_format, s, t, offset, nr, btag, sep, rsep, ssep, ns);
    1388             : }
    1389             : 
    1390             : int
    1391           0 : mvc_export(mvc *m, stream *s, res_table *t, BUN nr)
    1392             : {
    1393           0 :         backend b = {0};
    1394           0 :         b.mvc = m;
    1395           0 :         b.results = t;
    1396           0 :         b.reloptimizer = 0;
    1397           0 :         t->nr_rows = nr;
    1398           0 :         if (mvc_export_head(&b, s, t->id, TRUE, TRUE, 0/*starttime*/, 0/*maloptimizer*/) < 0)
    1399             :                 return -1;
    1400           0 :         return mvc_export_table_(m, OFMT_CSV, s, t, 0, nr, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1401             : }
    1402             : 
    1403             : 
    1404             : static lng
    1405      333980 : get_print_width(int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1406             : {
    1407      333980 :         size_t count = 0, incr = 0;
    1408             : 
    1409      333980 :         if (eclass == EC_SEC)
    1410             :                 incr = 1;
    1411      333517 :         else if (mtype == TYPE_oid)
    1412         133 :                 incr = 2;
    1413      333980 :         mtype = ATOMbasetype(mtype);
    1414      333980 :         if (mtype == TYPE_str) {
    1415       98938 :                 if (eclass == EC_CHAR && digits) {
    1416        1378 :                         return digits;
    1417             :                 } else {
    1418       97560 :                         int l = 0;
    1419       97560 :                         if (bid) {
    1420       97560 :                                 BAT *b = BATdescriptor(bid);
    1421             : 
    1422       97560 :                                 if (b) {
    1423             :                                         /* in practice, b can be a
    1424             :                                          * void(nil) bat, an oid bat
    1425             :                                          * with all nil values, or an
    1426             :                                          * empty void/oid bat */
    1427       97560 :                                         if (ATOMstorage(b->ttype) == TYPE_str)
    1428       97560 :                                                 l = bat_max_strlength(b);
    1429             :                                         else
    1430             :                                                 l = 0;
    1431       97560 :                                         BBPunfix(b->batCacheid);
    1432             :                                 } else {
    1433             :                                         return -2;
    1434             :                                 }
    1435           0 :                         } else if (p) {
    1436           0 :                                 l = UTF8_strwidth((const char *) p);
    1437           0 :                                 if (is_int_nil(l))
    1438           0 :                                         l = 0;
    1439             :                         }
    1440       97560 :                         return l;
    1441             :                 }
    1442      235042 :         } else if (eclass == EC_NUM || eclass == EC_POS || eclass == EC_MONTH || eclass == EC_SEC) {
    1443      222487 :                 count = 0;
    1444      222487 :                 if (bid) {
    1445      222487 :                         BAT *b = BATdescriptor(bid);
    1446             : 
    1447      222512 :                         if (b) {
    1448      222512 :                                 if (mtype == TYPE_bte) {
    1449       37144 :                                         count = bat_max_btelength(b);
    1450             :                                 } else if (mtype == TYPE_sht) {
    1451       29063 :                                         count = bat_max_shtlength(b);
    1452             :                                 } else if (mtype == TYPE_int) {
    1453      136917 :                                         count = bat_max_intlength(b);
    1454             :                                 } else if (mtype == TYPE_lng) {
    1455       19148 :                                         count = bat_max_lnglength(b);
    1456             : #ifdef HAVE_HGE
    1457             :                                 } else if (mtype == TYPE_hge) {
    1458         240 :                                         count = bat_max_hgelength(b);
    1459             : #endif
    1460             :                                 } else if (mtype == TYPE_void) {
    1461             :                                         count = 4;
    1462             :                                 } else {
    1463           0 :                                         assert(0);
    1464             :                                 }
    1465      222520 :                                 count += incr;
    1466      222520 :                                 BBPunfix(b->batCacheid);
    1467             :                         } else {
    1468             :                                 return -2;
    1469             :                         }
    1470             :                 } else {
    1471           0 :                         if (p) {
    1472             : #ifdef HAVE_HGE
    1473           0 :                                 hge val = 0;
    1474             : #else
    1475             :                                 lng val = 0;
    1476             : #endif
    1477           0 :                                 if (mtype == TYPE_bte) {
    1478           0 :                                         val = *((bte *) p);
    1479             :                                 } else if (mtype == TYPE_sht) {
    1480           0 :                                         val = *((sht *) p);
    1481             :                                 } else if (mtype == TYPE_int) {
    1482           0 :                                         val = *((int *) p);
    1483             :                                 } else if (mtype == TYPE_lng) {
    1484           0 :                                         val = *((lng *) p);
    1485             : #ifdef HAVE_HGE
    1486             :                                 } else if (mtype == TYPE_hge) {
    1487           0 :                                         val = *((hge *) p);
    1488             : #endif
    1489             :                                 } else {
    1490           0 :                                         assert(0);
    1491             :                                 }
    1492             : 
    1493           0 :                                 if (val < 0)
    1494           0 :                                         count++;
    1495           0 :                                 while (val /= 10)
    1496           0 :                                         count++;
    1497           0 :                                 count++;
    1498           0 :                                 count += incr;
    1499             :                         } else {
    1500             :                                 count = 0;
    1501             :                         }
    1502             :                 }
    1503      222523 :                 if (eclass == EC_SEC && count < 5)
    1504          45 :                         count = 5;
    1505      222523 :                 return count;
    1506             :                 /* the following two could be done once by taking the
    1507             :                    max value and calculating the number of digits from that
    1508             :                    value, instead of the maximum values taken now, which
    1509             :                    include the optional sign */
    1510             :         } else if (eclass == EC_FLT) {
    1511             :                 /* floats are printed using "%.9g":
    1512             :                  * [sign]+digit+period+[max 8 digits]+E+[sign]+[max 2 digits] */
    1513        2260 :                 if (mtype == TYPE_flt) {
    1514             :                         return 15;
    1515             :                         /* doubles are printed using "%.17g":
    1516             :                          * [sign]+digit+period+[max 16 digits]+E+[sign]+[max 3 digits] */
    1517             :                 } else {        /* TYPE_dbl */
    1518        1965 :                         return 24;
    1519             :                 }
    1520             :         } else if (eclass == EC_DEC) {
    1521        1267 :                 count = 1 + digits;
    1522        1267 :                 if (scale > 0)
    1523        1030 :                         count += 1;
    1524        1267 :                 if (scale == digits) // for preceding 0, e.g. 0.
    1525          13 :                         count += 1;
    1526        1267 :                 return count;
    1527             :         } else if (eclass == EC_DATE) {
    1528             :                 return 10;
    1529             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1530         278 :                 count = 8;
    1531         278 :                 if (tz)         /* time zone */
    1532          94 :                         count += 6;     /* +03:30 */
    1533         278 :                 if (digits > 1)      /* fractional seconds precision (including dot) */
    1534          96 :                         count += digits;
    1535         278 :                 return count;
    1536             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1537         802 :                 count = 10 + 1 + 8;
    1538         802 :                 if (tz)         /* time zone */
    1539         154 :                         count += 6;     /* +03:30 */
    1540         802 :                 if (digits)     /* fractional seconds precision */
    1541         802 :                         count += digits;
    1542         802 :                 return count;
    1543             :         } else if (eclass == EC_BIT) {
    1544        6947 :                 return 5;       /* max(strlen("true"), strlen("false")) */
    1545         618 :         } else if (strcmp(ATOMname(mtype), "uuid") == 0) {
    1546             :                 return 36;      /* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx */
    1547             :         } else {
    1548         536 :                 return 0;
    1549             :         }
    1550             : }
    1551             : 
    1552             : static int
    1553      333996 : export_length(stream *s, int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1554             : {
    1555      333996 :         lng length = get_print_width(mtype, eclass, digits, scale, tz, bid, p);
    1556      334014 :         if (length < 0)
    1557             :                 return -2;
    1558      334014 :         if (mvc_send_lng(s, length) != 1)
    1559           0 :                 return -4;
    1560             :         return 0;
    1561             : }
    1562             : 
    1563             : int
    1564       19204 : mvc_export_operation(backend *b, stream *s, str w, lng starttime, lng mal_optimizer)
    1565             : {
    1566       19204 :         mvc *m = b->mvc;
    1567             : 
    1568       19204 :         assert(m->type == Q_SCHEMA || m->type == Q_TRANS);
    1569       19204 :         if (m->type == Q_SCHEMA) {
    1570       16130 :                 if (!s)
    1571             :                         return 0;
    1572       16130 :                 if (mnstr_printf(s, "&3 " LLFMT " " LLFMT "\n", starttime > 0 ? GDKusec() - starttime : 0, mal_optimizer) < 0)
    1573             :                         return -4;
    1574             :         } else {
    1575        3074 :                 if (m->session->auto_commit) {
    1576        1428 :                         if (mnstr_write(s, "&4 t\n", 5, 1) != 1)
    1577             :                                 return -4;
    1578             :                 } else {
    1579        1646 :                         if (mnstr_write(s, "&4 f\n", 5, 1) != 1)
    1580             :                                 return -4;
    1581             :                 }
    1582             :         }
    1583             : 
    1584       19204 :         if (mvc_export_warning(s, w) != 1)
    1585             :                 return -4;
    1586             :         return 0;
    1587             : }
    1588             : 
    1589             : 
    1590             : int
    1591      137826 : mvc_affrows(mvc *m, stream *s, lng val, str w, oid query_id, lng last_id, lng starttime, lng maloptimizer, lng reloptimizer)
    1592             : {
    1593      137826 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), val);
    1594             : 
    1595             :         /* if we don't have a stream, nothing can go wrong, so we return
    1596             :          * success.  This is especially vital for execution of internal SQL
    1597             :          * commands, since they don't get a stream to suppress their output.
    1598             :          * If we would fail on having no stream here, those internal commands
    1599             :          * fail too.
    1600             :          */
    1601      138308 :         if (!s || GDKembedded())
    1602       46291 :                 return 0;
    1603      183875 :         if (mnstr_write(s, "&2 ", 3, 1) != 1 ||
    1604      183705 :             mvc_send_lng(s, val) != 1 ||
    1605      184111 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1606      183749 :             mvc_send_lng(s, last_id) != 1 ||
    1607      184160 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1608      183921 :             mvc_send_lng(s, (lng) query_id) != 1 ||
    1609      183847 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1610      184036 :             mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1 ||
    1611      183850 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1612      184033 :             mvc_send_lng(s, maloptimizer) != 1 ||
    1613      184164 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1614      184001 :             mvc_send_lng(s, reloptimizer) != 1 ||
    1615       92076 :             mnstr_write(s, "\n", 1, 1) != 1)
    1616           0 :                 return -4;
    1617       91932 :         if (mvc_export_warning(s, w) != 1)
    1618             :                 return -4;
    1619             : 
    1620             :         return 0;
    1621             : }
    1622             : 
    1623             : int
    1624      137852 : mvc_export_affrows(backend *b, stream *s, lng val, str w, oid query_id, lng starttime, lng maloptimizer)
    1625             : {
    1626      137852 :         b->rowcnt = val;
    1627      137852 :         return mvc_affrows(b->mvc, s, val, w, query_id, b->last_id, starttime, maloptimizer, b->reloptimizer);
    1628             : }
    1629             : 
    1630             : int
    1631      122694 : mvc_export_head(backend *b, stream *s, int res_id, int only_header, int compute_lengths, lng starttime, lng maloptimizer)
    1632             : {
    1633      122694 :         mvc *m = b->mvc;
    1634      122694 :         int i, res = 0;
    1635      122694 :         BUN count = 0;
    1636      122694 :         res_table *t = res_tables_find(b->results, res_id);
    1637             : 
    1638      122717 :         if (!s || !t)
    1639             :                 return 0;
    1640             : 
    1641             :         /* query type: Q_TABLE || Q_PREPARE */
    1642      122717 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1643      122717 :         if (mnstr_write(s, "&", 1, 1) != 1 || mvc_send_int(s, (int) t->query_type) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1644           0 :                 return -4;
    1645             : 
    1646             :         /* id */
    1647      122719 :         int result_id = t->query_type == Q_PREPARE?b->q->id:t->id;
    1648      122719 :         if (mvc_send_int(s, result_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1649           0 :                 return -4;
    1650             : 
    1651             :         /* tuple count */
    1652      122727 :         if (only_header) {
    1653      122727 :                 if (t->cols[0].b) {
    1654      122714 :                         count = t->nr_rows;
    1655             :                 } else {
    1656             :                         count = 1;
    1657             :                 }
    1658             :         }
    1659      122727 :         b->rowcnt = count;
    1660      122727 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), b->rowcnt);
    1661      122726 :         if (mvc_send_lng(s, (lng) count) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1662           0 :                 return -4;
    1663             : 
    1664             :         /* column count */
    1665      122727 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1666           0 :                 return -4;
    1667             : 
    1668             :         /* row count, min(count, reply_size) */
    1669             :         /* the columnar protocol ignores the reply size by fetching the entire resultset at once, so don't set it */
    1670      122724 :         if (mvc_send_int(s, (b->client && b->client->protocol != PROTOCOL_COLUMNAR && m->reply_size >= 0 && (BUN) m->reply_size < count) ? m->reply_size : (int) count) != 1)
    1671             :                 return -4;
    1672             : 
    1673             :         // export query id
    1674      122724 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, (lng) t->query_id) != 1)
    1675           0 :                 return -4;
    1676             : 
    1677             :         // export query time
    1678      122726 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1)
    1679           0 :                 return -4;
    1680             : 
    1681             :         // export MAL optimizer time
    1682      122727 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, maloptimizer) != 1)
    1683           0 :                 return -4;
    1684             : 
    1685      122727 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, b->reloptimizer) != 1)
    1686           0 :                 return -4;
    1687             : 
    1688      122725 :         if (mnstr_write(s, "\n% ", 3, 1) != 1)
    1689             :                 return -4;
    1690      456717 :         for (i = 0; i < t->nr_cols; i++) {
    1691      334000 :                 res_col *c = t->cols + i;
    1692      334000 :                 size_t len = strlen(c->tn);
    1693             : 
    1694      334000 :                 if (len && mnstr_write(s, c->tn, len, 1) != 1)
    1695             :                         return -4;
    1696      333990 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1697             :                         return -4;
    1698             :         }
    1699      122717 :         if (mnstr_write(s, " # table_name\n% ", 16, 1) != 1)
    1700             :                 return -4;
    1701             : 
    1702      456682 :         for (i = 0; i < t->nr_cols; i++) {
    1703      333976 :                 res_col *c = t->cols + i;
    1704             : 
    1705      333976 :                 if (strpbrk(c->name, ", \t#\"\\")) {
    1706         185 :                         char *p;
    1707         185 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1708             :                                 return -4;
    1709        2458 :                         for (p = c->name; *p; p++) {
    1710        2273 :                                 if (*p == '"' || *p == '\\') {
    1711          27 :                                         if (mnstr_write(s, "\\", 1, 1) != 1)
    1712             :                                                 return -4;
    1713             :                                 }
    1714        2273 :                                 if (mnstr_write(s, p, 1, 1) != 1)
    1715             :                                         return -4;
    1716             :                         }
    1717         185 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1718             :                                 return -4;
    1719             :                 } else {
    1720      333791 :                         if (mnstr_write(s, c->name, strlen(c->name), 1) != 1)
    1721             :                                 return -4;
    1722             :                 }
    1723             : 
    1724      333960 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1725             :                         return -4;
    1726             :         }
    1727      122706 :         if (mnstr_write(s, " # name\n% ", 10, 1) != 1)
    1728             :                 return -4;
    1729             : 
    1730      456702 :         for (i = 0; i < t->nr_cols; i++) {
    1731      333979 :                 res_col *c = t->cols + i;
    1732             : 
    1733      333979 :                 if (mnstr_write(s, c->type.type->base.name, strlen(c->type.type->base.name), 1) != 1)
    1734             :                         return -4;
    1735      333992 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1736             :                         return -4;
    1737             :         }
    1738      122723 :         if (mnstr_write(s, " # type\n% ", 10, 1) != 1)
    1739             :                 return -4;
    1740      122709 :         if (compute_lengths) {
    1741      456702 :                 for (i = 0; i < t->nr_cols; i++) {
    1742      333978 :                         res_col *c = t->cols + i;
    1743      333978 :                         int mtype = c->type.type->localtype;
    1744      333978 :                         sql_class eclass = c->type.type->eclass;
    1745             : 
    1746      333978 :                         if ((res = export_length(s, mtype, eclass, c->type.digits, c->type.scale, type_has_tz(&c->type), c->b, c->p)) < 0)
    1747           0 :                                 return res;
    1748      333993 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1749             :                                 return -4;
    1750             :                 }
    1751      122724 :                 if (mnstr_write(s, " # length\n", 10, 1) != 1)
    1752             :                         return -4;
    1753             :         }
    1754      122710 :         if (b->sizeheader) {
    1755       92626 :                 if (mnstr_write(s, "% ", 2, 1) != 1)
    1756             :                         return -4;
    1757      351661 :                 for (i = 0; i < t->nr_cols; i++) {
    1758      259021 :                         res_col *c = t->cols + i;
    1759             : 
    1760      259021 :                         if (mnstr_printf(s, "%u %u", c->type.digits, c->type.scale) < 0)
    1761             :                                 return -4;
    1762      259036 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1763             :                                 return -4;
    1764             :                 }
    1765       92640 :                 if (mnstr_write(s, " # typesizes\n", 13, 1) != 1)
    1766             :                         return -4;
    1767             :         }
    1768             :         return res;
    1769             : }
    1770             : 
    1771             : static int
    1772          25 : mvc_export_file(backend *b, stream *s, res_table *t)
    1773             : {
    1774          25 :         int res = 0;
    1775          25 :         BUN count;
    1776             : 
    1777          25 :         if (!t->cols[0].b) {
    1778           0 :                 res = mvc_export_row(b, s, t, "", t->tsep, t->rsep, t->ssep, t->ns);
    1779             :         } else {
    1780          25 :                 count = t->nr_rows;
    1781             : 
    1782          25 :                 res = mvc_export_table(b, s, t, 0, count, "", t->tsep, t->rsep, t->ssep, t->ns);
    1783          25 :                 b->results = res_tables_remove(b->results, t);
    1784             :         }
    1785          25 :         return res;
    1786             : }
    1787             : 
    1788             : int
    1789      124020 : mvc_export_result(backend *b, stream *s, int res_id, bool header, lng starttime, lng maloptimizer)
    1790             : {
    1791      124020 :         mvc *m = b->mvc;
    1792      124020 :         int clean = 0, res = 0;
    1793      124020 :         BUN count;
    1794      124020 :         res_table *t = res_tables_find(b->results, res_id);
    1795      124001 :         int json = (b->output_format == OFMT_JSON);
    1796             : 
    1797      124001 :         if (!s || !t)
    1798             :                 return 0;
    1799             : 
    1800             :         /* Proudly supporting SQLstatementIntern's output flag */
    1801      124001 :         if (b->output_format == OFMT_NONE)
    1802             :                 return 0;
    1803             : 
    1804      122701 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1805      122701 :         if (t->tsep) {
    1806             :                 /* need header */
    1807          25 :                 if (header && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1808             :                         return res;
    1809          25 :                 return mvc_export_file(b, s, t);
    1810             :         }
    1811             : 
    1812      122676 :         if (!json && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1813             :                 return res;
    1814             : 
    1815      122692 :         assert(t->cols[0].b);
    1816             : 
    1817      122692 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
    1818           1 :                 if (mnstr_flush(s, MNSTR_FLUSH_DATA) < 0)
    1819             :                         return -4;
    1820           1 :                 return mvc_export_table_columnar(s, t, m->scanner.rs);
    1821             :         }
    1822             : 
    1823      122691 :         count = m->reply_size;
    1824      122691 :         if (m->reply_size != -2 && (count <= 0 || count >= t->nr_rows)) {
    1825      122052 :                 count = t->nr_rows;
    1826      122052 :                 clean = 1;
    1827             :         }
    1828      122691 :         if (json) {
    1829           0 :                 switch(count) {
    1830             :                 case 0:
    1831           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\t", "", "}\n", "\"", "null");
    1832           0 :                         break;
    1833             :                 case 1:
    1834           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\n\t\"%s\" : ", ",\n\t\"%s\" : ", "\n}\n", "\"", "null");
    1835           0 :                         break;
    1836             :                 case 2:
    1837           0 :                         res = mvc_export_table(b, s, t, 0, 1, "[\n\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1838           0 :                         res = mvc_export_table(b, s, t, 1, count - 1, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t}\n]\n", "\"", "null");
    1839           0 :                         break;
    1840             :                 default:
    1841           0 :                         res = mvc_export_table(b, s, t, 0, 1, "[\n\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1842           0 :                         res = mvc_export_table(b, s, t, 1, count - 2, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1843           0 :                         res = mvc_export_table(b, s, t, count - 1, 1, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t}\n]\n", "\"", "null");
    1844             :                 }
    1845             :         } else {
    1846      122691 :                 res = mvc_export_table(b, s, t, 0, count, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1847             :         }
    1848      122665 :         if (clean)
    1849      122034 :                 b->results = res_tables_remove(b->results, t);
    1850             : 
    1851      122675 :         if (res > -1)
    1852      122675 :                 res = 1;
    1853             :         return res;
    1854             : }
    1855             : 
    1856             : int
    1857          77 : mvc_export_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1858             : {
    1859          77 :         int res = 0;
    1860          77 :         res_table *t = res_tables_find(b->results, res_id);
    1861          77 :         BUN cnt;
    1862             : 
    1863          77 :         if (!s || !t)
    1864             :                 return 0;
    1865             : 
    1866          65 :         cnt = nr;
    1867          65 :         if (cnt == 0)
    1868           0 :                 cnt = t->nr_rows;
    1869          65 :         if (offset >= t->nr_rows)
    1870             :                 cnt = 0;
    1871          65 :         if (cnt == BUN_NONE || offset + cnt > t->nr_rows)
    1872          19 :                 cnt = t->nr_rows - offset;
    1873             : 
    1874             :         /* query type: Q_BLOCK */
    1875          65 :         if (mnstr_write(s, "&6 ", 3, 1) != 1)
    1876             :                 return -4;
    1877             : 
    1878             :         /* result id */
    1879          65 :         if (mvc_send_int(s, res_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1880           0 :                 return -4;
    1881             : 
    1882             :         /* column count */
    1883          65 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1884           0 :                 return -4;
    1885             : 
    1886             :         /* row count */
    1887          65 :         if (mvc_send_lng(s, (lng) cnt) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1888           0 :                 return -4;
    1889             : 
    1890             :         /* block offset */
    1891          65 :         if (mvc_send_lng(s, (lng) offset) != 1)
    1892             :                 return -4;
    1893             : 
    1894          65 :         if (mnstr_write(s, "\n", 1, 1) != 1)
    1895             :                 return -4;
    1896             : 
    1897          65 :         res = mvc_export_table(b, s, t, offset, cnt, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1898          65 :         return res;
    1899             : }
    1900             : 
    1901             : int
    1902      123717 : mvc_result_table(backend *be, oid query_id, int nr_cols, mapi_query_t type)
    1903             : {
    1904      123717 :         res_table *t = res_table_create(be->mvc->session->tr, be->result_id++, query_id, nr_cols, type, be->results);
    1905      123717 :         be->results = t;
    1906      123717 :         return t ? t->id : -1;
    1907             : }
    1908             : 
    1909             : int
    1910      274109 : mvc_result_column(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, BAT *b)
    1911             : {
    1912             :         /* return 0 on success, non-zero on failure */
    1913      274109 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, true, b->ttype, b, false) ? 0 : -1;
    1914             : }
    1915             : 
    1916             : int
    1917       61622 : mvc_result_value(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, ptr *p, int mtype)
    1918             : {
    1919             :         /* return 0 on success, non-zero on failure */
    1920       61622 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, false, mtype, p, false) ? 0 : -1;
    1921             : }
    1922             : 
    1923             : /* Translate error code from export function to error string */
    1924             : const char *
    1925           0 : mvc_export_error(backend *be, stream *s, int err_code)
    1926             : {
    1927           0 :         (void) be;
    1928           0 :         switch (err_code) {
    1929             :         case -1: /* Allocation failure */
    1930             :                 return MAL_MALLOC_FAIL;
    1931           0 :         case -2: /* BAT descriptor error */
    1932           0 :                 return RUNTIME_OBJECT_MISSING;
    1933           0 :         case -3: /* GDK error */
    1934           0 :                 return GDKerrbuf;
    1935           0 :         case -4: /* Stream error */
    1936           0 :                 return mnstr_peek_error(s);
    1937           0 :         case -5:
    1938           0 :                 return "Query aborted";
    1939           0 :         default: /* Unknown, must be a bug */
    1940           0 :                 return "Unknown internal error";
    1941             :         }
    1942             : }
    1943             : 
    1944             : static ssize_t
    1945        3829 : align_dump(stream *s, uint64_t pos, unsigned int alignment)
    1946             : {
    1947        3829 :         uint64_t a = (uint64_t)alignment;
    1948             :         // must be a power of two
    1949        3829 :         assert(a > 0);
    1950        3829 :         assert((a & (a-1)) == 0);
    1951             : 
    1952        3829 :         static char zeroes[32] = { 0 };
    1953             : #ifdef _MSC_VER
    1954             : #pragma warning(suppress:4146)
    1955             : #endif
    1956        3829 :         uint64_t gap = (~pos + 1) % a;
    1957        3829 :         return mnstr_write(s, zeroes, 1, (size_t)gap);
    1958             : }
    1959             : 
    1960             : 
    1961             : struct bindump_record {
    1962             :         BAT *bat;
    1963             :         type_record_t *type_rec;
    1964             :         int64_t start;
    1965             :         int64_t length;
    1966             : };
    1967             : 
    1968             : int
    1969         600 : mvc_export_bin_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1970             : {
    1971         600 :         int ret = -42;
    1972         600 :         struct bindump_record *colinfo;
    1973         600 :         stream *countstream = NULL;
    1974         600 :         uint64_t byte_count = 0;
    1975         600 :         uint64_t toc_pos = 0;
    1976         600 :         BUN end_row = offset + nr;
    1977             : 
    1978         600 :         res_table *res = res_tables_find(b->results, res_id);
    1979         600 :         if (res == NULL)
    1980             :                 return 0;
    1981             : 
    1982         600 :         colinfo = GDKzalloc(res->nr_cols * sizeof(*colinfo));
    1983         600 :         if (!colinfo) {
    1984           0 :                 ret = -1;
    1985           0 :                 goto end;
    1986             :         }
    1987        3829 :         for (int i = 0; i < res->nr_cols; i++)
    1988        3229 :                 colinfo[i].bat = NULL;
    1989        3829 :         for (int i = 0; i < res->nr_cols; i++) {
    1990        3229 :                 bat bat_id = res->cols[i].b;
    1991        3229 :                 BAT *b = BATdescriptor(bat_id);
    1992        3229 :                 if (!b) {
    1993           0 :                         ret = -1;
    1994           0 :                         goto end;
    1995             :                 }
    1996        3229 :                 colinfo[i].bat = b;
    1997             : 
    1998        3229 :                 if (BATcount(b) < end_row)
    1999             :                         end_row = BATcount(b);
    2000             : 
    2001        3229 :                 int tpe = BATttype(b);
    2002        3229 :                 const char *gdk_name = ATOMname(tpe);
    2003        3229 :                 type_record_t *rec = find_type_rec(gdk_name);
    2004        3229 :                 if (!rec || !can_dump_binary_column(rec)) {
    2005           0 :                         GDKerror("column %d: don't know how to dump data type '%s'", i, gdk_name);
    2006           0 :                         ret = -3;
    2007           0 :                         goto end;
    2008             :                 }
    2009        3229 :                 colinfo[i].type_rec = rec;
    2010             :         }
    2011             : 
    2012             :         // The byte_counting_stream keeps track of the byte offsets
    2013         600 :         countstream = byte_counting_stream(s, &byte_count);
    2014             : 
    2015             :         // Make sure the message starts with a & and not with a !
    2016         600 :         mnstr_printf(countstream, "&6 %d %d " BUNFMT " " BUNFMT "\n", res_id, res->nr_cols, end_row - offset, offset);
    2017             : 
    2018        3829 :         for (int i = 0; i < res->nr_cols; i++) {
    2019        3229 :                 align_dump(countstream, byte_count, 32); // 32 looks nice in tcpflow
    2020        3229 :                 struct bindump_record *info = &colinfo[i];
    2021        3229 :                 info->start = byte_count;
    2022        3229 :                 str msg = dump_binary_column(info->type_rec, info->bat, offset, end_row - offset, false, countstream);
    2023        3229 :                 if (msg != MAL_SUCCEED) {
    2024           0 :                         GDKerror("%s", msg);
    2025           0 :                         GDKfree(msg);
    2026           0 :                         ret = -3;
    2027           0 :                         goto end;
    2028             :                 }
    2029        3229 :                 info->length = byte_count - info->start;
    2030             :         }
    2031             : 
    2032         600 :         assert(byte_count > 0);
    2033             : 
    2034         600 :         align_dump(countstream, byte_count, 32);
    2035         600 :         toc_pos = byte_count;
    2036        3829 :         for (int i = 0; i < res->nr_cols; i++) {
    2037        3229 :                 struct bindump_record *info = &colinfo[i];
    2038        3229 :                 lng start = info->start;
    2039        3229 :                 lng length = info->length;
    2040        3229 :                 mnstr_writeLng(countstream, start);
    2041        3229 :                 mnstr_writeLng(countstream, length);
    2042             :         }
    2043             : 
    2044         600 :         mnstr_writeLng(countstream, toc_pos);
    2045         600 :         ret = 0;
    2046             : 
    2047         600 : end:
    2048         600 :         if (colinfo) {
    2049        3829 :                 for (int i = 0; i < res->nr_cols; i++) {
    2050        3229 :                         if (colinfo[i].bat)
    2051        3229 :                                 BBPunfix(colinfo[i].bat->batCacheid);
    2052             :                 }
    2053         600 :                 GDKfree(colinfo);
    2054             :         }
    2055         600 :         mnstr_destroy(countstream);
    2056         600 :         return ret;
    2057             : }

Generated by: LCOV version 1.14