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 : }
|