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