1 /************* TabFmt C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABFMT */
3 /* ------------- */
4 /* Version 3.9.3 */
5 /* */
6 /* COPYRIGHT: */
7 /* ---------- */
8 /* (C) Copyright to the author Olivier BERTRAND 2001 - 2019 */
9 /* */
10 /* WHAT THIS PROGRAM DOES: */
11 /* ----------------------- */
12 /* This program are the TABFMT classes DB execution routines. */
13 /* The base class CSV is comma separated files. */
14 /* FMT (Formatted) files are those having a complex internal record */
15 /* format described in the Format keyword of their definition. */
16 /***********************************************************************/
17
18 /***********************************************************************/
19 /* Include relevant MariaDB header file. */
20 /***********************************************************************/
21 #include "my_global.h"
22
23 #if defined(_WIN32)
24 #include <io.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <locale.h>
28 #if defined(__BORLANDC__)
29 #define __MFC_COMPAT__ // To define min/max as macro
30 #endif
31 //#include <windows.h>
32 #include "osutil.h"
33 #else
34 #if defined(UNIX)
35 #include <errno.h>
36 #include <unistd.h>
37 #include "osutil.h"
38 #else
39 #include <io.h>
40 #endif
41 #include <fcntl.h>
42 #endif
43
44 /***********************************************************************/
45 /* Include application header files: */
46 /* global.h is header containing all global declarations. */
47 /* plgdbsem.h is header containing the DB application declarations. */
48 /* tabdos.h is header containing the TABDOS class declarations. */
49 /***********************************************************************/
50 #include "global.h"
51 #include "plgdbsem.h"
52 #include "mycat.h"
53 #include "filamap.h"
54 #if defined(GZ_SUPPORT)
55 #include "filamgz.h"
56 #endif // GZ_SUPPORT
57 #if defined(ZIP_SUPPORT)
58 #include "filamzip.h"
59 #endif // ZIP_SUPPORT
60 #include "tabfmt.h"
61 #include "tabmul.h"
62 #define NO_FUNC
63 #include "plgcnx.h" // For DB types
64 #include "resource.h"
65
66 /***********************************************************************/
67 /* This should be an option. */
68 /***********************************************************************/
69 #define MAXCOL 200 /* Default max column nb in result */
70 #define TYPE_UNKNOWN 12 /* Must be greater than other types */
71
72 /***********************************************************************/
73 /* External function. */
74 /***********************************************************************/
75 USETEMP UseTemp(void);
76
77 /***********************************************************************/
78 /* CSVColumns: constructs the result blocks containing the description */
79 /* of all the columns of a CSV file that will be retrieved by #GetData.*/
80 /* Note: the algorithm to set the type is based on the internal values */
81 /* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7). */
82 /* If these values are changed, this will have to be revisited. */
83 /***********************************************************************/
CSVColumns(PGLOBAL g,PCSZ dp,PTOS topt,bool info)84 PQRYRES CSVColumns(PGLOBAL g, PCSZ dp, PTOS topt, bool info)
85 {
86 static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
87 TYPE_INT, TYPE_INT, TYPE_SHORT};
88 static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME,
89 FLD_PREC, FLD_LENGTH, FLD_SCALE};
90 static unsigned int length[] = {6, 6, 8, 10, 10, 6};
91 const char *fn;
92 char sep, q;
93 int rc, mxr;
94 bool hdr;
95 char *p, *colname[MAXCOL], dechar, buf[8];
96 int i, imax, hmax, n, nerr, phase, blank, digit, dec, type;
97 int ncol = sizeof(buftyp) / sizeof(int);
98 int num_read = 0, num_max = 10000000; // Statistics
99 int len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
100 PCSVDEF tdp;
101 PTDBCSV tcvp;
102 PTDBASE tdbp;
103 PQRYRES qrp;
104 PCOLRES crp;
105
106 if (info) {
107 imax = hmax = 0;
108 length[0] = 128;
109 goto skipit;
110 } // endif info
111
112 //if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
113 // strcpy(g->Message, "Cannot find column definition for multiple table");
114 // return NULL;
115 //} // endif Multiple
116
117 // num_max = atoi(p+1); // Max num of record to test
118 imax = hmax = nerr = 0;
119
120 for (i = 0; i < MAXCOL; i++) {
121 colname[i] = NULL;
122 len[i] = 0;
123 typ[i] = TYPE_UNKNOWN;
124 prc[i] = 0;
125 } // endfor i
126
127 /*********************************************************************/
128 /* Get the CSV table description block. */
129 /*********************************************************************/
130 tdp = new(g) CSVDEF;
131 tdp->Database = dp;
132
133 if ((tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false))) {
134 #if defined(ZIP_SUPPORT)
135 tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
136 tdp->Mulentries = (tdp->Entry)
137 ? strchr(tdp->Entry, '*') || strchr(tdp->Entry, '?')
138 : GetBooleanTableOption(g, topt, "Mulentries", false);
139 #else // !ZIP_SUPPORT
140 strcpy(g->Message, "ZIP not supported by this version");
141 return NULL;
142 #endif // !ZIP_SUPPORT
143 } // endif // Zipped
144
145 fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
146
147 if (!tdp->Fn) {
148 strcpy(g->Message, MSG(MISSING_FNAME));
149 return NULL;
150 } // endif Fn
151
152 if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
153 tdp->Lrecl = 4096;
154
155 tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0);
156 p = (char*)GetStringTableOption(g, topt, "Separator", ",");
157 tdp->Sep = (strlen(p) == 2 && p[0] == '\\' && p[1] == 't') ? '\t' : *p;
158
159 #if defined(_WIN32)
160 if (tdp->Sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6))
161 dechar = '.';
162 else
163 dechar = ',';
164 #else // !_WIN32
165 dechar = '.';
166 #endif // !_WIN32
167
168 sep = tdp->Sep;
169 tdp->Quoted = GetIntegerTableOption(g, topt, "Quoted", -1);
170 p = (char*)GetStringTableOption(g, topt, "Qchar", "");
171 tdp->Qot = *p;
172
173 if (tdp->Qot && tdp->Quoted < 0)
174 tdp->Quoted = 0;
175 else if (!tdp->Qot && tdp->Quoted >= 0)
176 tdp->Qot = '"';
177
178 q = tdp->Qot;
179 hdr = GetBooleanTableOption(g, topt, "Header", false);
180 tdp->Maxerr = GetIntegerTableOption(g, topt, "Maxerr", 0);
181 tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
182
183 if (tdp->Accept && tdp->Maxerr == 0)
184 tdp->Maxerr = INT_MAX32; // Accept all bad lines
185
186 mxr = MY_MAX(0, tdp->Maxerr);
187
188 if (trace(1))
189 htrc("File %s Sep=%c Qot=%c Header=%d maxerr=%d\n",
190 SVP(tdp->Fn), tdp->Sep, tdp->Qot, tdp->Header, tdp->Maxerr);
191
192 #if defined(ZIP_SUPPORT)
193 if (tdp->Zipped)
194 tcvp = new(g)TDBCSV(tdp, new(g)UNZFAM(tdp));
195 else
196 #endif // ZIP_SUPPORT
197 tcvp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp));
198
199 tcvp->SetMode(MODE_READ);
200
201 if (tdp->Multiple) {
202 tdbp = new(g)TDBMUL(tcvp);
203 tdbp->SetMode(MODE_READ);
204 } else
205 tdbp = tcvp;
206
207 /*********************************************************************/
208 /* Open the CSV file. */
209 /*********************************************************************/
210 if (tdbp->OpenDB(g))
211 return NULL;
212
213 if (hdr) {
214 /*******************************************************************/
215 /* Make the column names from the first line. */
216 /*******************************************************************/
217 phase = 0;
218
219 if ((rc = tdbp->ReadDB(g)) == RC_OK) {
220 p = PlgDBDup(g, tcvp->To_Line);
221
222 //skip leading blanks
223 for (; *p == ' '; p++) ;
224
225 if (q && *p == q) {
226 // Header is quoted
227 p++;
228 phase = 1;
229 } // endif q
230
231 colname[0] = p;
232 } else if (rc == RC_EF) {
233 sprintf(g->Message, MSG(FILE_IS_EMPTY), fn);
234 goto err;
235 } else
236 goto err;
237
238 for (i = 1; *p; p++)
239 if (phase == 1 && *p == q) {
240 *p = '\0';
241 phase = 0;
242 } else if (*p == sep && !phase) {
243 *p = '\0';
244
245 //skip leading blanks
246 for (; *(p+1) == ' '; p++) ;
247
248 if (q && *(p+1) == q) {
249 // Header is quoted
250 p++;
251 phase = 1;
252 } // endif q
253
254 colname[i++] = p + 1;
255 } // endif sep
256
257 num_read++;
258 imax = hmax = i;
259
260 for (i = 0; i < hmax; i++)
261 length[0] = MY_MAX(length[0], strlen(colname[i]));
262
263 tcvp->Header = true; // In case of multiple table
264 } // endif hdr
265
266 for (num_read++; num_read <= num_max; num_read++) {
267 /*******************************************************************/
268 /* Now start the reading process. Read one line. */
269 /*******************************************************************/
270 if ((rc = tdbp->ReadDB(g)) == RC_OK) {
271 } else if (rc == RC_EF) {
272 sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1);
273 break;
274 } else {
275 sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn);
276 goto err;
277 } // endif's
278
279 /*******************************************************************/
280 /* Make the test for field lengths. */
281 /*******************************************************************/
282 i = n = phase = blank = digit = dec = 0;
283
284 for (p = tcvp->To_Line; *p; p++)
285 if (*p == sep) {
286 if (phase != 1) {
287 if (i == MAXCOL - 1) {
288 sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn);
289 goto err;
290 } // endif i
291
292 if (n) {
293 len[i] = MY_MAX(len[i], n);
294 type = (digit || (dec && n == 1)) ? TYPE_STRING
295 : (dec) ? TYPE_DOUBLE : TYPE_INT;
296 typ[i] = MY_MIN(type, typ[i]);
297 prc[i] = MY_MAX((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
298 } // endif n
299
300 i++;
301 n = phase = blank = digit = dec = 0;
302 } else // phase == 1
303 n++;
304
305 } else if (*p == ' ') {
306 if (phase < 2)
307 n++;
308
309 if (blank)
310 digit = 1;
311
312 } else if (*p == q) {
313 if (phase == 0) {
314 if (blank) {
315 if (++nerr > mxr) {
316 sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
317 goto err;
318 } else
319 goto skip;
320 }
321
322 n = 0;
323 phase = digit = 1;
324 } else if (phase == 1) {
325 if (*(p+1) == q) {
326 // This is currently not implemented for CSV tables
327 // if (++nerr > mxr) {
328 // sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read);
329 // goto err;
330 // } else
331 // goto skip;
332
333 p++;
334 n++;
335 } else
336 phase = 2;
337
338 } else if (++nerr > mxr) { // phase == 2
339 sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
340 goto err;
341 } else
342 goto skip;
343
344 } else {
345 if (phase == 2) {
346 if (++nerr > mxr) {
347 sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
348 goto err;
349 } else
350 goto skip;
351 }
352
353 // isdigit cannot be used here because of debug assert
354 if (!strchr("0123456789", *p)) {
355 if (!digit && *p == dechar)
356 dec = 1; // Decimal point found
357 else if (blank || !(*p == '-' || *p == '+'))
358 digit = 1;
359
360 } else if (dec)
361 dec++; // More decimals
362
363 n++;
364 blank = 1;
365 } // endif's *p
366
367 if (phase == 1) {
368 if (++nerr > mxr) {
369 sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read);
370 goto err;
371 } else
372 goto skip;
373 }
374
375 if (n) {
376 len[i] = MY_MAX(len[i], n);
377 type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING
378 : (dec) ? TYPE_DOUBLE : TYPE_INT;
379 typ[i] = MY_MIN(type, typ[i]);
380 prc[i] = MY_MAX((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
381 } // endif n
382
383 imax = MY_MAX(imax, i+1);
384 skip: ; // Skip erroneous line
385 } // endfor num_read
386
387 if (trace(1)) {
388 htrc("imax=%d Lengths:", imax);
389
390 for (i = 0; i < imax; i++)
391 htrc(" %d", len[i]);
392
393 htrc("\n");
394 } // endif trace
395
396 tdbp->CloseDB(g);
397
398 skipit:
399 if (trace(1))
400 htrc("CSVColumns: imax=%d hmax=%d len=%d\n",
401 imax, hmax, length[0]);
402
403 /*********************************************************************/
404 /* Allocate the structures used to refer to the result set. */
405 /*********************************************************************/
406 qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3,
407 buftyp, fldtyp, length, false, false);
408 if (info || !qrp)
409 return qrp;
410
411 qrp->Nblin = imax;
412
413 /*********************************************************************/
414 /* Now get the results into blocks. */
415 /*********************************************************************/
416 for (i = 0; i < imax; i++) {
417 if (i >= hmax) {
418 sprintf(buf, "COL%.3d", i+1);
419 p = buf;
420 } else
421 p = colname[i];
422
423 if (typ[i] == TYPE_UNKNOWN) // Void column
424 typ[i] = TYPE_STRING;
425
426 crp = qrp->Colresp; // Column Name
427 crp->Kdata->SetValue(p, i);
428 crp = crp->Next; // Data Type
429 crp->Kdata->SetValue(typ[i], i);
430 crp = crp->Next; // Type Name
431 crp->Kdata->SetValue(GetTypeName(typ[i]), i);
432 crp = crp->Next; // Precision
433 crp->Kdata->SetValue(len[i], i);
434 crp = crp->Next; // Length
435 crp->Kdata->SetValue(len[i], i);
436 crp = crp->Next; // Scale (precision)
437 crp->Kdata->SetValue(prc[i], i);
438 } // endfor i
439
440 /*********************************************************************/
441 /* Return the result pointer for use by GetData routines. */
442 /*********************************************************************/
443 return qrp;
444
445 err:
446 tdbp->CloseDB(g);
447 return NULL;
448 } // end of CSVCColumns
449
450 /* --------------------------- Class CSVDEF -------------------------- */
451
452 /***********************************************************************/
453 /* CSVDEF constructor. */
454 /***********************************************************************/
CSVDEF(void)455 CSVDEF::CSVDEF(void)
456 {
457 Fmtd = Header = false;
458 //Maxerr = 0;
459 Quoted = -1;
460 Sep = ',';
461 Qot = '\0';
462 } // end of CSVDEF constructor
463
464 /***********************************************************************/
465 /* DefineAM: define specific AM block values from XDB file. */
466 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int poff)467 bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
468 {
469 char buf[8];
470
471 // Double check correctness of offset values
472 if (Catfunc == FNC_NO)
473 for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
474 if (cdp->GetOffset() < 1 && !cdp->IsSpecial()) {
475 strcpy(g->Message, MSG(BAD_OFFSET_VAL));
476 return true;
477 } // endif Offset
478
479 // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX
480 if (DOSDEF::DefineAM(g, "CSV", poff))
481 return true;
482
483 Recfm = RECFM_CSV;
484 GetCharCatInfo("Separator", ",", buf, sizeof(buf));
485 Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf;
486 Quoted = GetIntCatInfo("Quoted", -1);
487 GetCharCatInfo("Qchar", "", buf, sizeof(buf));
488 Qot = *buf;
489
490 if (Qot && Quoted < 0)
491 Quoted = 0;
492 else if (!Qot && Quoted >= 0)
493 Qot = '"';
494
495 Fmtd = (!Sep || (am && (*am == 'F' || *am == 'f')));
496 Header = GetBoolCatInfo("Header", false);
497 Maxerr = GetIntCatInfo("Maxerr", 0);
498 Accept = GetBoolCatInfo("Accept", false);
499
500 if (Accept && Maxerr == 0)
501 Maxerr = INT_MAX32; // Accept all bad lines
502
503 return false;
504 } // end of DefineAM
505
506 /***********************************************************************/
507 /* GetTable: makes a new Table Description Block. */
508 /***********************************************************************/
GetTable(PGLOBAL g,MODE mode)509 PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
510 {
511 PTDBASE tdbp;
512
513 if (Catfunc != FNC_COL) {
514 USETEMP tmp = UseTemp();
515 bool map = Mapped && mode != MODE_INSERT &&
516 !(tmp != TMP_NO && mode == MODE_UPDATE) &&
517 !(tmp == TMP_FORCE &&
518 (mode == MODE_UPDATE || mode == MODE_DELETE));
519 PTXF txfp;
520
521 /*******************************************************************/
522 /* Allocate a file processing class of the proper type. */
523 /*******************************************************************/
524 if (Zipped) {
525 #if defined(ZIP_SUPPORT)
526 if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
527 txfp = new(g) UNZFAM(this);
528 } else if (mode == MODE_INSERT) {
529 txfp = new(g) ZIPFAM(this);
530 } else {
531 strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
532 return NULL;
533 } // endif's mode
534 #else // !ZIP_SUPPORT
535 strcpy(g->Message, "ZIP not supported");
536 return NULL;
537 #endif // !ZIP_SUPPORT
538 } else if (map) {
539 // Should be now compatible with UNIX
540 txfp = new(g) MAPFAM(this);
541 } else if (Compressed) {
542 #if defined(GZ_SUPPORT)
543 if (Compressed == 1)
544 txfp = new(g) GZFAM(this);
545 else
546 txfp = new(g) ZLBFAM(this);
547
548 #else // !GZ_SUPPORT
549 strcpy(g->Message, "Compress not supported");
550 return NULL;
551 #endif // !GZ_SUPPORT
552 } else
553 txfp = new(g) DOSFAM(this);
554
555 /*******************************************************************/
556 /* Allocate a TDB of the proper type. */
557 /* Column blocks will be allocated only when needed. */
558 /*******************************************************************/
559 if (!Fmtd)
560 tdbp = new(g) TDBCSV(this, txfp);
561 else
562 tdbp = new(g) TDBFMT(this, txfp);
563
564 if (Multiple)
565 tdbp = new(g) TDBMUL(tdbp);
566 else
567 /*****************************************************************/
568 /* For block tables, get eventually saved optimization values. */
569 /*****************************************************************/
570 if (tdbp->GetBlockValues(g)) {
571 PushWarning(g, tdbp);
572 // return NULL; // causes a crash when deleting index
573 } else {
574 if (IsOptimized()) {
575 if (map) {
576 txfp = new(g) MBKFAM(this);
577 } else if (Compressed) {
578 #if defined(GZ_SUPPORT)
579 if (Compressed == 1)
580 txfp = new(g) ZBKFAM(this);
581 else {
582 txfp->SetBlkPos(To_Pos);
583 ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL);
584 } // endelse
585 #else
586 sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
587 return NULL;
588 #endif
589 } else
590 txfp = new(g) BLKFAM(this);
591
592 ((PTDBDOS)tdbp)->SetTxfp(txfp);
593 } // endif Optimized
594
595 } // endelse
596
597 } else
598 tdbp = new(g)TDBCCL(this);
599
600 return tdbp;
601 } // end of GetTable
602
603 /* -------------------------- Class TDBCSV --------------------------- */
604
605 /***********************************************************************/
606 /* Implementation of the TDBCSV class. */
607 /***********************************************************************/
TDBCSV(PCSVDEF tdp,PTXF txfp)608 TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
609 {
610 #if defined(_DEBUG)
611 assert (tdp);
612 #endif
613 Field = NULL;
614 Offset = NULL;
615 Fldlen = NULL;
616 Fields = 0;
617 Nerr = 0;
618 Quoted = tdp->Quoted;
619 Maxerr = tdp->Maxerr;
620 Accept = tdp->Accept;
621 Header = tdp->Header;
622 Sep = tdp->GetSep();
623 Qot = tdp->GetQot();
624 } // end of TDBCSV standard constructor
625
TDBCSV(PGLOBAL g,PTDBCSV tdbp)626 TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
627 {
628 Fields = tdbp->Fields;
629
630 if (Fields) {
631 if (tdbp->Offset)
632 Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
633
634 if (tdbp->Fldlen)
635 Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
636
637 Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
638
639 for (int i = 0; i < Fields; i++) {
640 if (Offset)
641 Offset[i] = tdbp->Offset[i];
642
643 if (Fldlen)
644 Fldlen[i] = tdbp->Fldlen[i];
645
646 if (Field) {
647 assert (Fldlen);
648 Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1);
649 Field[i][Fldlen[i]] = '\0';
650 } // endif Field
651
652 } // endfor i
653
654 } else {
655 Field = NULL;
656 Offset = NULL;
657 Fldlen = NULL;
658 } // endif Fields
659
660 Nerr = tdbp->Nerr;
661 Maxerr = tdbp->Maxerr;
662 Quoted = tdbp->Quoted;
663 Accept = tdbp->Accept;
664 Header = tdbp->Header;
665 Sep = tdbp->Sep;
666 Qot = tdbp->Qot;
667 } // end of TDBCSV copy constructor
668
669 // Method
Clone(PTABS t)670 PTDB TDBCSV::Clone(PTABS t)
671 {
672 PTDB tp;
673 PCSVCOL cp1, cp2;
674 PGLOBAL g = t->G; // Is this really useful ???
675
676 tp = new(g) TDBCSV(g, this);
677
678 for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
679 cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
680 NewPointer(t, cp1, cp2);
681 } // endfor cp1
682
683 return tp;
684 } // end of Clone
685
686 /***********************************************************************/
687 /* Allocate CSV column description block. */
688 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)689 PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
690 {
691 return new(g) CSVCOL(g, cdp, this, cprec, n);
692 } // end of MakeCol
693
694 /***********************************************************************/
695 /* Check whether the number of errors is greater than the maximum. */
696 /***********************************************************************/
CheckErr(void)697 bool TDBCSV::CheckErr(void)
698 {
699 return (++Nerr) > Maxerr;
700 } // end of CheckErr
701
702 /***********************************************************************/
703 /* CSV EstimatedLength. Returns an estimated minimum line length. */
704 /***********************************************************************/
EstimatedLength(void)705 int TDBCSV::EstimatedLength(void)
706 {
707 int n = 0;
708 PCOLDEF cdp;
709
710 if (trace(1))
711 htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns);
712
713 for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
714 if (!cdp->IsSpecial() && !cdp->IsVirtual()) // A true column
715 n++;
716
717 return --n; // Number of separators if all fields are null
718 } // end of Estimated Length
719
720 #if 0
721 /***********************************************************************/
722 /* CSV tables needs the use temporary files for Update. */
723 /***********************************************************************/
724 bool TDBCSV::IsUsingTemp(PGLOBAL g)
725 {
726 return (Use_Temp == TMP_YES || Use_Temp == TMP_FORCE ||
727 (Use_Temp == TMP_AUTO && Mode == MODE_UPDATE));
728 } // end of IsUsingTemp
729 #endif // 0 (Same as TDBDOS one)
730
731 /***********************************************************************/
732 /* CSV Access Method opening routine. */
733 /* First allocate the Offset and Fldlen arrays according to the */
734 /* greatest field used in that query. Then call the DOS opening fnc. */
735 /***********************************************************************/
OpenDB(PGLOBAL g)736 bool TDBCSV::OpenDB(PGLOBAL g)
737 {
738 bool rc = false;
739 PCOLDEF cdp;
740 PDOSDEF tdp = (PDOSDEF)To_Def;
741
742 if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) {
743 // Allocate the storage used to read (or write) records
744 int i, len;
745 PCSVCOL colp;
746
747 if (!Fields) { // May have been set in TABFMT::OpenDB
748 if (Mode != MODE_UPDATE && Mode != MODE_INSERT) {
749 for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
750 if (!colp->IsSpecial() && !colp->IsVirtual())
751 Fields = MY_MAX(Fields, (int)colp->Fldnum);
752
753 if (Columns)
754 Fields++; // Fldnum was 0 based
755
756 } else
757 for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
758 if (!cdp->IsSpecial() && !cdp->IsVirtual())
759 Fields++;
760 }
761
762 Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
763 Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
764
765 if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
766 Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
767 Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields);
768 } // endif Mode
769
770 for (i = 0; i < Fields; i++) {
771 Offset[i] = 0;
772 Fldlen[i] = 0;
773
774 if (Field) {
775 Field[i] = NULL;
776 Fldtyp[i] = false;
777 } // endif Field
778
779 } // endfor i
780
781 if (Field) {
782 // Prepare writing fields
783 if (Mode != MODE_UPDATE) {
784 for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
785 if (!colp->IsSpecial() && !colp->IsVirtual()) {
786 i = colp->Fldnum;
787 len = colp->GetLength();
788 Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
789 Field[i][len] = '\0';
790 Fldlen[i] = len;
791 Fldtyp[i] = IsTypeNum(colp->GetResultType());
792 } // endif colp
793
794 } else // MODE_UPDATE
795 for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
796 if (!cdp->IsSpecial() && !cdp->IsVirtual()) {
797 i = cdp->GetOffset() - 1;
798 len = cdp->GetLength();
799 Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
800 Field[i][len] = '\0';
801 Fldlen[i] = len;
802 Fldtyp[i] = IsTypeNum(cdp->GetType());
803 } // endif cdp
804 }
805
806 } // endif Use
807
808 if (Header) {
809 // Check that the Lrecl is at least equal to the header line length
810 int headlen = 0;
811 PCOLDEF cdp;
812 PDOSDEF tdp = (PDOSDEF)To_Def;
813
814 for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
815 headlen += strlen(cdp->GetName()) + 3; // 3 if names are quoted
816
817 if (headlen > Lrecl) {
818 Lrecl = headlen;
819 Txfp->Lrecl = headlen;
820 } // endif headlen
821
822 } // endif Header
823
824 Nerr = 0;
825 rc = TDBDOS::OpenDB(g);
826
827 if (!rc && Mode == MODE_UPDATE && To_Kindex)
828 // Because KINDEX::Init is executed in mode READ, we must restore
829 // the Fldlen array that was modified when reading the table file.
830 for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
831 Fldlen[cdp->GetOffset() - 1] = cdp->GetLength();
832
833 return rc;
834 } // end of OpenDB
835
836 /***********************************************************************/
837 /* SkipHeader: Physically skip first header line if applicable. */
838 /* This is called from TDBDOS::OpenDB and must be executed before */
839 /* Kindex construction if the file is accessed using an index. */
840 /***********************************************************************/
SkipHeader(PGLOBAL g)841 bool TDBCSV::SkipHeader(PGLOBAL g)
842 {
843 int len = GetFileLength(g);
844 bool rc = false;
845
846 #if defined(_DEBUG)
847 if (len < 0)
848 return true;
849 #endif // _DEBUG
850
851 if (Header) {
852 if (Mode == MODE_INSERT) {
853 if (!len) {
854 // New file, the header line must be constructed and written
855 int i, n = 0;
856 int hlen = 0;
857 bool q = Qot && Quoted > 0;
858 PCOLDEF cdp;
859
860 // Estimate the length of the header list
861 for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
862 hlen += (1 + strlen(cdp->GetName()));
863 hlen += ((q) ? 2 : 0);
864 n++; // Calculate the number of columns
865 } // endfor cdp
866
867 if (hlen > Lrecl) {
868 sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen);
869 return true;
870 } // endif hlen
871
872 // File is empty, write a header record
873 memset(To_Line, 0, Lrecl);
874
875 // The column order in the file is given by the offset value
876 for (i = 1; i <= n; i++)
877 for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
878 if (cdp->GetOffset() == i) {
879 if (q)
880 To_Line[strlen(To_Line)] = Qot;
881
882 strcat(To_Line, cdp->GetName());
883
884 if (q)
885 To_Line[strlen(To_Line)] = Qot;
886
887 if (i < n)
888 To_Line[strlen(To_Line)] = Sep;
889
890 } // endif Offset
891
892 rc = (Txfp->WriteBuffer(g) == RC_FX);
893 } // endif !FileLength
894
895 } else if (Mode == MODE_DELETE) {
896 if (len)
897 rc = (Txfp->SkipRecord(g, true) == RC_FX);
898
899 } else if (len) // !Insert && !Delete
900 rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
901
902 } // endif Header
903
904 return rc;
905 } // end of SkipHeader
906
907 /***********************************************************************/
908 /* ReadBuffer: Physical read routine for the CSV access method. */
909 /***********************************************************************/
ReadBuffer(PGLOBAL g)910 int TDBCSV::ReadBuffer(PGLOBAL g)
911 {
912 //char *p1, *p2, *p = NULL;
913 char *p2, *p = NULL;
914 int i, n, len, rc = Txfp->ReadBuffer(g);
915 bool bad = false;
916
917 if (trace(2))
918 htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc);
919
920 if (rc != RC_OK || !Fields)
921 return rc;
922 else
923 p2 = To_Line;
924
925 // Find the offsets and lengths of the columns for this row
926 for (i = 0; i < Fields; i++) {
927 if (!bad) {
928 if (Qot && *p2 == Qot) { // Quoted field
929 //for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2)
930 // if (*(p + 1) == Qot)
931 // n++; // Doubled internal quotes
932 // else
933 // break; // Final quote
934
935 for (n = 0, p = ++p2; p; p++)
936 if (*p == Qot || *p == '\\') {
937 if (*(++p) == Qot)
938 n++; // Escaped internal quotes
939 else if (*(p - 1) == Qot)
940 break; // Final quote
941 } // endif *p
942
943 if (p) {
944 //len = p++ - p2;
945 len = (int)(p - p2 - 1);
946
947 // if (Sep != ' ')
948 // for (; *p == ' '; p++) ; // Skip blanks
949
950 if (*p != Sep && i != Fields - 1) { // Should be the separator
951 if (CheckErr()) {
952 sprintf(g->Message, MSG(MISSING_FIELD),
953 i+1, Name, RowNumber(g));
954 return RC_FX;
955 } else if (Accept)
956 bad = true;
957 else
958 return RC_NF;
959
960 } // endif p
961
962 if (n) {
963 int j, k;
964
965 // Suppress the escape of internal quotes
966 for (j = k = 0; j < len; j++, k++) {
967 if (p2[j] == Qot || (p2[j] == '\\' && p2[j + 1] == Qot))
968 j++; // skip escape char
969 else if (p2[j] == '\\')
970 p2[k++] = p2[j++]; // avoid \\Qot
971
972 p2[k] = p2[j];
973 } // endfor i, j
974
975 len -= n;
976 } // endif n
977
978 } else if (CheckErr()) {
979 sprintf(g->Message, MSG(BAD_QUOTE_FIELD),
980 Name, i+1, RowNumber(g));
981 return RC_FX;
982 } else if (Accept) {
983 len = strlen(p2);
984 bad = true;
985 } else
986 return RC_NF;
987
988 } else if ((p = strchr(p2, Sep)))
989 len = (int)(p - p2);
990 else if (i == Fields - 1)
991 len = strlen(p2);
992 else if (Accept && Maxerr == 0) {
993 len = strlen(p2);
994 bad = true;
995 } else if (CheckErr()) {
996 sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g));
997 return RC_FX;
998 } else if (Accept) {
999 len = strlen(p2);
1000 bad = true;
1001 } else
1002 return RC_NF;
1003
1004 } else
1005 len = 0;
1006
1007 Offset[i] = (int)(p2 - To_Line);
1008
1009 if (Mode != MODE_UPDATE)
1010 Fldlen[i] = len;
1011 else if (len > Fldlen[i]) {
1012 sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g));
1013 return RC_FX;
1014 } else {
1015 strncpy(Field[i], p2, len);
1016 Field[i][len] = '\0';
1017 } // endif Mode
1018
1019 if (p)
1020 p2 = p + 1;
1021
1022 } // endfor i
1023
1024 return rc;
1025 } // end of ReadBuffer
1026
1027 /***********************************************************************/
1028 /* Prepare the line to write. */
1029 /***********************************************************************/
PrepareWriting(PGLOBAL g)1030 bool TDBCSV::PrepareWriting(PGLOBAL g)
1031 {
1032 char sep[2], qot[2];
1033 int i, nlen, oldlen = strlen(To_Line);
1034
1035 if (trace(2))
1036 htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n",
1037 Tdb_No, Mode, To_Key_Col, To_Link);
1038
1039 // Before writing the line we must check its length
1040 if ((nlen = CheckWrite(g)) < 0)
1041 return true;
1042
1043 // Before writing the line we must make it
1044 sep[0] = Sep;
1045 sep[1] = '\0';
1046 qot[0] = Qot;
1047 qot[1] = '\0';
1048 *To_Line = '\0';
1049
1050 for (i = 0; i < Fields; i++) {
1051 if (i)
1052 strcat(To_Line, sep);
1053
1054 if (Field[i]) {
1055 if (!strlen(Field[i])) {
1056 // Generally null fields are not quoted
1057 if (Quoted > 2)
1058 // Except if explicitely required
1059 strcat(strcat(To_Line, qot), qot);
1060
1061 } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
1062 || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))) {
1063 if (strchr(Field[i], Qot)) {
1064 // Field contains quotes that must be doubled
1065 int j, k = strlen(To_Line), n = strlen(Field[i]);
1066
1067 To_Line[k++] = Qot;
1068
1069 for (j = 0; j < n; j++) {
1070 if (Field[i][j] == Qot)
1071 To_Line[k++] = Qot;
1072
1073 To_Line[k++] = Field[i][j];
1074 } // endfor j
1075
1076 To_Line[k++] = Qot;
1077 To_Line[k] = '\0';
1078 } else
1079 strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
1080 }
1081
1082 else
1083 strcat(To_Line, Field[i]);
1084 }
1085
1086 } // endfor i
1087
1088 #if defined(_DEBUG)
1089 assert ((unsigned)nlen == strlen(To_Line));
1090 #endif
1091
1092 if (Mode == MODE_UPDATE && nlen < oldlen
1093 && !((PDOSFAM)Txfp)->GetUseTemp()) {
1094 // In Update mode with no temp file, line length must not change
1095 To_Line[nlen] = Sep;
1096
1097 for (nlen++; nlen < oldlen; nlen++)
1098 To_Line[nlen] = ' ';
1099
1100 To_Line[nlen] = '\0';
1101 } // endif
1102
1103 if (trace(2))
1104 htrc("Write: line is=%s", To_Line);
1105
1106 return false;
1107 } // end of PrepareWriting
1108
1109 /***********************************************************************/
1110 /* Data Base write routine CSV file access method. */
1111 /***********************************************************************/
WriteDB(PGLOBAL g)1112 int TDBCSV::WriteDB(PGLOBAL g)
1113 {
1114 // Before writing the line we must check and prepare it
1115 if (PrepareWriting(g))
1116 return RC_FX;
1117
1118 /*********************************************************************/
1119 /* Now start the writing process. */
1120 /*********************************************************************/
1121 return Txfp->WriteBuffer(g);
1122 } // end of WriteDB
1123
1124 /***********************************************************************/
1125 /* Check whether a new line fit in the file lrecl size. */
1126 /***********************************************************************/
CheckWrite(PGLOBAL g)1127 int TDBCSV::CheckWrite(PGLOBAL g)
1128 {
1129 int maxlen, n, nlen = (Fields - 1);
1130
1131 if (trace(2))
1132 htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode);
1133
1134 // Before writing the line we must check its length
1135 maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp())
1136 ? strlen(To_Line) : Lrecl;
1137
1138 // Check whether record is too int
1139 for (int i = 0; i < Fields; i++)
1140 if (Field[i]) {
1141 if (!(n = strlen(Field[i])))
1142 n += (Quoted > 2 ? 2 : 0);
1143 else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot)
1144 || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))
1145 if (!Qot) {
1146 sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1);
1147 return -1;
1148 } else {
1149 // Quotes inside a quoted field must be doubled
1150 char *p1, *p2;
1151
1152 for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1)
1153 n++;
1154
1155 n += 2; // Outside quotes
1156 } // endif
1157
1158 if ((nlen += n) > maxlen) {
1159 strcpy(g->Message, MSG(LINE_TOO_LONG));
1160 return -1;
1161 } // endif nlen
1162
1163 } // endif Field
1164
1165 return nlen;
1166 } // end of CheckWrite
1167
1168 /* ------------------------------------------------------------------- */
1169
1170 /***********************************************************************/
1171 /* Implementation of the TDBFMT class. */
1172 /***********************************************************************/
TDBFMT(PGLOBAL g,PTDBFMT tdbp)1173 TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
1174 {
1175 FldFormat = tdbp->FldFormat;
1176 To_Fld = tdbp->To_Fld;
1177 FmtTest = tdbp->FmtTest;
1178 Linenum = tdbp->Linenum;
1179 } // end of TDBFMT copy constructor
1180
1181 // Method
Clone(PTABS t)1182 PTDB TDBFMT::Clone(PTABS t)
1183 {
1184 PTDB tp;
1185 PCSVCOL cp1, cp2;
1186 //PFMTCOL cp1, cp2;
1187 PGLOBAL g = t->G; // Is this really useful ???
1188
1189 tp = new(g) TDBFMT(g, this);
1190
1191 for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
1192 //for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) {
1193 cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
1194 // cp2 = new(g) FMTCOL(cp1, tp); // Make a copy
1195 NewPointer(t, cp1, cp2);
1196 } // endfor cp1
1197
1198 return tp;
1199 } // end of Clone
1200
1201 /***********************************************************************/
1202 /* Allocate FMT column description block. */
1203 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1204 PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1205 {
1206 return new(g) CSVCOL(g, cdp, this, cprec, n);
1207 //return new(g) FMTCOL(cdp, this, cprec, n);
1208 } // end of MakeCol
1209
1210 /***********************************************************************/
1211 /* FMT EstimatedLength. Returns an estimated minimum line length. */
1212 /* The big problem here is how can we astimated that minimum ? */
1213 /***********************************************************************/
EstimatedLength(void)1214 int TDBFMT::EstimatedLength(void)
1215 {
1216 // This is rather stupid !!!
1217 return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
1218 } // end of EstimatedLength
1219
1220 /***********************************************************************/
1221 /* FMT Access Method opening routine. */
1222 /***********************************************************************/
OpenDB(PGLOBAL g)1223 bool TDBFMT::OpenDB(PGLOBAL g)
1224 {
1225 Linenum = 0;
1226
1227 if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
1228 sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
1229 return true; // NIY
1230 } // endif Mode
1231
1232 if (Use != USE_OPEN && Columns) {
1233 // Make the formats used to read records
1234 PSZ pfm;
1235 int i, n;
1236 PCSVCOL colp;
1237 PCOLDEF cdp;
1238 PDOSDEF tdp = (PDOSDEF)To_Def;
1239
1240 for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
1241 if (!colp->IsSpecial() && !colp->IsVirtual()) // a true column
1242 Fields = MY_MAX(Fields, (int)colp->Fldnum);
1243
1244 if (Columns)
1245 Fields++; // Fldnum was 0 based
1246
1247 To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1);
1248 FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
1249 memset(FldFormat, 0, sizeof(PSZ) * Fields);
1250 FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
1251 memset(FmtTest, 0, sizeof(int) * Fields);
1252
1253 // Get the column formats
1254 for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
1255 if (!cdp->IsSpecial() && !cdp->IsVirtual()
1256 && (i = cdp->GetOffset() - 1) < Fields) {
1257 if (!(pfm = cdp->GetFmt())) {
1258 sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
1259 return true;
1260 } // endif pfm
1261
1262 // Roughly check the Fmt format
1263 if ((n = strlen(pfm) - 2) < 4) {
1264 sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name);
1265 return true;
1266 } // endif n
1267
1268 FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
1269 strcpy(FldFormat[i], pfm);
1270
1271 if (!strcmp(pfm + n, "%m")) {
1272 // This is a field that can be missing. Flag it so it can
1273 // be handled with special processing.
1274 FldFormat[i][n+1] = 'n'; // To have sscanf normal processing
1275 FmtTest[i] = 2;
1276 } else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
1277 // There are trailing characters after the field contents
1278 // add a marker for the next field start position.
1279 strcat(FldFormat[i], "%n");
1280 FmtTest[i] = 1;
1281 } // endif's
1282
1283 } // endif i
1284
1285 } // endif Use
1286
1287 return TDBCSV::OpenDB(g);
1288 } // end of OpenDB
1289
1290 /***********************************************************************/
1291 /* ReadBuffer: Physical read routine for the FMT access method. */
1292 /***********************************************************************/
ReadBuffer(PGLOBAL g)1293 int TDBFMT::ReadBuffer(PGLOBAL g)
1294 {
1295 int i, len, n, deb, fin, nwp, pos = 0, rc;
1296 bool bad = false;
1297
1298 if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields)
1299 return rc;
1300 else
1301 ++Linenum;
1302
1303 if (trace(2))
1304 htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc);
1305
1306 // Find the offsets and lengths of the columns for this row
1307 for (i = 0; i < Fields; i++) {
1308 if (!bad) {
1309 deb = fin = -1;
1310
1311 if (!FldFormat[i]) {
1312 n = 0;
1313 } else if (FmtTest[i] == 1) {
1314 nwp = -1;
1315 n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp);
1316 } else {
1317 n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin);
1318
1319 if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) {
1320 // Missing optional field, not an error
1321 n = 1;
1322
1323 if (i == Fields - 1)
1324 fin = deb = 0;
1325 else
1326 fin = deb;
1327
1328 } // endif n
1329
1330 nwp = fin;
1331 } // endif i
1332
1333 if (n != 1 || deb < 0 || fin < 0 || nwp < 0) {
1334 // This is to avoid a very strange sscanf bug occuring
1335 // with fields that ends with a null character.
1336 // This bug causes subsequent sscanf to return in error,
1337 // so next lines are not parsed correctly.
1338 sscanf("a", "%*c"); // Seems to reset things Ok
1339
1340 if (CheckErr()) {
1341 sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name);
1342 return RC_FX;
1343 } else if (Accept)
1344 bad = true;
1345 else
1346 return RC_NF;
1347
1348 } // endif n...
1349
1350 } // endif !bad
1351
1352 if (!bad) {
1353 Offset[i] = pos + deb;
1354 len = fin - deb;
1355 } else {
1356 nwp = 0;
1357 Offset[i] = pos;
1358 len = 0;
1359 } // endif bad
1360
1361 // if (Mode != MODE_UPDATE)
1362 Fldlen[i] = len;
1363 // else if (len > Fldlen[i]) {
1364 // sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g));
1365 // return RC_FX;
1366 // } else {
1367 // strncpy(Field[i], To_Line + pos, len);
1368 // Field[i][len] = '\0';
1369 // } // endif Mode
1370
1371 pos += nwp;
1372 } // endfor i
1373
1374 if (bad)
1375 Nerr++;
1376 else
1377 sscanf("a", "%*c"); // Seems to reset things Ok
1378
1379 return rc;
1380 } // end of ReadBuffer
1381
1382 /***********************************************************************/
1383 /* Data Base write routine FMT file access method. */
1384 /***********************************************************************/
WriteDB(PGLOBAL g)1385 int TDBFMT::WriteDB(PGLOBAL g)
1386 {
1387 sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
1388 return RC_FX; // NIY
1389 } // end of WriteDB
1390
1391 // ------------------------ CSVCOL functions ----------------------------
1392
1393 /***********************************************************************/
1394 /* CSVCOL public constructor */
1395 /***********************************************************************/
CSVCOL(PGLOBAL g,PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i)1396 CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
1397 : DOSCOL(g, cdp, tdbp, cprec, i, "CSV")
1398 {
1399 Fldnum = Deplac - 1;
1400 Deplac = 0;
1401 } // end of CSVCOL constructor
1402
1403 /***********************************************************************/
1404 /* CSVCOL constructor used for copying columns. */
1405 /* tdbp is the pointer to the new table descriptor. */
1406 /***********************************************************************/
CSVCOL(CSVCOL * col1,PTDB tdbp)1407 CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
1408 {
1409 Fldnum = col1->Fldnum;
1410 } // end of CSVCOL copy constructor
1411
1412 /***********************************************************************/
1413 /* VarSize: This function tells UpdateDB whether or not the block */
1414 /* optimization file must be redone if this column is updated, even */
1415 /* it is not sorted or clustered. This applies to a blocked table, */
1416 /* because if it is updated using a temporary file, the block size */
1417 /* may be modified. */
1418 /***********************************************************************/
VarSize(void)1419 bool CSVCOL::VarSize(void)
1420 {
1421 PTXF txfp = ((PTDBCSV)To_Tdb)->Txfp;
1422
1423 if (txfp->IsBlocked() && txfp->GetUseTemp())
1424 // Blocked table using a temporary file
1425 return true;
1426 else
1427 return false;
1428
1429 } // end VarSize
1430
1431 /***********************************************************************/
1432 /* ReadColumn: call DOSCOL::ReadColumn after having set the offet */
1433 /* and length of the field to read as calculated by TDBCSV::ReadDB. */
1434 /***********************************************************************/
ReadColumn(PGLOBAL g)1435 void CSVCOL::ReadColumn(PGLOBAL g)
1436 {
1437 int rc;
1438 PTDBCSV tdbp = (PTDBCSV)To_Tdb;
1439
1440 /*********************************************************************/
1441 /* If physical reading of the line was deferred, do it now. */
1442 /*********************************************************************/
1443 if (!tdbp->IsRead())
1444 if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
1445 if (rc == RC_EF)
1446 sprintf(g->Message, MSG(INV_DEF_READ), rc);
1447
1448 throw 34;
1449 } // endif
1450
1451 if (tdbp->Mode != MODE_UPDATE) {
1452 int colen = Long; // Column length
1453
1454 // Set the field offset and length for this row
1455 Deplac = tdbp->Offset[Fldnum]; // Field offset
1456 Long = tdbp->Fldlen[Fldnum]; // Field length
1457
1458 if (trace(2))
1459 htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n",
1460 Name, Fldnum, Deplac, Long);
1461
1462 if (Long > colen && tdbp->CheckErr()) {
1463 Long = colen; // Restore column length
1464 sprintf(g->Message, MSG(FLD_TOO_LNG_FOR),
1465 Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g));
1466 throw 34;
1467 } // endif Long
1468
1469 // Now do the reading
1470 DOSCOL::ReadColumn(g);
1471
1472 // Restore column length
1473 Long = colen;
1474 } else { // Mode Update
1475 // Field have been copied in TDB Field array
1476 PSZ fp = tdbp->Field[Fldnum];
1477
1478 if (Dsp)
1479 for (int i = 0; fp[i]; i++)
1480 if (fp[i] == Dsp)
1481 fp[i] = '.';
1482
1483 Value->SetValue_psz(fp);
1484
1485 // Set null when applicable
1486 if (Nullable)
1487 Value->SetNull(Value->IsZero());
1488
1489 } // endif Mode
1490
1491 } // end of ReadColumn
1492
1493 /***********************************************************************/
1494 /* WriteColumn: The column is written in TDBCSV matching Field. */
1495 /***********************************************************************/
WriteColumn(PGLOBAL g)1496 void CSVCOL::WriteColumn(PGLOBAL g)
1497 {
1498 char *p;
1499 int n, flen;
1500 PTDBCSV tdbp = (PTDBCSV)To_Tdb;
1501
1502 if (trace(2))
1503 htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
1504 Name, tdbp->GetTdb_No(), ColUse, Status);
1505
1506 flen = GetLength();
1507
1508 if (trace(2))
1509 htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n",
1510 tdbp->Lrecl, Long, flen, Buf_Type, Value);
1511
1512 /*********************************************************************/
1513 /* Check whether the new value has to be converted to Buf_Type. */
1514 /*********************************************************************/
1515 if (Value != To_Val)
1516 Value->SetValue_pval(To_Val, false); // Convert the updated value
1517
1518 /*********************************************************************/
1519 /* Get the string representation of the column value. */
1520 /*********************************************************************/
1521 p = Value->GetCharString(Buf);
1522 n = strlen(p);
1523
1524 if (trace(2))
1525 htrc("new length(%p)=%d\n", p, n);
1526
1527 if (n > flen) {
1528 sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, n,
1529 tdbp->RowNumber(g), tdbp->GetFile(g));
1530 throw 34;
1531 } else if (Dsp)
1532 for (int i = 0; p[i]; i++)
1533 if (p[i] == '.')
1534 p[i] = Dsp;
1535
1536 if (trace(2))
1537 htrc("buffer=%s\n", p);
1538
1539 /*********************************************************************/
1540 /* Updating must be done also during the first pass so writing the */
1541 /* updated record can be checked for acceptable record length. */
1542 /*********************************************************************/
1543 if (Fldnum < 0) {
1544 // This can happen for wrong offset value in XDB files
1545 sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name);
1546 throw 34;
1547 } else
1548 strncpy(tdbp->Field[Fldnum], p, flen);
1549
1550 if (trace(2))
1551 htrc(" col written: '%s'\n", p);
1552
1553 } // end of WriteColumn
1554
1555 /* ---------------------------TDBCCL class --------------------------- */
1556
1557 /***********************************************************************/
1558 /* TDBCCL class constructor. */
1559 /***********************************************************************/
TDBCCL(PCSVDEF tdp)1560 TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
1561 {
1562 Topt = tdp->GetTopt();
1563 } // end of TDBCCL constructor
1564
1565 /***********************************************************************/
1566 /* GetResult: Get the list the CSV file columns. */
1567 /***********************************************************************/
GetResult(PGLOBAL g)1568 PQRYRES TDBCCL::GetResult(PGLOBAL g)
1569 {
1570 return CSVColumns(g, ((PTABDEF)To_Def)->GetPath(), Topt, false);
1571 } // end of GetResult
1572
1573 /* ------------------------ End of TabFmt ---------------------------- */
1574