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