1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * author N.J. Nes, M.L. Kersten
11 * 01/07/1996, 31/01/2002
12 *
13 * Input/Output module
14 * The IO module provides simple @sc{ascii-io} rendering options.
15 * It is modeled after the tuple formats, but does not
16 * attempt to outline the results. Instead, it is geared at speed,
17 * which also means that some functionality regarding the built-in
18 * types is duplicated from the atoms definitions.
19 *
20 * A functional limited form of formatted printf is also provided.
21 * It accepts at most one variable.
22 * A more complete approach is the tablet module.
23 *
24 * The commands to load and save a BAT from/to an ASCII dump
25 * are efficient, but work only for binary tables.
26 */
27
28/*
29 * Printing
30 * The print commands are implemented as single instruction rules,
31 * because they need access to the calling context.
32 * At a later stage we can look into the issues related to
33 * parsing the format string as part of the initialization phase.
34 * The old method in V4 essentially causes a lot of overhead
35 * because you have to prepare for the worst (e.g. mismatch format
36 * identifier and argument value)
37 * Beware, the types of the objects to be printed should be
38 * obtained from the stack, because the symbol table may actually
39 * allow for any type to be assigned.
40 */
41#include "monetdb_config.h"
42#include "mal_io.h"
43
44#define MAXFORMAT 64*1024
45
46str
47io_stdin(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
48{
49 bstream **ret= (bstream**) getArgReference(stk,pci,0);
50 (void) mb;
51 *ret = cntxt->fdin;
52 return MAL_SUCCEED;
53}
54
55str
56io_stdout(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
57{
58 stream **ret= (stream**) getArgReference(stk,pci,0);
59 (void) mb;
60 *ret = cntxt->fdout;
61 return MAL_SUCCEED;
62}
63
64static str
65IOprintBoth(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int indx, str hd, str tl, int nobat)
66{
67 int tpe = getArgType(mb, pci, indx);
68 ptr val = getArgReference(stk, pci, indx);
69 stream *fp = cntxt->fdout;
70
71 (void) mb;
72
73 if (tpe == TYPE_any)
74 tpe = stk->stk[pci->argv[indx]].vtype;
75 if (val == NULL || tpe == TYPE_void) {
76 if (hd)
77 mnstr_printf(fp, "%s", hd);
78 mnstr_printf(fp, "nil");
79 if (tl)
80 mnstr_printf(fp, "%s", tl);
81 return MAL_SUCCEED;
82 }
83 if (isaBatType(tpe) ) {
84 BAT *b[2];
85
86 if (is_bat_nil(*(bat *) val)) {
87 if (hd)
88 mnstr_printf(fp, "%s", hd);
89 mnstr_printf(fp,"nil");
90 if (tl)
91 mnstr_printf(fp, "%s", tl);
92 return MAL_SUCCEED;
93 }
94 b[1] = BATdescriptor(*(bat *) val);
95 if (b[1] == NULL) {
96 throw(MAL, "io.print", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
97 }
98 if (nobat) {
99 if (hd)
100 mnstr_printf(fp, "%s", hd);
101 mnstr_printf(fp, "<%s>", BBPname(b[1]->batCacheid));
102 if (tl)
103 mnstr_printf(fp, "%s", tl);
104 } else {
105 b[0] = BATdense(b[1]->hseqbase, b[1]->hseqbase, BATcount(b[1]));
106 if (b[0] == NULL) {
107 BBPunfix(b[1]->batCacheid);
108 throw(MAL, "io.print", SQLSTATE(HY001) MAL_MALLOC_FAIL);
109 }
110 if (BATroles(b[0], "h") != GDK_SUCCEED) {
111 BBPunfix(b[0]->batCacheid);
112 BBPunfix(b[1]->batCacheid);
113 throw(MAL, "io.print", SQLSTATE(HY001) MAL_MALLOC_FAIL);
114 }
115 BATprintcolumns(cntxt->fdout, 2, b);
116 BBPunfix(b[0]->batCacheid);
117 }
118 BBPunfix(b[1]->batCacheid);
119 return MAL_SUCCEED;
120 }
121 if (hd)
122 mnstr_printf(fp, "%s", hd);
123
124 if (ATOMvarsized(tpe))
125 ATOMprint(tpe, *(str *) val, fp);
126 else
127 ATOMprint(tpe, val, fp);
128
129 if (tl)
130 mnstr_printf(fp, "%s", tl);
131 return MAL_SUCCEED;
132}
133
134str
135IOprint_val(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
136{
137 int i;
138 str msg;
139
140 (void) cntxt;
141 if (p->argc == 2)
142 msg = IOprintBoth(cntxt, mb, stk, p, 1, "[ ", " ]\n", 0);
143 else {
144 msg = IOprintBoth(cntxt, mb, stk, p, 1, "[ ", 0, 1);
145 if (msg)
146 return msg;
147 for (i = 2; i < p->argc - 1; i++)
148 if ((msg = IOprintBoth(cntxt,mb, stk, p, i, ", ", 0, 1)) != NULL)
149 return msg;
150 msg = IOprintBoth(cntxt,mb, stk, p, i, ", ", "]\n", 1);
151 }
152 return msg;
153
154}
155
156/*
157 * The IOprintf_() gets a format str, and a sequence of (ptr,int) parameters
158 * containing values and their type numbers. The printf() proved to be a
159 * great risk; people formatting badly their "%s" format strings were crashing
160 * the kernel. This function will prevent you from doing so.
161 *
162 * New implementation that repeatedly invokes sprintf => hacking the va_alist
163 * for using vfsprintf proved to be too compiler-dependent (OLD approach).
164 */
165#define writemem(X1) \
166 do { \
167 if (dst+X1 > buf+size) { \
168 ptrdiff_t offset = dst - buf; \
169 char *tmp; \
170 do { \
171 size *= 2; \
172 } while (dst+X1 > buf+size); \
173 tmp = GDKrealloc(buf, size); \
174 if (tmp == NULL) { \
175 va_end(ap); \
176 GDKfree(buf); \
177 GDKfree(add); \
178 throw(MAL, "io.printf", SQLSTATE(HY001) MAL_MALLOC_FAIL); \
179 } \
180 buf = tmp; \
181 dst = buf + offset; \
182 } \
183 } while (0)
184
185#define m5sprintf(X1)\
186 if (width > adds) {\
187 str newadd;\
188 newadd = GDKrealloc(add, width + 10);\
189 if (newadd != NULL) {\
190 adds = width + 10;\
191 add = newadd;\
192 }\
193 }\
194 n = snprintf(add, adds, meta, X1);\
195 while (n < 0 || (size_t) n >= adds) {\
196 size_t newadds;\
197 str newadd;\
198\
199 if (n >= 0) /* glibc 2.1 */\
200 newadds = n + 1; /* precisely what is needed */\
201 else /* glibc 2.0 */\
202 newadds = n * 2; /* twice the old size */\
203\
204 newadd = GDKrealloc(add, newadds);\
205 if (newadd == NULL)\
206 break;\
207\
208 adds = newadds;\
209 add = newadd;\
210 n = snprintf(add, adds, meta, X1);\
211 }
212
213
214static char toofew_error[80] = OPERATION_FAILED " At least %d parameter(s) expected.\n";
215static char format_error[80] = OPERATION_FAILED " Error in format before param %d.\n";
216static char type_error[80] = OPERATION_FAILED " Illegal type in param %d.\n";
217
218#define return_error(x) \
219 do { \
220 GDKfree(buf); \
221 GDKfree(add); \
222 throw(MAL,"io.printf", x,argc); \
223 } while (0)
224
225static char niltext[4] = "nil";
226
227static str
228IOprintf_(str *res, str format, ...)
229{
230 va_list ap;
231 int n;
232
233 int prec = 0, dotseen = 0, escaped = 0, type, size, argc = 1;
234 size_t adds = 100, width = 0;
235 char *add, *dst, *buf, *cur, *paramseen = NULL;
236 char *p;
237
238 if (format == NULL) {
239 throw(MAL,"io.printf", ILLEGAL_ARGUMENT " NULL pointer passed as format.\n");
240 } else if (strchr(format, '%') == NULL) {
241 *res = GDKstrdup(format);
242 if (*res == NULL)
243 throw(MAL,"io.printf", SQLSTATE(HY001) MAL_MALLOC_FAIL);
244 return MAL_SUCCEED;
245 }
246 buf = dst = (str) GDKmalloc(size = 80);
247 if ( buf == NULL)
248 throw(MAL,"io.printf", SQLSTATE(HY001) MAL_MALLOC_FAIL);
249 *res = NULL;
250
251 add = GDKmalloc(adds);
252 if (add == NULL) {
253 GDKfree(buf);
254 throw(MAL,"io.printf", SQLSTATE(HY001) MAL_MALLOC_FAIL);
255 }
256
257 va_start(ap,format);
258 for (cur = format; *cur; cur++) {
259 if (paramseen) {
260 char meta[100];
261 ptrdiff_t extra = 0;
262 ptrdiff_t len;
263
264 if (GDKisdigit(*cur)) {
265 if (dotseen) {
266 prec = 10 * prec + (*cur - '0');
267 } else {
268 width = 10 * width + (*cur - '0');
269 }
270 continue;
271 } else if (dotseen == 0 && *cur == '.') {
272 dotseen = 1;
273 continue;
274 } else if (cur == paramseen + 1 && (*cur == '+' || *cur == '-' || *cur == ' ')) {
275 continue;
276 } else if (*cur == 'l') {
277 cur++;
278 if (*cur == 'l') {
279 cur++;
280 /* start of ll */
281 extra = (cur - paramseen) - 2;
282 }
283 }
284 if ((p = va_arg(ap, char *)) == NULL) {
285 va_end(ap);
286 return_error(toofew_error);
287 }
288 type = va_arg(ap, int);
289 type = ATOMbasetype(type);
290
291 len = 1 + (cur - paramseen);
292 memcpy(meta, paramseen, len);
293 meta[len] = 0;
294 if (ATOMcmp(type, ATOMnilptr(type), p) == 0) {
295 /* value is nil; attempt to print formatted 'nil'
296 without generating %ls etc. */
297 char *csrc, *ctrg = meta;
298
299 for (csrc = paramseen; csrc < cur; csrc++) {
300 if (*csrc == '.')
301 break;
302 if (GDKisdigit(*csrc) || *csrc == '-')
303 *(++ctrg) = *csrc;
304 }
305 *(++ctrg) = 's';
306 *(++ctrg) = 0;
307 m5sprintf(niltext);
308 } else if (strchr("cdiouxX", *cur) && !extra) {
309 int ival;
310
311 if (dotseen) {
312 va_end(ap);
313 return_error(format_error);
314 } else if (type == TYPE_bte) {
315 ival = (int) *(bte *) p;
316 } else if (type == TYPE_sht) {
317 ival = (int) *(sht *) p;
318 } else if (type == TYPE_flt) {
319 ival = (int) *(flt *) p;
320 } else if (type == TYPE_lng) {
321 goto largetypes;
322#ifdef HAVE_HGE
323 } else if (type == TYPE_hge) {
324 /* Does this happen?
325 * If so, what do we have TODO ? */
326 va_end(ap);
327 return_error(type_error);
328#endif
329 } else if (type == TYPE_int) {
330 ival = *(int *) p;
331 } else {
332 va_end(ap);
333 return_error(type_error);
334 }
335 m5sprintf(ival);
336 } else if (strchr("diouxX", *cur)) {
337#ifdef NATIVE_WIN32
338 ptrdiff_t i;
339#endif
340 lng lval;
341
342 if (dotseen) {
343 va_end(ap);
344 return_error(format_error);
345 }
346 largetypes:
347 if (type == TYPE_bte) {
348 lval = (lng) *(bte *) p;
349 } else if (type == TYPE_sht) {
350 lval = (lng) *(sht *) p;
351 } else if (type == TYPE_int) {
352 lval = (lng) *(int *) p;
353 } else if (type == TYPE_flt) {
354 lval = (lng) *(flt *) p;
355 } else if (type == TYPE_dbl) {
356 lval = (lng) *(dbl *) p;
357 } else if (type == TYPE_lng) {
358 lval = *(lng *) p;
359#ifdef HAVE_HGE
360 } else if (type == TYPE_hge) {
361 /* Does this happen?
362 * If so, what do we have TODO ? */
363 va_end(ap);
364 return_error(type_error);
365#endif
366 } else {
367 va_end(ap);
368 return_error(type_error);
369 }
370 if (!extra) {
371 meta[len + 2] = meta[len];
372 meta[len + 1] = meta[len - 1];
373 meta[len] = 'l';
374 meta[len - 1] = 'l';
375 len += 2;
376 extra = len - 3;
377 }
378#ifdef NATIVE_WIN32
379 for (i = len; i >= (extra + 2); i--) {
380 meta[i + 1] = meta[i];
381 }
382 meta[extra] = 'I';
383 meta[extra + 1] = '6';
384 meta[extra + 2] = '4';
385#endif
386 m5sprintf(lval);
387 } else if (strchr("feEgG", *cur)) {
388 dbl dval;
389
390 if (type == TYPE_flt) {
391 dval = (dbl) *(flt *) p;
392 } else if (type == TYPE_dbl) {
393 dval = *(dbl *) p;
394 } else {
395 va_end(ap);
396 return_error(type_error);
397 }
398 width += (1 + prec);
399 m5sprintf(dval);
400 } else if (*cur == 's') {
401 size_t length;
402
403 if (extra) {
404 va_end(ap);
405 return_error(format_error);
406 } else if (type != TYPE_str) {
407 va_end(ap);
408 return_error(type_error);
409 }
410 length = strLen(p);
411 width++;
412 prec++; /* account for '\0' */
413 if (dotseen && (size_t) prec < length)
414 length = (size_t) prec;
415 if (length > width)
416 width = length;
417 m5sprintf(p);
418 } else {
419 va_end(ap);
420 return_error(format_error);
421 }
422 width = strlen(add);
423 writemem(width);
424 memcpy(dst, add, width);
425 dst += width;
426 paramseen = NULL;
427 argc++;
428 } else if (!escaped) {
429 if (*cur == '\\' || (*cur == '%' && cur[1] == '%')) {
430 escaped = 1;
431 } else if (*cur == '%') {
432 paramseen = cur;
433 dotseen = prec = 0;
434 width = 0;
435 } else {
436 writemem(1);
437 *dst++ = *cur;
438 }
439 } else {
440 escaped = 0;
441 writemem(1);
442 *dst++ = *cur;
443 }
444 }
445
446/*
447 if ( va_arg(ap, char *) != NULL){
448 GDKfree(buf);
449 throw(MAL,"io.printf", "params %d and beyond ignored %s.\n",argc);
450 }
451*/
452
453 writemem(1);
454 va_end(ap);
455 *dst = 0;
456 *res = buf;
457 GDKfree(add);
458 return MAL_SUCCEED;
459}
460
461#define getArgValue(s,p,k) VALptr(&(s)->stk[(p)->argv[k]])
462
463#define G(X) getArgValue(stk,pci,X), getArgType(mb,pci,X)
464
465str
466IOprintf(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
467{
468 str *fmt = getArgReference_str(stk,pci,1);
469 str fmt2 = NULL;
470 str msg= MAL_SUCCEED;
471
472 (void) cntxt;
473 (void) mb;
474 switch( pci->argc){
475 case 2: msg= IOprintf_(&fmt2,*fmt);
476 break;
477 case 3: msg= IOprintf_(&fmt2,*fmt,G(2));
478 break;
479 case 4: msg= IOprintf_(&fmt2,*fmt,G(2),G(3));
480 break;
481 case 5: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4));
482 break;
483 case 6: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5));
484 break;
485 case 7: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6));
486 break;
487 case 8: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7));
488 break;
489 case 9: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7),G(8));
490 break;
491 case 10: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7),G(8),G(9));
492 }
493 if (msg== MAL_SUCCEED) {
494 mnstr_printf(cntxt->fdout,"%s",fmt2);
495 GDKfree(fmt2);
496 }
497 return msg;
498}
499str
500IOprintfStream(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci){
501 str *fmt = getArgReference_str(stk,pci,2);
502 str fmt2 = NULL;
503 stream *f= (stream *) getArgReference(stk,pci,1);
504 str msg= MAL_SUCCEED;
505
506 (void) cntxt;
507 (void) mb;
508 switch( pci->argc){
509 case 3: msg= IOprintf_(&fmt2,*fmt);
510 break;
511 case 4: msg= IOprintf_(&fmt2,*fmt,G(3));
512 break;
513 case 5: msg= IOprintf_(&fmt2,*fmt,G(3),G(4));
514 break;
515 case 6: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5));
516 break;
517 case 7: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6));
518 break;
519 case 8: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7));
520 break;
521 case 9: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8));
522 break;
523 case 10: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8),G(9));
524 break;
525 case 11: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8),G(9),G(10));
526 }
527 if (msg== MAL_SUCCEED){
528 mnstr_printf(f,"%s",fmt2);
529 GDKfree(fmt2);
530 }
531 return msg;
532}
533
534/*
535 * The table printing routine implementations.
536 * They merely differ in destination and order prerequisite
537 */
538str
539IOtable(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
540{
541 BAT *piv[MAXPARAMS];
542 int i;
543 int tpe;
544 ptr val;
545
546 (void) cntxt;
547 if ( pci->retc != 1 || pci->argc < 2)
548 throw(MAL, "io.table", "INTERNAL ERROR" " assertion error retc %d argc %d", pci->retc, pci->argc);
549
550 memset(piv, 0, sizeof(BAT*) * MAXPARAMS);
551 for (i = 1; i < pci->argc; i++) {
552 tpe = getArgType(mb, pci, i);
553 val = getArgReference(stk, pci, i);
554 if (!isaBatType(tpe)) {
555 while (--i >= 1)
556 if (piv[i] != NULL)
557 BBPunfix(piv[i]->batCacheid);
558 throw(MAL, "io.table", ILLEGAL_ARGUMENT " BAT expected");
559 }
560 if ((piv[i] = BATdescriptor(*(bat *) val)) == NULL) {
561 while (--i >= 1)
562 BBPunfix(piv[i]->batCacheid);
563 throw(MAL, "io.table", ILLEGAL_ARGUMENT " null BAT encountered");
564 }
565 }
566 /* add materialized void column */
567 piv[0] = BATdense(piv[1]->hseqbase, 0, BATcount(piv[1]));
568 if (piv[0] == NULL) {
569 for (i = 1; i < pci->argc; i++)
570 BBPunfix(piv[i]->batCacheid);
571 throw(MAL, "io.table", SQLSTATE(HY001) MAL_MALLOC_FAIL);
572 }
573 BATprintcolumns(cntxt->fdout, pci->argc, piv);
574 for (i = 0; i < pci->argc; i++)
575 BBPunfix(piv[i]->batCacheid);
576 return MAL_SUCCEED;
577}
578
579
580/*
581 * Bulk export/loading
582 * To simplify conversion between versions and to interface with other
583 * applications, we use a simple import/export operation.
584 *
585 * The conversion routine assumes space in the buffer for storing the result.
586 */
587/*
588 * A BAT can be saved in Monet format using the export command.
589 * It is of particular use in preparing an ASCII version for migration.
590 * The exported file is saved in the context of the directory
591 * where the server was started unless an absolute file name was
592 * presented.
593 */
594
595str
596IOexport(void *ret, bat *bid, str *fnme)
597{
598 BAT *b;
599 stream *s;
600
601 (void) ret;
602 if ((b = BATdescriptor(*bid)) == NULL)
603 throw(MAL, "io.export", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
604
605 s = open_wastream(*fnme);
606 if (s == NULL ){
607 BBPunfix(b->batCacheid);
608 throw(MAL, "io.export", RUNTIME_FILE_NOT_FOUND ":%s", *fnme);
609 }
610 if (mnstr_errnr(s)) {
611 mnstr_close(s);
612 BBPunfix(b->batCacheid);
613 throw(MAL, "io.export", RUNTIME_FILE_NOT_FOUND ":%s", *fnme);
614 }
615 BATprintcolumns(s, 1, &b);
616 close_stream(s);
617 BBPunfix(b->batCacheid);
618 return MAL_SUCCEED;
619}
620
621/*
622 * The import command reads a single BAT from an ASCII file produced by export.
623 */
624str
625IOimport(void *ret, bat *bid, str *fnme)
626{
627 BAT *b;
628 ssize_t (*tconvert) (const char *, size_t *, ptr *, bool);
629 ssize_t n;
630 size_t bufsize = 2048; /* NIELS:tmp change used to be 1024 */
631 char *base, *cur, *end;
632 char *buf;
633 ptr t = 0;
634 size_t lt = 0;
635 FILE *fp = fopen(*fnme, "r");
636 char msg[BUFSIZ];
637
638 (void) ret;
639 if ((b = BATdescriptor(*bid)) == NULL) {
640 if (fp)
641 fclose(fp);
642 throw(MAL, "io.import", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
643 }
644
645 tconvert = BATatoms[BATttype(b)].atomFromStr;
646 /*
647 * Open the file. Memory map it to minimize buffering problems.
648 */
649 if (fp == NULL) {
650 BBPunfix(b->batCacheid);
651 throw(MAL, "io.import", RUNTIME_FILE_NOT_FOUND ":%s", *fnme);
652 } else {
653 int fn;
654 struct stat st;
655
656 buf = (char *) GDKmalloc(bufsize);
657 if ( buf == NULL) {
658 BBPunfix(b->batCacheid);
659 fclose(fp);
660 throw(MAL,"io.import", SQLSTATE(HY001) MAL_MALLOC_FAIL);
661 }
662
663 if ((fn = fileno(fp)) <= 0) {
664 BBPunfix(b->batCacheid);
665 fclose(fp);
666 GDKfree(buf);
667 throw(MAL, "io.import", OPERATION_FAILED " fileno()");
668 }
669 if (fstat(fn, &st) != 0) {
670 BBPunfix(b->batCacheid);
671 fclose(fp);
672 GDKfree(buf);
673 throw(MAL, "io.imports", OPERATION_FAILED "fstat()");
674 }
675
676 (void) fclose(fp);
677 if (st.st_size <= 0) {
678 BBPunfix(b->batCacheid);
679 GDKfree(buf);
680 throw(MAL, "io.imports", OPERATION_FAILED "Empty file");
681 }
682#if SIZEOF_SIZE_T == SIZEOF_INT
683 if (st.st_size > ~ (size_t) 0) {
684 BBPunfix(b->batCacheid);
685 GDKfree(buf);
686 throw(MAL, "io.imports", OPERATION_FAILED "File too large");
687 }
688#endif
689 base = cur = (char *) MT_mmap(*fnme, MMAP_SEQUENTIAL, (size_t) st.st_size);
690 if (cur == NULL) {
691 BBPunfix(b->batCacheid);
692 GDKfree(buf);
693 throw(MAL, "io.mport", OPERATION_FAILED "MT_mmap()");
694 }
695 end = cur + st.st_size;
696
697 }
698 /* Parse a line. Copy it into a buffer. Concat broken lines with a slash. */
699 while (cur < end) {
700 str dst = buf, src = cur, p;
701 size_t l;
702
703 /* like p = strchr(cur, '\n') but with extra bounds check */
704 for (p = cur; p < end && *p != '\n'; p++)
705 ;
706 l = p - cur;
707
708 if (p < end) {
709 while (src[l - 1] == '\\') {
710 if (buf+bufsize < dst+l) {
711 size_t len = dst - buf;
712 size_t inc = (size_t) ((dst+l) - buf);
713 char *tmp = GDKrealloc(buf, bufsize = MAX(inc,bufsize)*2);
714 if (tmp == NULL) {
715 BBPunfix(b->batCacheid);
716 GDKfree(buf);
717 GDKfree(t);
718 throw(MAL, "io.imports", SQLSTATE(HY001) MAL_MALLOC_FAIL);
719 }
720 buf = tmp;
721 dst = buf + len;
722 }
723 memcpy(dst, src, l-1);
724 dst += l - 1;
725 src += l + 1;
726 for (p = src; p < end && *p != '\n'; p++)
727 ;
728 if (p == end)
729 break;
730 l = p - src;
731 }
732 }
733
734 if (buf+bufsize < dst+l) {
735 size_t len = dst - buf;
736 size_t inc = (size_t) ((dst+l) - buf);
737 char *tmp = GDKrealloc(buf, bufsize = MAX(inc,bufsize)*2);
738 if (tmp == NULL) {
739 BBPunfix(b->batCacheid);
740 GDKfree(buf);
741 GDKfree(t);
742 throw(MAL, "io.imports", SQLSTATE(HY001) MAL_MALLOC_FAIL);
743 }
744 buf = tmp;
745 dst = buf + len;
746 }
747 memcpy(dst, src, l);
748 dst[l] = 0;
749 cur = p + 1;
750 /* Parse the line, and insert a BUN. */
751 for (p = buf; *p && GDKisspace(*p); p++)
752 ;
753 if (*p == '#')
754 continue;
755
756 for (;*p && *p != '['; p++)
757 ;
758 if (*p)
759 for (p++; *p && GDKisspace(*p); p++)
760 ;
761 if (*p == 0) {
762 BBPunfix(b->batCacheid);
763 snprintf(msg,sizeof(msg),"error in input %s",buf);
764 GDKfree(buf);
765 MT_munmap(base, end - base);
766 GDKfree(t);
767 throw(MAL, "io.import", "%s", msg);
768 }
769 n = tconvert(p, &lt, (ptr*)&t, true);
770 if (n < 0) {
771 BBPunfix(b->batCacheid);
772 snprintf(msg,sizeof(msg),"error in input %s",buf);
773 GDKfree(buf);
774 MT_munmap(base, end - base);
775 GDKfree(t);
776 throw(MAL, "io.import", "%s", msg);
777 }
778 p += n;
779 if (BUNappend(b, t, false) != GDK_SUCCEED) {
780 BBPunfix(b->batCacheid);
781 GDKfree(buf);
782 GDKfree(t);
783 MT_munmap(base, end - base);
784 throw(MAL, "io.import", "insert failed");
785 }
786
787#if 0 /* why do this? any measured effects? */
788/*
789 * Unmap already parsed memory, to keep the memory usage low.
790 */
791#ifndef WIN32
792#define MAXBUF 40*MT_pagesize()
793 if ((unsigned) (cur - base) > MAXBUF) {
794 MT_munmap(base, MAXBUF);
795 base += MAXBUF;
796 }
797#endif
798#endif
799 }
800 /* Cleanup and exit. Return the filled BAT. */
801 if (t)
802 GDKfree(t);
803 GDKfree(buf);
804 MT_munmap(base, end - base);
805 BBPunfix(b->batCacheid);
806 return MAL_SUCCEED;
807}
808
809
810
811str
812IOsetmallocsuccesscount(void *res, lng *count) {
813 (void) res;
814 GDKsetmallocsuccesscount(*count);
815 return MAL_SUCCEED;
816}
817