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