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