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   To_Filter = NULL;                     // Disable filtering
581 //To_BlkIdx = NULL;                     // and index filtering
582   To_BlkFil = NULL;                     // and block filtering
583 
584   // After the table was modified the indexes
585   // are invalid and we should mark them as such...
586   (void)((PDOSDEF)To_Def)->InvalidateIndex(g);
587 
588   if (dop) {
589     Columns = NULL;                     // Not used anymore
590 
591     if (Txfp->Blocked) {
592       // MakeBlockValues must be executed in non blocked mode
593       // except for ZLIB access method.
594       if        (Txfp->GetAmType() == TYPE_AM_MAP) {
595         Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
596 #if defined(GZ_SUPPORT)
597       } else if (Txfp->GetAmType() == TYPE_AM_GZ) {
598         Txfp = new(g) GZFAM((PDOSDEF)To_Def);
599       } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) {
600         Txfp->Reset();
601         ((PZLBFAM)Txfp)->SetOptimized(false);
602 #endif   // GZ_SUPPORT
603 			} else if (Txfp->GetAmType() == TYPE_AM_BLK)
604         Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
605 
606       Txfp->SetTdbp(this);
607     } else
608       Txfp->Reset();
609 
610     Use = USE_READY;                    // So the table can be reopened
611     Mode = MODE_ANY;                    // Just to be clean
612     rc = MakeBlockValues(g);            // Redo optimization
613     } // endif dop
614 
615   if (dox && (rc == RC_OK || rc == RC_INFO)) {
616     // Remake eventual indexes
617 //  if (Mode != MODE_UPDATE)
618       To_SetCols = NULL;                // Positions are changed
619 
620     Columns = NULL;                     // Not used anymore
621     Txfp->Reset();                      // New start
622     Use = USE_READY;                    // So the table can be reopened
623     Mode = MODE_READ;                   // New mode
624     prc = rc;
625 
626     if (PlgGetUser(g)->Check & CHK_OPT)
627       // We must remake all indexes.
628       rc = MakeIndex(g, NULL, false);
629 
630     rc = (rc == RC_INFO) ? prc : rc;
631     } // endif dox
632 
633   return rc;
634   } // end of ResetTableOpt
635 
636 /***********************************************************************/
637 /*  Calculate the block sizes so block I/O can be used and also the    */
638 /*  Min/Max values for clustered/sorted table columns.                 */
639 /***********************************************************************/
MakeBlockValues(PGLOBAL g)640 int TDBDOS::MakeBlockValues(PGLOBAL g)
641   {
642   int        i, lg, nrec, rc, n = 0;
643   int        curnum, curblk, block, savndv, savnbm;
644   void      *savmin, *savmax;
645   bool       blocked, xdb2 = false;
646 //POOLHEADER save;
647   PCOLDEF    cdp;
648   PDOSDEF    defp = (PDOSDEF)To_Def;
649   PDOSCOL    colp = NULL;
650   PDBUSER    dup = PlgGetUser(g);
651 //void      *memp = cat->GetDescp();
652   (void) defp->GetCat();                        // XXX Should be removed?
653 
654 
655   if ((nrec = defp->GetElemt()) < 2) {
656     if (!To_Def->Partitioned()) {
657       // This may be wrong to do in some cases
658       strcpy(g->Message, MSG(TABLE_NOT_OPT));
659       return RC_INFO;                   // Not to be optimized
660     } else
661       return RC_OK;
662 
663   } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) {
664     // Suppress the opt file firstly if the table is void,
665     // secondly when it was modified with OPTIMIZATION unchecked
666     // because it is no more valid.
667     defp->RemoveOptValues(g);           // Erase opt file
668     return RC_OK;                       // void table
669   } else if (MaxSize < 0)
670     return RC_FX;
671 
672   defp->SetOptimized(0);
673 
674   // Estimate the number of needed blocks
675 	if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) {
676 		// This may be wrong to do in some cases
677 		defp->RemoveOptValues(g);
678 		strcpy(g->Message, MSG(TABLE_NOT_OPT));
679 		return RC_INFO;                   // Not to be optimized
680 	}	// endif block
681 
682   // We have to use local variables because Txfp->CurBlk is set
683   // to Rows+1 by unblocked variable length table access methods.
684   curblk = -1;
685   curnum = nrec - 1;
686 //last = 0;
687   Txfp->Block = block;                  // This is useful mainly for
688   Txfp->CurBlk = curblk;                // blocked tables (ZLBFAM), for
689   Txfp->CurNum = curnum;                // others it is just to be clean.
690 
691   /*********************************************************************/
692   /*  Allocate the array of block starting positions.                  */
693   /*********************************************************************/
694 //if (memp)
695 //  save = *(PPOOLHEADER)memp;
696 
697   Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int));
698 
699   /*********************************************************************/
700   /*  Allocate the blocks for clustered columns.                       */
701   /*********************************************************************/
702   blocked = Txfp->Blocked;         // Save
703   Txfp->Blocked = true;            // So column block can be allocated
704 
705   for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
706     if (cdp->GetOpt()) {
707       lg = cdp->GetClen();
708 
709       if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) {
710         cdp->SetXdb2(true);
711         savndv = cdp->GetNdv();
712         cdp->SetNdv(0);              // Reset Dval number of values
713         xdb2 = true;
714         savmax = cdp->GetDval();
715         cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg));
716         savnbm = cdp->GetNbm();
717         cdp->SetNbm(0);              // Prevent Bmap allocation
718 //      savmin = cdp->GetBmap();
719 //      cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int)));
720 
721         if (trace(1))
722           htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n",
723               cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg);
724 
725         // colp will be initialized with proper Dval VALBLK
726         colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
727         colp->InitValue(g);          // Allocate column value buffer
728         cdp->SetNbm(savnbm);
729 //      cdp->SetBmap(savmin);        // Can be reused if the new size
730         cdp->SetDval(savmax);        // is not greater than this one.
731         cdp->SetNdv(savndv);
732       } else {
733         cdp->SetXdb2(false);         // Maxbmp may have been reset
734         savmin = cdp->GetMin();
735         savmax = cdp->GetMax();
736         cdp->SetMin(PlugSubAlloc(g, NULL, block * lg));
737         cdp->SetMax(PlugSubAlloc(g, NULL, block * lg));
738 
739         // Valgrind complains if there are uninitialised bytes
740         // after the null character ending
741         if (IsTypeChar(cdp->GetType())) {
742           memset(cdp->GetMin(), 0, block * lg);
743           memset(cdp->GetMax(), 0, block * lg);
744           } // endif Type
745 
746         if (trace(1))
747           htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n",
748               cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg);
749 
750         // colp will be initialized with proper opt VALBLK's
751         colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
752         colp->InitValue(g);          // Allocate column value buffer
753         cdp->SetMin(savmin);         // Can be reused if the number
754         cdp->SetMax(savmax);         // of blocks does not change.
755       } // endif Freq
756 
757       } // endif Clustered
758 
759   // No optimised columns. Still useful for blocked variable tables.
760   if (!colp && defp->Recfm != RECFM_VAR) {
761     strcpy(g->Message, "No optimised columns");
762     return RC_INFO;
763     } // endif colp
764 
765   Txfp->Blocked = blocked;
766 
767   /*********************************************************************/
768   /*  Now do calculate the optimization values.                        */
769   /*********************************************************************/
770   Mode = MODE_READ;
771 
772   if (OpenDB(g))
773     return RC_FX;
774 
775   if (xdb2) {
776     /*********************************************************************/
777     /*  Retrieve the distinct values of XDB2 columns.                    */
778     /*********************************************************************/
779     if (GetDistinctColumnValues(g, nrec))
780       return RC_FX;
781 
782     OpenDB(g);                   // Rewind the table file
783     } // endif xdb2
784 
785 #if defined(PROG_INFO)
786   /*********************************************************************/
787   /*  Initialize progress information                                  */
788   /*********************************************************************/
789   char   *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name));
790 
791   dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name);
792   dup->ProgMax = GetProgMax(g);
793   dup->ProgCur = 0;
794 #endif   // SOCKET_MODE  ||         THREAD
795 
796   /*********************************************************************/
797   /*  Make block starting pos and min/max values of cluster columns.   */
798   /*********************************************************************/
799   while ((rc = ReadDB(g)) == RC_OK) {
800     if (blocked) {
801       // A blocked FAM class handles CurNum and CurBlk (ZLBFAM)
802       if (!Txfp->CurNum)
803         Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos();
804 
805     } else {
806       if (++curnum >= nrec) {
807         if (++curblk >= block) {
808           strcpy(g->Message, MSG(BAD_BLK_ESTIM));
809           goto err;
810         } else
811           curnum = 0;
812 
813         // Get block starting position
814         Txfp->BlkPos[curblk] = Txfp->GetPos();
815         } // endif CurNum
816 
817 //    last = curnum + 1;              // curnum is zero based
818       Txfp->CurBlk = curblk;          // Used in COLDOS::SetMinMax
819       Txfp->CurNum = curnum;          // Used in COLDOS::SetMinMax
820     } // endif blocked
821 
822     /*******************************************************************/
823     /*  Now calculate the min and max values for the cluster columns.  */
824     /*******************************************************************/
825     for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext())
826       if (colp->Clustered == 2) {
827         if (colp->SetBitMap(g))
828           goto err;
829 
830       } else
831         if (colp->SetMinMax(g))
832           goto err;                   // Currently: column is not sorted
833 
834 #if defined(PROG_INFO)
835     if (!dup->Step) {
836       strcpy(g->Message, MSG(OPT_CANCELLED));
837       goto err;
838     } else
839       dup->ProgCur = GetProgCur();
840 #endif   // PROG_INFO
841 
842     n++;           // Used to calculate block and last
843     } // endwhile
844 
845   if (rc == RC_EF) {
846     Txfp->Nrec = nrec;
847 
848 #if 0 // No good because Curblk and CurNum after EOF are different
849       // depending on whether the file is mapped or not mapped.
850     if (blocked) {
851 //    Txfp->Block = Txfp->CurBlk + 1;
852       Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum : nrec;
853 //    Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec;
854       Txfp->Block = Txfp->CurBlk + (Txfp->Last == nrec ? 0 : 1);
855     } else {
856       Txfp->Block = curblk + 1;
857       Txfp->Last = last;
858     } // endif blocked
859 #endif // 0
860 
861     // New values of Block and Last
862     Txfp->Block = (n + nrec - 1) / nrec;
863     Txfp->Last = (n % nrec) ? (n % nrec) : nrec;
864 
865     // This is needed to be able to calculate the last block size
866     Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos();
867   } else
868     goto err;
869 
870   /*********************************************************************/
871   /*  Save the optimization values for this table.                     */
872   /*********************************************************************/
873   if (!SaveBlockValues(g)) {
874     defp->Block = Txfp->Block;
875     defp->Last = Txfp->Last;
876     CloseDB(g);
877     defp->SetIntCatInfo("Blocks", Txfp->Block);
878     defp->SetIntCatInfo("Last", Txfp->Last);
879     return RC_OK;
880     } // endif SaveBlockValues
881 
882  err:
883   // Restore Desc memory suballocation
884 //if (memp)
885 //  *(PPOOLHEADER)memp = save;
886 
887   defp->RemoveOptValues(g);
888   CloseDB(g);
889   return RC_FX;
890   } // end of MakeBlockValues
891 
892 /***********************************************************************/
893 /*  Save the block and Min/Max values for this table.                  */
894 /*  The problem here is to avoid name duplication, because more than   */
895 /*  one data file can have the same name (but different types) and/or  */
896 /*  the same data file can be used with different block sizes. This is */
897 /*  why we use Ofn that defaults to the file name but can be set to a  */
898 /*  different name if necessary.                                       */
899 /***********************************************************************/
SaveBlockValues(PGLOBAL g)900 bool TDBDOS::SaveBlockValues(PGLOBAL g)
901   {
902   char    filename[_MAX_PATH];
903   int     lg, n[NZ + 2];
904   size_t  nbk, ndv, nbm, block = Txfp->Block;
905   bool    rc = false;
906   FILE   *opfile;
907   PDOSCOL colp;
908   PDOSDEF defp = (PDOSDEF)To_Def;
909 
910   if (defp->GetOptFileName(g, filename))
911     return true;
912 
913   if (!(opfile = fopen(filename, "wb"))) {
914     sprintf(g->Message, MSG(OPEN_MODE_ERROR),
915             "wb", (int)errno, filename);
916     strcat(strcat(g->Message, ": "), strerror(errno));
917 
918     if (trace(1))
919       htrc("%s\n", g->Message);
920 
921     return true;
922     } // endif opfile
923 
924   memset(n, 0, sizeof(n));     // To avoid valgrind warning
925 
926   if (Ftype == RECFM_VAR || defp->Compressed == 2) {
927     /*******************************************************************/
928     /*  Write block starting positions into the opt file.              */
929     /*******************************************************************/
930     block++;
931     lg = sizeof(int);
932     n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block;
933 
934     if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
935       sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
936       rc = true;
937       } // endif size
938 
939     if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) {
940       sprintf(g->Message, MSG(OPTBLK_WR_ERR), strerror(errno));
941       rc = true;
942       } // endif size
943 
944     block--;                       // = Txfp->Block;
945     } // endif Ftype
946 
947   /*********************************************************************/
948   /*  Write the Min/Max values into the opt file.                      */
949   /*********************************************************************/
950   for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) {
951     lg = colp->Value->GetClen();
952 
953     //  Now start the writing process
954     if (colp->Clustered == 2) {
955       // New XDB2 block optimization. Will be recognized when reading
956       // because the column index is negated.
957       ndv = colp->Ndv; nbm = colp->Nbm;
958       nbk = nbm * block;
959       n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
960       n[4] = ndv; n[5] = nbm;
961 
962       if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) {
963         sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
964         rc = true;
965         } // endif size
966 
967       if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) {
968         sprintf(g->Message, MSG(OPT_DVAL_WR_ERR), strerror(errno));
969         rc = true;
970         } // endif size
971 
972       if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) {
973         sprintf(g->Message, MSG(OPT_BMAP_WR_ERR), strerror(errno));
974         rc = true;
975         } // endif size
976 
977     } else {
978       n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
979 
980       if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
981         sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno));
982         rc = true;
983         } // endif size
984 
985       if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) {
986         sprintf(g->Message, MSG(OPT_MIN_WR_ERR), strerror(errno));
987         rc = true;
988         } // endif size
989 
990       if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) {
991         sprintf(g->Message, MSG(OPT_MAX_WR_ERR), strerror(errno));
992         rc = true;
993         } // endif size
994 
995     } // endif Clustered
996 
997     } // endfor colp
998 
999   fclose(opfile);
1000   return rc;
1001   } // end of SaveBlockValues
1002 
1003 /***********************************************************************/
1004 /*  Read the Min/Max values for this table.                            */
1005 /*  The problem here is to avoid name duplication, because more than   */
1006 /*  one data file can have the same name (but different types) and/or  */
1007 /*  the same data file can be used with different block sizes. This is */
1008 /*  why we use Ofn that defaults to the file name but can be set to a  */
1009 /*  different name if necessary.                                       */
1010 /***********************************************************************/
GetBlockValues(PGLOBAL g)1011 bool TDBDOS::GetBlockValues(PGLOBAL g)
1012   {
1013   char       filename[_MAX_PATH];
1014   int        i, lg, n[NZ];
1015   int        nrec, block = 0, last = 0;
1016   int        len;
1017   bool       newblk = false;
1018   size_t     ndv, nbm, nbk, blk;
1019   FILE      *opfile;
1020   PCOLDEF    cdp;
1021   PDOSDEF    defp = (PDOSDEF)To_Def;
1022   PDBUSER    dup = PlgGetUser(g);
1023 
1024   (void) defp->GetCat();                        // XXX Should be removed?
1025 
1026 
1027 #if 0
1028   if (Mode == MODE_INSERT && Txfp->GetAmType() == TYPE_AM_DOS)
1029     return false;
1030 #endif   // _WIN32
1031 
1032 	if (defp->Optimized || !(dup->Check & CHK_OPT))
1033     return false;                   // Already done or to be redone
1034 
1035   if (Ftype == RECFM_VAR || defp->Compressed == 2) {
1036     /*******************************************************************/
1037     /*  Variable length file that can be read by block.                */
1038     /*******************************************************************/
1039     nrec = (defp->GetElemt()) ? defp->GetElemt() : 1;
1040 
1041     if (nrec > 1) {
1042       // The table can be declared optimized if it is void.
1043       // This is useful to handle Insert in optimized mode.
1044       char filename[_MAX_PATH];
1045       int  h;
1046       int  flen = -1;
1047 
1048       PlugSetPath(filename, defp->Fn, GetPath());
1049       h = open(filename, O_RDONLY);
1050       flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h);
1051 
1052       if (h != -1)
1053         close(h);
1054 
1055       if (!flen) {
1056         defp->SetOptimized(1);
1057         return false;
1058        } // endif flen
1059 
1060     } else
1061       return false;                 // Not optimisable
1062 
1063     cdp = defp->GetCols();
1064     i = 1;
1065   } else {
1066     /*******************************************************************/
1067     /*  Fixed length file. Opt file exists only for clustered columns. */
1068     /*******************************************************************/
1069     // Check for existence of clustered columns
1070     for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
1071       if (cdp->GetOpt())
1072         break;
1073 
1074     if (!cdp)
1075       return false;            // No optimization needed
1076 
1077     if ((len = Cardinality(g)) < 0)
1078       return true;             // Table error
1079     else if (!len)
1080       return false;            // File does not exist yet
1081 
1082     block = Txfp->Block;       // Was set in Cardinality
1083     nrec = Txfp->Nrec;
1084   } // endif Ftype
1085 
1086   if (defp->GetOptFileName(g, filename))
1087     return true;
1088 
1089   if (!(opfile = fopen(filename, "rb")))
1090     return false;                   // No saved values
1091 
1092   if (Ftype == RECFM_VAR || defp->Compressed == 2) {
1093     /*******************************************************************/
1094     /*  Read block starting positions from the opt file.               */
1095     /*******************************************************************/
1096     lg = sizeof(int);
1097 
1098     if (fread(n, sizeof(int), NZ, opfile) != NZ) {
1099       sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1100       goto err;
1101       } // endif size
1102 
1103     if (n[1] != lg || n[2] != nrec) {
1104       sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1105       goto err;
1106       } // endif
1107 
1108     last = n[0];
1109     block = n[3];
1110     blk = block + 1;
1111 
1112     defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg);
1113 
1114     if (fread(defp->To_Pos, lg, blk, opfile) != blk) {
1115       sprintf(g->Message, MSG(OPTBLK_RD_ERR), strerror(errno));
1116       goto err;
1117       } // endif size
1118 
1119     } // endif Ftype
1120 
1121   /*********************************************************************/
1122   /*  Read the Min/Max values from the opt file.                       */
1123   /*********************************************************************/
1124   for (; cdp; cdp = cdp->GetNext(), i++)
1125     if (cdp->GetOpt()) {
1126       lg = cdp->GetClen();
1127       blk = block;
1128 
1129       //  Now start the reading process.
1130       if (fread(n, sizeof(int), NZ, opfile) != NZ) {
1131         sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1132         goto err;
1133         } // endif size
1134 
1135       if (n[0] == -i) {
1136         // Read the XDB2 opt values from the opt file
1137         if (n[1] != lg || n[2] != nrec || n[3] != block) {
1138           sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1139           goto err;
1140           } // endif
1141 
1142         if (fread(n, sizeof(int), 2, opfile) != 2) {
1143           sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno));
1144           goto err;
1145           } // endif fread
1146 
1147         ndv = n[0]; nbm = n[1]; nbk = nbm * blk;
1148 
1149         if (cdp->GetNdv() < (int)ndv || !cdp->GetDval())
1150           cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg));
1151 
1152         cdp->SetNdv((int)ndv);
1153 
1154         if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) {
1155           sprintf(g->Message, MSG(OPT_DVAL_RD_ERR), strerror(errno));
1156           goto err;
1157           } // endif size
1158 
1159         if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap())
1160           cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int)));
1161 
1162         cdp->SetNbm((int)nbm);
1163 
1164         if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) {
1165           sprintf(g->Message, MSG(OPT_BMAP_RD_ERR), strerror(errno));
1166           goto err;
1167           } // endif size
1168 
1169         cdp->SetXdb2(true);
1170       } else {
1171         // Read the Min/Max values from the opt file
1172         if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) {
1173           sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
1174           goto err;
1175           } // endif
1176 
1177         if (newblk || !cdp->GetMin())
1178           cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg));
1179 
1180         if (fread(cdp->GetMin(), lg, blk, opfile) != blk) {
1181           sprintf(g->Message, MSG(OPT_MIN_RD_ERR), strerror(errno));
1182           goto err;
1183           } // endif size
1184 
1185         if (newblk || !cdp->GetMax())
1186           cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg));
1187 
1188         if (fread(cdp->GetMax(), lg, blk, opfile) != blk) {
1189           sprintf(g->Message, MSG(OPT_MAX_RD_ERR), strerror(errno));
1190           goto err;
1191           } // endif size
1192 
1193         cdp->SetXdb2(false);
1194       } // endif n[0] (XDB2)
1195 
1196       } // endif Clustered
1197 
1198   defp->SetBlock(block);
1199   defp->Last = last;     // For Cardinality
1200   defp->SetAllocBlks(block);
1201   defp->SetOptimized(1);
1202   fclose(opfile);
1203   MaxSize = -1;          // Can be refined later
1204   return false;
1205 
1206  err:
1207   defp->RemoveOptValues(g);
1208   fclose(opfile);
1209 
1210   // Ignore error if not in mode CHK_OPT
1211   return (PlgGetUser(g)->Check & CHK_OPT) != 0;
1212   } // end of GetBlockValues
1213 
1214 /***********************************************************************/
1215 /*  This fonction is used while making XDB2 block optimization.        */
1216 /*  It constructs for each elligible columns, the sorted list of the   */
1217 /*  distinct values existing in the column. This function uses an      */
1218 /*  algorithm that permit to get several sets of distinct values by    */
1219 /*  reading the table only once, which cannot be done using a standard */
1220 /*  SQL query.                                                         */
1221 /***********************************************************************/
GetDistinctColumnValues(PGLOBAL g,int nrec)1222 bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
1223   {
1224   char   *p;
1225   int     rc, blk, n = 0;
1226   PDOSCOL colp;
1227   PDBUSER dup = PlgGetUser(g);
1228 
1229   /*********************************************************************/
1230   /*  Initialize progress information                                  */
1231   /*********************************************************************/
1232   p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name));
1233   dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name);
1234   dup->ProgMax = GetProgMax(g);
1235   dup->ProgCur = 0;
1236 
1237   while ((rc = ReadDB(g)) == RC_OK) {
1238     for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
1239       if (colp->Clustered == 2)
1240         if (colp->AddDistinctValue(g))
1241           return true;                   // Too many distinct values
1242 
1243 #if defined(SOCKET_MODE)
1244     if (SendProgress(dup)) {
1245       strcpy(g->Message, MSG(OPT_CANCELLED));
1246       return true;
1247     } else
1248 #elif defined(THREAD)
1249     if (!dup->Step) {
1250       strcpy(g->Message, MSG(OPT_CANCELLED));
1251       return true;
1252     } else
1253 #endif     // THREAD
1254       dup->ProgCur = GetProgCur();
1255 
1256     n++;
1257     } // endwhile
1258 
1259   if (rc != RC_EF)
1260     return true;
1261 
1262   // Reset the number of table blocks
1263 //nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value)
1264   blk = (n + nrec - 1) / nrec;
1265   Txfp->Block = blk;                    // Useful mainly for ZLBFAM ???
1266 
1267   // Set Nbm, Bmap for XDB2 columns
1268   for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
1269     if (colp->Clustered == 2) {
1270 //    colp->Cdp->SetNdv(colp->Ndv);
1271       colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP;
1272       colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk);
1273       } // endif Clustered
1274 
1275   return false;
1276   } // end of GetDistinctColumnValues
1277 
1278 /***********************************************************************/
1279 /*  Analyze the filter and construct the Block Evaluation Filter.      */
1280 /*  This is possible when a filter contains predicates implying a      */
1281 /*  column marked as "clustered" or "sorted" matched to a constant     */
1282 /*  argument. It is then possible by comparison against the smallest   */
1283 /*  and largest column values in each block to determine whether the   */
1284 /*  filter condition will be always true or always false for the block.*/
1285 /***********************************************************************/
InitBlockFilter(PGLOBAL g,PFIL filp)1286 PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp)
1287   {
1288   bool blk = Txfp->Blocked;
1289 
1290   if (To_BlkFil)
1291     return To_BlkFil;      // Already done
1292   else if (!filp)
1293     return NULL;
1294   else if (blk) {
1295     if (Txfp->GetAmType() == TYPE_AM_DBF)
1296       /*****************************************************************/
1297       /*  If RowID is used in this query, block optimization cannot be */
1298       /*  used because currently the file must be read sequentially.   */
1299       /*****************************************************************/
1300       for (PCOL cp = Columns; cp; cp = cp->GetNext())
1301         if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm())
1302           return NULL;
1303 
1304     } // endif blk
1305 
1306   int  i, op = filp->GetOpc(), opm = filp->GetOpm();
1307   bool cnv[2];
1308   PCOL colp;
1309   PXOB arg[2] = {NULL,NULL};
1310   PBF *fp = NULL, bfp = NULL;
1311 
1312   switch (op) {
1313     case OP_EQ:
1314     case OP_NE:
1315     case OP_GT:
1316     case OP_GE:
1317     case OP_LT:
1318     case OP_LE:
1319       if (! opm) {
1320         for (i = 0; i < 2; i++) {
1321           arg[i] = filp->Arg(i);
1322           cnv[i] = filp->Conv(i);
1323           } // endfor i
1324 
1325         bfp = CheckBlockFilari(g, arg, op, cnv);
1326         break;
1327         } // endif !opm
1328 
1329       // if opm, pass thru
1330       // fall through
1331     case OP_IN:
1332       if (filp->GetArgType(0) == TYPE_COLBLK &&
1333           filp->GetArgType(1) == TYPE_ARRAY) {
1334         arg[0] = filp->Arg(0);
1335         arg[1] = filp->Arg(1);
1336         colp = (PCOL)arg[0];
1337 
1338         if (colp->GetTo_Tdb() == this) {
1339           // Block evaluation is possible for...
1340           if (colp->GetAmType() == TYPE_AM_ROWID) {
1341             // Special column ROWID and constant array, but
1342             // currently we don't know how to retrieve a RowID
1343             // from a DBF table that is not sequentially read.
1344 //          if (Txfp->GetAmType() != TYPE_AM_DBF ||
1345 //              ((RIDBLK*)arg[0])->GetRnm())
1346               bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec);
1347 
1348           } else if (blk && Txfp->Nrec > 1 && colp->IsClustered())
1349           {
1350             // Clustered column and constant array
1351             if (colp->GetClustered() == 2)
1352               bfp = new(g) BLKFILIN2(g, this, op, opm, arg);
1353             else
1354               bfp = new(g) BLKFILIN(g, this, op, opm, arg);
1355           }
1356         } // endif this
1357 
1358 #if 0
1359       } else if (filp->GetArgType(0) == TYPE_SCALF &&
1360                  filp->GetArgType(1) == TYPE_ARRAY) {
1361         arg[0] = filp->Arg(0);
1362         arg[1] = filp->Arg(1);
1363 
1364         if (((PSCALF)arg[0])->GetOp() == OP_ROW &&
1365             arg[1]->GetResultType() == TYPE_LIST) {
1366           PARRAY  par = (PARRAY)arg[1];
1367           LSTVAL *vlp = (LSTVAL*)par->GetValue();
1368 
1369           ((SFROW*)arg[0])->GetParms(n);
1370 
1371           if (n != vlp->GetN())
1372             return NULL;
1373           else
1374             n = par->GetNval();
1375 
1376           arg[1] = new(g) CONSTANT(vlp);
1377           fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF));
1378           cnv[0] = cnv[1] = false;
1379 
1380           if (op == OP_IN)
1381             op = OP_EQ;
1382 
1383           for (i = 0; i < n; i++) {
1384             par->GetNthValue(vlp, i);
1385 
1386             if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv)))
1387               return NULL;
1388 
1389             } // endfor i
1390 
1391           bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n);
1392           } // endif ROW
1393 #endif // 0
1394 
1395       } // endif Type
1396 
1397       break;
1398     case OP_AND:
1399     case OP_OR:
1400       fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF));
1401       fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0)));
1402       fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1)));
1403 
1404       if (fp[0] || fp[1])
1405         bfp = new(g) BLKFILLOG(this, op, fp, 2);
1406 
1407       break;
1408     case OP_NOT:
1409       fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF));
1410 
1411       if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0)))))
1412         bfp = new(g) BLKFILLOG(this, op, fp, 1);
1413 
1414       break;
1415     case OP_LIKE:
1416     default:
1417       break;
1418     } // endswitch op
1419 
1420   return bfp;
1421   } // end of InitBlockFilter
1422 
1423 /***********************************************************************/
1424 /*  Analyze the passed arguments and construct the Block Filter.       */
1425 /***********************************************************************/
CheckBlockFilari(PGLOBAL g,PXOB * arg,int op,bool * cnv)1426 PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv)
1427   {
1428 //int     i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0};
1429 //bool    conv = false, xdb2 = false, ok = false, b[2];
1430 //PXOB   *xarg1, *xarg2 = NULL, xp[2];
1431   int     i, n = 0, type[2] = {0,0};
1432   bool    conv = false, xdb2 = false;
1433   PXOB    xp[2];
1434   PCOL    colp;
1435   PBF     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;
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   {
2859     if (OldVal) {
2860       // Verify whether this column is sorted all right
2861       if (OldVal->CompareValue(Value) > 0) {
2862         // Column is no more in ascending order
2863         sprintf(g->Message, MSG(COL_NOT_SORTED), Name, To_Tdb->GetName());
2864         Sorted = false;
2865         return true;
2866       } else
2867         OldVal->SetValue_pval(Value);
2868 
2869     } else
2870       OldVal = AllocateValue(g, Value);
2871   }
2872   return false;
2873   } // end of CheckSorted
2874 
2875 /***********************************************************************/
2876 /*  AddDistinctValue: Check whether this value already exist in the    */
2877 /*  list and if not add it to the distinct values list.                */
2878 /***********************************************************************/
AddDistinctValue(PGLOBAL g)2879 bool DOSCOL::AddDistinctValue(PGLOBAL g)
2880   {
2881   bool found = false;
2882   int  i, m, n;
2883 
2884   ReadColumn(g);           // Extract column value from current line
2885 
2886   // Perhaps a better algorithm can be used when Ndv gets bigger
2887   // Here we cannot use Find because we must get the index of where
2888   // to insert a new value if it is not found in the array.
2889   for (n = 0; n < Ndv; n++) {
2890     m = Dval->CompVal(Value, n);
2891 
2892     if (m > 0)
2893       continue;
2894     else if (!m)
2895       found = true;        // Already there
2896 
2897     break;
2898     } // endfor n
2899 
2900   if (!found) {
2901     // Check whether we have room for an additional value
2902     if (Ndv == Freq) {
2903       // Too many values because of wrong Freq setting
2904       sprintf(g->Message, MSG(BAD_FREQ_SET), Name);
2905       return true;
2906       } // endif Ndv
2907 
2908     // New value, add it to the list before the nth value
2909     Dval->SetNval(Ndv + 1);
2910 
2911     for (i = Ndv; i > n; i--)
2912       Dval->Move(i - 1, i);
2913 
2914     Dval->SetValue(Value, n);
2915     Ndv++;
2916     } // endif found
2917 
2918   return false;
2919   } // end of AddDistinctValue
2920 
2921 /* ------------------------------------------------------------------- */
2922