LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_result.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 767 1065 72.0 %
Date: 2024-04-25 20:03:45 Functions: 38 45 84.4 %

          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       22314 : 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       22314 :         if (type == TYPE_bte) {
     109         365 :                 DEC_TOSTR(bte);
     110             :         } else if (type == TYPE_sht) {
     111        1329 :                 DEC_TOSTR(sht);
     112             :         } else if (type == TYPE_int) {
     113       20247 :                 DEC_TOSTR(int);
     114             :         } else if (type == TYPE_lng) {
     115       61426 :                 DEC_TOSTR(lng);
     116             : #ifdef HAVE_HGE
     117             :         } else if (type == TYPE_hge) {
     118       94617 :                 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        2639 : sql_time_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     134             : {
     135        2639 :         struct time_res *ts_res = TS_RES;
     136        2639 :         ssize_t len1;
     137        2639 :         size_t big = 128;
     138        2639 :         char buf1[128], *s1 = buf1, *s;
     139        2639 :         daytime tmp;
     140             : 
     141        2639 :         (void) type;
     142        2639 :         tmp = *(const daytime *) A;
     143        2639 :         if (ts_res->has_tz)
     144         194 :                 tmp = daytime_add_usec_modulo(tmp, ts_res->timezone * 1000);
     145             : 
     146        2639 :         len1 = daytime_precision_tostr(&s1, &big, tmp, ts_res->fraction, true);
     147        2639 :         if (len1 < 0)
     148             :                 return -1;
     149        2639 :         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        2639 :         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        2639 :         s = *buf;
     168        2639 :         strcpy(s, buf1);
     169        2639 :         s += len1;
     170             : 
     171        2639 :         if (ts_res->has_tz) {
     172         194 :                 lng timezone = llabs(ts_res->timezone / 60000);
     173         194 :                 s += sprintf(s, "%c%02d:%02d",
     174             :                              (ts_res->timezone >= 0) ? '+' : '-',
     175         194 :                              (int) (timezone / 60), (int) (timezone % 60));
     176             :         }
     177        2639 :         return (ssize_t) (s - *buf);
     178             : }
     179             : 
     180             : static ssize_t
     181        1578 : sql_timestamp_tostr(void *TS_RES, char **buf, size_t *len, int type, const void *A)
     182             : {
     183        1578 :         struct time_res *ts_res = TS_RES;
     184        1578 :         ssize_t len1, len2;
     185        1578 :         size_t big = 128;
     186        1578 :         char buf1[128], buf2[128], *s, *s1 = buf1, *s2 = buf2;
     187        1578 :         timestamp tmp;
     188        1578 :         lng timezone = ts_res->timezone;
     189        1578 :         date days;
     190        1578 :         daytime usecs;
     191             : 
     192        1578 :         (void) type;
     193        1578 :         tmp = *(const timestamp *)A;
     194        1578 :         if (ts_res->has_tz) {
     195         516 :                 tmp = timestamp_add_usec(tmp, timezone * 1000);
     196             :         }
     197        1578 :         days = timestamp_date(tmp);
     198        1578 :         usecs = timestamp_daytime(tmp);
     199        1578 :         len1 = date_tostr(&s1, &big, &days, true);
     200        1578 :         len2 = daytime_precision_tostr(&s2, &big, usecs, ts_res->fraction, true);
     201        1578 :         if (len1 < 0 || len2 < 0) {
     202           0 :                 GDKfree(s1);
     203           0 :                 GDKfree(s2);
     204           0 :                 return -1;
     205             :         }
     206             : 
     207        1578 :         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        1578 :         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        1578 :         s = *buf;
     227        1578 :         strcpy(s, buf1);
     228        1578 :         s += len1;
     229        1578 :         *s++ = ' ';
     230        1578 :         strcpy(s, buf2);
     231        1578 :         s += len2;
     232        1578 :         s[0] = 0;
     233             : 
     234        1578 :         if (ts_res->has_tz) {
     235         516 :                 timezone = ts_res->timezone / 60000;
     236         516 :                 *s++ = (ts_res->timezone >= 0) ? '+' : '-';
     237         516 :                 sprintf(s, "%02d:%02d", (int) (llabs(timezone) / 60), (int) (llabs(timezone) % 60));
     238         516 :                 s += 5;
     239             :         }
     240        1578 :         return (ssize_t) (s - *buf);
     241             : }
     242             : 
     243             : static int
     244       81688 : bat_max_strlength(BAT *b)
     245             : {
     246       81688 :         BUN p, q;
     247       81688 :         int l = 0;
     248       81688 :         int max = 0;
     249       81688 :         BATiter bi = bat_iterator(b);
     250             : 
     251     3804696 :         BATloop(b, p, q) {
     252     3723008 :                 l = UTF8_strwidth((const char *) BUNtvar(bi, p));
     253             : 
     254     3723008 :                 if (is_int_nil(l))
     255      214261 :                         l = 0;
     256     3723008 :                 if (l > max)
     257             :                         max = l;
     258             :         }
     259       81688 :         bat_iterator_end(&bi);
     260       81688 :         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     1417894 : bat_max_length(bte, lng)
     296     1196830 : bat_max_length(sht, lng)
     297     3561219 : bat_max_length(int, lng)
     298     2697800 : bat_max_length(lng, lng)
     299             : #ifdef HAVE_HGE
     300      289847 : 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 != '.' && ((res == 0 && *s == '0') || i < t->digits - t->scale); s++) { \
     321             :                         if (!isdigit((unsigned char) *s))                                                       \
     322             :                                 break;                                                                                                  \
     323             :                         res *= 10;                                                                                                      \
     324             :                         res += (*s-'0');                                                                                        \
     325             :                         if (res)                                                                                                        \
     326             :                                 i++;                                                                                                    \
     327             :                 }                                                                                                                               \
     328             :                 if (*s == '.') {                                                                                                \
     329             :                         s++;                                                                                                            \
     330             :                         while (*s && isdigit((unsigned char) *s) && scale > 0) {     \
     331             :                                 res *= 10;                                                                                              \
     332             :                                 res += *s++ - '0';                                                                              \
     333             :                                 scale--;                                                                                                \
     334             :                         }                                                                                                                       \
     335             :                 }                                                                                                                               \
     336             :                 while(*s && isspace((unsigned char) *s))                                                \
     337             :                         s++;                                                                                                            \
     338             :                 while (scale > 0) {                                                                                          \
     339             :                         res *= 10;                                                                                                      \
     340             :                         scale--;                                                                                                        \
     341             :                 }                                                                                                                               \
     342             :                 if (*s)                                                                                                                 \
     343             :                         return NULL;                                                                                            \
     344             :                 r = c->data;                                                                                                 \
     345             :                 if (r == NULL &&                                                                                                \
     346             :                     (r = GDKzalloc(sizeof(X))) == NULL)                                                 \
     347             :                         return NULL;                                                                                            \
     348             :                 c->data = r;                                                                                                 \
     349             :                 if (neg)                                                                                                                \
     350             :                         *r = -res;                                                                                                      \
     351             :                 else                                                                                                                    \
     352             :                         *r = res;                                                                                                       \
     353             :                 return (void *) r;                                                                                              \
     354             :         } while (0)
     355             : 
     356             : static void *
     357    66124866 : dec_frstr(Column *c, int type, const char *s)
     358             : {
     359             :         /* support dec map to bte, sht, int and lng */
     360    66124866 :         if( strcmp(s,"nil")== 0)
     361             :                 return NULL;
     362    66124866 :         if (type == TYPE_bte) {
     363        8369 :                 DEC_FRSTR(bte);
     364             :         } else if (type == TYPE_sht) {
     365       21228 :                 DEC_FRSTR(sht);
     366             :         } else if (type == TYPE_int) {
     367   502439219 :                 DEC_FRSTR(int);
     368             :         } else if (type == TYPE_lng) {
     369     4267332 :                 DEC_FRSTR(lng);
     370             : #ifdef HAVE_HGE
     371             :         } else if (type == TYPE_hge) {
     372           0 :                 DEC_FRSTR(hge);
     373             : #endif
     374             :         }
     375             :         return NULL;
     376             : }
     377             : 
     378             : static void *
     379         155 : sec_frstr(Column *c, int type, const char *s)
     380             : {
     381             :         /* read a sec_interval value
     382             :          * this knows that the stored scale is always 3 */
     383         155 :         unsigned int i, neg = 0;
     384         155 :         lng *r;
     385         155 :         lng res = 0;
     386             : 
     387         155 :         (void) c;
     388         155 :         (void) type;
     389         155 :         assert(type == TYPE_lng);
     390             : 
     391         155 :         if (*s == '-') {
     392          10 :                 neg = 1;
     393          10 :                 s++;
     394         145 :         } else if (*s == '+') {
     395           0 :                 neg = 0;
     396           0 :                 s++;
     397             :         }
     398        1201 :         for (i = 0; i < (19 - 3) && *s && *s != '.'; i++, s++) {
     399        1046 :                 if (!isdigit((unsigned char) *s))
     400             :                         return NULL;
     401        1046 :                 res *= 10;
     402        1046 :                 res += (*s - '0');
     403             :         }
     404         155 :         i = 0;
     405         155 :         if (*s) {
     406         135 :                 if (*s != '.')
     407             :                         return NULL;
     408         135 :                 s++;
     409         540 :                 for (; *s && i < 3; i++, s++) {
     410         405 :                         if (!isdigit((unsigned char) *s))
     411             :                                 return NULL;
     412         405 :                         res *= 10;
     413         405 :                         res += (*s - '0');
     414             :                 }
     415             :         }
     416         155 :         if (*s)
     417             :                 return NULL;
     418         215 :         for (; i < 3; i++) {
     419          60 :                 res *= 10;
     420             :         }
     421         155 :         r = c->data;
     422         155 :         if (r == NULL && (r = (lng *) GDKzalloc(sizeof(lng))) == NULL)
     423             :                 return NULL;
     424         155 :         c->data = r;
     425         155 :         if (neg)
     426          10 :                 *r = -res;
     427             :         else
     428         145 :                 *r = res;
     429             :         return (void *) r;
     430             : }
     431             : 
     432             : /* Literal parsing for SQL all pass through this routine */
     433             : static void *
     434   278458761 : _ASCIIadt_frStr(Column *c, int type, const char *s)
     435             : {
     436   278458761 :         ssize_t len;
     437             : 
     438   278458761 :         len = (*BATatoms[type].atomFromStr) (s, &c->len, &c->data, false);
     439   282412458 :         if (len < 0)
     440             :                 return NULL;
     441   282412445 :         switch (type) {
     442   251114265 :         case TYPE_bte:
     443             :         case TYPE_int:
     444             :         case TYPE_lng:
     445             :         case TYPE_sht:
     446             : #ifdef HAVE_HGE
     447             :         case TYPE_hge:
     448             : #endif
     449   251114265 :                 if (len == 0 || s[len]) {
     450             :                         /* decimals can be converted to integers when *.000 */
     451           8 :                         if (s[len++] == '.') {
     452          22 :                                 while (s[len] == '0')
     453          14 :                                         len++;
     454           8 :                                 if (s[len] == 0)
     455           4 :                                         return c->data;
     456             :                         }
     457             :                         return NULL;
     458             :                 }
     459             :                 break;
     460    28565120 :         case TYPE_str: {
     461    28565120 :                 sql_column *col = (sql_column *) c->extra;
     462    28565120 :                 int slen;
     463             : 
     464    28565120 :                 s = c->data;
     465    57130240 :                 slen = strNil(s) ? int_nil : UTF8_strlen(s);
     466    28545192 :                 if (col->type.digits > 0 && len > 0 && slen > (int) col->type.digits) {
     467           5 :                         len = UTF8_strwidth(c->data);
     468           5 :                         if (len > (ssize_t) col->type.digits)
     469             :                                 return NULL;
     470             :                 }
     471             :                 break;
     472             :         }
     473             :         default:
     474             :                 break;
     475             :         }
     476   282392504 :         return c->data;
     477             : }
     478             : 
     479             : 
     480             : static ssize_t
     481    13787378 : _ASCIIadt_toStr(void *extra, char **buf, size_t *len, int type, const void *a)
     482             : {
     483    13787378 :         if (type == TYPE_str) {
     484     7735013 :                 Column *c = extra;
     485     7735013 :                 char *dst;
     486     7735013 :                 const char *src = a;
     487     7735013 :                 size_t l = escapedStrlen(src, c->sep, c->rsep, c->quote), l2 = 0;
     488             : 
     489     7735013 :                 if (c->quote)
     490     7735013 :                         l = escapedStrlen(src, NULL, NULL, c->quote);
     491             :                 else
     492           0 :                         l = escapedStrlen(src, c->sep, c->rsep, 0);
     493     7735013 :                 if (l + 3 > *len) {
     494          52 :                         GDKfree(*buf);
     495          52 :                         *len = 2 * l + 3;
     496          52 :                         *buf = GDKzalloc(*len);
     497          52 :                         if (*buf == NULL) {
     498             :                                 return -1;
     499             :                         }
     500             :                 }
     501     7735013 :                 dst = *buf;
     502     7735013 :                 if (c->quote) {
     503     7735013 :                         dst[0] = c->quote;
     504     7735013 :                         l2 = 1;
     505     7735013 :                         l = escapedStr(dst + l2, src, *len - l2, NULL, NULL, c->quote);
     506             :                 } else {
     507           0 :                         l = escapedStr(dst + l2, src, *len - l2, c->sep, c->rsep, 0);
     508             :                 }
     509           0 :                 if (l2) {
     510     7735013 :                         dst[l + l2] = c->quote;
     511     7735013 :                         l2++;
     512             :                 }
     513     7735013 :                 dst[l + l2] = 0;
     514     7735013 :                 return l + l2;
     515             :         } else {
     516     6052365 :                 return (*BATatoms[type].atomToStr) (buf, len, a, true);
     517             :         }
     518             : }
     519             : 
     520             : 
     521             : static int
     522       10119 : has_whitespace(const char *s)
     523             : {
     524       10119 :         if (*s == ' ' || *s == '\t')
     525             :                 return 1;
     526        7514 :         while (*s)
     527        3768 :                 s++;
     528        3746 :         s--;
     529        3746 :         if (*s == ' ' || *s == '\t')
     530           0 :                 return 1;
     531             :         return 0;
     532             : }
     533             : 
     534             : str
     535        1071 : 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)
     536             : {
     537        1071 :         int i = 0, j;
     538        1071 :         node *n;
     539        1071 :         Tablet as;
     540        1071 :         Column *fmt;
     541        1071 :         str msg = MAL_SUCCEED;
     542             : 
     543        1071 :         *bats =0;       // initialize the receiver
     544             : 
     545        1071 :         if (!bs)
     546           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "No stream (pointer) provided");
     547        1071 :         if (mnstr_errnr(bs->s) != MNSTR_NO__ERROR) {
     548           0 :                 mnstr_error_kind errnr = mnstr_errnr(bs->s);
     549           0 :                 const char *stream_msg = mnstr_peek_error(bs->s);
     550           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");
     551           0 :                 return msg;
     552             :         }
     553        1071 :         if (offset < 0 || offset > (lng) BUN_MAX)
     554           0 :                 throw(IO, "sql.copy_from", SQLSTATE(42000) "Offset out of range");
     555             : 
     556        1071 :         if (offset > 0)
     557          22 :                 offset--;
     558        1071 :         if (ol_first_node(t->columns)) {
     559        1071 :                 stream *out = m->scanner.ws;
     560             : 
     561        2142 :                 as = (Tablet) {
     562        1071 :                         .nr_attrs = ol_length(t->columns),
     563        1071 :                         .nr = (sz < 1) ? BUN_NONE : (BUN) sz,
     564        1071 :                         .offset = (BUN) offset,
     565             :                         .error = NULL,
     566             :                         .tryall = 0,
     567             :                         .complaints = NULL,
     568        1071 :                         .filename = m->scanner.rs == bs ? NULL : "",
     569             :                 };
     570        1071 :                 fmt = GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
     571        1071 :                 if (fmt == NULL)
     572           0 :                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     573        1071 :                 as.format = fmt;
     574        1071 :                 if (!isa_block_stream(bs->s))
     575         532 :                         out = NULL;
     576             : 
     577       11190 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     578       10119 :                         sql_column *col = n->data;
     579             : 
     580       10119 :                         fmt[i].name = col->base.name;
     581       10119 :                         fmt[i].sep = (n->next) ? sep : rsep;
     582       10119 :                         fmt[i].rsep = rsep;
     583       10119 :                         fmt[i].seplen = _strlen(fmt[i].sep);
     584       10119 :                         fmt[i].type = sql_subtype_string(m->ta, &col->type);
     585       10119 :                         fmt[i].adt = ATOMindex(col->type.type->impl);
     586       10119 :                         fmt[i].tostr = &_ASCIIadt_toStr;
     587       10119 :                         fmt[i].frstr = &_ASCIIadt_frStr;
     588       10119 :                         fmt[i].extra = col;
     589       10119 :                         fmt[i].len = ATOMlen(fmt[i].adt, ATOMnilptr(fmt[i].adt));
     590       10119 :                         fmt[i].data = GDKzalloc(fmt[i].len);
     591       10119 :                         if(fmt[i].data == NULL || fmt[i].type == NULL) {
     592           0 :                                 for (j = 0; j < i; j++) {
     593           0 :                                         GDKfree(fmt[j].data);
     594           0 :                                         BBPunfix(fmt[j].c->batCacheid);
     595             :                                 }
     596           0 :                                 GDKfree(fmt[i].data);
     597           0 :                                 GDKfree(fmt);
     598           0 :                                 throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     599             :                         }
     600       10119 :                         fmt[i].c = NULL;
     601       10119 :                         fmt[i].ws = !has_whitespace(fmt[i].sep);
     602       10119 :                         fmt[i].quote = ssep ? ssep[0] : 0;
     603       10119 :                         fmt[i].nullstr = ns;
     604       10119 :                         fmt[i].null_length = strlen(ns);
     605       10119 :                         fmt[i].nildata = ATOMnilptr(fmt[i].adt);
     606       10119 :                         fmt[i].skip = (col->base.name[0] == '%');
     607       10119 :                         if (col->type.type->eclass == EC_DEC) {
     608         347 :                                 fmt[i].tostr = &dec_tostr;
     609         347 :                                 fmt[i].frstr = &dec_frstr;
     610        9772 :                         } else if (col->type.type->eclass == EC_SEC) {
     611          63 :                                 fmt[i].tostr = &dec_tostr;
     612          63 :                                 fmt[i].frstr = &sec_frstr;
     613             :                         }
     614       10119 :                         fmt[i].size = ATOMsize(fmt[i].adt);
     615             :                 }
     616        2071 :                 if ((msg = TABLETcreate_bats(&as, (BUN) (sz < 0 ? 1000 : sz))) == MAL_SUCCEED){
     617        1071 :                         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 &&
     618        1034 :                                 (best || !as.error))) {
     619        1046 :                                 *bats = (BAT**) GDKzalloc(sizeof(BAT *) * as.nr_attrs);
     620        1046 :                                 if ( *bats == NULL){
     621           0 :                                         TABLETdestroy_format(&as);
     622           0 :                                         throw(IO, "sql.copy_from", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     623             :                                 }
     624        1046 :                                 msg = TABLETcollect(*bats,&as);
     625             :                         }
     626             :                 }
     627        1071 :                 if (as.error) {
     628          30 :                         if( !best) msg = createException(SQL, "sql.copy_from", SQLSTATE(42000) "Failed to import table '%s', %s", t->base.name, getExceptionMessage(as.error));
     629          30 :                         freeException(as.error);
     630          30 :                         as.error = NULL;
     631             :                 }
     632       11190 :                 for (n = ol_first_node(t->columns), i = 0; n; n = n->next, i++) {
     633       10119 :                         fmt[i].sep = NULL;
     634       10119 :                         fmt[i].rsep = NULL;
     635       10119 :                         fmt[i].nullstr = NULL;
     636             :                 }
     637        1071 :                 TABLETdestroy_format(&as);
     638             :         }
     639             :         return msg;
     640             : }
     641             : 
     642             : /*
     643             :  * mvc_export_result dumps the sql header information and the
     644             :  * first part (reply_size) of the result set. It should be produced in Monet format to
     645             :  * enable mapi to work with it.
     646             :  */
     647             : 
     648             : static int
     649      109902 : mvc_export_warning(stream *s, str w)
     650             : {
     651      109902 :         str tmp = NULL;
     652      109902 :         while (w != NULL && *w != '\0') {
     653           0 :                 if ((tmp = strchr(w, (int) '\n')) != NULL)
     654           0 :                         *tmp++ = '\0';
     655           0 :                 if (mnstr_printf(s, "#%s", w) < 0)
     656             :                         return -4;
     657             :                 w = tmp;
     658             :         }
     659             :         return 1;
     660             : }
     661             : 
     662             : static int
     663           2 : mvc_export_binary_bat(stream *s, BAT* bn)
     664             : {
     665           2 :         BATiter bni = bat_iterator(bn);
     666           2 :         bool sendtheap = bni.type != TYPE_void, sendtvheap = sendtheap && bni.vh;
     667             : 
     668           4 :         if (mnstr_printf(s, /*JSON*/"{"
     669             :                                          "\"version\":1,"
     670             :                                          "\"ttype\":%d,"
     671             :                                          "\"hseqbase\":" OIDFMT ","
     672             :                                          "\"tseqbase\":" OIDFMT ","
     673             :                                          "\"tsorted\":%d,"
     674             :                                          "\"trevsorted\":%d,"
     675             :                                          "\"tkey\":%d,"
     676             :                                          "\"tnonil\":%d,"
     677             :                                          "\"tdense\":%d,"
     678             :                                          "\"size\":" BUNFMT ","
     679             :                                          "\"tailsize\":%zu,"
     680             :                                          "\"theapsize\":%zu"
     681             :                                          "}\n",
     682             :                                          bni.type,
     683             :                                          bn->hseqbase, bn->tseqbase,
     684           2 :                                          bni.sorted, bni.revsorted,
     685           2 :                                          bni.key,
     686           2 :                                          bni.nonil,
     687           2 :                                          BATtdensebi(&bni),
     688             :                                          bn->batCount,
     689           2 :                                          sendtheap ? (size_t)bni.count << bni.shift : 0,
     690           1 :                                          sendtvheap && bni.count > 0 ? bni.vhfree : 0) < 0) {
     691           0 :                 bat_iterator_end(&bni);
     692           0 :                 return -4;
     693             :         }
     694             : 
     695           2 :         if (sendtheap && bni.count > 0) {
     696           2 :                 if (mnstr_write(s, /* tail */ bni.base, bni.count * bni.width, 1) < 1) {
     697           0 :                         bat_iterator_end(&bni);
     698           0 :                         return -4;
     699             :                 }
     700           2 :                 if (sendtvheap && mnstr_write(s, /* tvheap */ bni.vh->base, bni.vhfree, 1) < 1) {
     701           0 :                         bat_iterator_end(&bni);
     702           0 :                         return -4;
     703             :                 }
     704             :         }
     705           2 :         bat_iterator_end(&bni);
     706           2 :         return 0;
     707             : }
     708             : 
     709             : static int
     710         332 : create_prepare_result(backend *b, cq *q, int nrows)
     711             : {
     712         332 :         int error = 0;
     713             : 
     714         332 :         BAT* btype              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     715         332 :         BAT* bimpl              = COLnew(0, TYPE_str, nrows, TRANSIENT);
     716         332 :         BAT* bdigits    = COLnew(0, TYPE_int, nrows, TRANSIENT);
     717         332 :         BAT* bscale             = COLnew(0, TYPE_int, nrows, TRANSIENT);
     718         332 :         BAT* bschema    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     719         332 :         BAT* btable             = COLnew(0, TYPE_str, nrows, TRANSIENT);
     720         332 :         BAT* bcolumn    = COLnew(0, TYPE_str, nrows, TRANSIENT);
     721         332 :         node *n;
     722             : 
     723         332 :         const int nr_columns = (b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) ? 7 : 6;
     724             : 
     725         332 :         int len1 = 0, len4 = 0, len5 = 0, len6 = 0, len7 =0;    /* column widths */
     726         332 :         int len2 = 1, len3 = 1;
     727         332 :         sql_arg *a;
     728         332 :         sql_subtype *t;
     729         332 :         sql_rel *r = q->rel;
     730             : 
     731         332 :         if (!btype || !bimpl || !bdigits || !bscale || !bschema || !btable || !bcolumn) {
     732           0 :                 error = -1;
     733           0 :                 goto wrapup;
     734             :         }
     735             : 
     736         332 :         if (r && (is_topn(r->op) || is_sample(r->op)))
     737           2 :                 r = r->l;
     738         332 :         if (r && is_project(r->op) && r->exps) {
     739         285 :                 unsigned int max2 = 10, max3 = 10;      /* to help calculate widths */
     740         285 :                 nrows += list_length(r->exps);
     741             : 
     742        1792 :                 for (n = r->exps->h; n; n = n->next) {
     743        1507 :                         const char *name = NULL, *rname = NULL, *schema = NULL;
     744        1507 :                         sql_exp *e = n->data;
     745        1507 :                         int slen;
     746             : 
     747        1507 :                         t = exp_subtype(e);
     748        1507 :                         slen = (int) strlen(t->type->base.name);
     749        1507 :                         if (slen > len1)
     750             :                                 len1 = slen;
     751        1607 :                         while (t->digits >= max2) {
     752         100 :                                 len2++;
     753         100 :                                 max2 *= 10;
     754             :                         }
     755        1507 :                         while (t->scale >= max3) {
     756           0 :                                 len3++;
     757           0 :                                 max3 *= 10;
     758             :                         }
     759        1507 :                         rname = exp_relname(e);
     760        1507 :                         if (!rname && e->type == e_column && e->l)
     761        1507 :                                 rname = e->l;
     762        1507 :                         slen = name ? (int) strlen(name) : 0;
     763        1507 :                         if (slen > len5)
     764             :                                 len5 = slen;
     765        1507 :                         name = exp_name(e);
     766        1507 :                         if (!name && e->type == e_column && e->r)
     767           0 :                                 name = e->r;
     768        1507 :                         slen = name ? (int) strlen(name) : 0;
     769        1507 :                         if (slen > len6)
     770             :                                 len6 = slen;
     771        1507 :                         slen = (int) strlen(t->type->impl);
     772        1507 :                         if (slen > len7)
     773             :                                 len7 = slen;
     774             : 
     775        1507 :                         if (!schema)
     776        1507 :                                 schema = "";
     777             : 
     778        1507 :                         if (!rname)
     779           7 :                                 rname = "";
     780             : 
     781        1507 :                         if (!name)
     782           0 :                                 name = "";
     783             : 
     784        3014 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     785        3014 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     786        3014 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     787        3014 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     788        3014 :                                         BUNappend(bschema,      schema                          , false) != GDK_SUCCEED ||
     789        3014 :                                         BUNappend(btable,       rname                           , false) != GDK_SUCCEED ||
     790        1507 :                                         BUNappend(bcolumn,      name                            , false) != GDK_SUCCEED) {
     791           0 :                                 error = -3;
     792           0 :                                 goto wrapup;
     793             :                         }
     794             :                 }
     795             :         }
     796             : 
     797         332 :         if (q->f->ops) {
     798         240 :                 int i;
     799             : 
     800        1789 :                 for (n = q->f->ops->h, i = 0; n; n = n->next, i++) {
     801        1549 :                         a = n->data;
     802        1549 :                         t = &a->type;
     803             : 
     804        3098 :                         if (    BUNappend(btype,        t->type->base.name        , false) != GDK_SUCCEED ||
     805        3098 :                                         BUNappend(bimpl,        t->type->impl             , false) != GDK_SUCCEED ||
     806        3098 :                                         BUNappend(bdigits,      &t->digits                       , false) != GDK_SUCCEED ||
     807        3098 :                                         BUNappend(bscale,       &t->scale                        , false) != GDK_SUCCEED ||
     808        3098 :                                         BUNappend(bschema,      str_nil                         , false) != GDK_SUCCEED ||
     809        3098 :                                         BUNappend(btable,       str_nil                         , false) != GDK_SUCCEED ||
     810        1549 :                                         BUNappend(bcolumn,      str_nil                         , false) != GDK_SUCCEED) {
     811           0 :                                 error = -3;
     812           0 :                                 goto wrapup;
     813             :                         }
     814             :                 }
     815             :         }
     816             : 
     817             :         // A little hack to inform the result receiver of the name of the compiled mal program.
     818         332 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
     819           0 :                 if (    BUNappend(btype,        str_nil         , false) != GDK_SUCCEED ||
     820           0 :                                 BUNappend(bimpl,        str_nil         , false) != GDK_SUCCEED ||
     821           0 :                                 BUNappend(bdigits,      &int_nil    , false) != GDK_SUCCEED ||
     822           0 :                                 BUNappend(bscale,       &int_nil    , false) != GDK_SUCCEED ||
     823           0 :                                 BUNappend(bschema,      str_nil         , false) != GDK_SUCCEED ||
     824           0 :                                 BUNappend(btable,       q->f->imp , false) != GDK_SUCCEED ||
     825           0 :                                 BUNappend(bcolumn,      str_nil         , false) != GDK_SUCCEED) {
     826           0 :                         error = -3;
     827           0 :                         goto wrapup;
     828             :                 }
     829             :         }
     830             : 
     831         148 :         b->results = res_table_create(
     832         332 :                                                         b->mvc->session->tr,
     833         332 :                                                         b->result_id++,
     834         332 :                                                         b->mb? b->mb->tag: 0 /*TODO check if this is sensible*/,
     835             :                                                         nr_columns,
     836             :                                                         Q_PREPARE,
     837             :                                                         b->results);
     838         332 :         if (!b->results) {
     839           0 :                 error = -1;
     840           0 :                 goto wrapup;
     841             :         }
     842             : 
     843         664 :         if (    mvc_result_column(b, ".prepare", "type"             , "varchar",  len1, 0, btype  ) ||
     844         664 :                         mvc_result_column(b, ".prepare", "digits"   , "int",              len2, 0, bdigits) ||
     845         664 :                         mvc_result_column(b, ".prepare", "scale"    , "int",              len3, 0, bscale ) ||
     846         664 :                         mvc_result_column(b, ".prepare", "schema"   , "varchar",  len4, 0, bschema) ||
     847         664 :                         mvc_result_column(b, ".prepare", "table"    , "varchar",  len5, 0, btable ) ||
     848         332 :                         mvc_result_column(b, ".prepare", "column"   , "varchar",  len6, 0, bcolumn)) {
     849           0 :                 error = -1;
     850           0 :                 goto wrapup;
     851             :         }
     852             : 
     853         332 :         if ((b->client->protocol == PROTOCOL_COLUMNAR || GDKembedded()) && mvc_result_column(b, "prepare", "impl" , "varchar", len7, 0, bimpl))
     854           0 :                 error = -1;
     855             : 
     856         332 :         wrapup:
     857         332 :                 BBPreclaim(btype);
     858         332 :                 BBPreclaim(bdigits);
     859         332 :                 BBPreclaim(bimpl);
     860         332 :                 BBPreclaim(bscale);
     861         332 :                 BBPreclaim(bschema);
     862         332 :                 BBPreclaim(btable);
     863         332 :                 BBPreclaim(bcolumn);
     864         332 :                 if (error < 0 && b->results) {
     865           0 :                         res_table_destroy(b->results);
     866           0 :                         b->results = NULL;
     867             :                 }
     868         332 :                 return error;
     869             : }
     870             : 
     871             : int
     872         332 : mvc_export_prepare(backend *b, stream *out)
     873             : {
     874         332 :         cq *q = b->q;
     875         332 :         int nparam = q->f->ops ? list_length(q->f->ops) : 0;
     876         332 :         int nrows = nparam, res;
     877             : 
     878         332 :         if ((res = create_prepare_result(b, q, nrows)) < 0)
     879             :                 return res;
     880             : 
     881         332 :         return mvc_export_result(b, out, b->results->id /*TODO is this right?*/, true, 0 /*TODO*/, 0 /*TODO*/);
     882             : }
     883             : 
     884             : /*
     885             :  * improved formatting of positive integers
     886             :  */
     887             : 
     888             : static ssize_t
     889           0 : mvc_send_bte(stream *s, bte cnt)
     890             : {
     891           0 :         char buf[50], *b;
     892           0 :         int neg = cnt < 0;
     893           0 :         if (neg)
     894           0 :                 cnt = -cnt;
     895             :         b = buf + 49;
     896           0 :         do {
     897           0 :                 *b-- = (char) ('0' + (cnt % 10));
     898           0 :                 cnt /= 10;
     899           0 :         } while (cnt > 0);
     900           0 :         if (neg)
     901           0 :                 *b = '-';
     902             :         else
     903             :                 b++;
     904           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     905             : }
     906             : 
     907             : static ssize_t
     908           0 : mvc_send_sht(stream *s, sht cnt)
     909             : {
     910           0 :         char buf[50], *b;
     911           0 :         int neg = cnt < 0;
     912           0 :         if (neg)
     913           0 :                 cnt = -cnt;
     914             :         b = buf + 49;
     915           0 :         do {
     916           0 :                 *b-- = (char) ('0' + (cnt % 10));
     917           0 :                 cnt /= 10;
     918           0 :         } while (cnt > 0);
     919           0 :         if (neg)
     920           0 :                 *b = '-';
     921             :         else
     922             :                 b++;
     923           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     924             : }
     925             : 
     926             : static ssize_t
     927      479777 : mvc_send_int(stream *s, int cnt)
     928             : {
     929      479777 :         char buf[50], *b;
     930      479777 :         int neg = cnt < 0;
     931      479777 :         if (neg)
     932             :                 cnt = -cnt;
     933             :         b = buf + 49;
     934      643007 :         do {
     935      643007 :                 *b-- = (char) ('0' + (cnt % 10));
     936      643007 :                 cnt /= 10;
     937      643007 :         } while (cnt > 0);
     938      479777 :         if (neg)
     939           0 :                 *b = '-';
     940             :         else
     941             :                 b++;
     942      479777 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     943             : }
     944             : 
     945             : static ssize_t
     946     1461837 : mvc_send_lng(stream *s, lng cnt)
     947             : {
     948     1461837 :         char buf[50], *b;
     949     1461837 :         int neg = cnt < 0;
     950     1461837 :         if (neg)
     951             :                 cnt = -cnt;
     952             :         b = buf + 49;
     953     3267055 :         do {
     954     3267055 :                 *b-- = (char) ('0' + (cnt % 10));
     955     3267055 :                 cnt /= 10;
     956     3267055 :         } while (cnt > 0);
     957     1461837 :         if (neg)
     958       83291 :                 *b = '-';
     959             :         else
     960             :                 b++;
     961     1461837 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     962             : }
     963             : 
     964             : #ifdef HAVE_HGE
     965             : static ssize_t
     966           0 : mvc_send_hge(stream *s, hge cnt)
     967             : {
     968           0 :         char buf[50], *b;
     969           0 :         int neg = cnt <0;
     970           0 :         if(neg) cnt = -cnt;
     971             :         b= buf+49;
     972           0 :         do{
     973           0 :                 *b--= (char) ('0'+ (cnt % 10));
     974           0 :                 cnt /=10;
     975           0 :         } while(cnt>0);
     976           0 :         if( neg)
     977           0 :                 *b = '-';
     978             :         else b++;
     979           0 :         return mnstr_write(s, b, 50 - (b - buf), 1);
     980             : }
     981             : #endif
     982             : 
     983             : ssize_t
     984     4115862 : convert2str(mvc *m, sql_class eclass, int d, int sc, int has_tz, const void *p, int mtype, char **buf, size_t *len)
     985             : {
     986     4115862 :         ssize_t l = 0;
     987             : 
     988     4115862 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
     989       20220 :                 (*buf)[0] = '\200';
     990       20220 :                 (*buf)[1] = 0;
     991     4091017 :         } else if (eclass == EC_DEC) {
     992         269 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
     993             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
     994          84 :                 struct time_res ts_res;
     995          84 :                 ts_res.has_tz = has_tz;
     996          84 :                 ts_res.fraction = d ? d - 1 : 0;
     997          84 :                 ts_res.timezone = m->timezone;
     998          84 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
     999             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1000          97 :                 struct time_res ts_res;
    1001          97 :                 ts_res.has_tz = has_tz;
    1002          97 :                 ts_res.fraction = d ? d - 1 : 0;
    1003          97 :                 ts_res.timezone = m->timezone;
    1004          97 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1005             :         } else if (eclass == EC_SEC) {
    1006        1019 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1007             :         } else if (eclass == EC_BIT) {
    1008        2566 :                 bit b = *(bit *) p;
    1009        2566 :                 if (*len == 0 || *len > 5) {
    1010        2540 :                         if (b) {
    1011        2452 :                                 strcpy(*buf, "true");
    1012        2452 :                                 l = 4;
    1013             :                         } else {
    1014          88 :                                 strcpy(*buf, "false");
    1015          88 :                                 l = 5;
    1016             :                         }
    1017             :                 } else {
    1018          26 :                         (*buf)[0] = b?'t':'f';
    1019          26 :                         (*buf)[1] = 0;
    1020          26 :                         l = 1;
    1021             :                 }
    1022             :         } else {
    1023     4086982 :                 l = (*BATatoms[mtype].atomToStr) (buf, len, p, false);
    1024             :         }
    1025     4128933 :         return l;
    1026             : }
    1027             : 
    1028             : static int
    1029           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)
    1030             : {
    1031           0 :         int ok = 0;
    1032           0 :         ssize_t l = 0;
    1033             : 
    1034           0 :         if (!p || ATOMcmp(mtype, ATOMnilptr(mtype), p) == 0) {
    1035           0 :                 if (mnstr_write(s, ns, strlen(ns), 1) < 1)
    1036           0 :                         ok = -4;
    1037           0 :         } else if (eclass == EC_DEC) {
    1038           0 :                 l = dec_tostr((void *) (ptrdiff_t) sc, buf, len, mtype, p);
    1039           0 :                 if (l > 0 && mnstr_write(s, *buf, l, 1) < 1)
    1040           0 :                         ok = -4;
    1041           0 :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1042           0 :                 struct time_res ts_res;
    1043           0 :                 ts_res.has_tz = (strcmp(sqlname, "timetz") == 0);
    1044           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1045           0 :                 ts_res.timezone = m->timezone;
    1046           0 :                 l = sql_time_tostr((void *) &ts_res, buf, len, mtype, p);
    1047           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1048           0 :                         ok = -4;
    1049           0 :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1050           0 :                 struct time_res ts_res;
    1051           0 :                 ts_res.has_tz = (strcmp(sqlname, "timestamptz") == 0);
    1052           0 :                 ts_res.fraction = d ? d - 1 : 0;
    1053           0 :                 ts_res.timezone = m->timezone;
    1054           0 :                 l = sql_timestamp_tostr((void *) &ts_res, buf, len, mtype, p);
    1055           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1056           0 :                         ok = -4;
    1057           0 :         } else if (eclass == EC_SEC) {
    1058           0 :                 l = dec_tostr((void *) (ptrdiff_t) 3, buf, len, mtype, p);
    1059           0 :                 if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1060           0 :                         ok = -4;
    1061             :         } else {
    1062           0 :                 switch (mtype) {
    1063           0 :                 case TYPE_bte:
    1064           0 :                         if (mvc_send_bte(s, *(bte *) p) < 1)
    1065           0 :                                 ok = -4;
    1066             :                         break;
    1067           0 :                 case TYPE_sht:
    1068           0 :                         if (mvc_send_sht(s, *(sht *) p) < 1)
    1069           0 :                                 ok = -4;
    1070             :                         break;
    1071           0 :                 case TYPE_int:
    1072           0 :                         if (mvc_send_int(s, *(int *) p) < 1)
    1073           0 :                                 ok = -4;
    1074             :                         break;
    1075           0 :                 case TYPE_lng:
    1076           0 :                         if (mvc_send_lng(s, *(lng *) p) < 1)
    1077           0 :                                 ok = -4;
    1078             :                         break;
    1079             : #ifdef HAVE_HGE
    1080           0 :                 case TYPE_hge:
    1081           0 :                         if (mvc_send_hge(s, *(hge *) p) < 1)
    1082           0 :                                 ok = -4;
    1083             :                         break;
    1084             : #endif
    1085           0 :                 default:
    1086           0 :                         l = (*BATatoms[mtype].atomToStr) (buf, len, p, true);
    1087           0 :                         if (l >= 0 && mnstr_write(s, *buf, l, 1) < 1)
    1088           0 :                                 ok = -4;
    1089             :                 }
    1090             :         }
    1091           0 :         return ok;
    1092             : }
    1093             : 
    1094             : static int
    1095           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)
    1096             : {
    1097           0 :         mvc *m = b->mvc;
    1098           0 :         size_t seplen = strlen(sep);
    1099           0 :         size_t rseplen = strlen(rsep);
    1100           0 :         char *buf = NULL;
    1101           0 :         size_t len = 0;
    1102           0 :         int i, ok = 1;
    1103           0 :         int csv = (b->output_format == OFMT_CSV);
    1104           0 :         int json = (b->output_format == OFMT_JSON);
    1105             : 
    1106           0 :         if (!s || !t)
    1107             :                 return 0;
    1108             : 
    1109           0 :         (void) ssep;
    1110           0 :         if (csv && btag[0] && mnstr_write(s, btag, strlen(btag), 1) < 1)
    1111           0 :                 ok = -4;
    1112           0 :         if (json) {
    1113           0 :                 sep = ", ";
    1114           0 :                 seplen = strlen(sep);
    1115             :         }
    1116           0 :         for (i = 0; i < t->nr_cols && ok > -1; i++) {
    1117           0 :                 res_col *c = t->cols + i;
    1118             : 
    1119           0 :                 if (i != 0 && mnstr_write(s, sep, seplen, 1) < 1) {
    1120             :                         ok = -4;
    1121             :                         break;
    1122             :                 }
    1123           0 :                 if (json && (mnstr_write(s, c->name, strlen(c->name), 1) < 1 || mnstr_write(s, ": ", 2, 1) < 1)) {
    1124             :                         ok = -4;
    1125             :                         break;
    1126             :                 }
    1127           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);
    1128             :         }
    1129           0 :         _DELETE(buf);
    1130           0 :         if (ok > -1 && mnstr_write(s, rsep, rseplen, 1) < 1)
    1131           0 :                 ok = -4;
    1132           0 :         b->results = res_tables_remove(b->results, t);
    1133           0 :         return ok;
    1134             : }
    1135             : 
    1136             : static int
    1137           1 : mvc_export_table_columnar(stream *s, res_table *t)
    1138             : {
    1139           1 :         int i, res = 0;
    1140             : 
    1141           1 :         if (!s || !t)
    1142             :                 return 0;
    1143             : 
    1144           3 :         for (i = 1; i <= t->nr_cols; i++) {
    1145           2 :                 res_col *c = t->cols + (i - 1);
    1146             : 
    1147           2 :                 if (!c->b)
    1148             :                         break;
    1149             : 
    1150           2 :                 BAT *b = BATdescriptor(c->b);
    1151           2 :                 if (b == NULL)
    1152             :                         return -2;
    1153             : 
    1154           2 :                 res = mvc_export_binary_bat(s, b);
    1155           2 :                 BBPunfix(b->batCacheid);
    1156           2 :                 if (res < 0)
    1157           0 :                         return res;
    1158             :         }
    1159             : 
    1160             :         return res;
    1161             : }
    1162             : 
    1163             : static int
    1164      119991 : 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)
    1165             : {
    1166      119991 :         Tablet as;
    1167      119991 :         Column *fmt;
    1168      119991 :         int i, ok = 0;
    1169      119991 :         struct time_res *tres;
    1170      119991 :         int csv = (output_format == OFMT_CSV);
    1171      119991 :         int json = (output_format == OFMT_JSON);
    1172      119991 :         char *bj;
    1173             : 
    1174      119991 :         if (!s || !t)
    1175             :                 return 0;
    1176             : 
    1177      119990 :         as.nr_attrs = t->nr_cols + 1;        /* for the leader */
    1178      119990 :         as.nr = nr;
    1179      119990 :         as.offset = offset;
    1180      119990 :         fmt = as.format = (Column *) GDKzalloc(sizeof(Column) * (as.nr_attrs + 1));
    1181      119988 :         tres = GDKzalloc(sizeof(struct time_res) * (as.nr_attrs));
    1182      119990 :         if (fmt == NULL || tres == NULL) {
    1183           0 :                 GDKfree(fmt);
    1184           0 :                 GDKfree(tres);
    1185           0 :                 return -4;
    1186             :         }
    1187             : 
    1188      119990 :         fmt[0].c = NULL;
    1189      119990 :         fmt[0].sep = (csv) ? btag : "";
    1190      119990 :         fmt[0].rsep = rsep;
    1191      119990 :         fmt[0].seplen = _strlen(fmt[0].sep);
    1192      119990 :         fmt[0].ws = 0;
    1193      119990 :         fmt[0].nullstr = NULL;
    1194             : 
    1195      435586 :         for (i = 1; i <= t->nr_cols; i++) {
    1196      315600 :                 res_col *c = t->cols + (i - 1);
    1197             : 
    1198      315600 :                 if (!c->b)
    1199             :                         break;
    1200             : 
    1201      315600 :                 fmt[i].c = BATdescriptor(c->b);
    1202      315580 :                 if (fmt[i].c == NULL) {
    1203           0 :                         while (--i >= 1) {
    1204           0 :                                 bat_iterator_end(&fmt[i].ci);
    1205           0 :                                 BBPunfix(fmt[i].c->batCacheid);
    1206             :                         }
    1207           0 :                         GDKfree(fmt);
    1208           0 :                         GDKfree(tres);
    1209           0 :                         return -2;
    1210             :                 }
    1211      315580 :                 fmt[i].ci = bat_iterator(fmt[i].c);
    1212      315598 :                 fmt[i].name = NULL;
    1213      315598 :                 if (csv) {
    1214      315597 :                         fmt[i].sep = ((i - 1) < (t->nr_cols - 1)) ? sep : rsep;
    1215      315597 :                         fmt[i].seplen = _strlen(fmt[i].sep);
    1216      315597 :                         fmt[i].rsep = rsep;
    1217             :                 }
    1218      315598 :                 if (json) {
    1219           0 :                         res_col *p = t->cols + (i - 1);
    1220             : 
    1221             :                         /*
    1222             :                          * We define the "proper" way of returning
    1223             :                          * a relational table in json format as a
    1224             :                          * json array of objects, where each row is
    1225             :                          * represented as a json object.
    1226             :                          */
    1227           0 :                         if (i == 1) {
    1228           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(btag));
    1229           0 :                                 snprintf(bj, strlen(p->name) + strlen(btag), btag, p->name);
    1230           0 :                                 fmt[i - 1].sep = bj;
    1231           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1232           0 :                                 fmt[i - 1].rsep = NULL;
    1233           0 :                         } else if (i <= t->nr_cols) {
    1234           0 :                                 bj = SA_NEW_ARRAY(m->sa, char, strlen(p->name) + strlen(sep));
    1235           0 :                                 snprintf(bj, strlen(p->name) + 10, sep, p->name);
    1236           0 :                                 fmt[i - 1].sep = bj;
    1237           0 :                                 fmt[i - 1].seplen = _strlen(fmt[i - 1].sep);
    1238           0 :                                 fmt[i - 1].rsep = NULL;
    1239             :                         }
    1240           0 :                         if (i == t->nr_cols) {
    1241           0 :                                 fmt[i].sep = rsep;
    1242           0 :                                 fmt[i].seplen = _strlen(fmt[i].sep);
    1243           0 :                                 fmt[i].rsep = NULL;
    1244             :                         }
    1245             :                 }
    1246      315598 :                 fmt[i].type = ATOMname(fmt[i].c->ttype);
    1247      315596 :                 fmt[i].adt = fmt[i].c->ttype;
    1248      315596 :                 fmt[i].tostr = &_ASCIIadt_toStr;
    1249      315596 :                 fmt[i].frstr = &_ASCIIadt_frStr;
    1250      315596 :                 fmt[i].extra = fmt + i;
    1251      315596 :                 fmt[i].data = NULL;
    1252      315596 :                 fmt[i].len = 0;
    1253      315596 :                 fmt[i].ws = 0;
    1254      315596 :                 fmt[i].quote = ssep ? ssep[0] : 0;
    1255      315596 :                 fmt[i].nullstr = ns;
    1256      315596 :                 if (c->type.type->eclass == EC_DEC) {
    1257        1249 :                         fmt[i].tostr = &dec_tostr;
    1258        1249 :                         fmt[i].frstr = &dec_frstr;
    1259        1249 :                         fmt[i].extra = (void *) (ptrdiff_t) c->type.scale;
    1260      314347 :                 } else if (c->type.type->eclass == EC_TIMESTAMP || c->type.type->eclass == EC_TIMESTAMP_TZ) {
    1261         712 :                         struct time_res *ts_res = tres + (i - 1);
    1262         712 :                         ts_res->has_tz = EC_TEMP_TZ(c->type.type->eclass);
    1263         712 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1264         712 :                         ts_res->timezone = m->timezone;
    1265             : 
    1266         712 :                         fmt[i].tostr = &sql_timestamp_tostr;
    1267         712 :                         fmt[i].frstr = NULL;
    1268         712 :                         fmt[i].extra = ts_res;
    1269      313635 :                 } else if (c->type.type->eclass == EC_TIME || c->type.type->eclass == EC_TIME_TZ) {
    1270         279 :                         struct time_res *ts_res = tres + (i - 1);
    1271         279 :                         ts_res->has_tz = (strcmp(c->type.type->base.name, "timetz") == 0);
    1272         279 :                         ts_res->fraction = c->type.digits ? c->type.digits - 1 : 0;
    1273         279 :                         ts_res->timezone = m->timezone;
    1274             : 
    1275         279 :                         fmt[i].tostr = &sql_time_tostr;
    1276         279 :                         fmt[i].frstr = NULL;
    1277         279 :                         fmt[i].extra = ts_res;
    1278      313356 :                 } else if (c->type.type->eclass == EC_SEC) {
    1279         453 :                         fmt[i].tostr = &dec_tostr;
    1280         453 :                         fmt[i].frstr = &sec_frstr;
    1281         453 :                         fmt[i].extra = (void *) (ptrdiff_t) 3;
    1282             :                 } else {
    1283             :                         fmt[i].extra = fmt + i;
    1284             :                 }
    1285             :         }
    1286      119986 :         if (i == t->nr_cols + 1)
    1287      119986 :                 ok = TABLEToutput_file(&as, NULL, s);
    1288      555574 :         for (i = 0; i <= t->nr_cols; i++) {
    1289      435584 :                 fmt[i].sep = NULL;
    1290      435584 :                 fmt[i].rsep = NULL;
    1291      435584 :                 fmt[i].type = NULL;
    1292      435584 :                 fmt[i].nullstr = NULL;
    1293             :         }
    1294      435592 :         for (i = 1; i <= t->nr_cols; i++)
    1295      315602 :                 bat_iterator_end(&fmt[i].ci);
    1296      119990 :         TABLETdestroy_format(&as);
    1297      119990 :         GDKfree(tres);
    1298      119990 :         if (mnstr_errnr(s) != MNSTR_NO__ERROR)
    1299             :                 return -4;
    1300      119989 :         if (ok < 0)
    1301             :                 return ok;
    1302             :         return 0;
    1303             : }
    1304             : 
    1305             : static int
    1306      119989 : 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)
    1307             : {
    1308      119989 :         return mvc_export_table_(b->mvc, b->output_format, s, t, offset, nr, btag, sep, rsep, ssep, ns);
    1309             : }
    1310             : 
    1311             : int
    1312           0 : mvc_export(mvc *m, stream *s, res_table *t, BUN nr)
    1313             : {
    1314           0 :         backend b = {0};
    1315           0 :         b.mvc = m;
    1316           0 :         b.results = t;
    1317           0 :         b.reloptimizer = 0;
    1318           0 :         t->nr_rows = nr;
    1319           0 :         if (mvc_export_head(&b, s, t->id, TRUE, TRUE, 0/*starttime*/, 0/*maloptimizer*/) < 0)
    1320             :                 return -1;
    1321           0 :         return mvc_export_table_(m, OFMT_CSV, s, t, 0, nr, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1322             : }
    1323             : 
    1324             : 
    1325             : static lng
    1326      314876 : get_print_width(int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1327             : {
    1328      314876 :         size_t count = 0, incr = 0;
    1329             : 
    1330      314876 :         if (eclass == EC_SEC)
    1331             :                 incr = 1;
    1332      314420 :         else if (mtype == TYPE_oid)
    1333          89 :                 incr = 2;
    1334      314876 :         mtype = ATOMbasetype(mtype);
    1335      314876 :         if (mtype == TYPE_str) {
    1336       89294 :                 if (eclass == EC_CHAR && digits) {
    1337        7606 :                         return digits;
    1338             :                 } else {
    1339       81688 :                         int l = 0;
    1340       81688 :                         if (bid) {
    1341       81688 :                                 BAT *b = BATdescriptor(bid);
    1342             : 
    1343       81688 :                                 if (b) {
    1344             :                                         /* in practice, b can be a
    1345             :                                          * void(nil) bat, an oid bat
    1346             :                                          * with all nil values, or an
    1347             :                                          * empty void/oid bat */
    1348       81688 :                                         if (ATOMstorage(b->ttype) == TYPE_str)
    1349       81688 :                                                 l = bat_max_strlength(b);
    1350             :                                         else
    1351             :                                                 l = 0;
    1352       81688 :                                         BBPunfix(b->batCacheid);
    1353             :                                 } else {
    1354             :                                         return -2;
    1355             :                                 }
    1356           0 :                         } else if (p) {
    1357           0 :                                 l = UTF8_strwidth((const char *) p);
    1358           0 :                                 if (is_int_nil(l))
    1359           0 :                                         l = 0;
    1360             :                         }
    1361       81688 :                         return l;
    1362             :                 }
    1363      225582 :         } else if (eclass == EC_NUM || eclass == EC_POS || eclass == EC_MONTH || eclass == EC_SEC) {
    1364      214749 :                 count = 0;
    1365      214749 :                 if (bid) {
    1366      214749 :                         BAT *b = BATdescriptor(bid);
    1367             : 
    1368      214761 :                         if (b) {
    1369      214761 :                                 if (mtype == TYPE_bte) {
    1370       36977 :                                         count = bat_max_btelength(b);
    1371             :                                 } else if (mtype == TYPE_sht) {
    1372       28128 :                                         count = bat_max_shtlength(b);
    1373             :                                 } else if (mtype == TYPE_int) {
    1374      125597 :                                         count = bat_max_intlength(b);
    1375             :                                 } else if (mtype == TYPE_lng) {
    1376       19317 :                                         count = bat_max_lnglength(b);
    1377             : #ifdef HAVE_HGE
    1378             :                                 } else if (mtype == TYPE_hge) {
    1379        4742 :                                         count = bat_max_hgelength(b);
    1380             : #endif
    1381             :                                 } else if (mtype == TYPE_void) {
    1382             :                                         count = 4;
    1383             :                                 } else {
    1384           0 :                                         assert(0);
    1385             :                                 }
    1386      214751 :                                 count += incr;
    1387      214751 :                                 BBPunfix(b->batCacheid);
    1388             :                         } else {
    1389             :                                 return -2;
    1390             :                         }
    1391             :                 } else {
    1392           0 :                         if (p) {
    1393             : #ifdef HAVE_HGE
    1394           0 :                                 hge val = 0;
    1395             : #else
    1396             :                                 lng val = 0;
    1397             : #endif
    1398           0 :                                 if (mtype == TYPE_bte) {
    1399           0 :                                         val = *((bte *) p);
    1400             :                                 } else if (mtype == TYPE_sht) {
    1401           0 :                                         val = *((sht *) p);
    1402             :                                 } else if (mtype == TYPE_int) {
    1403           0 :                                         val = *((int *) p);
    1404             :                                 } else if (mtype == TYPE_lng) {
    1405           0 :                                         val = *((lng *) p);
    1406             : #ifdef HAVE_HGE
    1407             :                                 } else if (mtype == TYPE_hge) {
    1408           0 :                                         val = *((hge *) p);
    1409             : #endif
    1410             :                                 } else {
    1411           0 :                                         assert(0);
    1412             :                                 }
    1413             : 
    1414           0 :                                 if (val < 0)
    1415           0 :                                         count++;
    1416           0 :                                 while (val /= 10)
    1417           0 :                                         count++;
    1418           0 :                                 count++;
    1419           0 :                                 count += incr;
    1420             :                         } else {
    1421             :                                 count = 0;
    1422             :                         }
    1423             :                 }
    1424      214752 :                 if (eclass == EC_SEC && count < 5)
    1425          45 :                         count = 5;
    1426      214752 :                 return count;
    1427             :                 /* the following two could be done once by taking the
    1428             :                    max value and calculating the number of digits from that
    1429             :                    value, instead of the maximum values taken now, which
    1430             :                    include the optional sign */
    1431             :         } else if (eclass == EC_FLT) {
    1432             :                 /* floats are printed using "%.9g":
    1433             :                  * [sign]+digit+period+[max 8 digits]+E+[sign]+[max 2 digits] */
    1434        2235 :                 if (mtype == TYPE_flt) {
    1435             :                         return 15;
    1436             :                         /* doubles are printed using "%.17g":
    1437             :                          * [sign]+digit+period+[max 16 digits]+E+[sign]+[max 3 digits] */
    1438             :                 } else {        /* TYPE_dbl */
    1439        1953 :                         return 24;
    1440             :                 }
    1441             :         } else if (eclass == EC_DEC) {
    1442        1249 :                 count = 1 + digits;
    1443        1249 :                 if (scale > 0)
    1444        1033 :                         count += 1;
    1445        1249 :                 if (scale == digits) // for preceding 0, e.g. 0.
    1446          13 :                         count += 1;
    1447        1249 :                 return count;
    1448             :         } else if (eclass == EC_DATE) {
    1449             :                 return 10;
    1450             :         } else if (eclass == EC_TIME || eclass == EC_TIME_TZ) {
    1451         270 :                 count = 8;
    1452         270 :                 if (tz)         /* time zone */
    1453          88 :                         count += 6;     /* +03:30 */
    1454         270 :                 if (digits > 1)      /* fractional seconds precision (including dot) */
    1455          92 :                         count += digits;
    1456         270 :                 return count;
    1457             :         } else if (eclass == EC_TIMESTAMP || eclass == EC_TIMESTAMP_TZ) {
    1458         712 :                 count = 10 + 1 + 8;
    1459         712 :                 if (tz)         /* time zone */
    1460         146 :                         count += 6;     /* +03:30 */
    1461         712 :                 if (digits)     /* fractional seconds precision */
    1462         712 :                         count += digits;
    1463         712 :                 return count;
    1464             :         } else if (eclass == EC_BIT) {
    1465        5386 :                 return 5;       /* max(strlen("true"), strlen("false")) */
    1466         608 :         } else if (strcmp(ATOMname(mtype), "uuid") == 0) {
    1467             :                 return 36;      /* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx */
    1468             :         } else {
    1469         528 :                 return 0;
    1470             :         }
    1471             : }
    1472             : 
    1473             : static int
    1474      314873 : export_length(stream *s, int mtype, sql_class eclass, int digits, int scale, int tz, bat bid, ptr p)
    1475             : {
    1476      314873 :         lng length = get_print_width(mtype, eclass, digits, scale, tz, bid, p);
    1477      314878 :         if (length < 0)
    1478             :                 return -2;
    1479      314878 :         if (mvc_send_lng(s, length) != 1)
    1480           0 :                 return -4;
    1481             :         return 0;
    1482             : }
    1483             : 
    1484             : int
    1485       18675 : mvc_export_operation(backend *b, stream *s, str w, lng starttime, lng mal_optimizer)
    1486             : {
    1487       18675 :         mvc *m = b->mvc;
    1488             : 
    1489       18675 :         assert(m->type == Q_SCHEMA || m->type == Q_TRANS);
    1490       18675 :         if (m->type == Q_SCHEMA) {
    1491       15608 :                 if (!s)
    1492             :                         return 0;
    1493       15608 :                 if (mnstr_printf(s, "&3 " LLFMT " " LLFMT "\n", starttime > 0 ? GDKusec() - starttime : 0, mal_optimizer) < 0)
    1494             :                         return -4;
    1495             :         } else {
    1496        3067 :                 if (m->session->auto_commit) {
    1497        1424 :                         if (mnstr_write(s, "&4 t\n", 5, 1) != 1)
    1498             :                                 return -4;
    1499             :                 } else {
    1500        1643 :                         if (mnstr_write(s, "&4 f\n", 5, 1) != 1)
    1501             :                                 return -4;
    1502             :                 }
    1503             :         }
    1504             : 
    1505       18675 :         if (mvc_export_warning(s, w) != 1)
    1506             :                 return -4;
    1507             :         return 0;
    1508             : }
    1509             : 
    1510             : 
    1511             : int
    1512      132118 : mvc_affrows(mvc *m, stream *s, lng val, str w, oid query_id, lng last_id, lng starttime, lng maloptimizer, lng reloptimizer)
    1513             : {
    1514      132118 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), val);
    1515             : 
    1516             :         /* if we don't have a stream, nothing can go wrong, so we return
    1517             :          * success.  This is especially vital for execution of internal SQL
    1518             :          * commands, since they don't get a stream to suppress their output.
    1519             :          * If we would fail on having no stream here, those internal commands
    1520             :          * fail too.
    1521             :          */
    1522      132116 :         if (!s || GDKembedded())
    1523       40888 :                 return 0;
    1524      182460 :         if (mnstr_write(s, "&2 ", 3, 1) != 1 ||
    1525      182460 :             mvc_send_lng(s, val) != 1 ||
    1526      182459 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1527      182459 :             mvc_send_lng(s, last_id) != 1 ||
    1528      182456 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1529      182456 :             mvc_send_lng(s, (lng) query_id) != 1 ||
    1530      182456 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1531      182455 :             mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1 ||
    1532      182457 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1533      182457 :             mvc_send_lng(s, maloptimizer) != 1 ||
    1534      182457 :             mnstr_write(s, " ", 1, 1) != 1 ||
    1535      182456 :             mvc_send_lng(s, reloptimizer) != 1 ||
    1536       91229 :             mnstr_write(s, "\n", 1, 1) != 1)
    1537           0 :                 return -4;
    1538       91227 :         if (mvc_export_warning(s, w) != 1)
    1539             :                 return -4;
    1540             : 
    1541             :         return 0;
    1542             : }
    1543             : 
    1544             : int
    1545      132117 : mvc_export_affrows(backend *b, stream *s, lng val, str w, oid query_id, lng starttime, lng maloptimizer)
    1546             : {
    1547      132117 :         b->rowcnt = val;
    1548      132117 :         return mvc_affrows(b->mvc, s, val, w, query_id, b->last_id, starttime, maloptimizer, b->reloptimizer);
    1549             : }
    1550             : 
    1551             : int
    1552      119918 : mvc_export_head(backend *b, stream *s, int res_id, int only_header, int compute_lengths, lng starttime, lng maloptimizer)
    1553             : {
    1554      119918 :         mvc *m = b->mvc;
    1555      119918 :         int i, res = 0;
    1556      119918 :         BUN count = 0;
    1557      119918 :         res_table *t = res_tables_find(b->results, res_id);
    1558             : 
    1559      119918 :         if (!s || !t)
    1560             :                 return 0;
    1561             : 
    1562             :         /* query type: Q_TABLE || Q_PREPARE */
    1563      119918 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1564      119918 :         if (mnstr_write(s, "&", 1, 1) != 1 || mvc_send_int(s, (int) t->query_type) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1565           0 :                 return -4;
    1566             : 
    1567             :         /* id */
    1568      119916 :         int result_id = t->query_type == Q_PREPARE?b->q->id:t->id;
    1569      119916 :         if (mvc_send_int(s, result_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1570           0 :                 return -4;
    1571             : 
    1572             :         /* tuple count */
    1573      119916 :         if (only_header) {
    1574      119916 :                 if (t->cols[0].b) {
    1575      119916 :                         count = t->nr_rows;
    1576             :                 } else {
    1577             :                         count = 1;
    1578             :                 }
    1579             :         }
    1580      119916 :         b->rowcnt = count;
    1581      119916 :         sqlvar_set_number(find_global_var(m, mvc_bind_schema(m, "sys"), "rowcnt"), b->rowcnt);
    1582      119917 :         if (mvc_send_lng(s, (lng) count) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1583           0 :                 return -4;
    1584             : 
    1585             :         /* column count */
    1586      119916 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1587           0 :                 return -4;
    1588             : 
    1589             :         /* row count, min(count, reply_size) */
    1590             :         /* the columnar protocol ignores the reply size by fetching the entire resultset at once, so don't set it */
    1591      119916 :         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)
    1592             :                 return -4;
    1593             : 
    1594             :         // export query id
    1595      119916 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, (lng) t->query_id) != 1)
    1596           0 :                 return -4;
    1597             : 
    1598             :         // export query time
    1599      119917 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, starttime > 0 ? GDKusec() - starttime : 0) != 1)
    1600           0 :                 return -4;
    1601             : 
    1602             :         // export MAL optimizer time
    1603      119918 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, maloptimizer) != 1)
    1604           0 :                 return -4;
    1605             : 
    1606      119918 :         if (mnstr_write(s, " ", 1, 1) != 1 || mvc_send_lng(s, b->reloptimizer) != 1)
    1607           0 :                 return -4;
    1608             : 
    1609      119918 :         if (mnstr_write(s, "\n% ", 3, 1) != 1)
    1610             :                 return -4;
    1611      434796 :         for (i = 0; i < t->nr_cols; i++) {
    1612      314878 :                 res_col *c = t->cols + i;
    1613      314878 :                 size_t len = strlen(c->tn);
    1614             : 
    1615      314878 :                 if (len && mnstr_write(s, c->tn, len, 1) != 1)
    1616             :                         return -4;
    1617      314876 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1618             :                         return -4;
    1619             :         }
    1620      119918 :         if (mnstr_write(s, " # table_name\n% ", 16, 1) != 1)
    1621             :                 return -4;
    1622             : 
    1623      434803 :         for (i = 0; i < t->nr_cols; i++) {
    1624      314885 :                 res_col *c = t->cols + i;
    1625             : 
    1626      314885 :                 if (strpbrk(c->name, ", \t#\"\\")) {
    1627         183 :                         char *p;
    1628         183 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1629             :                                 return -4;
    1630        2454 :                         for (p = c->name; *p; p++) {
    1631        2271 :                                 if (*p == '"' || *p == '\\') {
    1632          25 :                                         if (mnstr_write(s, "\\", 1, 1) != 1)
    1633             :                                                 return -4;
    1634             :                                 }
    1635        2271 :                                 if (mnstr_write(s, p, 1, 1) != 1)
    1636             :                                         return -4;
    1637             :                         }
    1638         183 :                         if (mnstr_write(s, "\"", 1, 1) != 1)
    1639             :                                 return -4;
    1640             :                 } else {
    1641      314702 :                         if (mnstr_write(s, c->name, strlen(c->name), 1) != 1)
    1642             :                                 return -4;
    1643             :                 }
    1644             : 
    1645      314884 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1646             :                         return -4;
    1647             :         }
    1648      119918 :         if (mnstr_write(s, " # name\n% ", 10, 1) != 1)
    1649             :                 return -4;
    1650             : 
    1651      434799 :         for (i = 0; i < t->nr_cols; i++) {
    1652      314882 :                 res_col *c = t->cols + i;
    1653             : 
    1654      314882 :                 if (mnstr_write(s, c->type.type->base.name, strlen(c->type.type->base.name), 1) != 1)
    1655             :                         return -4;
    1656      314884 :                 if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1657             :                         return -4;
    1658             :         }
    1659      119917 :         if (mnstr_write(s, " # type\n% ", 10, 1) != 1)
    1660             :                 return -4;
    1661      119916 :         if (compute_lengths) {
    1662      434784 :                 for (i = 0; i < t->nr_cols; i++) {
    1663      314867 :                         res_col *c = t->cols + i;
    1664      314867 :                         int mtype = c->type.type->localtype;
    1665      314867 :                         sql_class eclass = c->type.type->eclass;
    1666             : 
    1667      314867 :                         if ((res = export_length(s, mtype, eclass, c->type.digits, c->type.scale, type_has_tz(&c->type), c->b, c->p)) < 0)
    1668           0 :                                 return res;
    1669      314869 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1670             :                                 return -4;
    1671             :                 }
    1672      119917 :                 if (mnstr_write(s, " # length\n", 10, 1) != 1)
    1673             :                         return -4;
    1674             :         }
    1675      119915 :         if (b->sizeheader) {
    1676       92317 :                 if (mnstr_write(s, "% ", 2, 1) != 1)
    1677             :                         return -4;
    1678      350575 :                 for (i = 0; i < t->nr_cols; i++) {
    1679      258256 :                         res_col *c = t->cols + i;
    1680             : 
    1681      258256 :                         if (mnstr_printf(s, "%u %u", c->type.digits, c->type.scale) < 0)
    1682             :                                 return -4;
    1683      258259 :                         if (i + 1 < t->nr_cols && mnstr_write(s, ",\t", 2, 1) != 1)
    1684             :                                 return -4;
    1685             :                 }
    1686       92319 :                 if (mnstr_write(s, " # typesizes\n", 13, 1) != 1)
    1687             :                         return -4;
    1688             :         }
    1689             :         return res;
    1690             : }
    1691             : 
    1692             : static int
    1693          25 : mvc_export_file(backend *b, stream *s, res_table *t)
    1694             : {
    1695          25 :         int res = 0;
    1696          25 :         BUN count;
    1697             : 
    1698          25 :         if (!t->cols[0].b) {
    1699           0 :                 res = mvc_export_row(b, s, t, "", t->tsep, t->rsep, t->ssep, t->ns);
    1700             :         } else {
    1701          25 :                 count = t->nr_rows;
    1702             : 
    1703          25 :                 res = mvc_export_table(b, s, t, 0, count, "", t->tsep, t->rsep, t->ssep, t->ns);
    1704          25 :                 b->results = res_tables_remove(b->results, t);
    1705             :         }
    1706          25 :         return res;
    1707             : }
    1708             : 
    1709             : int
    1710      121519 : mvc_export_result(backend *b, stream *s, int res_id, bool header, lng starttime, lng maloptimizer)
    1711             : {
    1712      121519 :         mvc *m = b->mvc;
    1713      121519 :         int clean = 0, res = 0;
    1714      121519 :         BUN count;
    1715      121519 :         res_table *t = res_tables_find(b->results, res_id);
    1716      121519 :         int json = (b->output_format == OFMT_JSON);
    1717             : 
    1718      121519 :         if (!s || !t)
    1719             :                 return 0;
    1720             : 
    1721             :         /* Proudly supporting SQLstatementIntern's output flag */
    1722      121519 :         if (b->output_format == OFMT_NONE)
    1723             :                 return 0;
    1724             : 
    1725      119934 :         assert(t->query_type == Q_TABLE || t->query_type == Q_PREPARE);
    1726      119934 :         if (t->tsep) {
    1727             :                 /* need header */
    1728          25 :                 if (header && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1729             :                         return res;
    1730          25 :                 return mvc_export_file(b, s, t);
    1731             :         }
    1732             : 
    1733      119909 :         if (!json && (res = mvc_export_head(b, s, res_id, TRUE, TRUE, starttime, maloptimizer)) < 0)
    1734             :                 return res;
    1735             : 
    1736      119908 :         assert(t->cols[0].b);
    1737             : 
    1738      119908 :         if (b->client->protocol == PROTOCOL_COLUMNAR) {
    1739           1 :                 if (mnstr_flush(s, MNSTR_FLUSH_DATA) < 0)
    1740             :                         return -4;
    1741           1 :                 return mvc_export_table_columnar(s, t);
    1742             :         }
    1743             : 
    1744      119907 :         count = m->reply_size;
    1745      119907 :         if (m->reply_size != -2 && (count <= 0 || count >= t->nr_rows)) {
    1746      119285 :                 count = t->nr_rows;
    1747      119285 :                 clean = 1;
    1748             :         }
    1749      119907 :         if (json) {
    1750           0 :                 switch(count) {
    1751             :                 case 0:
    1752           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\t", "", "}\n", "\"", "null");
    1753           0 :                         break;
    1754             :                 case 1:
    1755           0 :                         res = mvc_export_table(b, s, t, 0, count, "{\n\t\"%s\" : ", ",\n\t\"%s\" : ", "\n}\n", "\"", "null");
    1756           0 :                         break;
    1757             :                 case 2:
    1758           0 :                         res = mvc_export_table(b, s, t, 0, 1, "[\n\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1759           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");
    1760           0 :                         break;
    1761             :                 default:
    1762           0 :                         res = mvc_export_table(b, s, t, 0, 1, "[\n\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1763           0 :                         res = mvc_export_table(b, s, t, 1, count - 2, "\t{\n\t\t\"%s\" : ", ",\n\t\t\"%s\" : ", "\n\t},\n", "\"", "null");
    1764           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");
    1765             :                 }
    1766             :         } else {
    1767      119907 :                 res = mvc_export_table(b, s, t, 0, count, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1768             :         }
    1769      119908 :         if (clean)
    1770      119286 :                 b->results = res_tables_remove(b->results, t);
    1771             : 
    1772      119908 :         if (res > -1)
    1773      119908 :                 res = 1;
    1774             :         return res;
    1775             : }
    1776             : 
    1777             : int
    1778          69 : mvc_export_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1779             : {
    1780          69 :         int res = 0;
    1781          69 :         res_table *t = res_tables_find(b->results, res_id);
    1782          69 :         BUN cnt;
    1783             : 
    1784          69 :         if (!s || !t)
    1785             :                 return 0;
    1786             : 
    1787          57 :         cnt = nr;
    1788          57 :         if (cnt == 0)
    1789           0 :                 cnt = t->nr_rows;
    1790          57 :         if (offset >= t->nr_rows)
    1791             :                 cnt = 0;
    1792          57 :         if (cnt == BUN_NONE || offset + cnt > t->nr_rows)
    1793          11 :                 cnt = t->nr_rows - offset;
    1794             : 
    1795             :         /* query type: Q_BLOCK */
    1796          57 :         if (mnstr_write(s, "&6 ", 3, 1) != 1)
    1797             :                 return -4;
    1798             : 
    1799             :         /* result id */
    1800          57 :         if (mvc_send_int(s, res_id) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1801           0 :                 return -4;
    1802             : 
    1803             :         /* column count */
    1804          57 :         if (mvc_send_int(s, t->nr_cols) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1805           0 :                 return -4;
    1806             : 
    1807             :         /* row count */
    1808          57 :         if (mvc_send_lng(s, (lng) cnt) != 1 || mnstr_write(s, " ", 1, 1) != 1)
    1809           0 :                 return -4;
    1810             : 
    1811             :         /* block offset */
    1812          57 :         if (mvc_send_lng(s, (lng) offset) != 1)
    1813             :                 return -4;
    1814             : 
    1815          57 :         if (mnstr_write(s, "\n", 1, 1) != 1)
    1816             :                 return -4;
    1817             : 
    1818          57 :         res = mvc_export_table(b, s, t, offset, cnt, "[ ", ",\t", "\t]\n", "\"", "NULL");
    1819          57 :         return res;
    1820             : }
    1821             : 
    1822             : int
    1823      121197 : mvc_result_table(backend *be, oid query_id, int nr_cols, mapi_query_t type)
    1824             : {
    1825      121197 :         res_table *t = res_table_create(be->mvc->session->tr, be->result_id++, query_id, nr_cols, type, be->results);
    1826      121197 :         be->results = t;
    1827      121197 :         return t ? t->id : -1;
    1828             : }
    1829             : 
    1830             : int
    1831      254896 : mvc_result_column(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, BAT *b)
    1832             : {
    1833             :         /* return 0 on success, non-zero on failure */
    1834      254896 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, TYPE_bat, b, false) ? 0 : -1;
    1835             : }
    1836             : 
    1837             : int
    1838       61788 : mvc_result_value(backend *be, const char *tn, const char *name, const char *typename, int digits, int scale, ptr *p, int mtype)
    1839             : {
    1840             :         /* return 0 on success, non-zero on failure */
    1841       61788 :         return res_col_create(be->mvc->session->tr, be->results, tn, name, typename, digits, scale, mtype, p, false) ? 0 : -1;
    1842             : }
    1843             : 
    1844             : /* Translate error code from export function to error string */
    1845             : const char *
    1846           0 : mvc_export_error(backend *be, stream *s, int err_code)
    1847             : {
    1848           0 :         (void) be;
    1849           0 :         switch (err_code) {
    1850             :                 case -1: /* Allocation failure */
    1851             :                         return MAL_MALLOC_FAIL;
    1852           0 :                 case -2: /* BAT descriptor error */
    1853           0 :                         return RUNTIME_OBJECT_MISSING;
    1854           0 :                 case -3: /* GDK error */
    1855           0 :                         return GDKerrbuf;
    1856           0 :                 case -4: /* Stream error */
    1857           0 :                         return mnstr_peek_error(s);
    1858           0 :                 default: /* Unknown, must be a bug */
    1859           0 :                         return "Unknown internal error";
    1860             :         }
    1861             : }
    1862             : 
    1863             : static ssize_t
    1864        3833 : align_dump(stream *s, uint64_t pos, unsigned int alignment)
    1865             : {
    1866        3833 :         uint64_t a = (uint64_t)alignment;
    1867             :         // must be a power of two
    1868        3833 :         assert(a > 0);
    1869        3833 :         assert((a & (a-1)) == 0);
    1870             : 
    1871        3833 :         static char zeroes[32] = { 0 };
    1872             : #ifdef _MSC_VER
    1873             : #pragma warning(suppress:4146)
    1874             : #endif
    1875        3833 :         uint64_t gap = (~pos + 1) % a;
    1876        3833 :         return mnstr_write(s, zeroes, 1, (size_t)gap);
    1877             : }
    1878             : 
    1879             : 
    1880             : struct bindump_record {
    1881             :         BAT *bat;
    1882             :         type_record_t *type_rec;
    1883             :         int64_t start;
    1884             :         int64_t length;
    1885             : };
    1886             : 
    1887             : int
    1888         601 : mvc_export_bin_chunk(backend *b, stream *s, int res_id, BUN offset, BUN nr)
    1889             : {
    1890         601 :         int ret = -42;
    1891         601 :         struct bindump_record *colinfo;
    1892         601 :         stream *countstream = NULL;
    1893         601 :         uint64_t byte_count = 0;
    1894         601 :         uint64_t toc_pos = 0;
    1895         601 :         BUN end_row = offset + nr;
    1896             : 
    1897         601 :         res_table *res = res_tables_find(b->results, res_id);
    1898         601 :         if (res == NULL)
    1899             :                 return 0;
    1900             : 
    1901         601 :         colinfo = GDKzalloc(res->nr_cols * sizeof(*colinfo));
    1902         601 :         if (!colinfo) {
    1903           0 :                 ret = -1;
    1904           0 :                 goto end;
    1905             :         }
    1906        3833 :         for (int i = 0; i < res->nr_cols; i++)
    1907        3232 :                 colinfo[i].bat = NULL;
    1908        3833 :         for (int i = 0; i < res->nr_cols; i++) {
    1909        3232 :                 bat bat_id = res->cols[i].b;
    1910        3232 :                 BAT *b = BATdescriptor(bat_id);
    1911        3232 :                 if (!b) {
    1912           0 :                         ret = -1;
    1913           0 :                         goto end;
    1914             :                 }
    1915        3232 :                 colinfo[i].bat = b;
    1916             : 
    1917        3232 :                 if (BATcount(b) < end_row)
    1918             :                         end_row = BATcount(b);
    1919             : 
    1920        3232 :                 int tpe = BATttype(b);
    1921        3232 :                 const char *gdk_name = ATOMname(tpe);
    1922        3232 :                 type_record_t *rec = find_type_rec(gdk_name);
    1923        3232 :                 if (!rec || !can_dump_binary_column(rec)) {
    1924           0 :                         GDKerror("column %d: don't know how to dump data type '%s'", i, gdk_name);
    1925           0 :                         ret = -3;
    1926           0 :                         goto end;
    1927             :                 }
    1928        3232 :                 colinfo[i].type_rec = rec;
    1929             :         }
    1930             : 
    1931             :         // The byte_counting_stream keeps track of the byte offsets
    1932         601 :         countstream = byte_counting_stream(s, &byte_count);
    1933             : 
    1934             :         // Make sure the message starts with a & and not with a !
    1935         601 :         mnstr_printf(countstream, "&6 %d %d " BUNFMT " " BUNFMT "\n", res_id, res->nr_cols, end_row - offset, offset);
    1936             : 
    1937        3833 :         for (int i = 0; i < res->nr_cols; i++) {
    1938        3232 :                 align_dump(countstream, byte_count, 32); // 32 looks nice in tcpflow
    1939        3232 :                 struct bindump_record *info = &colinfo[i];
    1940        3232 :                 info->start = byte_count;
    1941        3232 :                 str msg = dump_binary_column(info->type_rec, info->bat, offset, end_row - offset, false, countstream);
    1942        3232 :                 if (msg != MAL_SUCCEED) {
    1943           0 :                         GDKerror("%s", msg);
    1944           0 :                         GDKfree(msg);
    1945           0 :                         ret = -3;
    1946           0 :                         goto end;
    1947             :                 }
    1948        3232 :                 info->length = byte_count - info->start;
    1949             :         }
    1950             : 
    1951         601 :         assert(byte_count > 0);
    1952             : 
    1953         601 :         align_dump(countstream, byte_count, 32);
    1954         601 :         toc_pos = byte_count;
    1955        3833 :         for (int i = 0; i < res->nr_cols; i++) {
    1956        3232 :                 struct bindump_record *info = &colinfo[i];
    1957        3232 :                 lng start = info->start;
    1958        3232 :                 lng length = info->length;
    1959        3232 :                 mnstr_writeLng(countstream, start);
    1960        3232 :                 mnstr_writeLng(countstream, length);
    1961             :         }
    1962             : 
    1963         601 :         mnstr_writeLng(countstream, toc_pos);
    1964         601 :         ret = 0;
    1965             : 
    1966         601 : end:
    1967         601 :         if (colinfo) {
    1968        3833 :                 for (int i = 0; i < res->nr_cols; i++) {
    1969        3232 :                         if (colinfo[i].bat)
    1970        3232 :                                 BBPunfix(colinfo[i].bat->batCacheid);
    1971             :                 }
    1972         601 :                 GDKfree(colinfo);
    1973             :         }
    1974         601 :         mnstr_destroy(countstream);
    1975         601 :         return ret;
    1976             : }

Generated by: LCOV version 1.14