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