LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_result.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 796 1100 72.4 %
Date: 2024-04-26 00:35:57 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       21006 : 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       21006 :         if (type == TYPE_bte) {
     109         357 :                 DEC_TOSTR(bte);
     110             :         } else if (type == TYPE_sht) {
     111        1294 :                 DEC_TOSTR(sht);
     112             :         } else if (type == TYPE_int) {
     113       20356 :                 DEC_TOSTR(int);
     114             :         } else if (type == TYPE_lng) {
     115       48216 :                 DEC_TOSTR(lng);
     116             : #ifdef HAVE_HGE
     117             :         } else if (type == TYPE_hge) {
     118       92811 :                 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        2637 : sql_time_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     134             : {
     135        2637 :         struct time_res *ts_res = TS_RES;
     136        2637 :         ssize_t len1;
     137        2637 :         size_t big = 128;
     138        2637 :         char buf1[128], *s1 = buf1, *s;
     139        2637 :         daytime tmp;
     140             : 
     141        2637 :         (void) type;
     142        2637 :         tmp = *(const daytime *) A;
     143        2637 :         if (ts_res->has_tz)
     144         200 :                 tmp = daytime_add_usec_modulo(tmp, ts_res->timezone * 1000);
     145             : 
     146        2637 :         len1 = daytime_precision_tostr(&s1, &big, tmp, ts_res->fraction, true);
     147        2637 :         if (len1 < 0)
     148             :                 return -1;
     149        2637 :         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        2637 :         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        2637 :         s = *buf;
     168        2637 :         strcpy(s, buf1);
     169        2637 :         s += len1;
     170             : 
     171        2637 :         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        2637 :         return (ssize_t) (s - *buf);
     178             : }
     179             : 
     180             : static ssize_t
     181        1493 : sql_timestamp_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     182             : {
     183        1493 :         struct time_res *ts_res = TS_RES;
     184        1493 :         ssize_t len1, len2;
     185        1493 :         size_t big = 128;
     186        1493 :         char buf1[128], buf2[128], *s, *s1 = buf1, *s2 = buf2;
     187        1493 :         timestamp tmp;
     188        1493 :         lng timezone = ts_res->timezone;
     189        1493 :         date days;
     190        1493 :         daytime usecs;
     191             : 
     192        1493 :         (void) type;
     193        1493 :         tmp = *(const timestamp *)A;
     194        1493 :         if (ts_res->has_tz) {
     195         526 :                 tmp = timestamp_add_usec(tmp, timezone * 1000);
     196             :         }
     197        1493 :         days = timestamp_date(tmp);
     198        1493 :         usecs = timestamp_daytime(tmp);
     199        1493 :         len1 = date_tostr(&s1, &big, &days, true);
     200        1494 :         len2 = daytime_precision_tostr(&s2, &big, usecs, ts_res->fraction, true);
     201        1494 :         if (len1 < 0 || len2 < 0) {
     202           0 :                 GDKfree(s1);
     203           0 :                 GDKfree(s2);
     204           0 :                 return -1;
     205             :         }
     206             : 
     207        1494 :         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        1494 :         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        1494 :         s = *buf;
     227        1494 :         strcpy(s, buf1);
     228        1494 :         s += len1;
     229        1494 :         *s++ = ' ';
     230        1494 :         strcpy(s, buf2);
     231        1494 :         s += len2;
     232        1494 :         s[0] = 0;
     233             : 
     234        1494 :         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        1494 :         return (ssize_t) (s - *buf);
     241             : }
     242             : 
     243             : static int
     244       86710 : bat_max_strlength(BAT *b)
     245             : {
     246       86710 :         BUN p, q;
     247       86710 :         int l = 0;
     248       86710 :         int max = 0;
     249       86710 :         BATiter bi = bat_iterator(b);
     250             : 
     251     4391554 :         BATloop(b, p, q) {
     252     4304844 :                 l = UTF8_strwidth((const char *) BUNtvar(bi, p));
     253             : 
     254     4304844 :                 if (is_int_nil(l))
     255      283294 :                         l = 0;
     256     4304844 :                 if (l > max)
     257             :                         max = l;
     258             :         }
     259       86710 :         bat_iterator_end(&bi);
     260       86710 :         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     1417565 : bat_max_length(bte, lng)
     296     1197585 : bat_max_length(sht, lng)
     297     5736664 : bat_max_length(int, lng)
     298      806161 : bat_max_length(lng, lng)
     299             : #ifdef HAVE_HGE
     300        5670 : 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    63966039 : dec_frstr(Column *c, int type, const char *s)
     366             : {
     367    63966039 :         assert(c->decsep != '\0');
     368             : 
     369             :         /* support dec map to bte, sht, int and lng */
     370    63966039 :         if( strcmp(s,"nil")== 0)
     371             :                 return NULL;
     372    63966039 :         if (type == TYPE_bte) {
     373        8329 :                 DEC_FRSTR(bte);
     374             :         } else if (type == TYPE_sht) {
     375       21188 :                 DEC_FRSTR(sht);
     376             :         } else if (type == TYPE_int) {
     377   476163126 :                 DEC_FRSTR(int);
     378             :         } else if (type == TYPE_lng) {
     379     4214228 :                 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     1912581 : 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     1912581 :         char tmp[120];
     464     1912581 :         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     1912581 :         ssize_t len = (*BATatoms[type].atomFromStr) (s, &c->len, &c->data, false);
     505     1913724 :         return (len > 0) ? c->data : NULL;
     506             : }
     507             : 
     508             : /* Literal parsing for SQL all pass through this routine */
     509             : static void *
     510   256042574 : _ASCIIadt_frStr(Column *c, int type, const char *s)
     511             : {
     512   256042574 :         ssize_t len;
     513             : 
     514   256042574 :         len = (*BATatoms[type].atomFromStr) (s, &c->len, &c->data, false);
     515   264001661 :         if (len < 0)
     516             :                 return NULL;
     517   264001648 :         switch (type) {
     518   234531246 :         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   234531246 :                 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    28675029 :         case TYPE_str: {
     537    28675029 :                 sql_column *col = (sql_column *) c->extra;
     538    28675029 :                 int slen;
     539             : 
     540    28675029 :                 s = c->data;
     541    57350058 :                 slen = strNil(s) ? int_nil : UTF8_strlen(s);
     542    28788568 :                 if (col->type.digits > 0 && len > 0 && slen > (int) col->type.digits) {
     543           5 :                         len = UTF8_strwidth(c->data);
     544           5 :                         if (len > (ssize_t) col->type.digits)
     545             :                                 return NULL;
     546             :                 }
     547             :                 break;
     548             :         }
     549             :         default:
     550             :                 break;
     551             :         }
     552   264115174 :         return c->data;
     553             : }
     554             : 
     555             : 
     556             : static ssize_t
     557    14380405 : _ASCIIadt_toStr(void *extra, char **buf, size_t *len, int type, const void *a)
     558             : {
     559    14380405 :         if (type == TYPE_str) {
     560     8333778 :                 Column *c = extra;
     561     8333778 :                 char *dst;
     562     8333778 :                 const char *src = a;
     563     8333778 :                 size_t l = escapedStrlen(src, c->sep, c->rsep, c->quote), l2 = 0;
     564             : 
     565     8333778 :                 if (c->quote)
     566     8333778 :                         l = escapedStrlen(src, NULL, NULL, c->quote);
     567             :                 else
     568           0 :                         l = escapedStrlen(src, c->sep, c->rsep, 0);
     569     8333778 :                 if (l + 3 > *len) {
     570          52 :                         GDKfree(*buf);
     571          52 :                         *len = 2 * l + 3;
     572          52 :                         *buf = GDKzalloc(*len);
     573          52 :                         if (*buf == NULL) {
     574             :                                 return -1;
     575             :                         }
     576             :                 }
     577     8333778 :                 dst = *buf;
     578     8333778 :                 if (c->quote) {
     579     8333778 :                         dst[0] = c->quote;
     580     8333778 :                         l2 = 1;
     581     8333778 :                         l = escapedStr(dst + l2, src, *len - l2, NULL, NULL, c->quote);
     582             :                 } else {
     583           0 :                         l = escapedStr(dst + l2, src, *len - l2, c->sep, c->rsep, 0);
     584             :                 }
     585           0 :                 if (l2) {
     586     8333778 :                         dst[l + l2] = c->quote;
     587     8333778 :                         l2++;
     588             :                 }
     589     8333778 :                 dst[l + l2] = 0;
     590     8333778 :                 return l + l2;
     591             :         } else {
     592     6046627 :                 return (*BATatoms[type].atomToStr) (buf, len, a, true);
     593             :         }
     594             : }
     595             : 
     596             : 
     597             : static int
     598       10050 : has_whitespace(const char *s)
     599             : {
     600       10050 :         if (*s == ' ' || *s == '\t')
     601             :                 return 1;
     602        7330 :         while (*s)
     603        3676 :                 s++;
     604        3654 :         s--;
     605        3654 :         if (*s == ' ' || *s == '\t')
     606           0 :                 return 1;
     607             :         return 0;
     608             : }
     609             : 
     610             : str
     611        1070 : 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)
     612             : {
     613        1070 :         int i = 0, j;
     614        1070 :         node *n;
     615        1070 :         Tablet as;
     616        1070 :         Column *fmt;
     617        1070 :         str msg = MAL_SUCCEED;
     618             : 
     619        1070 :         *bats =0;       // initialize the receiver
     620             : 
     621        1070 :         if (!bs)
     622           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "No stream (pointer) provided");
     623        1070 :         if (mnstr_errnr(bs->s) != MNSTR_NO__ERROR) {
     624           0 :                 mnstr_error_kind errnr = mnstr_errnr(bs->s);
     625           0 :                 const char *stream_msg = mnstr_peek_error(bs->s);
     626           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");
     627           0 :                 return msg;
     628             :         }
     629        1070 :         if (offset < 0 || offset > (lng) BUN_MAX)
     630           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "Offset out of range");
     631             : 
     632        1070 :         if (offset > 0)
     633          22 :                 offset--;
     634        1070 :         if (ol_first_node(t->columns)) {
     635        1070 :                 stream *out = m->scanner.ws;
     636             : 
     637        2140 :                 as = (Tablet) {
     638        1070 :                         .nr_attrs = ol_length(t->columns),
     639        1070 :                         .nr = (sz < 1) ? BUN_NONE : (BUN) sz,
     640        1070 :                         .offset = (BUN) offset,
     641             :                         .error = NULL,
     642             :                         .tryall = 0,
     643             :                         .complaints = NULL,
     644        1070 :                         .filename = m->scanner.rs == bs ? NULL : "",
     645             :                 };
     646        1070 :                 fmt = GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
     647        1070 :                 if (fmt == NULL)
     648           0 :                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     649        1070 :                 as.format = fmt;
     650        1070 :                 if (!isa_block_stream(bs->s))
     651         547 :                         out = NULL;
     652             : 
     653       11120 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     654       10050 :                         sql_column *col = n->data;
     655             : 
     656       10050 :                         fmt[i].name = col->base.name;
     657       10050 :                         fmt[i].sep = (n->next) ? sep : rsep;
     658       10050 :                         fmt[i].rsep = rsep;
     659       10050 :                         fmt[i].seplen = _strlen(fmt[i].sep);
     660       10050 :                         fmt[i].decsep = decsep[0],
     661       10050 :                         fmt[i].decskip = decskip != NULL ? decskip[0] : '\0',
     662       10050 :                         fmt[i].type = sql_subtype_string(m->ta, &col->type);
     663       10050 :                         fmt[i].adt = ATOMindex(col->type.type->impl);
     664       10050 :                         fmt[i].tostr = &_ASCIIadt_toStr;
     665       10050 :                         fmt[i].frstr = &_ASCIIadt_frStr;
     666       10050 :                         fmt[i].extra = col;
     667       10050 :                         fmt[i].len = ATOMlen(fmt[i].adt, ATOMnilptr(fmt[i].adt));
     668       10050 :                         fmt[i].data = GDKzalloc(fmt[i].len);
     669       10050 :                         if(fmt[i].data == NULL || fmt[i].type == NULL) {
     670           0 :                                 for (j = 0; j < i; j++) {
     671           0 :                                         GDKfree(fmt[j].data);
     672           0 :                                         BBPunfix(fmt[j].c->batCacheid);
     673             :                                 }
     674           0 :                                 GDKfree(fmt[i].data);
     675           0 :                                 GDKfree(fmt);
     676           0 :                                 throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     677             :                         }
     678       10050 :                         fmt[i].c = NULL;
     679       10050 :                         fmt[i].ws = !has_whitespace(fmt[i].sep);
     680       10050 :                         fmt[i].quote = ssep ? ssep[0] : 0;
     681       10050 :                         fmt[i].nullstr = ns;
     682       10050 :                         fmt[i].null_length = strlen(ns);
     683       10050 :                         fmt[i].nildata = ATOMnilptr(fmt[i].adt);
     684       10050 :                         fmt[i].skip = (col->base.name[0] == '%');
     685       10050 :                         if (col->type.type->eclass == EC_DEC) {
     686         338 :                                 fmt[i].tostr = &dec_tostr;
     687         338 :                                 fmt[i].frstr = &dec_frstr;
     688        9712 :                         } else if (col->type.type->eclass == EC_SEC) {
     689          87 :                                 fmt[i].tostr = &dec_tostr;
     690          87 :                                 fmt[i].frstr = &sec_frstr;
     691        9625 :                         } else if (col->type.type->eclass == EC_FLT) {
     692             :                                 // no need to override .tostr, only .frstr
     693        3905 :                                 fmt[i].frstr = &fltdbl_frStr;
     694             :                         }
     695       10050 :                         fmt[i].size = ATOMsize(fmt[i].adt);
     696             :                 }
     697        2070 :                 if ((msg = TABLETcreate_bats(&as, (BUN) (sz < 0 ? 1000 : sz))) == MAL_SUCCEED){
     698        1070 :                         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 &&
     699        1034 :                                 (best || !as.error))) {
     700        1046 :                                 *bats = (BAT**) GDKzalloc(sizeof(BAT *) * as.nr_attrs);
     701        1046 :                                 if ( *bats == NULL){
     702           0 :                                         TABLETdestroy_format(&as);
     703           0 :                                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     704             :                                 }
     705        1046 :                                 msg = TABLETcollect(*bats,&as);
     706             :                         }
     707             :                 }
     708        1070 :                 if (as.error) {
     709          29 :                         if( !best) msg = createException(SQL, "sql.copy_from", SQLSTATE(42000) "Failed to import table '%s', %s", t->base.name, getExceptionMessage(as.error));
     710          29 :                         freeException(as.error);
     711          29 :                         as.error = NULL;
     712             :                 }
     713       11120 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     714       10050 :                         fmt[i].sep = NULL;
     715       10050 :                         fmt[i].rsep = NULL;
     716       10050 :                         fmt[i].nullstr = NULL;
     717             :                 }
     718        1070 :                 TABLETdestroy_format(&as);
     719             :         }
     720             :         return msg;
     721             : }
     722             : 
     723             : /*
     724             :  * mvc_export_result dumps the sql header information and the
     725             :  * first part (reply_size) of the result set. It should be produced in Monet format to
     726             :  * enable mapi to work with it.
     727             :  */
     728             : 
     729             : static int
     730      108124 : mvc_export_warning(stream *s, str w)
     731             : {
     732      108124 :         str tmp = NULL;
     733      107961 :         while (w != NULL && *w != '\0') {
     734           0 :                 if ((tmp = strchr(w, (int) '\n')) != NULL)
     735           0 :                         *tmp++ = '\0';
     736           0 :                 if (mnstr_printf(s, "#%s", w) < 0)
     737             :                         return -4;
     738             :                 w = tmp;
     739             :         }
     740             :         return 1;
     741             : }
     742             : 
     743             : static int
     744           2 : mvc_export_binary_bat(stream *s, BAT* bn, bstream *in)
     745             : {
     746           2 :         BATiter bni = bat_iterator(bn);
     747           2 :         bool sendtheap = bni.type != TYPE_void, sendtvheap = sendtheap && bni.vh;
     748             : 
     749           4 :         if (mnstr_printf(s, /*JSON*/"{"
     750             :                                          "\"version\":1,"
     751             :                                          "\"ttype\":%d,"
     752             :                                          "\"hseqbase\":" OIDFMT ","
     753             :                                          "\"tseqbase\":" OIDFMT ","
     754             :                                          "\"tsorted\":%d,"
     755             :                                          "\"trevsorted\":%d,"
     756             :                                          "\"tkey\":%d,"
     757             :                                          "\"tnonil\":%d,"
     758             :                                          "\"tdense\":%d,"
     759             :                                          "\"size\":" BUNFMT ","
     760             :                                          "\"tailsize\":%zu,"
     761             :                                          "\"theapsize\":%zu"
     762             :                                          "}\n",
     763             :                                          bni.type,
     764             :                                          bn->hseqbase, bn->tseqbase,
     765           2 :                                          bni.sorted, bni.revsorted,
     766           2 :                                          bni.key,
     767           2 :                                          bni.nonil,
     768           2 :                                          BATtdensebi(&bni),
     769             :                                          bn->batCount,
     770           2 :                                          sendtheap ? (size_t)bni.count << bni.shift : 0,
     771           1 :                                          sendtvheap && bni.count > 0 ? bni.vhfree : 0) < 0) {
     772           0 :                 bat_iterator_end(&bni);
     773           0 :                 return -4;
     774             :         }
     775             : 
     776           2 :         if (sendtheap && bni.count > 0) {
     777           2 :                 if (mnstr_write(s, /* tail */ bni.base, bni.count * bni.width, 1) < 1) {
     778           0 :                         bat_iterator_end(&bni);
     779           0 :                         return -4;
     780             :                 }
     781           2 :                 if (sendtvheap && mnstr_write(s, /* tvheap */ bni.vh->base, bni.vhfree, 1) < 1) {
     782           0 :                         bat_iterator_end(&bni);
     783           0 :                         return -4;
     784             :                 }
     785             :         }
     786           2 :         bat_iterator_end(&bni);
     787           2 :         if (bstream_getoob(in))
     788           0 :                 return -5;
     789             :         return 0;
     790             : }
     791             : 
     792             : static int
     793         334 : create_prepare_result(backend *b, cq *q, int nrows)
     794             : {
     795         334 :         int error = 0;
     796             : 
     797         334 :         BAT* btype              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     798         334 :         BAT* bimpl              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     799         334 :         BAT* bdigits    = COLnew(0, TYPE_int, nrows, TRANSIENT);
     800         334 :         BAT* bscale             = COLnew(0, TYPE_int, nrows, TRANSIENT);
     801         334 :         BAT* bschema    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     802         334 :         BAT* btable             = COLnew(0, TYPE_str, nrows, TRANSIENT);
     803         334 :         BAT* bcolumn    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     804         334 :         node *n;
     805             : 
     806         334 :         const int nr_columns = (b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) ? 7 : 6;
     807             : 
     808         334 :         int len1 = 0, len4 = 0, len5 = 0, len6 = 0, len7 =0;    /* column widths */
     809         334 :         int len2 = 1, len3 = 1;
     810         334 :         sql_arg *a;
     811         334 :         sql_subtype *t;
     812         334 :         sql_rel *r = q->rel;
     813             : 
     814         334 :         if (!btype || !bimpl || !bdigits || !bscale || !bschema || !btable || !bcolumn) {
     815           0 :                 error = -1;
     816           0 :                 goto wrapup;
     817             :         }
     818             : 
     819         334 :         if (r && (is_topn(r->op) || is_sample(r->op)))
     820           2 :                 r = r->l;
     821         334 :         if (r && is_project(r->op) && r->exps) {
     822         287 :                 unsigned int max2 = 10, max3 = 10;      /* to help calculate widths */
     823         287 :                 nrows += list_length(r->exps);
     824             : 
     825        1796 :                 for (n = r->exps->h; n; n = n->next) {
     826        1509 :                         const char *name = NULL, *rname = NULL, *schema = NULL;
     827        1509 :                         sql_exp *e = n->data;
     828        1509 :                         int slen;
     829             : 
     830        1509 :                         t = exp_subtype(e);
     831        1509 :                         slen = (int) strlen(t->type->base.name);
     832        1509 :                         if (slen > len1)
     833             :                                 len1 = slen;
     834        1594 :                         while (t->digits >= max2) {
     835          85 :                                 len2++;
     836          85 :                                 max2 *= 10;
     837             :                         }
     838        1509 :                         while (t->scale >= max3) {
     839           0 :                                 len3++;
     840           0 :                                 max3 *= 10;
     841             :                         }
     842        1509 :                         rname = exp_relname(e);
     843        1509 :                         if (!rname && e->type == e_column && e->l)
     844        1509 :                                 rname = e->l;
     845        1509 :                         slen = name ? (int) strlen(name) : 0;
     846        1509 :                         if (slen > len5)
     847             :                                 len5 = slen;
     848        1509 :                         name = exp_name(e);
     849        1509 :                         if (!name && e->type == e_column && e->r)
     850           0 :                                 name = e->r;
     851        1509 :                         slen = name ? (int) strlen(name) : 0;
     852        1509 :                         if (slen > len6)
     853             :                                 len6 = slen;
     854        1509 :                         slen = (int) strlen(t->type->impl);
     855        1509 :                         if (slen > len7)
     856             :                                 len7 = slen;
     857             : 
     858        1509 :                         if (!schema)
     859        1509 :                                 schema = "";
     860             : 
     861        1509 :                         if (!rname)
     862           7 :                                 rname = "";
     863             : 
     864        1509 :                         if (!name)
     865           0 :                                 name = "";
     866             : 
     867        3018 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     868        3018 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     869        3018 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     870        3018 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     871        3018 :                                         BUNappend(bschema,      schema                          , false) != GDK_SUCCEED ||
     872        3018 :                                         BUNappend(btable,       rname                           , false) != GDK_SUCCEED ||
     873        1509 :                                         BUNappend(bcolumn,      name                            , false) != GDK_SUCCEED) {
     874           0 :                                 error = -3;
     875           0 :                                 goto wrapup;
     876             :                         }
     877             :                 }
     878             :         }
     879             : 
     880         334 :         if (q->f->ops) {
     881         242 :                 int i;
     882             : 
     883        1794 :                 for (n = q->f->ops->h, i = 0; n; n = n->next, i++) {
     884        1552 :                         a = n->data;
     885        1552 :                         t = &a->type;
     886             : 
     887        3104 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     888        3104 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     889        3104 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     890        3104 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     891        3104 :                                         BUNappend(bschema,      str_nil                         , false) != GDK_SUCCEED ||
     892        3104 :                                         BUNappend(btable,       str_nil                         , false) != GDK_SUCCEED ||
     893        1552 :                                         BUNappend(bcolumn,      str_nil                         , false) != GDK_SUCCEED) {
     894           0 :                                 error = -3;
     895           0 :                                 goto wrapup;
     896             :                         }
     897             :                 }
     898             :         }
     899             : 
     900             :         // A little hack to inform the result receiver of the name of the compiled mal program.
     901         334 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
     902           0 :                 if (    BUNappend(btype,        str_nil         , false) != GDK_SUCCEED ||
     903           0 :                                 BUNappend(bimpl,        str_nil         , false) != GDK_SUCCEED ||
     904           0 :                                 BUNappend(bdigits,      &int_nil    , false) != GDK_SUCCEED ||
     905           0 :                                 BUNappend(bscale,       &int_nil    , false) != GDK_SUCCEED ||
     906           0 :                                 BUNappend(bschema,      str_nil         , false) != GDK_SUCCEED ||
     907           0 :                                 BUNappend(btable,       q->f->imp , false) != GDK_SUCCEED ||
     908           0 :                                 BUNappend(bcolumn,      str_nil         , false) != GDK_SUCCEED) {
     909           0 :                         error = -3;
     910           0 :                         goto wrapup;
     911             :                 }
     912             :         }
     913             : 
     914         148 :         b->results = res_table_create(
     915         334 :                                                         b->mvc->session->tr,
     916         334 :                                                         b->result_id++,
     917         334 :                                                         b->mb? b->mb->tag: 0 /*TODO check if this is sensible*/,
     918             :                                                         nr_columns,
     919             :                                                         Q_PREPARE,
     920             :                                                         b->results);
     921         334 :         if (!b->results) {
     922           0 :                 error = -1;
     923           0 :                 goto wrapup;
     924             :         }
     925             : 
     926         668 :         if (    mvc_result_column(b, ".prepare", "type"             , "varchar",  len1, 0, btype  ) ||
     927         668 :                         mvc_result_column(b, ".prepare", "digits"   , "int",              len2, 0, bdigits) ||
     928         668 :                         mvc_result_column(b, ".prepare", "scale"    , "int",              len3, 0, bscale ) ||
     929         668 :                         mvc_result_column(b, ".prepare", "schema"   , "varchar",  len4, 0, bschema) ||
     930         668 :                         mvc_result_column(b, ".prepare", "table"    , "varchar",  len5, 0, btable ) ||
     931         334 :                         mvc_result_column(b, ".prepare", "column"   , "varchar",  len6, 0, bcolumn)) {
     932           0 :                 error = -1;
     933           0 :                 goto wrapup;
     934             :         }
     935             : 
     936         334 :         if ((b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) && mvc_result_column(b, "prepare", "impl" , "varchar", len7, 0, bimpl))
     937           0 :                 error = -1;
     938             : 
     939         334 :         wrapup:
     940         334 :                 BBPreclaim(btype);
     941         334 :                 BBPreclaim(bdigits);
     942         334 :                 BBPreclaim(bimpl);
     943         334 :                 BBPreclaim(bscale);
     944         334 :                 BBPreclaim(bschema);
     945         334 :                 BBPreclaim(btable);
     946         334 :                 BBPreclaim(bcolumn);
     947         334 :                 if (error < 0 && b->results) {
     948           0 :                         res_table_destroy(b->results);
     949           0 :                         b->results = NULL;
     950             :                 }
     951         334 :                 return error;
     952             : }
     953             : 
     954             : int
     955         334 : mvc_export_prepare(backend *b, stream *out)
     956             : {
     957         334 :         cq *q = b->q;
     958         334 :         int nparam = q->f->ops ? list_length(q->f->ops) : 0;
     959         334 :         int nrows = nparam, res;
     960             : 
     961         334 :         if ((res = create_prepare_result(b, q, nrows)) < 0)
     962             :                 return res;
     963             : 
     964         334 :         return mvc_export_result(b, out, b->results->id /*TODO is this right?*/, true, 0 /*TODO*/, 0 /*TODO*/);
     965             : }
     966             : 
     967             : /*
     968             :  * improved formatting of positive integers
     969             :  */
     970             : 
     971             : static ssize_t
     972           0 : mvc_send_bte(stream *s, bte cnt)
     973             : {
     974           0 :         char buf[50], *b;
     975           0 :         int neg = cnt < 0;
     976           0 :         if (neg)
     977           0 :                 cnt = -cnt;
     978             :         b = buf + 49;
     979           0 :         do {
     980           0 :                 *b-- = (char) ('0' + (cnt % 10));
     981           0 :                 cnt /= 10;
     982           0 :         } while (cnt > 0);
     983           0 :         if (neg)
     984           0 :                 *b = '-';
     985             :         else
     986             :                 b++;
     987           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     988             : }
     989             : 
     990             : static ssize_t
     991           0 : mvc_send_sht(stream *s, sht cnt)
     992             : {
     993           0 :         char buf[50], *b;
     994           0 :         int neg = cnt < 0;
     995           0 :         if (neg)
     996           0 :                 cnt = -cnt;
     997             :         b = buf + 49;
     998           0 :         do {
     999           0 :                 *b-- = (char) ('0' + (cnt % 10));
    1000           0 :                 cnt /= 10;
    1001           0 :         } while (cnt > 0);
    1002           0 :         if (neg)
    1003           0 :                 *b = '-';
    1004             :         else
    1005             :                 b++;
    1006           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1007             : }
    1008             : 
    1009             : static ssize_t
    1010      475823 : mvc_send_int(stream *s, int cnt)
    1011             : {
    1012      475823 :         char buf[50], *b;
    1013      475823 :         int neg = cnt < 0;
    1014      475823 :         if (neg)
    1015             :                 cnt = -cnt;
    1016             :         b = buf + 49;
    1017      640424 :         do {
    1018      640424 :                 *b-- = (char) ('0' + (cnt % 10));
    1019      640424 :                 cnt /= 10;
    1020      640424 :         } while (cnt > 0);
    1021      475823 :         if (neg)
    1022           0 :                 *b = '-';
    1023             :         else
    1024             :                 b++;
    1025      475823 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1026             : }
    1027             : 
    1028             : static ssize_t
    1029     1449319 : mvc_send_lng(stream *s, lng cnt)
    1030             : {
    1031     1449319 :         char buf[50], *b;
    1032     1449319 :         int neg = cnt < 0;
    1033     1449319 :         if (neg)
    1034             :                 cnt = -cnt;
    1035             :         b = buf + 49;
    1036     3022984 :         do {
    1037     3022984 :                 *b-- = (char) ('0' + (cnt % 10));
    1038     3022984 :                 cnt /= 10;
    1039     3022984 :         } while (cnt > 0);
    1040     1449319 :         if (neg)
    1041       82452 :                 *b = '-';
    1042             :         else
    1043             :                 b++;
    1044     1449319 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1045             : }
    1046             : 
    1047             : #ifdef HAVE_HGE
    1048             : static ssize_t
    1049           0 : mvc_send_hge(stream *s, hge cnt)
    1050             : {
    1051           0 :         char buf[50], *b;
    1052           0 :         int neg = cnt <0;
    1053           0 :         if(neg) cnt = -cnt;
    1054             :         b= buf+49;
    1055           0 :         do{
    1056           0 :                 *b--= (char) ('0'+ (cnt % 10));
    1057           0 :                 cnt /=10;
    1058           0 :         } while(cnt>0);
    1059           0 :         if( neg)
    1060           0 :                 *b = '-';
    1061             :         else b++;
    1062           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
    1063             : }
    1064             : #endif
    1065             : 
    1066             : ssize_t
    1067    11163535 : convert2str(mvc *m, sql_class eclass, int d, int sc, int has_tz, const void *p, int mtype, char **buf, size_t *len)
    1068             : {
    1069    11163535 :         ssize_t l = 0;
    1070             : 
    1071    11163535 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
    1072       21538 :                 (*buf)[0] = '\200';
    1073       21538 :                 (*buf)[1] = 0;
    1074    11216866 :         } else if (eclass == EC_DEC) {
    1075         267 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
    1076             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1077          84 :                 struct time_res ts_res;
    1078          84 :                 ts_res.has_tz = has_tz;
    1079          84 :                 ts_res.fraction = d ? d - 1 : 0;
    1080          84 :                 ts_res.timezone = m->timezone;
    1081          84 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
    1082             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1083          96 :                 struct time_res ts_res;
    1084          96 :                 ts_res.has_tz = has_tz;
    1085          96 :                 ts_res.fraction = d ? d - 1 : 0;
    1086          96 :                 ts_res.timezone = m->timezone;
    1087          96 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1088             :         } else if (eclass == EC_SEC) {
    1089          20 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1090             :         } else if (eclass == EC_BIT) {
    1091        2568 :                 bit b = *(bit *) p;
    1092        2568 :                 if (*len == 0 || *len > 5) {
    1093        2553 :                         if (b) {
    1094        2454 :                                 strcpy(*buf, "true");
    1095        2454 :                                 l = 4;
    1096             :                         } else {
    1097          99 :                                 strcpy(*buf, "false");
    1098          99 :                                 l = 5;
    1099             :                         }
    1100             :                 } else {
    1101          15 :                         (*buf)[0] = b?'t':'f';
    1102          15 :                         (*buf)[1] = 0;
    1103          15 :                         l = 1;
    1104             :                 }
    1105             :         } else {
    1106    11213831 :                 l = (*BATatoms[mtype].atomToStr) (buf, len, p, false);
    1107             :         }
    1108    11086506 :         return l;
    1109             : }
    1110             : 
    1111             : static int
    1112           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)
    1113             : {
    1114           0 :         int ok = 0;
    1115           0 :         ssize_t l = 0;
    1116             : 
    1117           0 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
    1118           0 :                 if (mnstr_write(s, ns, strlen(ns), 1) < 1)
    1119           0 :                         ok = -4;
    1120           0 :         } else if (eclass == EC_DEC) {
    1121           0 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
    1122           0 :                 if (l > 0 && mnstr_write(s, *buf, l, 1) < 1)
    1123           0 :                         ok = -4;
    1124           0 :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1125           0 :                 struct time_res ts_res;
    1126           0 :                 ts_res.has_tz = (strcmp(sqlname, "timetz") == 0);
    1127           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1128           0 :                 ts_res.timezone = m->timezone;
    1129           0 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
    1130           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1131           0 :                         ok = -4;
    1132           0 :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1133           0 :                 struct time_res ts_res;
    1134           0 :                 ts_res.has_tz = (strcmp(sqlname, "timestamptz") == 0);
    1135           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1136           0 :                 ts_res.timezone = m->timezone;
    1137           0 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1138           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1139           0 :                         ok = -4;
    1140           0 :         } else if (eclass == EC_SEC) {
    1141           0 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1142           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1143           0 :                         ok = -4;
    1144             :         } else {
    1145           0 :                 switch (mtype) {
    1146           0 :                 case TYPE_bte:
    1147           0 :                         if (mvc_send_bte(s, *(bte *) p) < 1)
    1148           0 :                                 ok = -4;
    1149             :                         break;
    1150           0 :                 case TYPE_sht:
    1151           0 :                         if (mvc_send_sht(s, *(sht *) p) < 1)
    1152           0 :                                 ok = -4;
    1153             :                         break;
    1154           0 :                 case TYPE_int:
    1155           0 :                         if (mvc_send_int(s, *(int *) p) < 1)
    1156           0 :                                 ok = -4;
    1157             :                         break;
    1158           0 :                 case TYPE_lng:
    1159           0 :                         if (mvc_send_lng(s, *(lng *) p) < 1)
    1160           0 :                                 ok = -4;
    1161             :                         break;
    1162             : #ifdef HAVE_HGE
    1163           0 :                 case TYPE_hge:
    1164           0 :                         if (mvc_send_hge(s, *(hge *) p) < 1)
    1165           0 :                                 ok = -4;
    1166             :                         break;
    1167             : #endif
    1168           0 :                 default:
    1169           0 :                         l = (*BATatoms[mtype].atomToStr) (buf, len, p, true);
    1170           0 :                         if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1171           0 :                                 ok = -4;
    1172             :                 }
    1173             :         }
    1174           0 :         return ok;
    1175             : }
    1176             : 
    1177             : static int
    1178           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)
    1179             : {
    1180           0 :         mvc *m = b->mvc;
    1181           0 :         size_t seplen = strlen(sep);
    1182           0 :         size_t rseplen = strlen(rsep);
    1183           0 :         char *buf = NULL;
    1184           0 :         size_t len = 0;
    1185           0 :         int i, ok = 1;
    1186           0 :         int csv = (b->output_format == OFMT_CSV);
    1187           0 :         int json = (b->output_format == OFMT_JSON);
    1188             : 
    1189           0 :         if (!s || !t)
    1190             :                 return 0;
    1191             : 
    1192           0 :         (void) ssep;
    1193           0 :         if (csv && btag[0] && mnstr_write(s, btag, strlen(btag), 1) < 1)
    1194           0 :                 ok = -4;
    1195           0 :         if (json) {
    1196           0 :                 sep = ", ";
    1197           0 :                 seplen = strlen(sep);
    1198             :         }
    1199           0 :         for (i = 0; i < t->nr_cols && ok > -1; i++) {
    1200           0 :                 res_col *c = t->cols + i;
    1201             : 
    1202           0 :                 if (i != 0 && mnstr_write(s, sep, seplen, 1) < 1) {
    1203             :                         ok = -4;
    1204             :                         break;
    1205             :                 }
    1206           0 :                 if (json && (mnstr_write(s, c->name, strlen(c->name), 1) < 1 || mnstr_write(s, ": ", 2, 1) < 1)) {
    1207             :                         ok = -4;
    1208             :                         break;
    1209             :                 }
    1210           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);
    1211             :         }
    1212           0 :         _DELETE(buf);
    1213           0 :         if (ok > -1 && mnstr_write(s, rsep, rseplen, 1) < 1)
    1214           0 :                 ok = -4;
    1215           0 :         b->results = res_tables_remove(b->results, t);
    1216           0 :         return ok;
    1217             : }
    1218             : 
    1219             : static int
    1220           1 : mvc_export_table_columnar(stream *s, res_table *t, bstream *in)
    1221             : {
    1222           1 :         int i, res = 0;
    1223             : 
    1224           1 :         if (!s || !t)
    1225             :                 return 0;
    1226             : 
    1227           3 :         for (i = 1; i <= t->nr_cols; i++) {
    1228           2 :                 res_col *c = t->cols + (i - 1);
    1229             : 
    1230           2 :                 if (!c->b)
    1231             :                         break;
    1232             : 
    1233           2 :                 BAT *b = BATdescriptor(c->b);
    1234           2 :                 if (b == NULL)
    1235             :                         return -2;
    1236             : 
    1237           2 :                 res = mvc_export_binary_bat(s, b, in);
    1238           2 :                 BBPunfix(b->batCacheid);
    1239           2 :                 if (res < 0)
    1240           0 :                         return res;
    1241             :         }
    1242             : 
    1243             :         return res;
    1244             : }
    1245             : 
    1246             : static int
    1247      118941 : 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)
    1248             : {
    1249      118941 :         Tablet as;
    1250      118941 :         Column *fmt;
    1251      118941 :         int i, ok = 0;
    1252      118941 :         struct time_res *tres;
    1253      118941 :         int csv = (output_format == OFMT_CSV);
    1254      118941 :         int json = (output_format == OFMT_JSON);
    1255      118941 :         char *bj;
    1256             : 
    1257      118941 :         if (!s || !t)
    1258             :                 return 0;
    1259             : 
    1260      118943 :         as.nr_attrs = t->nr_cols + 1;        /* for the leader */
    1261      118943 :         as.nr = nr;
    1262      118943 :         as.offset = offset;
    1263      118943 :         fmt = as.format = (Column *) GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
    1264      119009 :         tres = GDKzalloc(sizeof(struct time_res) * (as.nr_attrs));
    1265      118999 :         if (fmt == NULL || tres == NULL) {
    1266           0 :                 GDKfree(fmt);
    1267           0 :                 GDKfree(tres);
    1268           0 :                 return -4;
    1269             :         }
    1270             : 
    1271      118999 :         fmt[0].c = NULL;
    1272      118999 :         fmt[0].sep = (csv) ? btag : "";
    1273      118999 :         fmt[0].rsep = rsep;
    1274      118999 :         fmt[0].seplen = _strlen(fmt[0].sep);
    1275      118999 :         fmt[0].ws = 0;
    1276      118999 :         fmt[0].nullstr = NULL;
    1277             : 
    1278      432793 :         for (i = 1; i <= t->nr_cols; i++) {
    1279      313841 :                 res_col *c = t->cols + (i - 1);
    1280             : 
    1281      313841 :                 if (!c->b)
    1282             :                         break;
    1283             : 
    1284      313841 :                 fmt[i].c = BATdescriptor(c->b);
    1285      313827 :                 if (fmt[i].c == NULL) {
    1286           0 :                         while (--i >= 1) {
    1287           0 :                                 bat_iterator_end(&fmt[i].ci);
    1288           0 :                                 BBPunfix(fmt[i].c->batCacheid);
    1289             :                         }
    1290           0 :                         GDKfree(fmt);
    1291           0 :                         GDKfree(tres);
    1292           0 :                         return -2;
    1293             :                 }
    1294      313827 :                 fmt[i].ci = bat_iterator(fmt[i].c);
    1295      313827 :                 fmt[i].name = NULL;
    1296      313827 :                 if (csv) {
    1297      313817 :                         fmt[i].sep = ((i - 1) < (t->nr_cols - 1)) ? sep : rsep;
    1298      313817 :                         fmt[i].seplen = _strlen(fmt[i].sep);
    1299      313817 :                         fmt[i].rsep = rsep;
    1300             :                 }
    1301      313827 :                 if (json) {
    1302           0 :                         res_col *p = t->cols + (i - 1);
    1303             : 
    1304             :                         /*
    1305             :                          * We define the "proper" way of returning
    1306             :                          * a relational table in json format as a
    1307             :                          * json array of objects, where each row is
    1308             :                          * represented as a json object.
    1309             :                          */
    1310           0 :                         if (i == 1) {
    1311           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(btag));
    1312           0 :                                 snprintf(bj, strlen(p->name) + strlen(btag), btag, p->name);
    1313           0 :                                 fmt[i - 1].sep = bj;
    1314           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1315           0 :                                 fmt[i - 1].rsep = NULL;
    1316           0 :                         } else if (i <= t->nr_cols) {
    1317           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(sep));
    1318           0 :                                 snprintf(bj, strlen(p->name) + 10, sep, p->name);
    1319           0 :                                 fmt[i - 1].sep = bj;
    1320           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1321           0 :                                 fmt[i - 1].rsep = NULL;
    1322             :                         }
    1323           0 :                         if (i == t->nr_cols) {
    1324           0 :                                 fmt[i].sep = rsep;
    1325           0 :                                 fmt[i].seplen = _strlen(fmt[i].sep);
    1326           0 :                                 fmt[i].rsep = NULL;
    1327             :                         }
    1328             :                 }
    1329      313827 :                 fmt[i].type = ATOMname(fmt[i].c->ttype);
    1330      313794 :                 fmt[i].adt = fmt[i].c->ttype;
    1331      313794 :                 fmt[i].tostr = &_ASCIIadt_toStr;
    1332      313794 :                 fmt[i].frstr = &_ASCIIadt_frStr;
    1333      313794 :                 fmt[i].extra = fmt + i;
    1334      313794 :                 fmt[i].data = NULL;
    1335      313794 :                 fmt[i].len = 0;
    1336      313794 :                 fmt[i].ws = 0;
    1337      313794 :                 fmt[i].quote = ssep ? ssep[0] : 0;
    1338      313794 :                 fmt[i].nullstr = ns;
    1339      313794 :                 if (c->type.type->eclass == EC_DEC) {
    1340        1217 :                         fmt[i].tostr = &dec_tostr;
    1341        1217 :                         fmt[i].frstr = &dec_frstr;
    1342        1217 :                         fmt[i].extra = (void *) (ptrdiff_t) c->type.scale;
    1343      312577 :                 } else if (c->type.type->eclass == EC_TIMESTAMP || c->type.type->eclass == EC_TIMESTAMP_TZ) {
    1344         710 :                         struct time_res *ts_res = tres + (i - 1);
    1345         710 :                         ts_res->has_tz = EC_TEMP_TZ(c->type.type->eclass);
    1346         710 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1347         710 :                         ts_res->timezone = m->timezone;
    1348             : 
    1349         710 :                         fmt[i].tostr = &sql_timestamp_tostr;
    1350         710 :                         fmt[i].frstr = NULL;
    1351         710 :                         fmt[i].extra = ts_res;
    1352      311867 :                 } else if (c->type.type->eclass == EC_TIME || c->type.type->eclass == EC_TIME_TZ) {
    1353         285 :                         struct time_res *ts_res = tres + (i - 1);
    1354         285 :                         ts_res->has_tz = (strcmp(c->type.type->base.name, "timetz") == 0);
    1355         285 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1356         285 :                         ts_res->timezone = m->timezone;
    1357             : 
    1358         285 :                         fmt[i].tostr = &sql_time_tostr;
    1359         285 :                         fmt[i].frstr = NULL;
    1360         285 :                         fmt[i].extra = ts_res;
    1361      311582 :                 } else if (c->type.type->eclass == EC_SEC) {
    1362         471 :                         fmt[i].tostr = &dec_tostr;
    1363         471 :                         fmt[i].frstr = &sec_frstr;
    1364         471 :                         fmt[i].extra = (void *) (ptrdiff_t) 3;
    1365             :                 } else {
    1366             :                         fmt[i].extra = fmt + i;
    1367             :                 }
    1368             :         }
    1369      118952 :         if (i == t->nr_cols + 1)
    1370      118952 :                 ok = TABLEToutput_file(&as, NULL, s, m->scanner.rs);
    1371      551779 :         for (i = 0; i <= t->nr_cols; i++) {
    1372      432770 :                 fmt[i].sep = NULL;
    1373      432770 :                 fmt[i].rsep = NULL;
    1374      432770 :                 fmt[i].type = NULL;
    1375      432770 :                 fmt[i].nullstr = NULL;
    1376             :         }
    1377      432800 :         for (i = 1; i <= t->nr_cols; i++)
    1378      313806 :                 bat_iterator_end(&fmt[i].ci);
    1379      118994 :         TABLETdestroy_format(&as);
    1380      119008 :         GDKfree(tres);
    1381      119009 :         if (ok < 0)
    1382             :                 return ok;
    1383      119009 :         if (mnstr_errnr(s) != MNSTR_NO__ERROR)
    1384             :                 return -4;
    1385             :         return 0;
    1386             : }
    1387             : 
    1388             : static int
    1389      118954 : 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)
    1390             : {
    1391      118954 :         return mvc_export_table_(b->mvc, b->output_format, s, t, offset, nr, btag, sep, rsep, ssep, ns);
    1392             : }
    1393             : 
    1394             : int
    1395           0 : mvc_export(mvc *m, stream *s, res_table *t, BUN nr)
    1396             : {
    1397           0 :         backend b = {0};
    1398           0 :         b.mvc = m;
    1399           0 :         b.results = t;
    1400           0 :         b.reloptimizer = 0;
    1401           0 :         t->nr_rows = nr;
    1402           0 :         if (mvc_export_head(&b, s, t->id, TRUE, TRUE, 0/*starttime*/, 0/*maloptimizer*/) < 0)
    1403             :                 return -1;
    1404           0 :         return mvc_export_table_(m, OFMT_CSV, s, t, 0, nr, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1405             : }
    1406             : 
    1407             : 
    1408             : static lng
    1409      313055 : get_print_width(int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1410             : {
    1411      313055 :         size_t count = 0, incr = 0;
    1412             : 
    1413      313055 :         if (eclass == EC_SEC)
    1414             :                 incr = 1;
    1415      312605 :         else if (mtype == TYPE_oid)
    1416          88 :                 incr = 2;
    1417      313055 :         mtype = ATOMbasetype(mtype);
    1418      313055 :         if (mtype == TYPE_str) {
    1419       88084 :                 if (eclass == EC_CHAR && digits) {
    1420        1374 :                         return digits;
    1421             :                 } else {
    1422       86710 :                         int l = 0;
    1423       86710 :                         if (bid) {
    1424       86710 :                                 BAT *b = BATdescriptor(bid);
    1425             : 
    1426       86710 :                                 if (b) {
    1427             :                                         /* in practice, b can be a
    1428             :                                          * void(nil) bat, an oid bat
    1429             :                                          * with all nil values, or an
    1430             :                                          * empty void/oid bat */
    1431       86710 :                                         if (ATOMstorage(b->ttype) == TYPE_str)
    1432       86710 :                                                 l = bat_max_strlength(b);
    1433             :                                         else
    1434             :                                                 l = 0;
    1435       86710 :                                         BBPunfix(b->batCacheid);
    1436             :                                 } else {
    1437             :                                         return -2;
    1438             :                                 }
    1439           0 :                         } else if (p) {
    1440           0 :                                 l = UTF8_strwidth((const char *) p);
    1441           0 :                                 if (is_int_nil(l))
    1442           0 :                                         l = 0;
    1443             :                         }
    1444       86710 :                         return l;
    1445             :                 }
    1446      224971 :         } else if (eclass == EC_NUM || eclass == EC_POS || eclass == EC_MONTH || eclass == EC_SEC) {
    1447      214248 :                 count = 0;
    1448      214248 :                 if (bid) {
    1449      214248 :                         BAT *b = BATdescriptor(bid);
    1450             : 
    1451      214295 :                         if (b) {
    1452      214295 :                                 if (mtype == TYPE_bte) {
    1453       36947 :                                         count = bat_max_btelength(b);
    1454             :                                 } else if (mtype == TYPE_sht) {
    1455       28361 :                                         count = bat_max_shtlength(b);
    1456             :                                 } else if (mtype == TYPE_int) {
    1457      131520 :                                         count = bat_max_intlength(b);
    1458             :                                 } else if (mtype == TYPE_lng) {
    1459       17228 :                                         count = bat_max_lnglength(b);
    1460             : #ifdef HAVE_HGE
    1461             :                                 } else if (mtype == TYPE_hge) {
    1462         239 :                                         count = bat_max_hgelength(b);
    1463             : #endif
    1464             :                                 } else if (mtype == TYPE_void) {
    1465             :                                         count = 4;
    1466             :                                 } else {
    1467           0 :                                         assert(0);
    1468             :                                 }
    1469      214275 :                                 count += incr;
    1470      214275 :                                 BBPunfix(b->batCacheid);
    1471             :                         } else {
    1472             :                                 return -2;
    1473             :                         }
    1474             :                 } else {
    1475           0 :                         if (p) {
    1476             : #ifdef HAVE_HGE
    1477           0 :                                 hge val = 0;
    1478             : #else
    1479             :                                 lng val = 0;
    1480             : #endif
    1481           0 :                                 if (mtype == TYPE_bte) {
    1482           0 :                                         val = *((bte *) p);
    1483             :                                 } else if (mtype == TYPE_sht) {
    1484           0 :                                         val = *((sht *) p);
    1485             :                                 } else if (mtype == TYPE_int) {
    1486           0 :                                         val = *((int *) p);
    1487             :                                 } else if (mtype == TYPE_lng) {
    1488           0 :                                         val = *((lng *) p);
    1489             : #ifdef HAVE_HGE
    1490             :                                 } else if (mtype == TYPE_hge) {
    1491           0 :                                         val = *((hge *) p);
    1492             : #endif
    1493             :                                 } else {
    1494           0 :                                         assert(0);
    1495             :                                 }
    1496             : 
    1497           0 :                                 if (val < 0)
    1498           0 :                                         count++;
    1499           0 :                                 while (val /= 10)
    1500           0 :                                         count++;
    1501           0 :                                 count++;
    1502           0 :                                 count += incr;
    1503             :                         } else {
    1504             :                                 count = 0;
    1505             :                         }
    1506             :                 }
    1507      214268 :                 if (eclass == EC_SEC && count < 5)
    1508          45 :                         count = 5;
    1509      214268 :                 return count;
    1510             :                 /* the following two could be done once by taking the
    1511             :                    max value and calculating the number of digits from that
    1512             :                    value, instead of the maximum values taken now, which
    1513             :                    include the optional sign */
    1514             :         } else if (eclass == EC_FLT) {
    1515             :                 /* floats are printed using "%.9g":
    1516             :                  * [sign]+digit+period+[max 8 digits]+E+[sign]+[max 2 digits] */
    1517        2173 :                 if (mtype == TYPE_flt) {
    1518             :                         return 15;
    1519             :                         /* doubles are printed using "%.17g":
    1520             :                          * [sign]+digit+period+[max 16 digits]+E+[sign]+[max 3 digits] */
    1521             :                 } else {        /* TYPE_dbl */
    1522        1886 :                         return 24;
    1523             :                 }
    1524             :         } else if (eclass == EC_DEC) {
    1525        1217 :                 count = 1 + digits;
    1526        1217 :                 if (scale > 0)
    1527        1008 :                         count += 1;
    1528        1217 :                 if (scale == digits) // for preceding 0, e.g. 0.
    1529          13 :                         count += 1;
    1530        1217 :                 return count;
    1531             :         } else if (eclass == EC_DATE) {
    1532             :                 return 10;
    1533             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1534         276 :                 count = 8;
    1535         276 :                 if (tz)         /* time zone */
    1536          94 :                         count += 6;     /* +03:30 */
    1537         276 :                 if (digits > 1)      /* fractional seconds precision (including dot) */
    1538          96 :                         count += digits;
    1539         276 :                 return count;
    1540             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1541         710 :                 count = 10 + 1 + 8;
    1542         710 :                 if (tz)         /* time zone */
    1543         154 :                         count += 6;     /* +03:30 */
    1544         710 :                 if (digits)     /* fractional seconds precision */
    1545         710 :                         count += digits;
    1546         710 :                 return count;
    1547             :         } else if (eclass == EC_BIT) {
    1548        5396 :                 return 5;       /* max(strlen("true"), strlen("false")) */
    1549         602 :         } else if (strcmp(ATOMname(mtype), "uuid") == 0) {
    1550             :                 return 36;      /* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx */
    1551             :         } else {
    1552         532 :                 return 0;
    1553             :         }
    1554             : }
    1555             : 
    1556             : static int
    1557      313080 : export_length(stream *s, int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1558             : {
    1559      313080 :         lng length = get_print_width(mtype, eclass, digits, scale, tz, bid, p);
    1560      313080 :         if (length < 0)
    1561             :                 return -2;
    1562      313080 :         if (mvc_send_lng(s, length) != 1)
    1563           0 :                 return -4;
    1564             :         return 0;
    1565             : }
    1566             : 
    1567             : int
    1568       17978 : mvc_export_operation(backend *b, stream *s, str w, lng starttime, lng mal_optimizer)
    1569             : {
    1570       17978 :         mvc *m = b->mvc;
    1571             : 
    1572       17978 :         assert(m->type == Q_SCHEMA || m->type == Q_TRANS);
    1573       17978 :         if (m->type == Q_SCHEMA) {
    1574       15025 :                 if (!s)
    1575             :                         return 0;
    1576       15025 :                 if (mnstr_printf(s, "&3 " LLFMT " " LLFMT "\n", starttime > 0 ? GDKusec() - starttime : 0, mal_optimizer) < 0)
    1577             :                         return -4;
    1578             :         } else {
    1579        2953 :                 if (m->session->auto_commit) {
    1580        1367 :                         if (mnstr_write(s, "&4 t\n", 5, 1) != 1)
    1581             :                                 return -4;
    1582             :                 } else {
    1583        1586 :                         if (mnstr_write(s, "&4 f\n", 5, 1) != 1)
    1584             :                                 return -4;
    1585             :                 }
    1586             :         }
    1587             : 
    1588       17978 :         if (mvc_export_warning(s, w) != 1)
    1589             :                 return -4;
    1590             :         return 0;
    1591             : }
    1592             : 
    1593             : 
    1594             : int
    1595      136174 : mvc_affrows(mvc *m, stream *s, lng val, str w, oid query_id, lng last_id, lng starttime, lng maloptimizer, lng reloptimizer)
    1596             : {
    1597      136174 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), val);
    1598             : 
    1599             :         /* if we don't have a stream, nothing can go wrong, so we return
    1600             :          * success.  This is especially vital for execution of internal SQL
    1601             :          * commands, since they don't get a stream to suppress their output.
    1602             :          * If we would fail on having no stream here, those internal commands
    1603             :          * fail too.
    1604             :          */
    1605      136733 :         if (!s || GDKembedded())
    1606       46260 :                 return 0;
    1607      180693 :         if (mnstr_write(s, "&2 ", 3, 1) != 1 ||
    1608      180593 :             mvc_send_lng(s, val) != 1 ||
    1609      180927 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1610      180777 :             mvc_send_lng(s, last_id) != 1 ||
    1611      180981 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1612      180631 :             mvc_send_lng(s, (lng) query_id) != 1 ||
    1613      180956 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1614      180634 :             mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1 ||
    1615      180851 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1616      180946 :             mvc_send_lng(s, maloptimizer) != 1 ||
    1617      180881 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1618      180885 :             mvc_send_lng(s, reloptimizer) != 1 ||
    1619       90489 :             mnstr_write(s, "\n", 1, 1) != 1)
    1620           0 :                 return -4;
    1621       90435 :         if (mvc_export_warning(s, w) != 1)
    1622             :                 return -4;
    1623             : 
    1624             :         return 0;
    1625             : }
    1626             : 
    1627             : int
    1628      136318 : mvc_export_affrows(backend *b, stream *s, lng val, str w, oid query_id, lng starttime, lng maloptimizer)
    1629             : {
    1630      136318 :         b->rowcnt = val;
    1631      136318 :         return mvc_affrows(b->mvc, s, val, w, query_id, b->last_id, starttime, maloptimizer, b->reloptimizer);
    1632             : }
    1633             : 
    1634             : int
    1635      118893 : mvc_export_head(backend *b, stream *s, int res_id, int only_header, int compute_lengths, lng starttime, lng maloptimizer)
    1636             : {
    1637      118893 :         mvc *m = b->mvc;
    1638      118893 :         int i, res = 0;
    1639      118893 :         BUN count = 0;
    1640      118893 :         res_table *t = res_tables_find(b->results, res_id);
    1641             : 
    1642      118923 :         if (!s || !t)
    1643             :                 return 0;
    1644             : 
    1645             :         /* query type: Q_TABLE || Q_PREPARE */
    1646      118923 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1647      118923 :         if (mnstr_write(s, "&", 1, 1) != 1 || mvc_send_int(s, (int) t->query_type) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1648           0 :                 return -4;
    1649             : 
    1650             :         /* id */
    1651      118929 :         int result_id = t->query_type == Q_PREPARE?b->q->id:t->id;
    1652      118929 :         if (mvc_send_int(s, result_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1653           0 :                 return -4;
    1654             : 
    1655             :         /* tuple count */
    1656      118925 :         if (only_header) {
    1657      118929 :                 if (t->cols[0].b) {
    1658      118932 :                         count = t->nr_rows;
    1659             :                 } else {
    1660             :                         count = 1;
    1661             :                 }
    1662             :         }
    1663      118925 :         b->rowcnt = count;
    1664      118925 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), b->rowcnt);
    1665      118937 :         if (mvc_send_lng(s, (lng) count) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1666           0 :                 return -4;
    1667             : 
    1668             :         /* column count */
    1669      118937 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1670           0 :                 return -4;
    1671             : 
    1672             :         /* row count, min(count, reply_size) */
    1673             :         /* the columnar protocol ignores the reply size by fetching the entire resultset at once, so don't set it */
    1674      118936 :         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)
    1675             :                 return -4;
    1676             : 
    1677             :         // export query id
    1678      118936 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, (lng) t->query_id) != 1)
    1679           0 :                 return -4;
    1680             : 
    1681             :         // export query time
    1682      118934 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1)
    1683           0 :                 return -4;
    1684             : 
    1685             :         // export MAL optimizer time
    1686      118935 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, maloptimizer) != 1)
    1687           0 :                 return -4;
    1688             : 
    1689      118934 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, b->reloptimizer) != 1)
    1690           0 :                 return -4;
    1691             : 
    1692      118934 :         if (mnstr_write(s, "\n% ", 3, 1) != 1)
    1693             :                 return -4;
    1694      431996 :         for (i = 0; i < t->nr_cols; i++) {
    1695      313074 :                 res_col *c = t->cols + i;
    1696      313074 :                 size_t len = strlen(c->tn);
    1697             : 
    1698      313074 :                 if (len && mnstr_write(s, c->tn, len, 1) != 1)
    1699             :                         return -4;
    1700      313076 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1701             :                         return -4;
    1702             :         }
    1703      118922 :         if (mnstr_write(s, " # table_name\n% ", 16, 1) != 1)
    1704             :                 return -4;
    1705             : 
    1706      431965 :         for (i = 0; i < t->nr_cols; i++) {
    1707      313049 :                 res_col *c = t->cols + i;
    1708             : 
    1709      313049 :                 if (strpbrk(c->name, ", \t#\"\\")) {
    1710         183 :                         char *p;
    1711         183 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1712             :                                 return -4;
    1713        2443 :                         for (p = c->name; *p; p++) {
    1714        2260 :                                 if (*p == '"' || *p == '\\') {
    1715          27 :                                         if (mnstr_write(s, "\\", 1, 1) != 1)
    1716             :                                                 return -4;
    1717             :                                 }
    1718        2260 :                                 if (mnstr_write(s, p, 1, 1) != 1)
    1719             :                                         return -4;
    1720             :                         }
    1721         183 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1722             :                                 return -4;
    1723             :                 } else {
    1724      312866 :                         if (mnstr_write(s, c->name, strlen(c->name), 1) != 1)
    1725             :                                 return -4;
    1726             :                 }
    1727             : 
    1728      313027 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1729             :                         return -4;
    1730             :         }
    1731      118916 :         if (mnstr_write(s, " # name\n% ", 10, 1) != 1)
    1732             :                 return -4;
    1733             : 
    1734      431996 :         for (i = 0; i < t->nr_cols; i++) {
    1735      313073 :                 res_col *c = t->cols + i;
    1736             : 
    1737      313073 :                 if (mnstr_write(s, c->type.type->base.name, strlen(c->type.type->base.name), 1) != 1)
    1738             :                         return -4;
    1739      313066 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1740             :                         return -4;
    1741             :         }
    1742      118923 :         if (mnstr_write(s, " # type\n% ", 10, 1) != 1)
    1743             :                 return -4;
    1744      118924 :         if (compute_lengths) {
    1745      432008 :                 for (i = 0; i < t->nr_cols; i++) {
    1746      313081 :                         res_col *c = t->cols + i;
    1747      313081 :                         int mtype = c->type.type->localtype;
    1748      313081 :                         sql_class eclass = c->type.type->eclass;
    1749             : 
    1750      313081 :                         if ((res = export_length(s, mtype, eclass, c->type.digits, c->type.scale, type_has_tz(&c->type), c->b, c->p)) < 0)
    1751           0 :                                 return res;
    1752      313085 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1753             :                                 return -4;
    1754             :                 }
    1755      118927 :                 if (mnstr_write(s, " # length\n", 10, 1) != 1)
    1756             :                         return -4;
    1757             :         }
    1758      118917 :         if (b->sizeheader) {
    1759       90535 :                 if (mnstr_write(s, "% ", 2, 1) != 1)
    1760             :                         return -4;
    1761      344073 :                 for (i = 0; i < t->nr_cols; i++) {
    1762      253537 :                         res_col *c = t->cols + i;
    1763             : 
    1764      253537 :                         if (mnstr_printf(s, "%u %u", c->type.digits, c->type.scale) < 0)
    1765             :                                 return -4;
    1766      253533 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1767             :                                 return -4;
    1768             :                 }
    1769       90536 :                 if (mnstr_write(s, " # typesizes\n", 13, 1) != 1)
    1770             :                         return -4;
    1771             :         }
    1772             :         return res;
    1773             : }
    1774             : 
    1775             : static int
    1776          25 : mvc_export_file(backend *b, stream *s, res_table *t)
    1777             : {
    1778          25 :         int res = 0;
    1779          25 :         BUN count;
    1780             : 
    1781          25 :         if (!t->cols[0].b) {
    1782           0 :                 res = mvc_export_row(b, s, t, "", t->tsep, t->rsep, t->ssep, t->ns);
    1783             :         } else {
    1784          25 :                 count = t->nr_rows;
    1785             : 
    1786          25 :                 res = mvc_export_table(b, s, t, 0, count, "", t->tsep, t->rsep, t->ssep, t->ns);
    1787          25 :                 b->results = res_tables_remove(b->results, t);
    1788             :         }
    1789          25 :         return res;
    1790             : }
    1791             : 
    1792             : int
    1793      120704 : mvc_export_result(backend *b, stream *s, int res_id, bool header, lng starttime, lng maloptimizer)
    1794             : {
    1795      120704 :         mvc *m = b->mvc;
    1796      120704 :         int clean = 0, res = 0;
    1797      120704 :         BUN count;
    1798      120704 :         res_table *t = res_tables_find(b->results, res_id);
    1799      120662 :         int json = (b->output_format == OFMT_JSON);
    1800             : 
    1801      120662 :         if (!s || !t)
    1802             :                 return 0;
    1803             : 
    1804             :         /* Proudly supporting SQLstatementIntern's output flag */
    1805      120662 :         if (b->output_format == OFMT_NONE)
    1806             :                 return 0;
    1807             : 
    1808      118875 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1809      118875 :         if (t->tsep) {
    1810             :                 /* need header */
    1811          25 :                 if (header && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1812             :                         return res;
    1813          25 :                 return mvc_export_file(b, s, t);
    1814             :         }
    1815             : 
    1816      118850 :         if (!json && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1817             :                 return res;
    1818             : 
    1819      118873 :         assert(t->cols[0].b);
    1820             : 
    1821      118873 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
    1822           1 :                 if (mnstr_flush(s, MNSTR_FLUSH_DATA) < 0)
    1823             :                         return -4;
    1824           1 :                 return mvc_export_table_columnar(s, t, m->scanner.rs);
    1825             :         }
    1826             : 
    1827      118872 :         count = m->reply_size;
    1828      118872 :         if (m->reply_size != -2 && (count <= 0 || count >= t->nr_rows)) {
    1829      118257 :                 count = t->nr_rows;
    1830      118257 :                 clean = 1;
    1831             :         }
    1832      118872 :         if (json) {
    1833           0 :                 switch(count) {
    1834             :                 case 0:
    1835           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\t", "", "}\n", "\"", "null");
    1836           0 :                         break;
    1837             :                 case 1:
    1838           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\n\t\"%s\" : ", ",\n\t\"%s\" : ", "\n}\n", "\"", "null");
    1839           0 :                         break;
    1840             :                 case 2:
    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 - 1, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t}\n]\n", "\"", "null");
    1843           0 :                         break;
    1844             :                 default:
    1845           0 :                         res = mvc_export_table(b, s, t, 0, 1, "[\n\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1846           0 :                         res = mvc_export_table(b, s, t, 1, count - 2, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1847           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");
    1848             :                 }
    1849             :         } else {
    1850      118872 :                 res = mvc_export_table(b, s, t, 0, count, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1851             :         }
    1852      118894 :         if (clean)
    1853      118271 :                 b->results = res_tables_remove(b->results, t);
    1854             : 
    1855      118894 :         if (res > -1)
    1856      118894 :                 res = 1;
    1857             :         return res;
    1858             : }
    1859             : 
    1860             : int
    1861          69 : mvc_export_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1862             : {
    1863          69 :         int res = 0;
    1864          69 :         res_table *t = res_tables_find(b->results, res_id);
    1865          69 :         BUN cnt;
    1866             : 
    1867          69 :         if (!s || !t)
    1868             :                 return 0;
    1869             : 
    1870          57 :         cnt = nr;
    1871          57 :         if (cnt == 0)
    1872           0 :                 cnt = t->nr_rows;
    1873          57 :         if (offset >= t->nr_rows)
    1874             :                 cnt = 0;
    1875          57 :         if (cnt == BUN_NONE || offset + cnt > t->nr_rows)
    1876          11 :                 cnt = t->nr_rows - offset;
    1877             : 
    1878             :         /* query type: Q_BLOCK */
    1879          57 :         if (mnstr_write(s, "&6 ", 3, 1) != 1)
    1880             :                 return -4;
    1881             : 
    1882             :         /* result id */
    1883          57 :         if (mvc_send_int(s, res_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1884           0 :                 return -4;
    1885             : 
    1886             :         /* column count */
    1887          57 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1888           0 :                 return -4;
    1889             : 
    1890             :         /* row count */
    1891          57 :         if (mvc_send_lng(s, (lng) cnt) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1892           0 :                 return -4;
    1893             : 
    1894             :         /* block offset */
    1895          57 :         if (mvc_send_lng(s, (lng) offset) != 1)
    1896             :                 return -4;
    1897             : 
    1898          57 :         if (mnstr_write(s, "\n", 1, 1) != 1)
    1899             :                 return -4;
    1900             : 
    1901          57 :         res = mvc_export_table(b, s, t, offset, cnt, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1902          57 :         return res;
    1903             : }
    1904             : 
    1905             : int
    1906      120409 : mvc_result_table(backend *be, oid query_id, int nr_cols, mapi_query_t type)
    1907             : {
    1908      120409 :         res_table *t = res_table_create(be->mvc->session->tr, be->result_id++, query_id, nr_cols, type, be->results);
    1909      120416 :         be->results = t;
    1910      120416 :         return t ? t->id : -1;
    1911             : }
    1912             : 
    1913             : int
    1914      253606 : mvc_result_column(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, BAT *b)
    1915             : {
    1916             :         /* return 0 on success, non-zero on failure */
    1917      253606 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, true, b->ttype, b, false) ? 0 : -1;
    1918             : }
    1919             : 
    1920             : int
    1921       61515 : mvc_result_value(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, ptr *p, int mtype)
    1922             : {
    1923             :         /* return 0 on success, non-zero on failure */
    1924       61515 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, false, mtype, p, false) ? 0 : -1;
    1925             : }
    1926             : 
    1927             : /* Translate error code from export function to error string */
    1928             : const char *
    1929           0 : mvc_export_error(backend *be, stream *s, int err_code)
    1930             : {
    1931           0 :         (void) be;
    1932           0 :         switch (err_code) {
    1933             :         case -1: /* Allocation failure */
    1934             :                 return MAL_MALLOC_FAIL;
    1935           0 :         case -2: /* BAT descriptor error */
    1936           0 :                 return RUNTIME_OBJECT_MISSING;
    1937           0 :         case -3: /* GDK error */
    1938           0 :                 return GDKerrbuf;
    1939           0 :         case -4: /* Stream error */
    1940           0 :                 return mnstr_peek_error(s);
    1941           0 :         case -5:
    1942           0 :                 return "Query aborted";
    1943           0 :         default: /* Unknown, must be a bug */
    1944           0 :                 return "Unknown internal error";
    1945             :         }
    1946             : }
    1947             : 
    1948             : static ssize_t
    1949        3830 : align_dump(stream *s, uint64_t pos, unsigned int alignment)
    1950             : {
    1951        3830 :         uint64_t a = (uint64_t)alignment;
    1952             :         // must be a power of two
    1953        3830 :         assert(a > 0);
    1954        3830 :         assert((a & (a-1)) == 0);
    1955             : 
    1956        3830 :         static char zeroes[32] = { 0 };
    1957             : #ifdef _MSC_VER
    1958             : #pragma warning(suppress:4146)
    1959             : #endif
    1960        3830 :         uint64_t gap = (~pos + 1) % a;
    1961        3830 :         return mnstr_write(s, zeroes, 1, (size_t)gap);
    1962             : }
    1963             : 
    1964             : 
    1965             : struct bindump_record {
    1966             :         BAT *bat;
    1967             :         type_record_t *type_rec;
    1968             :         int64_t start;
    1969             :         int64_t length;
    1970             : };
    1971             : 
    1972             : int
    1973         600 : mvc_export_bin_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1974             : {
    1975         600 :         int ret = -42;
    1976         600 :         struct bindump_record *colinfo;
    1977         600 :         stream *countstream = NULL;
    1978         600 :         uint64_t byte_count = 0;
    1979         600 :         uint64_t toc_pos = 0;
    1980         600 :         BUN end_row = offset + nr;
    1981             : 
    1982         600 :         res_table *res = res_tables_find(b->results, res_id);
    1983         600 :         if (res == NULL)
    1984             :                 return 0;
    1985             : 
    1986         600 :         colinfo = GDKzalloc(res->nr_cols * sizeof(*colinfo));
    1987         600 :         if (!colinfo) {
    1988           0 :                 ret = -1;
    1989           0 :                 goto end;
    1990             :         }
    1991        3830 :         for (int i = 0; i < res->nr_cols; i++)
    1992        3230 :                 colinfo[i].bat = NULL;
    1993        3830 :         for (int i = 0; i < res->nr_cols; i++) {
    1994        3230 :                 bat bat_id = res->cols[i].b;
    1995        3230 :                 BAT *b = BATdescriptor(bat_id);
    1996        3230 :                 if (!b) {
    1997           0 :                         ret = -1;
    1998           0 :                         goto end;
    1999             :                 }
    2000        3230 :                 colinfo[i].bat = b;
    2001             : 
    2002        3230 :                 if (BATcount(b) < end_row)
    2003             :                         end_row = BATcount(b);
    2004             : 
    2005        3230 :                 int tpe = BATttype(b);
    2006        3230 :                 const char *gdk_name = ATOMname(tpe);
    2007        3230 :                 type_record_t *rec = find_type_rec(gdk_name);
    2008        3230 :                 if (!rec || !can_dump_binary_column(rec)) {
    2009           0 :                         GDKerror("column %d: don't know how to dump data type '%s'", i, gdk_name);
    2010           0 :                         ret = -3;
    2011           0 :                         goto end;
    2012             :                 }
    2013        3230 :                 colinfo[i].type_rec = rec;
    2014             :         }
    2015             : 
    2016             :         // The byte_counting_stream keeps track of the byte offsets
    2017         600 :         countstream = byte_counting_stream(s, &byte_count);
    2018             : 
    2019             :         // Make sure the message starts with a & and not with a !
    2020         600 :         mnstr_printf(countstream, "&6 %d %d " BUNFMT " " BUNFMT "\n", res_id, res->nr_cols, end_row - offset, offset);
    2021             : 
    2022        3830 :         for (int i = 0; i < res->nr_cols; i++) {
    2023        3230 :                 align_dump(countstream, byte_count, 32); // 32 looks nice in tcpflow
    2024        3230 :                 struct bindump_record *info = &colinfo[i];
    2025        3230 :                 info->start = byte_count;
    2026        3230 :                 str msg = dump_binary_column(info->type_rec, info->bat, offset, end_row - offset, false, countstream);
    2027        3230 :                 if (msg != MAL_SUCCEED) {
    2028           0 :                         GDKerror("%s", msg);
    2029           0 :                         GDKfree(msg);
    2030           0 :                         ret = -3;
    2031           0 :                         goto end;
    2032             :                 }
    2033        3230 :                 info->length = byte_count - info->start;
    2034             :         }
    2035             : 
    2036         600 :         assert(byte_count > 0);
    2037             : 
    2038         600 :         align_dump(countstream, byte_count, 32);
    2039         600 :         toc_pos = byte_count;
    2040        3830 :         for (int i = 0; i < res->nr_cols; i++) {
    2041        3230 :                 struct bindump_record *info = &colinfo[i];
    2042        3230 :                 lng start = info->start;
    2043        3230 :                 lng length = info->length;
    2044        3230 :                 mnstr_writeLng(countstream, start);
    2045        3230 :                 mnstr_writeLng(countstream, length);
    2046             :         }
    2047             : 
    2048         600 :         mnstr_writeLng(countstream, toc_pos);
    2049         600 :         ret = 0;
    2050             : 
    2051         600 : end:
    2052         600 :         if (colinfo) {
    2053        3830 :                 for (int i = 0; i < res->nr_cols; i++) {
    2054        3230 :                         if (colinfo[i].bat)
    2055        3230 :                                 BBPunfix(colinfo[i].bat->batCacheid);
    2056             :                 }
    2057         600 :                 GDKfree(colinfo);
    2058             :         }
    2059         600 :         mnstr_destroy(countstream);
    2060         600 :         return ret;
    2061             : }

Generated by: LCOV version 1.14