1 /************* TabDos C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABDOS */
3 /* ------------- */
4 /* Version 4.9.5 */
5 /* */
6 /* COPYRIGHT: */
7 /* ---------- */
8 /* (C) Copyright to the author Olivier BERTRAND 1998-2020 */
9 /* */
10 /* WHAT THIS PROGRAM DOES: */
11 /* ----------------------- */
12 /* This program are the DOS tables classes. */
13 /* */
14 /***********************************************************************/
15
16 /***********************************************************************/
17 /* Include relevant sections of the System header files. */
18 /***********************************************************************/
19 #include "my_global.h"
20 #if defined(_WIN32)
21 #include <io.h>
22 #include <sys\timeb.h> // For testing only
23 #include <fcntl.h>
24 #include <errno.h>
25 #if defined(__BORLANDC__)
26 #define __MFC_COMPAT__ // To define min/max as macro
27 #endif // __BORLANDC__
28 //#include <windows.h>
29 #else // !_WIN32
30 #if defined(UNIX)
31 #include <errno.h>
32 #include <unistd.h>
33 #else // !UNIX
34 #include <io.h>
35 #endif // !UNIX
36 #include <fcntl.h>
37 #endif // !_WIN32
38
39 /***********************************************************************/
40 /* Include application header files: */
41 /* global.h is header containing all global declarations. */
42 /* plgdbsem.h is header containing the DB application declarations. */
43 /* filamtxt.h is header containing the file AM classes declarations. */
44 /***********************************************************************/
45 #include "global.h"
46 #include "osutil.h"
47 #include "plgdbsem.h"
48 //#include "catalog.h"
49 #include "mycat.h"
50 #include "xindex.h"
51 #include "filamap.h"
52 #include "filamfix.h"
53 #include "filamdbf.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 "tabdos.h"
61 #include "tabfix.h"
62 #include "tabmul.h"
63 #include "array.h"
64 #include "blkfil.h"
65
66 /***********************************************************************/
67 /* DB static variables. */
68 /***********************************************************************/
69 int num_read, num_there, num_eq[2]; // Statistics
70
71 /***********************************************************************/
72 /* Size of optimize file header. */
73 /***********************************************************************/
74 #define NZ 4
75
76 /***********************************************************************/
77 /* External function. */
78 /***********************************************************************/
79 bool ExactInfo(void);
80 USETEMP UseTemp(void);
81
82 /***********************************************************************/
83 /* Min and Max blocks contains zero ended fields (blank = false). */
84 /* No conversion of block values (check = true). */
85 /***********************************************************************/
86 PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len= 0, int prec= 0,
87 bool check= true, bool blank= false, bool un= false);
88
89 /* --------------------------- Class DOSDEF -------------------------- */
90
91 /***********************************************************************/
92 /* Constructor. */
93 /***********************************************************************/
DOSDEF(void)94 DOSDEF::DOSDEF(void)
95 {
96 Pseudo = 3;
97 Fn = NULL;
98 Ofn = NULL;
99 Entry = NULL;
100 To_Indx = NULL;
101 Pwd = NULL;
102 Recfm = RECFM_VAR;
103 Mapped = false;
104 Zipped = false;
105 Mulentries = false;
106 Append = false;
107 Padded = false;
108 Huge = false;
109 Accept = false;
110 Eof = false;
111 To_Pos = NULL;
112 Optimized = 0;
113 AllocBlks = 0;
114 Compressed = 0;
115 Lrecl = 0;
116 AvgLen = 0;
117 Block = 0;
118 Last = 0;
119 Blksize = 0;
120 Maxerr = 0;
121 ReadMode = 0;
122 Ending = 0;
123 Teds = 0;
124 } // end of DOSDEF constructor
125
126 /***********************************************************************/
127 /* DefineAM: define specific AM block values from XDB file. */
128 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int)129 bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
130 {
131 char buf[8];
132 bool map = (am && (*am == 'M' || *am == 'm'));
133 LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F"
134 : (am && (*am == 'B' || *am == 'b')) ? "B"
135 : (am && (*am == 'X' || *am == 'x')) ? "X"
136 : (am && !stricmp(am, "DBF")) ? "D" : "V";
137
138 if ((Zipped = GetBoolCatInfo("Zipped", false))) {
139 Entry = GetStringCatInfo(g, "Entry", NULL);
140 Mulentries = (Entry && *Entry) ? strchr(Entry, '*') || strchr(Entry, '?')
141 : false;
142 Mulentries = GetBoolCatInfo("Mulentries", Mulentries);
143 Append = GetBoolCatInfo("Append", false);
144 Pwd = GetStringCatInfo(g, "Password", NULL);
145 } // endif Zipped
146
147 Desc = Fn = GetStringCatInfo(g, "Filename", NULL);
148 Ofn = GetStringCatInfo(g, "Optname", Fn);
149 GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf));
150 Recfm = (toupper(*buf) == 'F') ? RECFM_FIX :
151 (toupper(*buf) == 'B') ? RECFM_BIN :
152 (toupper(*buf) == 'X') ? RECFM_NAF : // MGO
153 (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR;
154 Lrecl = GetIntCatInfo("Lrecl", 0);
155
156 if (Recfm != RECFM_DBF)
157 Compressed = GetIntCatInfo("Compressed", 0);
158
159 Mapped = GetBoolCatInfo("Mapped", map);
160 //Block = GetIntCatInfo("Blocks", 0);
161 //Last = GetIntCatInfo("Last", 0);
162 Ending = GetIntCatInfo("Ending", CRLF);
163
164 if (Ending <= 0) {
165 Ending = (Recfm == RECFM_BIN || Recfm == RECFM_VCT) ? 0 : CRLF;
166 SetIntCatInfo("Ending", Ending);
167 } // endif ending
168
169 if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) {
170 Huge = GetBoolCatInfo("Huge", Cat->GetDefHuge());
171 Padded = GetBoolCatInfo("Padded", false);
172 Blksize = GetIntCatInfo("Blksize", 0);
173 Eof = (GetIntCatInfo("EOF", 0) != 0);
174 Teds = toupper(*GetStringCatInfo(g, "Endian", ""));
175 } else if (Recfm == RECFM_DBF) {
176 Maxerr = GetIntCatInfo("Maxerr", 0);
177 Accept = GetBoolCatInfo("Accept", false);
178 ReadMode = GetIntCatInfo("Readmode", 0);
179 } else // (Recfm == RECFM_VAR)
180 AvgLen = GetIntCatInfo("Avglen", 0);
181
182 // Ignore wrong Index definitions for catalog commands
183 SetIndexInfo();
184 return false;
185 } // end of DefineAM
186
187 /***********************************************************************/
188 /* Get the full path/name of the optization file. */
189 /***********************************************************************/
GetOptFileName(PGLOBAL g,char * filename)190 bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename)
191 {
192 PCSZ ftype;
193
194 switch (Recfm) {
195 case RECFM_VAR: ftype = ".dop"; break;
196 case RECFM_FIX: ftype = ".fop"; break;
197 case RECFM_BIN: ftype = ".bop"; break;
198 case RECFM_VCT: ftype = ".vop"; break;
199 case RECFM_CSV: ftype = ".cop"; break;
200 case RECFM_DBF: ftype = ".dbp"; break;
201 default:
202 sprintf(g->Message, MSG(INVALID_FTYPE), Recfm);
203 return true;
204 } // endswitch Ftype
205
206 PlugSetPath(filename, Ofn, GetPath());
207 strcat(PlugRemoveType(filename, filename), ftype);
208 return false;
209 } // end of GetOptFileName
210
211 /***********************************************************************/
212 /* After an optimize error occurred, remove all set optimize values. */
213 /***********************************************************************/
RemoveOptValues(PGLOBAL g)214 void DOSDEF::RemoveOptValues(PGLOBAL g)
215 {
216 char filename[_MAX_PATH];
217 PCOLDEF cdp;
218
219 // Delete settings of optimized columns
220 for (cdp = To_Cols; cdp; cdp = cdp->GetNext())
221 if (cdp->GetOpt()) {
222 cdp->SetMin(NULL);
223 cdp->SetMax(NULL);
224 cdp->SetNdv(0);
225 cdp->SetNbm(0);
226 cdp->SetDval(NULL);
227 cdp->SetBmap(NULL);
228 } // endif Opt
229
230 // Delete block position setting for not fixed tables
231 To_Pos = NULL;
232 AllocBlks = 0;
233
234 // Delete any eventually ill formed non matching optimization file
235 if (!GetOptFileName(g, filename))
236 #if defined(_WIN32)
237 DeleteFile(filename);
238 #else // UNIX
239 remove(filename);
240 #endif // _WIN32
241
242 Optimized = 0;
243 } // end of RemoveOptValues
244
245 /***********************************************************************/
246 /* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */
247 /***********************************************************************/
DeleteIndexFile(PGLOBAL g,PIXDEF pxdf)248 bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
249 {
250 PCSZ ftype;
251 char filename[_MAX_PATH];
252 bool sep, rc = false;
253
254 if (!To_Indx)
255 return false; // No index
256
257 // If true indexes are in separate files
258 sep = GetBoolCatInfo("SepIndex", false);
259
260 if (!sep && pxdf) {
261 strcpy(g->Message, MSG(NO_RECOV_SPACE));
262 return true;
263 } // endif sep
264
265 switch (Recfm) {
266 case RECFM_VAR: ftype = ".dnx"; break;
267 case RECFM_FIX: ftype = ".fnx"; break;
268 case RECFM_BIN: ftype = ".bnx"; break;
269 case RECFM_VCT: ftype = ".vnx"; break;
270 case RECFM_CSV: ftype = ".cnx"; break;
271 case RECFM_DBF: ftype = ".dbx"; break;
272 default:
273 sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm);
274 return true;
275 } // endswitch Ftype
276
277 /*********************************************************************/
278 /* Check for existence of an index file. */
279 /*********************************************************************/
280 if (sep) {
281 // Indexes are save in separate files
282 #if defined(_WIN32)
283 char drive[_MAX_DRIVE];
284 #else
285 char *drive = NULL;
286 #endif
287 char direc[_MAX_DIR];
288 char fname[_MAX_FNAME];
289 bool all = !pxdf;
290
291 if (all)
292 pxdf = To_Indx;
293
294 for (; pxdf; pxdf = pxdf->GetNext()) {
295 _splitpath(Ofn, drive, direc, fname, NULL);
296 strcat(strcat(fname, "_"), pxdf->GetName());
297 _makepath(filename, drive, direc, fname, ftype);
298 PlugSetPath(filename, filename, GetPath());
299 #if defined(_WIN32)
300 if (!DeleteFile(filename))
301 rc |= (GetLastError() != ERROR_FILE_NOT_FOUND);
302 #else // UNIX
303 if (remove(filename))
304 rc |= (errno != ENOENT);
305 #endif // UNIX
306
307 if (!all)
308 break;
309
310 } // endfor pxdf
311
312 } else { // !sep
313 // Drop all indexes, delete the common file
314 PlugSetPath(filename, Ofn, GetPath());
315 strcat(PlugRemoveType(filename, filename), ftype);
316 #if defined(_WIN32)
317 if (!DeleteFile(filename))
318 rc = (GetLastError() != ERROR_FILE_NOT_FOUND);
319 #else // UNIX
320 if (remove(filename))
321 rc = (errno != ENOENT);
322 #endif // UNIX
323 } // endif sep
324
325 if (rc)
326 sprintf(g->Message, MSG(DEL_FILE_ERR), filename);
327
328 return rc; // Return true if error
329 } // end of DeleteIndexFile
330
331 /***********************************************************************/
332 /* InvalidateIndex: mark all indexes as invalid. */
333 /***********************************************************************/
InvalidateIndex(PGLOBAL)334 bool DOSDEF::InvalidateIndex(PGLOBAL)
335 {
336 if (To_Indx)
337 for (PIXDEF xp = To_Indx; xp; xp = xp->Next)
338 xp->Invalid = true;
339
340 return false;
341 } // end of InvalidateIndex
342
343 /***********************************************************************/
344 /* GetTable: makes a new Table Description Block. */
345 /***********************************************************************/
GetTable(PGLOBAL g,MODE mode)346 PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
347 {
348 // Mapping not used for insert
349 USETEMP tmp = UseTemp();
350 bool map = Mapped && mode != MODE_INSERT &&
351 !(tmp != TMP_NO && Recfm == RECFM_VAR
352 && mode == MODE_UPDATE) &&
353 !(tmp == TMP_FORCE &&
354 (mode == MODE_UPDATE || mode == MODE_DELETE));
355 PTXF txfp = NULL;
356 PTDBASE tdbp;
357
358 /*********************************************************************/
359 /* Allocate table and file processing class of the proper type. */
360 /* Column blocks will be allocated only when needed. */
361 /*********************************************************************/
362 if (Recfm == RECFM_DBF) {
363 if (Catfunc == FNC_NO) {
364 if (Zipped) {
365 if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
366 txfp = new(g) UZDFAM(this);
367 } else {
368 strcpy(g->Message, "Zipped DBF tables are read only");
369 return NULL;
370 } // endif's mode
371
372 } else if (map)
373 txfp = new(g) DBMFAM(this);
374 else
375 txfp = new(g) DBFFAM(this);
376
377 tdbp = new(g) TDBFIX(this, txfp);
378 } else
379 tdbp = new(g) TDBDCL(this); // Catfunc should be 'C'
380
381 } else if (Zipped) {
382 #if defined(ZIP_SUPPORT)
383 if (Recfm == RECFM_VAR) {
384 if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
385 txfp = new(g) UNZFAM(this);
386 } else if (mode == MODE_INSERT) {
387 txfp = new(g) ZIPFAM(this);
388 } else {
389 strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
390 return NULL;
391 } // endif's mode
392
393 tdbp = new(g) TDBDOS(this, txfp);
394 } else {
395 if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
396 txfp = new(g) UZXFAM(this);
397 } else if (mode == MODE_INSERT) {
398 txfp = new(g) ZPXFAM(this);
399 } else {
400 strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
401 return NULL;
402 } // endif's mode
403
404 tdbp = new(g)TDBFIX(this, txfp);
405 } // endif Recfm
406
407 #else // !ZIP_SUPPORT
408 sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
409 return NULL;
410 #endif // !ZIP_SUPPORT
411 } else if (Recfm != RECFM_VAR && Compressed < 2) {
412 if (Huge)
413 txfp = new(g) BGXFAM(this);
414 else if (map)
415 txfp = new(g) MPXFAM(this);
416 else if (Compressed) {
417 #if defined(GZ_SUPPORT)
418 txfp = new(g) GZXFAM(this);
419 #else // !GZ_SUPPORT
420 sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
421 return NULL;
422 #endif // !GZ_SUPPORT
423 } else
424 txfp = new(g) FIXFAM(this);
425
426 tdbp = new(g) TDBFIX(this, txfp);
427 } else {
428 if (Compressed) {
429 #if defined(GZ_SUPPORT)
430 if (Compressed == 1)
431 txfp = new(g) GZFAM(this);
432 else
433 txfp = new(g) ZLBFAM(this);
434
435 #else // !GZ_SUPPORT
436 sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
437 return NULL;
438 #endif // !GZ_SUPPORT
439 } else if (map)
440 txfp = new(g) MAPFAM(this);
441 else
442 txfp = new(g) DOSFAM(this);
443
444 // Txfp must be set even for not multiple tables because
445 // it is needed when calling Cardinality in GetBlockValues.
446 tdbp = new(g) TDBDOS(this, txfp);
447 } // endif Recfm
448
449 if (Multiple)
450 tdbp = new(g) TDBMUL(tdbp);
451 else
452 /*******************************************************************/
453 /* For block tables, get eventually saved optimization values. */
454 /*******************************************************************/
455 if (tdbp->GetBlockValues(g)) {
456 PushWarning(g, tdbp);
457 // return NULL; // causes a crash when deleting index
458 } else if (Recfm == RECFM_VAR || Compressed > 1) {
459 if (IsOptimized()) {
460 if (map) {
461 txfp = new(g) MBKFAM(this);
462 } else if (Compressed) {
463 #if defined(GZ_SUPPORT)
464 if (Compressed == 1)
465 txfp = new(g) ZBKFAM(this);
466 else {
467 txfp->SetBlkPos(To_Pos);
468 ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL);
469 } // endelse
470 #else
471 sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
472 return NULL;
473 #endif
474 } else
475 txfp = new(g) BLKFAM(this);
476
477 ((PTDBDOS)tdbp)->SetTxfp(txfp);
478 } // endif Optimized
479
480 } // endif Recfm
481
482 return tdbp;
483 } // end of GetTable
484
485 /* ------------------------ Class TDBDOS ----------------------------- */
486
487 /***********************************************************************/
488 /* Implementation of the TDBDOS class. This is the common class that */
489 /* contain all that is common between the TDBDOS and TDBMAP classes. */
490 /***********************************************************************/
TDBDOS(PDOSDEF tdp,PTXF txfp)491 TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
492 {
493 if ((Txfp = txfp))
494 Txfp->SetTdbp(this);
495
496 Lrecl = tdp->Lrecl;
497 AvgLen = tdp->AvgLen;
498 Ftype = tdp->Recfm;
499 To_Line = NULL;
500 //To_BlkIdx = NULL;
501 To_BlkFil = NULL;
502 SavFil = NULL;
503 //Xeval = 0;
504 Beval = 0;
505 Abort = false;
506 Indxd = false;
507 } // end of TDBDOS standard constructor
508
TDBDOS(PGLOBAL g,PTDBDOS tdbp)509 TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
510 {
511 Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp;
512 Lrecl = tdbp->Lrecl;
513 AvgLen = tdbp->AvgLen;
514 Ftype = tdbp->Ftype;
515 To_Line = tdbp->To_Line;
516 //To_BlkIdx = tdbp->To_BlkIdx;
517 To_BlkFil = tdbp->To_BlkFil;
518 SavFil = tdbp->SavFil;
519 //Xeval = tdbp->Xeval;
520 Beval = tdbp->Beval;
521 Abort = tdbp->Abort;
522 Indxd = tdbp->Indxd;
523 } // end of TDBDOS copy constructor
524
525 // Method
Clone(PTABS t)526 PTDB TDBDOS::Clone(PTABS t)
527 {
528 PTDB tp;
529 PDOSCOL cp1, cp2;
530 PGLOBAL g = t->G;
531
532 tp = new(g) TDBDOS(g, this);
533
534 for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
535 cp2 = new(g) DOSCOL(cp1, tp); // Make a copy
536 NewPointer(t, cp1, cp2);
537 } // endfor cp1
538
539 return tp;
540 } // end of Clone
541
542 /***********************************************************************/
543 /* Allocate DOS column description block. */
544 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)545 PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
546 {
547 return new(g) DOSCOL(g, cdp, this, cprec, n);
548 } // end of MakeCol
549
550 /***********************************************************************/
551 /* Print debug information. */
552 /***********************************************************************/
PrintAM(FILE * f,char * m)553 void TDBDOS::PrintAM(FILE *f, char *m)
554 {
555 fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
556
557 if (Txfp->To_File)
558 fprintf(f, "%s File: %s\n", m, Txfp->To_File);
559
560 } // end of PrintAM
561
562 /***********************************************************************/
563 /* Remake the indexes after the table was modified. */
564 /***********************************************************************/
ResetTableOpt(PGLOBAL g,bool dop,bool dox)565 int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
566 {
567 int prc = RC_OK, rc = RC_OK;
568
569 if (!GetFileLength(g)) {
570 // Void table, delete all opt and index files
571 PDOSDEF defp = (PDOSDEF)To_Def;
572
573 defp->RemoveOptValues(g);
574 return (defp->DeleteIndexFile(g, NULL)) ? RC_INFO : RC_OK;
575 } // endif GetFileLength
576
577 MaxSize = -1; // Size must be recalculated
578 Cardinal = -1; // as well as Cardinality
579
580 PTXF xp = Txfp;
581
582 To_Filter = NULL; // Disable filtering
583 //To_BlkIdx = NULL; // and index filtering
584 To_BlkFil = NULL; // and block filtering
585
586 // After the table was modified the indexes
587 // are invalid and we should mark them as such...
588 (void)((PDOSDEF)To_Def)->InvalidateIndex(g);
589
590 if (dop) {
591 Columns = NULL; // Not used anymore
592
593 if (Txfp->Blocked) {
594 // MakeBlockValues must be executed in non blocked mode
595 // except for ZLIB access method.
596 if (Txfp->GetAmType() == TYPE_AM_MAP) {
597 Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
598 #if defined(GZ_SUPPORT)
599 } else if (Txfp->GetAmType() == TYPE_AM_GZ) {
600 Txfp = new(g) GZFAM((PDOSDEF)To_Def);
601 } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) {
602 Txfp->Reset();
603 ((PZLBFAM)Txfp)->SetOptimized(false);
604 #endif // GZ_SUPPORT
605 } else if (Txfp->GetAmType() == TYPE_AM_BLK)
606 Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
607
608 Txfp->SetTdbp(this);
609 } else
610 Txfp->Reset();
611
612 Use = USE_READY; // So the table can be reopened
613 Mode = MODE_ANY; // Just to be clean
614 rc = MakeBlockValues(g); // Redo optimization
615 } // endif dop
616
617 if (dox && (rc == RC_OK || rc == RC_INFO)) {
618 // Remake eventual indexes
619 // if (Mode != MODE_UPDATE)
620 To_SetCols = NULL; // Positions are changed
621
622 Columns = NULL; // Not used anymore
623 Txfp->Reset(); // New start
624 Use = USE_READY; // So the table can be reopened
625 Mode = MODE_READ; // New mode
626 prc = rc;
627
628 if (PlgGetUser(g)->Check & CHK_OPT)
629 // We must remake all indexes.
630 rc = MakeIndex(g, NULL, false);
631
632 rc = (rc == RC_INFO) ? prc : rc;
633 } // endif dox
634
635 return rc;
636 } // end of ResetTableOpt
637
638 /***********************************************************************/
639 /* Calculate the block sizes so block I/O can be used and also the */
640 /* Min/Max values for clustered/sorted table columns. */
641 /***********************************************************************/
MakeBlockValues(PGLOBAL g)642 int TDBDOS::MakeBlockValues(PGLOBAL g)
643 {
644 int i, lg, nrec, rc, n = 0;
645 int curnum, curblk, block, savndv, savnbm;
646 void *savmin, *savmax;
647 bool blocked, xdb2 = false;
648 //POOLHEADER save;
649 PCOLDEF cdp;
650 PDOSDEF defp = (PDOSDEF)To_Def;
651 PDOSCOL colp = NULL;
652 PDBUSER dup = PlgGetUser(g);
653 PCATLG cat = defp->GetCat();
654 //void *memp = cat->GetDescp();
655
656 if ((nrec = defp->GetElemt()) < 2) {
657 if (!To_Def->Partitioned()) {
658 // This may be wrong to do in some cases
659 strcpy(g->Message, MSG(TABLE_NOT_OPT));
660 return RC_INFO; // Not to be optimized
661 } else
662 return RC_OK;
663
664 } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) {
665 // Suppress the opt file firstly if the table is void,
666 // secondly when it was modified with OPTIMIZATION unchecked
667 // because it is no more valid.
668 defp->RemoveOptValues(g); // Erase opt file
669 return RC_OK; // void table
670 } else if (MaxSize < 0)
671 return RC_FX;
672
673 defp->SetOptimized(0);
674
675 // Estimate the number of needed blocks
676 if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) {
677 // This may be wrong to do in some cases
678 defp->RemoveOptValues(g);
679 strcpy(g->Message, MSG(TABLE_NOT_OPT));
680 return RC_INFO; // Not to be optimized
681 } // endif block
682
683 // We have to use local variables because Txfp->CurBlk is set
684 // to Rows+1 by unblocked variable length table access methods.
685 curblk = -1;
686 curnum = nrec - 1;
687 //last = 0;
688 Txfp->Block = block; // This is useful mainly for
689 Txfp->CurBlk = curblk; // blocked tables (ZLBFAM), for
690 Txfp->CurNum = curnum; // others it is just to be clean.
691
692 /*********************************************************************/
693 /* Allocate the array of block starting positions. */
694 /*********************************************************************/
695 //if (memp)
696 // save = *(PPOOLHEADER)memp;
697
698 Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int));
699
700 /*********************************************************************/
701 /* Allocate the blocks for clustered columns. */
702 /*********************************************************************/
703 blocked = Txfp->Blocked; // Save
704 Txfp->Blocked = true; // So column block can be allocated
705
706 for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
707 if (cdp->GetOpt()) {
708 lg = cdp->GetClen();
709
710 if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) {
711 cdp->SetXdb2(true);
712 savndv = cdp->GetNdv();
713 cdp->SetNdv(0); // Reset Dval number of values
714 xdb2 = true;
715 savmax = cdp->GetDval();
716 cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg));
717 savnbm = cdp->GetNbm();
718 cdp->SetNbm(0); // Prevent Bmap allocation
719 // savmin = cdp->GetBmap();
720 // cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int)));
721
722 if (trace(1))
723 htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n",
724 cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg);
725
726 // colp will be initialized with proper Dval VALBLK
727 colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
728 colp->InitValue(g); // Allocate column value buffer
729 cdp->SetNbm(savnbm);
730 // cdp->SetBmap(savmin); // Can be reused if the new size
731 cdp->SetDval(savmax); // is not greater than this one.
732 cdp->SetNdv(savndv);
733 } else {
734 cdp->SetXdb2(false); // Maxbmp may have been reset
735 savmin = cdp->GetMin();
736 savmax = cdp->GetMax();
737 cdp->SetMin(PlugSubAlloc(g, NULL, block * lg));
738 cdp->SetMax(PlugSubAlloc(g, NULL, block * lg));
739
740 // Valgrind complains if there are uninitialised bytes
741 // after the null character ending
742 if (IsTypeChar(cdp->GetType())) {
743 memset(cdp->GetMin(), 0, block * lg);
744 memset(cdp->GetMax(), 0, block * lg);
745 } // endif Type
746
747 if (trace(1))
748 htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n",
749 cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg);
750
751 // colp will be initialized with proper opt VALBLK's
752 colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
753 colp->InitValue(g); // Allocate column value buffer
754 cdp->SetMin(savmin); // Can be reused if the number
755 cdp->SetMax(savmax); // of blocks does not change.
756 } // endif Freq
757
758 } // endif Clustered
759
760 // No optimised columns. Still useful for blocked variable tables.
761 if (!colp && defp->Recfm != RECFM_VAR) {
762 strcpy(g->Message, "No optimised columns");
763 return RC_INFO;
764 } // endif colp
765
766 Txfp->Blocked = blocked;
767
768 /*********************************************************************/
769 /* Now do calculate the optimization values. */
770 /*********************************************************************/
771 Mode = MODE_READ;
772
773 if (OpenDB(g))
774 return RC_FX;
775
776 if (xdb2) {
777 /*********************************************************************/
778 /* Retrieve the distinct values of XDB2 columns. */
779 /*********************************************************************/
780 if (GetDistinctColumnValues(g, nrec))
781 return RC_FX;
782
783 OpenDB(g); // Rewind the table file
784 } // endif xdb2
785
786 #if defined(PROG_INFO)
787 /*********************************************************************/
788 /* Initialize progress information */
789 /*********************************************************************/
790 char *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name));
791
792 dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name);
793 dup->ProgMax = GetProgMax(g);
794 dup->ProgCur = 0;
795 #endif // SOCKET_MODE || THREAD
796
797 /*********************************************************************/
798 /* Make block starting pos and min/max values of cluster columns. */
799 /*********************************************************************/
800 while ((rc = ReadDB(g)) == RC_OK) {
801 if (blocked) {
802 // A blocked FAM class handles CurNum and CurBlk (ZLBFAM)
803 if (!Txfp->CurNum)
804 Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos();
805
806 } else {
807 if (++curnum >= nrec) {
808 if (++curblk >= block) {
809 strcpy(g->Message, MSG(BAD_BLK_ESTIM));
810 goto err;
811 } else
812 curnum = 0;
813
814 // Get block starting position
815 Txfp->BlkPos[curblk] = Txfp->GetPos();
816 } // endif CurNum
817
818 // last = curnum + 1; // curnum is zero based
819 Txfp->CurBlk = curblk; // Used in COLDOS::SetMinMax
820 Txfp->CurNum = curnum; // Used in COLDOS::SetMinMax
821 } // endif blocked
822
823 /*******************************************************************/
824 /* Now calculate the min and max values for the cluster columns. */
825 /*******************************************************************/
826 for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext())
827 if (colp->Clustered == 2) {
828 if (colp->SetBitMap(g))
829 goto err;
830
831 } else
832 if (colp->SetMinMax(g))
833 goto err; // Currently: column is not sorted
834
835 #if defined(PROG_INFO)
836 if (!dup->Step) {
837 strcpy(g->Message, MSG(OPT_CANCELLED));
838 goto err;
839 } else
840 dup->ProgCur = GetProgCur();
841 #endif // PROG_INFO
842
843 n++; // Used to calculate block and last
844 } // endwhile
845
846 if (rc == RC_EF) {
847 Txfp->Nrec = nrec;
848
849 #if 0 // No good because Curblk and CurNum after EOF are different
850 // depending on whether the file is mapped or not mapped.
851 if (blocked) {
852 // Txfp->Block = Txfp->CurBlk + 1;
853 Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum : nrec;
854 // Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec;
855 Txfp->Block = Txfp->CurBlk + (Txfp->Last == nrec ? 0 : 1);
856 } else {
857 Txfp->Block = curblk + 1;
858 Txfp->Last = last;
859 } // endif blocked
860 #endif // 0
861
862 // New values of Block and Last
863 Txfp->Block = (n + nrec - 1) / nrec;
864 Txfp->Last = (n % nrec) ? (n % nrec) : nrec;
865
866 // This is needed to be able to calculate the last block size
867 Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos();
868 } else
869 goto err;
870
871 /*********************************************************************/
872 /* Save the optimization values for this table. */
873 /*********************************************************************/
874 if (!SaveBlockValues(g)) {
875 defp->Block = Txfp->Block;
876 defp->Last = Txfp->Last;
877 CloseDB(g);
878 defp->SetIntCatInfo("Blocks", Txfp->Block);
879 defp->SetIntCatInfo("Last", Txfp->Last);
880 return RC_OK;
881 } // endif SaveBlockValues
882
883 err:
884 // Restore Desc memory suballocation
885 //if (memp)
886 // *(PPOOLHEADER)memp = save;
887
888 defp->RemoveOptValues(g);
889 CloseDB(g);
890 return RC_FX;
891 } // end of MakeBlockValues
892
893 /***********************************************************************/
894 /* Save the block and Min/Max values for this table. */
895 /* The problem here is to avoid name duplication, because more than */
896 /* one data file can have the same name (but different types) and/or */
897 /* the same data file can be used with different block sizes. This is */
898 /* why we use Ofn that defaults to the file name but can be set to a */
899 /* different name if necessary. */
900 /***********************************************************************/
SaveBlockValues(PGLOBAL g)901 bool TDBDOS::SaveBlockValues(PGLOBAL g)
902 {
903 char filename[_MAX_PATH];
904 int lg, n[NZ + 2];
905 size_t nbk, ndv, nbm, block = Txfp->Block;
906 bool rc = false;
907 FILE *opfile;
908 PDOSCOL colp;
909 PDOSDEF defp = (PDOSDEF)To_Def;
910
911 if (defp->GetOptFileName(g, filename))
912 return true;
913
914 if (!(opfile = fopen(filename, "wb"))) {
915 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
916 "wb", (int)errno, filename);
917 strcat(strcat(g->Message, ": "), strerror(errno));
918
919 if (trace(1))
920 htrc("%s\n", g->Message);
921
922 return true;
923 } // endif opfile
924
925 memset(n, 0, sizeof(n)); // To avoid valgrind warning
926
927 if (Ftype == RECFM_VAR || defp->Compressed == 2) {
928 /*******************************************************************/
929 /* Write block starting positions into the opt file. */
930 /*******************************************************************/
931 block++;
932 lg = sizeof(int);
933 n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block;
934
935 if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
936 sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
937 rc = true;
938 } // endif size
939
940 if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) {
941 sprintf(g->Message, MSG(OPTBLK_WR_ERR), strerror(errno));
942 rc = true;
943 } // endif size
944
945 block--; // = Txfp->Block;
946 } // endif Ftype
947
948 /*********************************************************************/
949 /* Write the Min/Max values into the opt file. */
950 /*********************************************************************/
951 for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) {
952 lg = colp->Value->GetClen();
953
954 // Now start the writing process
955 if (colp->Clustered == 2) {
956 // New XDB2 block optimization. Will be recognized when reading
957 // because the column index is negated.
958 ndv = colp->Ndv; nbm = colp->Nbm;
959 nbk = nbm * block;
960 n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
961 n[4] = ndv; n[5] = nbm;
962
963 if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) {
964 sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
965 rc = true;
966 } // endif size
967
968 if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) {
969 sprintf(g->Message, MSG(OPT_DVAL_WR_ERR), strerror(errno));
970 rc = true;
971 } // endif size
972
973 if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) {
974 sprintf(g->Message, MSG(OPT_BMAP_WR_ERR), strerror(errno));
975 rc = true;
976 } // endif size
977
978 } else {
979 n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
980
981 if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
982 sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
983 rc = true;
984 } // endif size
985
986 if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) {
987 sprintf(g->Message, MSG(OPT_MIN_WR_ERR), strerror(errno));
988 rc = true;
989 } // endif size
990
991 if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) {
992 sprintf(g->Message, MSG(OPT_MAX_WR_ERR), strerror(errno));
993 rc = true;
994 } // endif size
995
996 } // endif Clustered
997
998 } // endfor colp
999
1000 fclose(opfile);
1001 return rc;
1002 } // end of SaveBlockValues
1003
1004 /***********************************************************************/
1005 /* Read the Min/Max values for this table. */
1006 /* The problem here is to avoid name duplication, because more than */
1007 /* one data file can have the same name (but different types) and/or */
1008 /* the same data file can be used with different block sizes. This is */
1009 /* why we use Ofn that defaults to the file name but can be set to a */
1010 /* different name if necessary. */
1011 /***********************************************************************/
GetBlockValues(PGLOBAL g)1012 bool TDBDOS::GetBlockValues(PGLOBAL g)
1013 {
1014 char filename[_MAX_PATH];
1015 int i, lg, n[NZ];
1016 int nrec, block = 0, last = 0, allocblk = 0;
1017 int len;
1018 bool newblk = false;
1019 size_t ndv, nbm, nbk, blk;
1020 FILE *opfile;
1021 PCOLDEF cdp;
1022 PDOSDEF defp = (PDOSDEF)To_Def;
1023 PCATLG cat = defp->GetCat();
1024 PDBUSER dup = PlgGetUser(g);
1025
1026 #if 0
1027 if (Mode == MODE_INSERT && Txfp->GetAmType() == TYPE_AM_DOS)
1028 return false;
1029 #endif // _WIN32
1030
1031 if (defp->Optimized || !(dup->Check & CHK_OPT))
1032 return false; // Already done or to be redone
1033
1034 if (Ftype == RECFM_VAR || defp->Compressed == 2) {
1035 /*******************************************************************/
1036 /* Variable length file that can be read by block. */
1037 /*******************************************************************/
1038 nrec = (defp->GetElemt()) ? defp->GetElemt() : 1;
1039
1040 if (nrec > 1) {
1041 // The table can be declared optimized if it is void.
1042 // This is useful to handle Insert in optimized mode.
1043 char filename[_MAX_PATH];
1044 int h;
1045 int flen = -1;
1046
1047 PlugSetPath(filename, defp->Fn, GetPath());
1048 h = open(filename, O_RDONLY);
1049 flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h);
1050
1051 if (h != -1)
1052 close(h);
1053
1054 if (!flen) {
1055 defp->SetOptimized(1);
1056 return false;
1057 } // endif flen
1058
1059 } else
1060 return false; // Not optimisable
1061
1062 cdp = defp->GetCols();
1063 i = 1;
1064 } else {
1065 /*******************************************************************/
1066 /* Fixed length file. Opt file exists only for clustered columns. */
1067 /*******************************************************************/
1068 // Check for existence of clustered columns
1069 for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
1070 if (cdp->GetOpt())
1071 break;
1072
1073 if (!cdp)
1074 return false; // No optimization needed
1075
1076 if ((len = Cardinality(g)) < 0)
1077 return true; // Table error
1078 else if (!len)
1079 return false; // File does not exist yet
1080
1081 block = Txfp->Block; // Was set in Cardinality
1082 nrec = Txfp->Nrec;
1083 } // endif Ftype
1084
1085 if (defp->GetOptFileName(g, filename))
1086 return true;
1087
1088 if (!(opfile = fopen(filename, "rb")))
1089 return false; // No saved values
1090
1091 if (Ftype == RECFM_VAR || defp->Compressed == 2) {
1092 /*******************************************************************/
1093 /* Read block starting positions from the opt file. */
1094 /*******************************************************************/
1095 lg = sizeof(int);
1096
1097 if (fread(n, sizeof(int), NZ, opfile) != NZ) {
1098 sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1099 goto err;
1100 } // endif size
1101
1102 if (n[1] != lg || n[2] != nrec) {
1103 sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1104 goto err;
1105 } // endif
1106
1107 last = n[0];
1108 block = n[3];
1109 blk = block + 1;
1110
1111 defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg);
1112
1113 if (fread(defp->To_Pos, lg, blk, opfile) != blk) {
1114 sprintf(g->Message, MSG(OPTBLK_RD_ERR), strerror(errno));
1115 goto err;
1116 } // endif size
1117
1118 } // endif Ftype
1119
1120 /*********************************************************************/
1121 /* Read the Min/Max values from the opt file. */
1122 /*********************************************************************/
1123 for (; cdp; cdp = cdp->GetNext(), i++)
1124 if (cdp->GetOpt()) {
1125 lg = cdp->GetClen();
1126 blk = block;
1127
1128 // Now start the reading process.
1129 if (fread(n, sizeof(int), NZ, opfile) != NZ) {
1130 sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1131 goto err;
1132 } // endif size
1133
1134 if (n[0] == -i) {
1135 // Read the XDB2 opt values from the opt file
1136 if (n[1] != lg || n[2] != nrec || n[3] != block) {
1137 sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1138 goto err;
1139 } // endif
1140
1141 if (fread(n, sizeof(int), 2, opfile) != 2) {
1142 sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1143 goto err;
1144 } // endif fread
1145
1146 ndv = n[0]; nbm = n[1]; nbk = nbm * blk;
1147
1148 if (cdp->GetNdv() < (int)ndv || !cdp->GetDval())
1149 cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg));
1150
1151 cdp->SetNdv((int)ndv);
1152
1153 if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) {
1154 sprintf(g->Message, MSG(OPT_DVAL_RD_ERR), strerror(errno));
1155 goto err;
1156 } // endif size
1157
1158 if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap())
1159 cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int)));
1160
1161 cdp->SetNbm((int)nbm);
1162
1163 if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) {
1164 sprintf(g->Message, MSG(OPT_BMAP_RD_ERR), strerror(errno));
1165 goto err;
1166 } // endif size
1167
1168 cdp->SetXdb2(true);
1169 } else {
1170 // Read the Min/Max values from the opt file
1171 if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) {
1172 sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1173 goto err;
1174 } // endif
1175
1176 if (newblk || !cdp->GetMin())
1177 cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg));
1178
1179 if (fread(cdp->GetMin(), lg, blk, opfile) != blk) {
1180 sprintf(g->Message, MSG(OPT_MIN_RD_ERR), strerror(errno));
1181 goto err;
1182 } // endif size
1183
1184 if (newblk || !cdp->GetMax())
1185 cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg));
1186
1187 if (fread(cdp->GetMax(), lg, blk, opfile) != blk) {
1188 sprintf(g->Message, MSG(OPT_MAX_RD_ERR), strerror(errno));
1189 goto err;
1190 } // endif size
1191
1192 cdp->SetXdb2(false);
1193 } // endif n[0] (XDB2)
1194
1195 } // endif Clustered
1196
1197 defp->SetBlock(block);
1198 defp->Last = last; // For Cardinality
1199 defp->SetAllocBlks(block);
1200 defp->SetOptimized(1);
1201 fclose(opfile);
1202 MaxSize = -1; // Can be refined later
1203 return false;
1204
1205 err:
1206 defp->RemoveOptValues(g);
1207 fclose(opfile);
1208
1209 // Ignore error if not in mode CHK_OPT
1210 return (PlgGetUser(g)->Check & CHK_OPT) != 0;
1211 } // end of GetBlockValues
1212
1213 /***********************************************************************/
1214 /* This fonction is used while making XDB2 block optimization. */
1215 /* It constructs for each elligible columns, the sorted list of the */
1216 /* distinct values existing in the column. This function uses an */
1217 /* algorithm that permit to get several sets of distinct values by */
1218 /* reading the table only once, which cannot be done using a standard */
1219 /* SQL query. */
1220 /***********************************************************************/
GetDistinctColumnValues(PGLOBAL g,int nrec)1221 bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
1222 {
1223 char *p;
1224 int rc, blk, n = 0;
1225 PDOSCOL colp;
1226 PDBUSER dup = PlgGetUser(g);
1227
1228 /*********************************************************************/
1229 /* Initialize progress information */
1230 /*********************************************************************/
1231 p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name));
1232 dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name);
1233 dup->ProgMax = GetProgMax(g);
1234 dup->ProgCur = 0;
1235
1236 while ((rc = ReadDB(g)) == RC_OK) {
1237 for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
1238 if (colp->Clustered == 2)
1239 if (colp->AddDistinctValue(g))
1240 return true; // Too many distinct values
1241
1242 #if defined(SOCKET_MODE)
1243 if (SendProgress(dup)) {
1244 strcpy(g->Message, MSG(OPT_CANCELLED));
1245 return true;
1246 } else
1247 #elif defined(THREAD)
1248 if (!dup->Step) {
1249 strcpy(g->Message, MSG(OPT_CANCELLED));
1250 return true;
1251 } else
1252 #endif // THREAD
1253 dup->ProgCur = GetProgCur();
1254
1255 n++;
1256 } // endwhile
1257
1258 if (rc != RC_EF)
1259 return true;
1260
1261 // Reset the number of table blocks
1262 //nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value)
1263 blk = (n + nrec - 1) / nrec;
1264 Txfp->Block = blk; // Useful mainly for ZLBFAM ???
1265
1266 // Set Nbm, Bmap for XDB2 columns
1267 for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
1268 if (colp->Clustered == 2) {
1269 // colp->Cdp->SetNdv(colp->Ndv);
1270 colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP;
1271 colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk);
1272 } // endif Clustered
1273
1274 return false;
1275 } // end of GetDistinctColumnValues
1276
1277 /***********************************************************************/
1278 /* Analyze the filter and construct the Block Evaluation Filter. */
1279 /* This is possible when a filter contains predicates implying a */
1280 /* column marked as "clustered" or "sorted" matched to a constant */
1281 /* argument. It is then possible by comparison against the smallest */
1282 /* and largest column values in each block to determine whether the */
1283 /* filter condition will be always true or always false for the block.*/
1284 /***********************************************************************/
InitBlockFilter(PGLOBAL g,PFIL filp)1285 PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp)
1286 {
1287 bool blk = Txfp->Blocked;
1288
1289 if (To_BlkFil)
1290 return To_BlkFil; // Already done
1291 else if (!filp)
1292 return NULL;
1293 else if (blk) {
1294 if (Txfp->GetAmType() == TYPE_AM_DBF)
1295 /*****************************************************************/
1296 /* If RowID is used in this query, block optimization cannot be */
1297 /* used because currently the file must be read sequentially. */
1298 /*****************************************************************/
1299 for (PCOL cp = Columns; cp; cp = cp->GetNext())
1300 if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm())
1301 return NULL;
1302
1303 } // endif blk
1304
1305 int i, op = filp->GetOpc(), opm = filp->GetOpm(), n = 0;
1306 bool cnv[2];
1307 PCOL colp;
1308 PXOB arg[2] = {NULL,NULL};
1309 PBF *fp = NULL, bfp = NULL;
1310
1311 switch (op) {
1312 case OP_EQ:
1313 case OP_NE:
1314 case OP_GT:
1315 case OP_GE:
1316 case OP_LT:
1317 case OP_LE:
1318 if (! opm) {
1319 for (i = 0; i < 2; i++) {
1320 arg[i] = filp->Arg(i);
1321 cnv[i] = filp->Conv(i);
1322 } // endfor i
1323
1324 bfp = CheckBlockFilari(g, arg, op, cnv);
1325 break;
1326 } // endif !opm
1327
1328 // if opm, pass thru
1329 // fall through
1330 case OP_IN:
1331 if (filp->GetArgType(0) == TYPE_COLBLK &&
1332 filp->GetArgType(1) == TYPE_ARRAY) {
1333 arg[0] = filp->Arg(0);
1334 arg[1] = filp->Arg(1);
1335 colp = (PCOL)arg[0];
1336
1337 if (colp->GetTo_Tdb() == this) {
1338 // Block evaluation is possible for...
1339 if (colp->GetAmType() == TYPE_AM_ROWID) {
1340 // Special column ROWID and constant array, but
1341 // currently we don't know how to retrieve a RowID
1342 // from a DBF table that is not sequentially read.
1343 // if (Txfp->GetAmType() != TYPE_AM_DBF ||
1344 // ((RIDBLK*)arg[0])->GetRnm())
1345 bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec);
1346
1347 } else if (blk && Txfp->Nrec > 1 && colp->IsClustered())
1348 // Clustered column and constant array
1349 if (colp->GetClustered() == 2)
1350 bfp = new(g) BLKFILIN2(g, this, op, opm, arg);
1351 else
1352 bfp = new(g) BLKFILIN(g, this, op, opm, arg);
1353
1354 } // endif this
1355
1356 #if 0
1357 } else if (filp->GetArgType(0) == TYPE_SCALF &&
1358 filp->GetArgType(1) == TYPE_ARRAY) {
1359 arg[0] = filp->Arg(0);
1360 arg[1] = filp->Arg(1);
1361
1362 if (((PSCALF)arg[0])->GetOp() == OP_ROW &&
1363 arg[1]->GetResultType() == TYPE_LIST) {
1364 PARRAY par = (PARRAY)arg[1];
1365 LSTVAL *vlp = (LSTVAL*)par->GetValue();
1366
1367 ((SFROW*)arg[0])->GetParms(n);
1368
1369 if (n != vlp->GetN())
1370 return NULL;
1371 else
1372 n = par->GetNval();
1373
1374 arg[1] = new(g) CONSTANT(vlp);
1375 fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF));
1376 cnv[0] = cnv[1] = false;
1377
1378 if (op == OP_IN)
1379 op = OP_EQ;
1380
1381 for (i = 0; i < n; i++) {
1382 par->GetNthValue(vlp, i);
1383
1384 if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv)))
1385 return NULL;
1386
1387 } // endfor i
1388
1389 bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n);
1390 } // endif ROW
1391 #endif // 0
1392
1393 } // endif Type
1394
1395 break;
1396 case OP_AND:
1397 case OP_OR:
1398 fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF));
1399 fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0)));
1400 fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1)));
1401
1402 if (fp[0] || fp[1])
1403 bfp = new(g) BLKFILLOG(this, op, fp, 2);
1404
1405 break;
1406 case OP_NOT:
1407 fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF));
1408
1409 if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0)))))
1410 bfp = new(g) BLKFILLOG(this, op, fp, 1);
1411
1412 break;
1413 case OP_LIKE:
1414 default:
1415 break;
1416 } // endswitch op
1417
1418 return bfp;
1419 } // end of InitBlockFilter
1420
1421 /***********************************************************************/
1422 /* Analyze the passed arguments and construct the Block Filter. */
1423 /***********************************************************************/
CheckBlockFilari(PGLOBAL g,PXOB * arg,int op,bool * cnv)1424 PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv)
1425 {
1426 //int i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0};
1427 //bool conv = false, xdb2 = false, ok = false, b[2];
1428 //PXOB *xarg1, *xarg2 = NULL, xp[2];
1429 int i, n = 0, type[2] = {0,0};
1430 bool conv = false, xdb2 = false, ok = false;
1431 PXOB *xarg2 = NULL, xp[2];
1432 PCOL colp;
1433 //LSTVAL *vlp = NULL;
1434 //SFROW *sfr[2];
1435 PBF *fp = NULL, bfp = NULL;
1436
1437 for (i = 0; i < 2; i++) {
1438 switch (arg[i]->GetType()) {
1439 case TYPE_CONST:
1440 type[i] = 1;
1441 // ctype = arg[i]->GetResultType();
1442 break;
1443 case TYPE_COLBLK:
1444 conv = cnv[i];
1445 colp = (PCOL)arg[i];
1446
1447 if (colp->GetTo_Tdb() == this) {
1448 if (colp->GetAmType() == TYPE_AM_ROWID) {
1449 // Currently we don't know how to retrieve a RowID
1450 // from a DBF table that is not sequentially read.
1451 // if (Txfp->GetAmType() != TYPE_AM_DBF ||
1452 // ((RIDBLK*)arg[i])->GetRnm())
1453 type[i] = 5;
1454
1455 } else if (Txfp->Blocked && Txfp->Nrec > 1 &&
1456 colp->IsClustered()) {
1457 type[i] = 2;
1458 xdb2 = colp->GetClustered() == 2;
1459 } // endif Clustered
1460
1461 } else if (colp->GetColUse(U_CORREL)) {
1462 // This is a column pointing to the outer query of a
1463 // correlated subquery, it has a constant value during
1464 // each execution of the subquery.
1465 type[i] = 1;
1466 // ctype = arg[i]->GetResultType();
1467 } // endif this
1468
1469 break;
1470 // case TYPE_SCALF:
1471 // if (((PSCALF)arg[i])->GetOp() == OP_ROW) {
1472 // sfr[i] = (SFROW*)arg[i];
1473 // type[i] = 7;
1474 // } // endif Op
1475
1476 // break;
1477 default:
1478 break;
1479 } // endswitch ArgType
1480
1481 if (!type[i])
1482 break;
1483
1484 n += type[i];
1485 } // endfor i
1486
1487 if (n == 3 || n == 6) {
1488 if (conv) {
1489 // The constant has not the good type and will not match
1490 // the block min/max values. Warn and abort.
1491 sprintf(g->Message, "Block opt: %s", MSG(VALTYPE_NOMATCH));
1492 PushWarning(g, this);
1493 return NULL;
1494 } // endif Conv
1495
1496 if (type[0] == 1) {
1497 // Make it always as Column-op-Value
1498 *xp = arg[0];
1499 arg[0] = arg[1];
1500 arg[1] = *xp;
1501
1502 switch (op) {
1503 case OP_GT: op = OP_LT; break;
1504 case OP_GE: op = OP_LE; break;
1505 case OP_LT: op = OP_GT; break;
1506 case OP_LE: op = OP_GE; break;
1507 } // endswitch op
1508
1509 } // endif
1510
1511 #if defined(_DEBUG)
1512 // assert(arg[0]->GetResultType() == ctype);
1513 #endif
1514
1515 if (n == 3) {
1516 if (xdb2) {
1517 if (((PDOSCOL)arg[0])->GetNbm() == 1)
1518 bfp = new(g) BLKFILAR2(g, this, op, arg);
1519 else // Multiple bitmap made of several ULONG's
1520 bfp = new(g) BLKFILMR2(g, this, op, arg);
1521 } else
1522 bfp = new(g) BLKFILARI(g, this, op, arg);
1523
1524 } else // n = 6
1525 bfp = new(g) BLKSPCARI(this, op, arg, Txfp->Nrec);
1526
1527 #if 0
1528 } else if (n == 8 || n == 14) {
1529 if (n == 8 && ctype != TYPE_LIST) {
1530 // Should never happen
1531 strcpy(g->Message, "Block opt: bad constant");
1532 throw 99;
1533 } // endif Conv
1534
1535 if (type[0] == 1) {
1536 // Make it always as Column-op-Value
1537 sfr[0] = sfr[1];
1538 arg[1] = arg[0];
1539
1540 switch (op) {
1541 case OP_GT: op = OP_LT; break;
1542 case OP_GE: op = OP_LE; break;
1543 case OP_LT: op = OP_GT; break;
1544 case OP_LE: op = OP_GE; break;
1545 } // endswitch op
1546
1547 } // endif
1548
1549 xarg1 = sfr[0]->GetParms(n1);
1550
1551 if (n == 8) {
1552 vlp = (LSTVAL*)arg[1]->GetValue();
1553 n2 = vlp->GetN();
1554 xp[1] = new(g) CONSTANT((PVAL)NULL);
1555 } else
1556 xarg2 = sfr[1]->GetParms(n2);
1557
1558 if (n1 != n2)
1559 return NULL; // Should we flag an error ?
1560
1561 fp = (PBF*)PlugSubAlloc(g, NULL, n1 * sizeof(PBF));
1562
1563 for (i = 0; i < n1; i++) {
1564 xp[0] = xarg1[i];
1565
1566 if (n == 8)
1567 ((CONSTANT*)xp[1])->SetValue(vlp->GetSubVal(i));
1568 else
1569 xp[1] = xarg2[i];
1570
1571 b[0] = b[1] = (xp[0]->GetResultType() != xp[1]->GetResultType());
1572 ok |= ((fp[i] = CheckBlockFilari(g, xp, op, b)) != NULL);
1573 } // endfor i
1574
1575 if (ok)
1576 bfp = new(g) BLKFILLOG(this, OP_AND, fp, n1);
1577 #endif // 0
1578
1579 } // endif n
1580
1581 return bfp;
1582 } // end of CheckBlockFilari
1583
1584 /***********************************************************************/
1585 /* ResetBlkFil: reset the block filter and restore filtering, or make */
1586 /* the block filter if To_Filter was not set when opening the table. */
1587 /***********************************************************************/
ResetBlockFilter(PGLOBAL g)1588 void TDBDOS::ResetBlockFilter(PGLOBAL g)
1589 {
1590 if (!To_BlkFil) {
1591 if (To_Filter)
1592 if ((To_BlkFil = InitBlockFilter(g, To_Filter))) {
1593 htrc("BlkFil=%p\n", To_BlkFil);
1594 MaxSize = -1; // To be recalculated
1595 } // endif To_BlkFil
1596
1597 return;
1598 } // endif To_BlkFil
1599
1600 To_BlkFil->Reset(g);
1601
1602 if (SavFil && !To_Filter) {
1603 // Restore filter if it was disabled by optimization
1604 To_Filter = SavFil;
1605 SavFil = NULL;
1606 } // endif
1607
1608 Beval = 0;
1609 } // end of ResetBlockFilter
1610
1611 /***********************************************************************/
1612 /* Block optimization: evaluate the block index filter against */
1613 /* the min and max values of this block and return: */
1614 /* RC_OK: if some records in the block can meet filter criteria. */
1615 /* RC_NF: if no record in the block can meet filter criteria. */
1616 /* RC_EF: if no record in the remaining file can meet filter criteria.*/
1617 /* In addition, temporarily supress filtering if all the records in */
1618 /* the block meet filter criteria. */
1619 /***********************************************************************/
TestBlock(PGLOBAL g)1620 int TDBDOS::TestBlock(PGLOBAL g)
1621 {
1622 int rc = RC_OK;
1623
1624 if (To_BlkFil && Beval != 2) {
1625 // Check for block filtering evaluation
1626 if (Beval == 1) {
1627 // Filter was removed for last block, restore it
1628 To_Filter = SavFil;
1629 SavFil = NULL;
1630 } // endif Beval
1631
1632 // Check for valid records in new block
1633 switch (Beval = To_BlkFil->BlockEval(g)) {
1634 case -2: // No more valid values in file
1635 rc = RC_EF;
1636 break;
1637 case -1: // No valid values in block
1638 rc = RC_NF;
1639 break;
1640 case 1: // All block values are valid
1641 case 2: // All subsequent file values are Ok
1642 // Before suppressing the filter for the block(s) it is
1643 // necessary to reset the filtered columns to NOT_READ
1644 // so their new values are retrieved by the SELECT list.
1645 if (To_Filter) // Can be NULL when externally called (XDB)
1646 To_Filter->Reset();
1647
1648 SavFil = To_Filter;
1649 To_Filter = NULL; // So remove filter
1650 } // endswitch Beval
1651
1652 if (trace(1))
1653 htrc("BF Eval Beval=%d\n", Beval);
1654
1655 } // endif To_BlkFil
1656
1657 return rc;
1658 } // end of TestBlock
1659
1660 /***********************************************************************/
1661 /* Check whether we have to create/update permanent indexes. */
1662 /***********************************************************************/
MakeIndex(PGLOBAL g,PIXDEF pxdf,bool add)1663 int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
1664 {
1665 int k, n, rc = RC_OK;
1666 bool fixed, doit, sep, b = (pxdf != NULL);
1667 PCOL *keycols, colp;
1668 PIXDEF xdp, sxp = NULL;
1669 PKPDEF kdp;
1670 PDOSDEF dfp;
1671 //PCOLDEF cdp;
1672 PXINDEX x;
1673 PXLOAD pxp;
1674
1675 Mode = MODE_READ;
1676 Use = USE_READY;
1677 dfp = (PDOSDEF)To_Def;
1678
1679 if (!Cardinality(g)) {
1680 // Void table erase eventual index file(s)
1681 (void)dfp->DeleteIndexFile(g, NULL);
1682 return RC_OK;
1683 } else
1684 fixed = Ftype != RECFM_VAR;
1685
1686 // Are we are called from CreateTable or CreateIndex?
1687 if (pxdf) {
1688 if (!add && dfp->GetIndx()) {
1689 strcpy(g->Message, MSG(INDX_EXIST_YET));
1690 return RC_FX;
1691 } // endif To_Indx
1692
1693 if (add && dfp->GetIndx()) {
1694 for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext())
1695 if (!stricmp(sxp->GetName(), pxdf->GetName())) {
1696 sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name);
1697 return RC_FX;
1698 } else if (!sxp->GetNext())
1699 break;
1700
1701 sxp->SetNext(pxdf);
1702 // first = false;
1703 } else
1704 dfp->SetIndx(pxdf);
1705
1706 // pxdf->SetDef(dfp);
1707 } else if (!(pxdf = dfp->GetIndx()))
1708 return RC_INFO; // No index to make
1709
1710 try {
1711 // Allocate all columns that will be used by indexes.
1712 // This must be done before opening the table so specific
1713 // column initialization can be done (in particular by TDBVCT)
1714 for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext())
1715 for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
1716 if (!(colp = ColDB(g, kdp->GetName(), 0))) {
1717 sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name);
1718 goto err;
1719 } else if (colp->GetResultType() == TYPE_DECIM) {
1720 sprintf(g->Message, "Decimal columns are not indexable yet");
1721 goto err;
1722 } // endif Type
1723
1724 colp->InitValue(g);
1725 n = MY_MAX(n, xdp->GetNparts());
1726 } // endfor kdp
1727
1728 keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL));
1729 sep = dfp->GetBoolCatInfo("SepIndex", false);
1730
1731 /*********************************************************************/
1732 /* Construct and save the defined indexes. */
1733 /*********************************************************************/
1734 for (xdp = pxdf; xdp; xdp = xdp->GetNext())
1735 if (!OpenDB(g)) {
1736 if (xdp->IsAuto() && fixed)
1737 // Auto increment key and fixed file: use an XXROW index
1738 continue; // XXROW index doesn't need to be made
1739
1740 // On Update, redo only indexes that are modified
1741 doit = !To_SetCols;
1742 n = 0;
1743
1744 if (sxp)
1745 xdp->SetID(sxp->GetID() + 1);
1746
1747 for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
1748 // Check whether this column was updated
1749 for (colp = To_SetCols; !doit && colp; colp = colp->GetNext())
1750 if (!stricmp(kdp->GetName(), colp->GetName()))
1751 doit = true;
1752
1753 keycols[n++] = ColDB(g, kdp->GetName(), 0);
1754 } // endfor kdp
1755
1756 // If no indexed columns were updated, don't remake the index
1757 // if indexes are in separate files.
1758 if (!doit && sep)
1759 continue;
1760
1761 k = xdp->GetNparts();
1762
1763 // Make the index and save it
1764 if (dfp->Huge)
1765 pxp = new(g) XHUGE;
1766 else
1767 pxp = new(g) XFILE;
1768
1769 if (k == 1) // Simple index
1770 x = new(g) XINDXS(this, xdp, pxp, keycols);
1771 else // Multi-Column index
1772 x = new(g) XINDEX(this, xdp, pxp, keycols);
1773
1774 if (!x->Make(g, sxp)) {
1775 // Retreive define values from the index
1776 xdp->SetMaxSame(x->GetMaxSame());
1777 // xdp->SetSize(x->GetSize());
1778
1779 // store KXYCOL Mxs in KPARTDEF Mxsame
1780 xdp->SetMxsame(x);
1781
1782 #if defined(TRACE)
1783 printf("Make done...\n");
1784 #endif // TRACE
1785
1786 // if (x->GetSize() > 0)
1787 sxp = xdp;
1788
1789 xdp->SetInvalid(false);
1790 } else
1791 goto err;
1792
1793 } else
1794 return RC_INFO; // Error or Physical table does not exist
1795
1796 } catch (int n) {
1797 if (trace(1))
1798 htrc("Exception %d: %s\n", n, g->Message);
1799 rc = RC_FX;
1800 } catch (const char *msg) {
1801 strcpy(g->Message, msg);
1802 rc = RC_FX;
1803 } // end catch
1804
1805 if (Use == USE_OPEN)
1806 CloseDB(g);
1807
1808 return rc;
1809
1810 err:
1811 if (sxp)
1812 sxp->SetNext(NULL);
1813 else
1814 dfp->SetIndx(NULL);
1815
1816 return RC_FX;
1817 } // end of MakeIndex
1818
1819 /***********************************************************************/
1820 /* Make a dynamic index. */
1821 /***********************************************************************/
InitialyzeIndex(PGLOBAL g,volatile PIXDEF xdp,bool sorted)1822 bool TDBDOS::InitialyzeIndex(PGLOBAL g, volatile PIXDEF xdp, bool sorted)
1823 {
1824 int k;
1825 volatile bool dynamic;
1826 bool brc;
1827 PCOL colp;
1828 PCOLDEF cdp;
1829 PVAL valp;
1830 PXLOAD pxp;
1831 volatile PKXBASE kxp;
1832 PKPDEF kdp;
1833
1834 if (!xdp && !(xdp = To_Xdp)) {
1835 strcpy(g->Message, "NULL dynamic index");
1836 return true;
1837 } else
1838 dynamic = To_Filter && xdp->IsUnique() && xdp->IsDynamic();
1839 // dynamic = To_Filter && xdp->IsDynamic(); NIY
1840
1841 // Allocate the key columns definition block
1842 Knum = xdp->GetNparts();
1843 To_Key_Col = (PCOL*)PlugSubAlloc(g, NULL, Knum * sizeof(PCOL));
1844
1845 // Get the key column description list
1846 for (k = 0, kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext())
1847 if (!(colp = ColDB(g, kdp->GetName(), 0)) || colp->InitValue(g)) {
1848 sprintf(g->Message, "Wrong column %s", kdp->GetName());
1849 return true;
1850 } else
1851 To_Key_Col[k++] = colp;
1852
1853 #if defined(_DEBUG)
1854 if (k != Knum) {
1855 sprintf(g->Message, "Key part number mismatch for %s",
1856 xdp->GetName());
1857 return 0;
1858 } // endif k
1859 #endif // _DEBUG
1860
1861 // Allocate the pseudo constants that will contain the key values
1862 To_Link = (PXOB*)PlugSubAlloc(g, NULL, Knum * sizeof(PXOB));
1863
1864 for (k = 0, kdp = xdp->GetToKeyParts(); kdp; k++, kdp = kdp->GetNext()) {
1865 if ((cdp = Key(k)->GetCdp()))
1866 valp = AllocateValue(g, cdp->GetType(), cdp->GetLength());
1867 else { // Special column ?
1868 colp = Key(k);
1869 valp = AllocateValue(g, colp->GetResultType(), colp->GetLength());
1870 } // endif cdp
1871
1872 To_Link[k]= new(g) CONSTANT(valp);
1873 } // endfor k
1874
1875 // Make the index on xdp
1876 if (!xdp->IsAuto()) {
1877 if (!dynamic) {
1878 if (((PDOSDEF)To_Def)->Huge)
1879 pxp = new(g) XHUGE;
1880 else
1881 pxp = new(g) XFILE;
1882
1883 } else
1884 pxp = NULL;
1885
1886 if (Knum == 1) // Single index
1887 kxp = new(g) XINDXS(this, xdp, pxp, To_Key_Col, To_Link);
1888 else // Multi-Column index
1889 kxp = new(g) XINDEX(this, xdp, pxp, To_Key_Col, To_Link);
1890
1891 } else // Column contains same values as ROWID
1892 kxp = new(g) XXROW(this);
1893
1894 try {
1895 if (dynamic) {
1896 ResetBlockFilter(g);
1897 kxp->SetDynamic(dynamic);
1898 brc = kxp->Make(g, xdp);
1899 } else
1900 brc = kxp->Init(g);
1901
1902 if (!brc) {
1903 if (Txfp->GetAmType() == TYPE_AM_BLK) {
1904 // Cannot use indexing in DOS block mode
1905 Txfp = new(g) DOSFAM((PBLKFAM)Txfp, (PDOSDEF)To_Def);
1906 Txfp->AllocateBuffer(g);
1907 To_BlkFil = NULL;
1908 } // endif AmType
1909
1910 To_Kindex= kxp;
1911
1912 if (!(sorted && To_Kindex->IsSorted()) &&
1913 ((Mode == MODE_UPDATE && IsUsingTemp(g)) ||
1914 (Mode == MODE_DELETE && Txfp->GetAmType() != TYPE_AM_DBF)))
1915 Indxd = true;
1916
1917 } // endif brc
1918
1919 } catch (int n) {
1920 if (trace(1))
1921 htrc("Exception %d: %s\n", n, g->Message);
1922 brc = true;
1923 } catch (const char *msg) {
1924 strcpy(g->Message, msg);
1925 brc = true;
1926 } // end catch
1927
1928 return brc;
1929 } // end of InitialyzeIndex
1930
1931 /***********************************************************************/
1932 /* DOS GetProgMax: get the max value for progress information. */
1933 /***********************************************************************/
GetProgMax(PGLOBAL g)1934 int TDBDOS::GetProgMax(PGLOBAL g)
1935 {
1936 return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g);
1937 } // end of GetProgMax
1938
1939 /***********************************************************************/
1940 /* DOS GetProgCur: get the current value for progress information. */
1941 /***********************************************************************/
GetProgCur(void)1942 int TDBDOS::GetProgCur(void)
1943 {
1944 return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos();
1945 } // end of GetProgCur
1946
1947 /***********************************************************************/
1948 /* RowNumber: return the ordinal number of the current row. */
1949 /***********************************************************************/
RowNumber(PGLOBAL g,bool)1950 int TDBDOS::RowNumber(PGLOBAL g, bool)
1951 {
1952 if (To_Kindex) {
1953 /*******************************************************************/
1954 /* Don't know how to retrieve RowID from file address. */
1955 /*******************************************************************/
1956 sprintf(g->Message, MSG(NO_ROWID_FOR_AM),
1957 GetAmName(g, Txfp->GetAmType()));
1958 return 0;
1959 } else
1960 return Txfp->GetRowID();
1961
1962 } // end of RowNumber
1963
1964 /***********************************************************************/
1965 /* DOS Cardinality: returns table cardinality in number of rows. */
1966 /* This function can be called with a null argument to test the */
1967 /* availability of Cardinality implementation (1 yes, 0 no). */
1968 /***********************************************************************/
Cardinality(PGLOBAL g)1969 int TDBDOS::Cardinality(PGLOBAL g)
1970 {
1971 int n = Txfp->Cardinality(NULL);
1972
1973 if (!g)
1974 return (Mode == MODE_ANY) ? 1 : n;
1975
1976 if (Cardinal < 0) {
1977 if (!Txfp->Blocked && n == 0) {
1978 // Info command, we try to return exact row number
1979 PDOSDEF dfp = (PDOSDEF)To_Def;
1980 PIXDEF xdp = dfp->To_Indx;
1981
1982 if (xdp && xdp->IsValid()) {
1983 // Cardinality can be retreived from one index
1984 PXLOAD pxp;
1985
1986 if (dfp->Huge)
1987 pxp = new(g) XHUGE;
1988 else
1989 pxp = new(g) XFILE;
1990
1991 PXINDEX kxp = new(g) XINDEX(this, xdp, pxp, NULL, NULL);
1992
1993 if (!(kxp->GetAllSizes(g, Cardinal)))
1994 return Cardinal;
1995
1996 } // endif Mode
1997
1998 if (Mode == MODE_ANY && ExactInfo()) {
1999 // Using index impossible or failed, do it the hard way
2000 Mode = MODE_READ;
2001 To_Line = (char*)PlugSubAlloc(g, NULL, (size_t)Lrecl + 1);
2002
2003 if (Txfp->OpenTableFile(g))
2004 return (Cardinal = Txfp->Cardinality(g));
2005
2006 for (Cardinal = 0; n != RC_EF;)
2007 if (!(n = Txfp->ReadBuffer(g)))
2008 Cardinal++;
2009
2010 Txfp->CloseTableFile(g, false);
2011 Mode = MODE_ANY;
2012 } else {
2013 // Return the best estimate
2014 int len = GetFileLength(g);
2015
2016 if (len >= 0) {
2017 int rec;
2018
2019 if (trace(1))
2020 htrc("Estimating lines len=%d ending=%d/n",
2021 len, ((PDOSDEF)To_Def)->Ending);
2022
2023 /*************************************************************/
2024 /* Estimate the number of lines in the table (if not known) */
2025 /* by dividing the file length by the average record length. */
2026 /*************************************************************/
2027 rec = ((PDOSDEF)To_Def)->Ending;
2028
2029 if (AvgLen <= 0) // No given average estimate
2030 rec += EstimatedLength();
2031 else // An estimate was given for the average record length
2032 rec += AvgLen;
2033
2034 Cardinal = (len + rec - 1) / rec;
2035
2036 if (trace(1))
2037 htrc("avglen=%d MaxSize%d\n", rec, Cardinal);
2038
2039 } // endif len
2040
2041 } // endif Mode
2042
2043 } else
2044 Cardinal = Txfp->Cardinality(g);
2045
2046 } // endif Cardinal
2047
2048 return Cardinal;
2049 } // end of Cardinality
2050
2051 /***********************************************************************/
2052 /* DOS GetMaxSize: returns file size estimate in number of lines. */
2053 /* This function covers variable record length files. */
2054 /***********************************************************************/
GetMaxSize(PGLOBAL g)2055 int TDBDOS::GetMaxSize(PGLOBAL g)
2056 {
2057 if (MaxSize >= 0)
2058 return MaxSize;
2059
2060 if (!Cardinality(NULL)) {
2061 int len = GetFileLength(g);
2062
2063 if (len >= 0) {
2064 int rec;
2065
2066 if (trace(1))
2067 htrc("Estimating lines len=%d ending=%d/n",
2068 len, ((PDOSDEF)To_Def)->Ending);
2069
2070 /*****************************************************************/
2071 /* Estimate the number of lines in the table (if not known) by */
2072 /* dividing the file length by minimum record length. */
2073 /*****************************************************************/
2074 rec = EstimatedLength() + ((PDOSDEF)To_Def)->Ending;
2075 MaxSize = (len + rec - 1) / rec;
2076
2077 if (trace(1))
2078 htrc("avglen=%d MaxSize%d\n", rec, MaxSize);
2079
2080 } // endif len
2081
2082 } else
2083 MaxSize = Cardinality(g);
2084
2085 return MaxSize;
2086 } // end of GetMaxSize
2087
2088 /***********************************************************************/
2089 /* DOS EstimatedLength. Returns an estimated minimum line length. */
2090 /***********************************************************************/
EstimatedLength(void)2091 int TDBDOS::EstimatedLength(void)
2092 {
2093 int dep = 0;
2094 PCOLDEF cdp = To_Def->GetCols();
2095
2096 if (!cdp->GetNext()) {
2097 // One column table, we are going to return a ridiculous
2098 // result if we set dep to 1
2099 dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
2100 } else for (; cdp; cdp = cdp->GetNext())
2101 if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL)))
2102 dep = MY_MAX(dep, cdp->GetOffset());
2103
2104 return (int)dep;
2105 } // end of Estimated Length
2106
2107 /***********************************************************************/
2108 /* DOS tables favor the use temporary files for Update. */
2109 /***********************************************************************/
IsUsingTemp(PGLOBAL)2110 bool TDBDOS::IsUsingTemp(PGLOBAL)
2111 {
2112 USETEMP utp = UseTemp();
2113
2114 return (utp == TMP_YES || utp == TMP_FORCE ||
2115 (utp == TMP_AUTO && Mode == MODE_UPDATE));
2116 } // end of IsUsingTemp
2117
2118 /***********************************************************************/
2119 /* DOS Access Method opening routine. */
2120 /* New method now that this routine is called recursively (last table */
2121 /* first in reverse order): index blocks are immediately linked to */
2122 /* join block of next table if it exists or else are discarted. */
2123 /***********************************************************************/
OpenDB(PGLOBAL g)2124 bool TDBDOS::OpenDB(PGLOBAL g)
2125 {
2126 if (trace(1))
2127 htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
2128 this, Tdb_No, Use, Mode);
2129
2130 if (Use == USE_OPEN) {
2131 /*******************************************************************/
2132 /* Table already open, just replace it at its beginning. */
2133 /*******************************************************************/
2134 if (!To_Kindex) {
2135 Txfp->Rewind(); // see comment in Work.log
2136
2137 if (SkipHeader(g))
2138 return true;
2139
2140 } else
2141 /*****************************************************************/
2142 /* Table is to be accessed through a sorted index table. */
2143 /*****************************************************************/
2144 To_Kindex->Reset();
2145
2146 ResetBlockFilter(g);
2147 return false;
2148 } // endif use
2149
2150 if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS
2151 #if defined(BSON_SUPPORT)
2152 && Txfp->GetAmType() != TYPE_AM_BIN
2153 #endif // BSON_SUPPORT
2154 && Txfp->GetAmType() != TYPE_AM_MGO) {
2155 // Delete all lines. Not handled in MAP or block mode
2156 Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
2157 Txfp->SetTdbp(this);
2158 } else if (Txfp->Blocked && (Mode == MODE_DELETE ||
2159 (Mode == MODE_UPDATE && UseTemp() != TMP_NO))) {
2160 /*******************************************************************/
2161 /* Delete is not currently handled in block mode neither Update */
2162 /* when using a temporary file. */
2163 /*******************************************************************/
2164 if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE)
2165 Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
2166 #if defined(GZ_SUPPORT)
2167 else if (Txfp->GetAmType() == TYPE_AM_GZ)
2168 Txfp = new(g) GZFAM((PDOSDEF)To_Def);
2169 #endif // GZ_SUPPORT
2170 else // if (Txfp->GetAmType() != TYPE_AM_DOS) ???
2171 Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
2172
2173 Txfp->SetTdbp(this);
2174 } // endif Mode
2175
2176 /*********************************************************************/
2177 /* Open according to logical input/output mode required. */
2178 /* Use conventionnal input/output functions. */
2179 /* Treat files as binary in Delete mode (for line moving) */
2180 /*********************************************************************/
2181 if (Txfp->OpenTableFile(g))
2182 return true;
2183
2184 Use = USE_OPEN; // Do it now in case we are recursively called
2185
2186 /*********************************************************************/
2187 /* Allocate the block filter tree if evaluation is possible. */
2188 /*********************************************************************/
2189 To_BlkFil = InitBlockFilter(g, To_Filter);
2190
2191 /*********************************************************************/
2192 /* Lrecl does not include line ending */
2193 /*********************************************************************/
2194 size_t linelen = Lrecl + ((PDOSDEF)To_Def)->Ending + 1;
2195
2196 To_Line = (char*)PlugSubAlloc(g, NULL, linelen);
2197
2198 if (Mode == MODE_INSERT) {
2199 // Spaces between fields must be filled with blanks
2200 memset(To_Line, ' ', Lrecl);
2201 To_Line[Lrecl] = '\0';
2202 } else
2203 memset(To_Line, 0, linelen);
2204
2205 if (trace(1))
2206 htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
2207
2208 if (SkipHeader(g)) // When called from CSV/FMT files
2209 return true;
2210
2211 /*********************************************************************/
2212 /* Reset statistics values. */
2213 /*********************************************************************/
2214 num_read = num_there = num_eq[0] = num_eq[1] = 0;
2215 return false;
2216 } // end of OpenDB
2217
2218 /***********************************************************************/
2219 /* ReadDB: Data Base read routine for DOS access method. */
2220 /***********************************************************************/
ReadDB(PGLOBAL g)2221 int TDBDOS::ReadDB(PGLOBAL g)
2222 {
2223 if (trace(2))
2224 htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n",
2225 GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line);
2226
2227 if (To_Kindex) {
2228 /*******************************************************************/
2229 /* Reading is by an index table. */
2230 /*******************************************************************/
2231 int recpos = To_Kindex->Fetch(g);
2232
2233 switch (recpos) {
2234 case -1: // End of file reached
2235 return RC_EF;
2236 case -2: // No match for join
2237 return RC_NF;
2238 case -3: // Same record as non null last one
2239 num_there++;
2240 return RC_OK;
2241 default:
2242 /***************************************************************/
2243 /* Set the file position according to record to read. */
2244 /***************************************************************/
2245 if (SetRecpos(g, recpos))
2246 return RC_FX;
2247
2248 if (trace(2))
2249 htrc("File position is now %d\n", GetRecpos());
2250
2251 if (Mode == MODE_READ)
2252 /*************************************************************/
2253 /* Defer physical reading until one column setting needs it */
2254 /* as it can be a big saving on joins where no other column */
2255 /* than the keys are used, so reading is unnecessary. */
2256 /*************************************************************/
2257 if (Txfp->DeferReading())
2258 return RC_OK;
2259
2260 } // endswitch recpos
2261
2262 } // endif To_Kindex
2263
2264 if (trace(2))
2265 htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line);
2266
2267 /*********************************************************************/
2268 /* Now start the reading process. */
2269 /*********************************************************************/
2270 return ReadBuffer(g);
2271 } // end of ReadDB
2272
2273 /***********************************************************************/
2274 /* PrepareWriting: Prepare the line to write. */
2275 /***********************************************************************/
PrepareWriting(PGLOBAL)2276 bool TDBDOS::PrepareWriting(PGLOBAL)
2277 {
2278 if (Ftype == RECFM_VAR && (Mode == MODE_INSERT || Txfp->GetUseTemp())) {
2279 char *p;
2280
2281 /*******************************************************************/
2282 /* Suppress trailing blanks. */
2283 /* Also suppress eventual null from last line. */
2284 /*******************************************************************/
2285 for (p = To_Line + Lrecl -1; p >= To_Line; p--)
2286 if (*p && *p != ' ')
2287 break;
2288
2289 *(++p) = '\0';
2290 } // endif Mode
2291
2292 return false;
2293 } // end of PrepareWriting
2294
2295 /***********************************************************************/
2296 /* WriteDB: Data Base write routine for DOS access method. */
2297 /***********************************************************************/
WriteDB(PGLOBAL g)2298 int TDBDOS::WriteDB(PGLOBAL g)
2299 {
2300 if (trace(2))
2301 htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);
2302
2303 // Make the line to write
2304 if (PrepareWriting(g))
2305 return RC_FX;
2306
2307 if (trace(2))
2308 htrc("Write: line is='%s'\n", To_Line);
2309
2310 // Now start the writing process
2311 return Txfp->WriteBuffer(g);
2312 } // end of WriteDB
2313
2314 /***********************************************************************/
2315 /* Data Base delete line routine for DOS (and FIX) access method. */
2316 /* RC_FX means delete all. Nothing to do here (was done at open). */
2317 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)2318 int TDBDOS::DeleteDB(PGLOBAL g, int irc)
2319 {
2320 return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc);
2321 } // end of DeleteDB
2322
2323 /***********************************************************************/
2324 /* Data Base close routine for DOS access method. */
2325 /***********************************************************************/
CloseDB(PGLOBAL g)2326 void TDBDOS::CloseDB(PGLOBAL g)
2327 {
2328 if (To_Kindex) {
2329 To_Kindex->Close();
2330 To_Kindex = NULL;
2331 } // endif
2332
2333 Txfp->CloseTableFile(g, Abort);
2334 RestoreNrec();
2335 } // end of CloseDB
2336
2337 // ------------------------ DOSCOL functions ----------------------------
2338
2339 /***********************************************************************/
2340 /* DOSCOL public constructor (also called by MAPCOL). */
2341 /***********************************************************************/
DOSCOL(PGLOBAL g,PCOLDEF cdp,PTDB tp,PCOL cp,int i,PCSZ am)2342 DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PCSZ am)
2343 : COLBLK(cdp, tp, i)
2344 {
2345 char *p;
2346 int prec = Format.Prec;
2347 PTXF txfp = ((PTDBDOS)tp)->Txfp;
2348
2349 assert(cdp);
2350
2351 if (cp) {
2352 Next = cp->GetNext();
2353 cp->SetNext(this);
2354 } else {
2355 Next = tp->GetColumns();
2356 tp->SetColumns(this);
2357 } // endif cprec
2358
2359 // Set additional Dos access method information for column.
2360 Deplac = cdp->GetOffset();
2361 Long = cdp->GetLong();
2362 To_Val = NULL;
2363 Clustered = cdp->GetOpt();
2364 Sorted = (cdp->GetOpt() == 2) ? 1 : 0;
2365 Ndv = 0; // Currently used only for XDB2
2366 Nbm = 0; // Currently used only for XDB2
2367 Min = NULL;
2368 Max = NULL;
2369 Bmap = NULL;
2370 Dval = NULL;
2371 Buf = NULL;
2372
2373 if (txfp && txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) {
2374 int nblk = txfp->GetBlock();
2375
2376 Clustered = (cdp->GetXdb2()) ? 2 : 1;
2377 Sorted = (cdp->GetOpt() > 1) ? 1 : 0; // Currently ascending only
2378
2379 if (Clustered == 1) {
2380 Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec);
2381 Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec);
2382 } else { // Clustered == 2
2383 // Ndv is the number of distinct values in Dval. Ndv and Nbm
2384 // may be 0 when optimizing because Ndval is not filled yet,
2385 // but the size of the passed Dval memory block is Ok.
2386 Ndv = cdp->GetNdv();
2387 Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec);
2388
2389 // Bmap cannot be allocated when optimizing, we must know Nbm first
2390 if ((Nbm = cdp->GetNbm()))
2391 Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk);
2392
2393 } // endif Clustered
2394
2395 } // endif Opt
2396
2397 OldVal = NULL; // Currently used only in MinMax
2398 Dsp = 0;
2399 Ldz = false;
2400 Nod = false;
2401 Dcm = -1;
2402 p = cdp->GetFmt();
2403 Buf = NULL;
2404
2405 if (p && IsTypeNum(Buf_Type)) {
2406 // Formatted numeric value
2407 for (; p && *p && isalpha(*p); p++)
2408 switch (toupper(*p)) {
2409 case 'Z': // Have leading zeros
2410 Ldz = true;
2411 break;
2412 case 'N': // Have no decimal point
2413 Nod = true;
2414 break;
2415 case 'D': // Decimal separator
2416 Dsp = *(++p);
2417 break;
2418 } // endswitch p
2419
2420 // Set number of decimal digits
2421 Dcm = (*p) ? atoi(p) : GetScale();
2422 } // endif fmt
2423
2424 if (trace(1))
2425 htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
2426
2427 } // end of DOSCOL constructor
2428
2429 /***********************************************************************/
2430 /* DOSCOL constructor used for copying columns. */
2431 /* tdbp is the pointer to the new table descriptor. */
2432 /***********************************************************************/
DOSCOL(DOSCOL * col1,PTDB tdbp)2433 DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
2434 {
2435 Deplac = col1->Deplac;
2436 Long = col1->Long;
2437 To_Val = col1->To_Val;
2438 Ldz = col1->Ldz;
2439 Dsp = col1->Dsp;
2440 Nod = col1->Nod;
2441 Dcm = col1->Dcm;
2442 OldVal = col1->OldVal;
2443 Buf = col1->Buf;
2444 Clustered = col1->Clustered;
2445 Sorted = col1->Sorted;
2446 Min = col1->Min;
2447 Max = col1->Max;
2448 Bmap = col1->Bmap;
2449 Dval = col1->Dval;
2450 Ndv = col1->Ndv;
2451 Nbm = col1->Nbm;
2452 } // end of DOSCOL copy constructor
2453
2454 /***********************************************************************/
2455 /* VarSize: This function tells UpdateDB whether or not the block */
2456 /* optimization file must be redone if this column is updated, even */
2457 /* it is not sorted or clustered. This applies to the last column of */
2458 /* a variable length table that is blocked, because if it is updated */
2459 /* using a temporary file, the block size may be modified. */
2460 /***********************************************************************/
VarSize(void)2461 bool DOSCOL::VarSize(void)
2462 {
2463 PTDBDOS tdbp = (PTDBDOS)To_Tdb;
2464 PTXF txfp = tdbp->Txfp;
2465
2466 if (Cdp && !Cdp->GetNext() // Must be the last column
2467 && tdbp->Ftype == RECFM_VAR // of a DOS variable length
2468 && txfp->Blocked // blocked table
2469 && txfp->GetUseTemp()) // using a temporary file.
2470 return true;
2471 else
2472 return false;
2473
2474 } // end VarSize
2475
2476 /***********************************************************************/
2477 /* SetBuffer: prepare a column block for write operation. */
2478 /***********************************************************************/
SetBuffer(PGLOBAL g,PVAL value,bool ok,bool check)2479 bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
2480 {
2481 if (!(To_Val = value)) {
2482 sprintf(g->Message, MSG(VALUE_ERROR), Name);
2483 return true;
2484 } else if (Buf_Type == value->GetType()) {
2485 // Values are of the (good) column type
2486 if (Buf_Type == TYPE_DATE) {
2487 // If any of the date values is formatted
2488 // output format must be set for the receiving table
2489 if (GetDomain() || ((DTVAL *)value)->IsFormatted())
2490 goto newval; // This will make a new value;
2491
2492 } else if (Buf_Type == TYPE_DOUBLE)
2493 // Float values must be written with the correct (column) precision
2494 // Note: maybe this should be forced by ShowValue instead of this ?
2495 value->SetPrec(GetScale());
2496
2497 Value = value; // Directly access the external value
2498 } else {
2499 // Values are not of the (good) column type
2500 if (check) {
2501 sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
2502 GetTypeName(Buf_Type), GetTypeName(value->GetType()));
2503 return true;
2504 } // endif check
2505
2506 newval:
2507 if (InitValue(g)) // Allocate the matching value block
2508 return true;
2509
2510 } // endif's Value, Buf_Type
2511
2512 // Allocate the buffer used in WriteColumn for numeric columns
2513 if (!Buf && IsTypeNum(Buf_Type))
2514 Buf = (char*)PlugSubAlloc(g, NULL, MY_MAX(64, Long + 1));
2515 else // Text columns do not need additional buffer
2516 Buf = (char*)Value->GetTo_Val();
2517
2518 // Because Colblk's have been made from a copy of the original TDB in
2519 // case of Update, we must reset them to point to the original one.
2520 if (To_Tdb->GetOrig())
2521 To_Tdb = (PTDB)To_Tdb->GetOrig();
2522
2523 // Set the Column
2524 Status = (ok) ? BUF_EMPTY : BUF_NO;
2525 return false;
2526 } // end of SetBuffer
2527
2528 /***********************************************************************/
2529 /* ReadColumn: what this routine does is to access the last line */
2530 /* read from the corresponding table, extract from it the field */
2531 /* corresponding to this column and convert it to buffer type. */
2532 /***********************************************************************/
ReadColumn(PGLOBAL g)2533 void DOSCOL::ReadColumn(PGLOBAL g)
2534 {
2535 char *p = NULL;
2536 int i, rc;
2537 int field;
2538 bool err = false;
2539 double dval;
2540 PTDBDOS tdbp = (PTDBDOS)To_Tdb;
2541
2542 if (trace(2))
2543 htrc(
2544 "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
2545 Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);
2546
2547 /*********************************************************************/
2548 /* If physical reading of the line was deferred, do it now. */
2549 /*********************************************************************/
2550 if (!tdbp->IsRead())
2551 if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
2552 if (rc == RC_EF)
2553 sprintf(g->Message, MSG(INV_DEF_READ), rc);
2554
2555 throw 11;
2556 } // endif
2557
2558 p = tdbp->To_Line + Deplac;
2559 field = Long;
2560
2561 /*********************************************************************/
2562 /* For a variable length file, check if the field exists. */
2563 /*********************************************************************/
2564 if ((tdbp->Ftype == RECFM_VAR || tdbp->Ftype == RECFM_CSV)
2565 && strlen(tdbp->To_Line) < (unsigned)Deplac)
2566 field = 0;
2567 else if (Dsp)
2568 for(i = 0; i < field; i++)
2569 if (p[i] == Dsp)
2570 p[i] = '.';
2571
2572 switch (tdbp->Ftype) {
2573 case RECFM_VAR:
2574 case RECFM_FIX: // Fixed length text file
2575 case RECFM_CSV: // Variable length CSV or FMT file
2576 case RECFM_DBF: // Fixed length DBase file
2577 if (Nod) switch (Buf_Type) {
2578 case TYPE_INT:
2579 case TYPE_SHORT:
2580 case TYPE_TINY:
2581 case TYPE_BIGINT:
2582 err = Value->SetValue_char(p, field - Dcm);
2583 break;
2584 case TYPE_DOUBLE:
2585 if (!(err = Value->SetValue_char(p, field))) {
2586 dval = Value->GetFloatValue();
2587
2588 for (i = 0; i < Dcm; i++)
2589 dval /= 10.0;
2590
2591 Value->SetValue(dval);
2592 } // endif err
2593
2594 break;
2595 default:
2596 err = Value->SetValue_char(p, field);
2597
2598 if (!err && Buf_Type == TYPE_DECIM) {
2599 char* s = Value->GetCharValue();
2600
2601 if (!(err = ((i = strlen(s)) >= Value->GetClen()))) {
2602 for (int d = Dcm + 1; d; i--, d--)
2603 s[i + 1] = s[i];
2604
2605 s[i + 1] = '.';
2606 } // endif err
2607
2608 } // endif DECIM
2609
2610 break;
2611 } // endswitch Buf_Type
2612
2613 else
2614 err = Value->SetValue_char(p, field);
2615
2616 break;
2617 default:
2618 sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype);
2619 throw 34;
2620 } // endswitch Ftype
2621
2622 if (err) {
2623 sprintf(g->Message, "Out of range value for column %s at row %d",
2624 Name, tdbp->RowNumber(g));
2625 PushWarning(g, tdbp);
2626 } // endif err
2627
2628 // Set null when applicable
2629 if (Nullable)
2630 Value->SetNull(Value->IsZero());
2631
2632 } // end of ReadColumn
2633
2634 /***********************************************************************/
2635 /* WriteColumn: what this routine does is to access the last line */
2636 /* read from the corresponding table, and rewrite the field */
2637 /* corresponding to this column from the column buffer and type. */
2638 /***********************************************************************/
WriteColumn(PGLOBAL g)2639 void DOSCOL::WriteColumn(PGLOBAL g)
2640 {
2641 char *p, fmt[32];
2642 int i, k, n, len, field;
2643 PTDBDOS tdbp = (PTDBDOS)To_Tdb;
2644
2645 if (trace(2))
2646 htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
2647 Name, tdbp->GetTdb_No(), ColUse, Status);
2648
2649 p = tdbp->To_Line + Deplac;
2650
2651 if (trace(2))
2652 htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long);
2653
2654 field = Long;
2655
2656 if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) {
2657 len = (signed)strlen(tdbp->To_Line);
2658
2659 if (tdbp->IsUsingTemp(g))
2660 // Because of eventual missing field(s) the buffer must be reset
2661 memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len);
2662 else
2663 // The size actually available must be recalculated
2664 field = MY_MIN(len - Deplac, Long);
2665
2666 } // endif Ftype
2667
2668 if (trace(2))
2669 htrc("Long=%d field=%d coltype=%d colval=%p\n",
2670 Long, field, Buf_Type, Value);
2671
2672 /*********************************************************************/
2673 /* Get the string representation of Value according to column type. */
2674 /*********************************************************************/
2675 if (Value != To_Val)
2676 Value->SetValue_pval(To_Val, false); // Convert the updated value
2677
2678 /*********************************************************************/
2679 /* This test is only useful for compressed(2) tables. */
2680 /*********************************************************************/
2681 if (tdbp->Ftype != RECFM_BIN) {
2682 if (Ldz || Nod || Dcm >= 0) {
2683 switch (Buf_Type) {
2684 case TYPE_SHORT:
2685 strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd");
2686 i = 0;
2687
2688 if (Nod)
2689 for (; i < Dcm; i++)
2690 strcat(fmt, "0");
2691
2692 len = sprintf(Buf, fmt, field - i, Value->GetShortValue());
2693 break;
2694 case TYPE_INT:
2695 strcpy(fmt, (Ldz) ? "%0*d" : "%*.d");
2696 i = 0;
2697
2698 if (Nod)
2699 for (; i < Dcm; i++)
2700 strcat(fmt, "0");
2701
2702 len = sprintf(Buf, fmt, field - i, Value->GetIntValue());
2703 break;
2704 case TYPE_TINY:
2705 strcpy(fmt, (Ldz) ? "%0*d" : "%*.d");
2706 i = 0;
2707
2708 if (Nod)
2709 for (; i < Dcm; i++)
2710 strcat(fmt, "0");
2711
2712 len = sprintf(Buf, fmt, field - i, Value->GetTinyValue());
2713 break;
2714 case TYPE_DOUBLE:
2715 case TYPE_DECIM:
2716 strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf");
2717 len = field + ((Nod && Dcm) ? 1 : 0);
2718 snprintf(Buf, len + 1, fmt, len, Dcm, Value->GetFloatValue());
2719 len = strlen(Buf);
2720
2721 if (Nod && Dcm)
2722 for (i = k = 0; i < len; i++, k++)
2723 if (Buf[i] != ' ') {
2724 if (Buf[i] == '.')
2725 k++;
2726
2727 Buf[i] = Buf[k];
2728 } // endif Buf(i)
2729
2730 len = strlen(Buf);
2731 break;
2732 default:
2733 sprintf(g->Message, "Invalid field format for column %s", Name);
2734 throw 31;
2735 } // endswitch BufType
2736
2737 n = strlen(Buf);
2738 } else // Standard CONNECT format
2739 n = Value->ShowValue(Buf, field);
2740
2741 if (trace(1))
2742 htrc("new length(%p)=%d\n", Buf, n);
2743
2744 if ((len = n) > field) {
2745 char *p = Value->GetCharString(Buf);
2746
2747 sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, field);
2748 throw 31;
2749 } else if (Dsp)
2750 for (i = 0; i < len; i++)
2751 if (Buf[i] == '.')
2752 Buf[i] = Dsp;
2753
2754 if (trace(2))
2755 htrc("buffer=%s\n", Buf);
2756
2757 /*******************************************************************/
2758 /* Updating must be done only when not in checking pass. */
2759 /*******************************************************************/
2760 if (Status) {
2761 memset(p, ' ', field);
2762 memcpy(p, Buf, len);
2763
2764 if (trace(2))
2765 htrc(" col write: '%.*s'\n", len, p);
2766
2767 } // endif Status
2768
2769 } else // BIN compressed table
2770 /*******************************************************************/
2771 /* Check if updating is Ok, meaning col value is not too long. */
2772 /* Updating to be done only during the second pass (Status=true) */
2773 /*******************************************************************/
2774 if (Value->GetBinValue(p, Long, Status)) {
2775 sprintf(g->Message, MSG(BIN_F_TOO_LONG),
2776 Name, Value->GetSize(), Long);
2777 throw 31;
2778 } // endif
2779
2780 } // end of WriteColumn
2781
2782 /***********************************************************************/
2783 /* SetMinMax: Calculate minimum and maximum values for one block. */
2784 /* Note: TYPE_STRING is stored and processed with zero ended strings */
2785 /* to be matching the way the FILTER Eval function processes them. */
2786 /***********************************************************************/
SetMinMax(PGLOBAL g)2787 bool DOSCOL::SetMinMax(PGLOBAL g)
2788 {
2789 PTDBDOS tp = (PTDBDOS)To_Tdb;
2790
2791 ReadColumn(g); // Extract column value from current line
2792
2793 if (CheckSorted(g))
2794 return true;
2795
2796 if (!tp->Txfp->CurNum) {
2797 Min->SetValue(Value, tp->Txfp->CurBlk);
2798 Max->SetValue(Value, tp->Txfp->CurBlk);
2799 } else {
2800 Min->SetMin(Value, tp->Txfp->CurBlk);
2801 Max->SetMax(Value, tp->Txfp->CurBlk);
2802 } // endif CurNum
2803
2804 return false;
2805 } // end of SetMinMax
2806
2807 /***********************************************************************/
2808 /* SetBitMap: Calculate the bit map of existing values in one block. */
2809 /* Note: TYPE_STRING is processed with zero ended strings */
2810 /* to be matching the way the FILTER Eval function processes them. */
2811 /***********************************************************************/
SetBitMap(PGLOBAL g)2812 bool DOSCOL::SetBitMap(PGLOBAL g)
2813 {
2814 int i, m, n;
2815 uint *bmp;
2816 PTDBDOS tp = (PTDBDOS)To_Tdb;
2817 PDBUSER dup = PlgGetUser(g);
2818
2819 n = tp->Txfp->CurNum;
2820 bmp = (uint*)Bmap->GetValPtr(Nbm * tp->Txfp->CurBlk);
2821
2822 // Extract column value from current line
2823 ReadColumn(g);
2824
2825 if (CheckSorted(g))
2826 return true;
2827
2828 if (!n) // New block
2829 for (m = 0; m < Nbm; m++)
2830 bmp[m] = 0; // Reset the new bit map
2831
2832 if ((i = Dval->Find(Value)) < 0) {
2833 char buf[32];
2834
2835 sprintf(g->Message, MSG(DVAL_NOTIN_LIST),
2836 Value->GetCharString(buf), Name);
2837 return true;
2838 } else if (i >= dup->Maxbmp) {
2839 sprintf(g->Message, MSG(OPT_LOGIC_ERR), i);
2840 return true;
2841 } else {
2842 m = i / MAXBMP;
2843 #if defined(_DEBUG)
2844 assert (m < Nbm);
2845 #endif // _DEBUG
2846 bmp[m] |= (1 << (i % MAXBMP));
2847 } // endif's i
2848
2849 return false;
2850 } // end of SetBitMap
2851
2852 /***********************************************************************/
2853 /* Checks whether a column declared as sorted is sorted indeed. */
2854 /***********************************************************************/
CheckSorted(PGLOBAL g)2855 bool DOSCOL::CheckSorted(PGLOBAL g)
2856 {
2857 if (Sorted)
2858 if (OldVal) {
2859 // Verify whether this column is sorted all right
2860 if (OldVal->CompareValue(Value) > 0) {
2861 // Column is no more in ascending order
2862 sprintf(g->Message, MSG(COL_NOT_SORTED), Name, To_Tdb->GetName());
2863 Sorted = false;
2864 return true;
2865 } else
2866 OldVal->SetValue_pval(Value);
2867
2868 } else
2869 OldVal = AllocateValue(g, Value);
2870
2871 return false;
2872 } // end of CheckSorted
2873
2874 /***********************************************************************/
2875 /* AddDistinctValue: Check whether this value already exist in the */
2876 /* list and if not add it to the distinct values list. */
2877 /***********************************************************************/
AddDistinctValue(PGLOBAL g)2878 bool DOSCOL::AddDistinctValue(PGLOBAL g)
2879 {
2880 bool found = false;
2881 int i, m, n;
2882
2883 ReadColumn(g); // Extract column value from current line
2884
2885 // Perhaps a better algorithm can be used when Ndv gets bigger
2886 // Here we cannot use Find because we must get the index of where
2887 // to insert a new value if it is not found in the array.
2888 for (n = 0; n < Ndv; n++) {
2889 m = Dval->CompVal(Value, n);
2890
2891 if (m > 0)
2892 continue;
2893 else if (!m)
2894 found = true; // Already there
2895
2896 break;
2897 } // endfor n
2898
2899 if (!found) {
2900 // Check whether we have room for an additional value
2901 if (Ndv == Freq) {
2902 // Too many values because of wrong Freq setting
2903 sprintf(g->Message, MSG(BAD_FREQ_SET), Name);
2904 return true;
2905 } // endif Ndv
2906
2907 // New value, add it to the list before the nth value
2908 Dval->SetNval(Ndv + 1);
2909
2910 for (i = Ndv; i > n; i--)
2911 Dval->Move(i - 1, i);
2912
2913 Dval->SetValue(Value, n);
2914 Ndv++;
2915 } // endif found
2916
2917 return false;
2918 } // end of AddDistinctValue
2919
2920 /* ------------------------------------------------------------------- */
2921