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