1 /***************** Xindex C++ Class Xindex Code (.CPP) *****************/
2 /* Name: XINDEX.CPP Version 3.0 */
3 /* */
4 /* (C) Copyright to the author Olivier BERTRAND 2004-2017 */
5 /* */
6 /* This file contains the class XINDEX implementation code. */
7 /***********************************************************************/
8
9 /***********************************************************************/
10 /* Include relevant sections of the System header files. */
11 /***********************************************************************/
12 #include "my_global.h"
13 #if defined(_WIN32)
14 #include <io.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 //#include <windows.h>
18 #else // !_WIN32
19 #if defined(UNIX)
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #else // !UNIX
25 #include <io.h>
26 #endif // !UNIX
27 #include <fcntl.h>
28 #endif // !_WIN32
29
30 /***********************************************************************/
31 /* Include required application header files */
32 /* global.h is header containing all global Plug declarations. */
33 /* plgdbsem.h is header containing the DB applic. declarations. */
34 /* kindex.h is header containing the KINDEX class definition. */
35 /***********************************************************************/
36 #include "global.h"
37 #include "plgdbsem.h"
38 #include "osutil.h"
39 #include "maputil.h"
40 //nclude "filter.h"
41 #include "tabcol.h"
42 #include "xindex.h"
43 #include "xobject.h"
44 //nclude "scalfnc.h"
45 //nclude "array.h"
46 #include "filamtxt.h"
47 #include "tabdos.h"
48 #if defined(VCT_SUPPORT)
49 #include "tabvct.h"
50 #endif // VCT_SUPPORT
51
52 /***********************************************************************/
53 /* Macro or external routine definition */
54 /***********************************************************************/
55 #define NZ 8
56 #define NW 5
57 #define MAX_INDX 10
58 #ifndef INVALID_SET_FILE_POINTER
59 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
60 #endif
61
62 /***********************************************************************/
63 /* DB external variables. */
64 /***********************************************************************/
65 extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
66 #if defined(XMAP)
67 extern my_bool xmap;
68 #endif // XMAP
69
70 /***********************************************************************/
71 /* Last two parameters are true to enable type checking, and last one */
72 /* to have rows filled by blanks to be compatible with QRY blocks. */
73 /***********************************************************************/
74 PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
75 bool check = true, bool blank = true, bool un = false);
76
77 /***********************************************************************/
78 /* Check whether we have to create/update permanent indexes. */
79 /***********************************************************************/
PlgMakeIndex(PGLOBAL g,PSZ name,PIXDEF pxdf,bool add)80 int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
81 {
82 int rc;
83 PTABLE tablep;
84 PTDB tdbp;
85 PCATLG cat = PlgGetCatalog(g, true);
86
87 /*********************************************************************/
88 /* Open a new table in mode read and with only the keys columns. */
89 /*********************************************************************/
90 tablep = new(g) XTAB(name);
91
92 if (!(tdbp = cat->GetTable(g, tablep)))
93 rc = RC_NF;
94 else if (!tdbp->GetDef()->Indexable()) {
95 sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
96 rc = RC_NF;
97 } else if ((rc = ((PTDBASE)tdbp)->MakeIndex(g, pxdf, add)) == RC_INFO)
98 rc = RC_OK; // No or remote index
99
100 return rc;
101 } // end of PlgMakeIndex
102
103 /* -------------------------- Class INDEXDEF ------------------------- */
104
105 /***********************************************************************/
106 /* INDEXDEF Constructor. */
107 /***********************************************************************/
INDEXDEF(char * name,bool uniq,int n)108 INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
109 {
110 //To_Def = NULL;
111 Next = NULL;
112 ToKeyParts = NULL;
113 Name = name;
114 Unique = uniq;
115 Invalid = false;
116 AutoInc = false;
117 Dynamic = false;
118 Mapped = false;
119 Nparts = 0;
120 ID = n;
121 //Offset = 0;
122 //Offhigh = 0;
123 //Size = 0;
124 MaxSame = 1;
125 } // end of INDEXDEF constructor
126
127 /***********************************************************************/
128 /* Set the max same values for each colum after making the index. */
129 /***********************************************************************/
SetMxsame(PXINDEX x)130 void INDEXDEF::SetMxsame(PXINDEX x)
131 {
132 PKPDEF kdp;
133 PXCOL xcp;
134
135 for (kdp = ToKeyParts, xcp = x->To_KeyCol;
136 kdp && xcp; kdp = kdp->Next, xcp = xcp->Next)
137 kdp->Mxsame = xcp->Mxs;
138 } // end of SetMxsame
139
140 /* -------------------------- Class KPARTDEF ------------------------- */
141
142 /***********************************************************************/
143 /* KPARTDEF Constructor. */
144 /***********************************************************************/
KPARTDEF(PSZ name,int n)145 KPARTDEF::KPARTDEF(PSZ name, int n)
146 {
147 Next = NULL;
148 Name = name;
149 Mxsame = 0;
150 Ncol = n;
151 Klen = 0;
152 } // end of KPARTDEF constructor
153
154 /* -------------------------- XXBASE Class --------------------------- */
155
156 /***********************************************************************/
157 /* XXBASE public constructor. */
158 /***********************************************************************/
XXBASE(PTDBDOS tbxp,bool b)159 XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
160 To_Rec((int*&)Record.Memp)
161 {
162 Tbxp = tbxp;
163 Record = Nmblk;
164 Cur_K = -1;
165 Old_K = -1;
166 Num_K = 0;
167 Ndif = 0;
168 Bot = Top = Inf = Sup = 0;
169 Op = OP_EQ;
170 To_KeyCol = NULL;
171 Mul = false;
172 Srtd = false;
173 Dynamic = false;
174 Val_K = -1;
175 Nblk = Sblk = 0;
176 Thresh = 7;
177 ID = -1;
178 Nth = 0;
179 } // end of XXBASE constructor
180
181 /***********************************************************************/
182 /* Make file output of XINDEX contents. */
183 /***********************************************************************/
Printf(PGLOBAL,FILE * f,uint n)184 void XXBASE::Printf(PGLOBAL, FILE *f, uint n)
185 {
186 char m[64];
187
188 memset(m, ' ', n); // Make margin string
189 m[n] = '\0';
190 fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K);
191 } // end of Printf
192
193 /***********************************************************************/
194 /* Make string output of XINDEX contents. */
195 /***********************************************************************/
Prints(PGLOBAL,char * ps,uint z)196 void XXBASE::Prints(PGLOBAL, char *ps, uint z)
197 {
198 *ps = '\0';
199 strncat(ps, "Xindex", z);
200 } // end of Prints
201
202 /* -------------------------- XINDEX Class --------------------------- */
203
204 /***********************************************************************/
205 /* XINDEX public constructor. */
206 /***********************************************************************/
XINDEX(PTDBDOS tdbp,PIXDEF xdp,PXLOAD pxp,PCOL * cp,PXOB * xp,int k)207 XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k)
208 : XXBASE(tdbp, !xdp->IsUnique())
209 {
210 Xdp = xdp;
211 ID = xdp->GetID();
212 Tdbp = tdbp;
213 X = pxp;
214 To_LastCol = NULL;
215 To_LastVal = NULL;
216 To_Cols = cp;
217 To_Vals = xp;
218 Mul = !xdp->IsUnique();
219 Srtd = false;
220 Nk = xdp->GetNparts();
221 Nval = (k) ? k : Nk;
222 Incr = 0;
223 //Defoff = xdp->GetOffset();
224 //Defhigh = xdp->GetOffhigh();
225 //Size = xdp->GetSize();
226 MaxSame = xdp->GetMaxSame();
227 } // end of XINDEX constructor
228
229 /***********************************************************************/
230 /* XINDEX Reset: re-initialize a Xindex block. */
231 /***********************************************************************/
Reset(void)232 void XINDEX::Reset(void)
233 {
234 for (PXCOL kp = To_KeyCol; kp; kp = kp->Next)
235 kp->Val_K = kp->Ndf;
236
237 Cur_K = Num_K;
238 Old_K = -1; // Needed to avoid not setting CurBlk for Update
239 Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST :
240 (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ;
241 Nth = 0;
242 } // end of Reset
243
244 /***********************************************************************/
245 /* XINDEX Close: terminate index and free all allocated data. */
246 /* Do not reset values that are used at return to make. */
247 /***********************************************************************/
Close(void)248 void XINDEX::Close(void)
249 {
250 // Close file or view of file
251 if (X)
252 X->Close();
253
254 // De-allocate data
255 PlgDBfree(Record);
256 PlgDBfree(Index);
257 PlgDBfree(Offset);
258
259 for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next) {
260 // Column values cannot be retrieved from key anymore
261 if (kcp->Colp)
262 kcp->Colp->SetKcol(NULL);
263
264 // De-allocate Key data
265 kcp->FreeData();
266 } // endfor kcp
267
268 } // end of Close
269
270 /***********************************************************************/
271 /* XINDEX compare routine for C Quick/Insertion sort. */
272 /***********************************************************************/
Qcompare(int * i1,int * i2)273 int XINDEX::Qcompare(int *i1, int *i2)
274 {
275 int k;
276 PXCOL kcp;
277
278 for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next)
279 if ((k = kcp->Compare(*i1, *i2)))
280 break;
281
282 //num_comp++;
283 return k;
284 } // end of Qcompare
285
286 /***********************************************************************/
287 /* AddColumns: here we try to determine whether it is worthwhile to */
288 /* add to the keys the values of the columns selected for this table. */
289 /* Sure enough, it is done while records are read and permit to avoid */
290 /* reading the table while doing the join (Dynamic index only) */
291 /***********************************************************************/
AddColumns(void)292 bool XINDEX::AddColumns(void)
293 {
294 if (!Dynamic)
295 return false; // Not applying to static index
296 else if (IsMul())
297 return false; // Not done yet for multiple index
298 #if defined(VCT_SUPPORT)
299 else if (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit())
300 return false; // This would require to read additional files
301 #endif // VCT_SUPPORT
302 else
303 return true;
304
305 } // end of AddColumns
306
307 /***********************************************************************/
308 /* Make: Make and index on key column(s). */
309 /***********************************************************************/
Make(PGLOBAL g,PIXDEF sxp)310 bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
311 {
312 /*********************************************************************/
313 /* Table can be accessed through an index. */
314 /*********************************************************************/
315 int k, nk = Nk, rc = RC_OK;
316 int *bof, i, j, n, ndf, nkey;
317 PKPDEF kdfp = Xdp->GetToKeyParts();
318 bool brc = false;
319 PCOL colp;
320 PFIL filp = Tdbp->GetFilter();
321 PXCOL kp, addcolp, prev = NULL, kcp = NULL;
322 //PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
323
324 #if defined(_DEBUG)
325 assert(X || Nk == 1);
326 #endif // _DEBUG
327
328 /*********************************************************************/
329 /* Allocate the storage that will contain the keys and the file */
330 /* positions corresponding to them. */
331 /*********************************************************************/
332 if ((n = Tdbp->GetMaxSize(g)) < 0)
333 return true;
334 else if (!n) {
335 Num_K = Ndif = 0;
336 MaxSame = 1;
337
338 // The if condition was suppressed because this may be an existing
339 // index that is now void because all table lines were deleted.
340 // if (sxp)
341 goto nox; // Truncate eventually existing index file
342 // else
343 // return false;
344
345 } // endif n
346
347 if (trace(1))
348 htrc("XINDEX Make: n=%d\n", n);
349
350 // File position must be stored
351 Record.Size = n * sizeof(int);
352
353 if (!PlgDBalloc(g, NULL, Record)) {
354 sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n);
355 goto err; // Error
356 } // endif
357
358 /*********************************************************************/
359 /* Allocate the KXYCOL blocks used to store column values. */
360 /*********************************************************************/
361 for (k = 0; k < Nk; k++) {
362 colp = To_Cols[k];
363
364 if (!kdfp) {
365 sprintf(g->Message, MSG(INT_COL_ERROR),
366 (colp) ? colp->GetName() : "???");
367 goto err; // Error
368 } // endif kdfp
369
370 kcp = new(g) KXYCOL(this);
371
372 if (kcp->Init(g, colp, n, true, kdfp->Klen))
373 goto err; // Error
374
375 if (prev) {
376 kcp->Previous = prev;
377 prev->Next = kcp;
378 } else
379 To_KeyCol = kcp;
380
381 prev = kcp;
382 kdfp = kdfp->Next;
383 } // endfor k
384
385 To_LastCol = prev;
386
387 if (AddColumns()) {
388 PCOL kolp = To_Cols[0]; // Temporary while imposing Nk = 1
389
390 i = 0;
391
392 // Allocate the accompanying
393 for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) {
394 // Count how many columns to add
395 // for (k = 0; k < Nk; k++)
396 // if (colp == To_Cols[k])
397 // break;
398
399 // if (k == nk)
400 if (colp != kolp)
401 i++;
402
403 } // endfor colp
404
405 if (i && i < 10) // Should be a parameter
406 for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) {
407 // for (k = 0; k < Nk; k++)
408 // if (colp == To_Cols[k])
409 // break;
410
411 // if (k < nk)
412 if (colp == kolp)
413 continue; // This is a key column
414
415 kcp = new(g) KXYCOL(this);
416
417 if (kcp->Init(g, colp, n, true, 0))
418 return true;
419
420 if (trace(1))
421 htrc("Adding colp=%p Buf_Type=%d size=%d\n",
422 colp, colp->GetResultType(), n);
423
424 nk++;
425 prev->Next = kcp;
426 prev = kcp;
427 } // endfor colp
428
429 } // endif AddColumns
430
431 #if 0
432 /*********************************************************************/
433 /* Get the starting information for progress. */
434 /*********************************************************************/
435 dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
436 sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
437 dup->ProgMax = Tdbp->GetProgMax(g);
438 dup->ProgCur = 0;
439 #endif // 0
440
441 /*********************************************************************/
442 /* Standard init: read the file and construct the index table. */
443 /* Note: reading will be sequential as To_Kindex is not set. */
444 /*********************************************************************/
445 for (i = nkey = 0; rc != RC_EF; i++) {
446 #if 0
447 if (!dup->Step) {
448 strcpy(g->Message, MSG(QUERY_CANCELLED));
449 throw 99;
450 } // endif Step
451 #endif // 0
452
453 /*******************************************************************/
454 /* Read a valid record from table file. */
455 /*******************************************************************/
456 rc = Tdbp->ReadDB(g);
457
458 // Update progress information
459 // dup->ProgCur = Tdbp->GetProgCur();
460
461 // Check return code and do whatever must be done according to it
462 switch (rc) {
463 case RC_OK:
464 if (ApplyFilter(g, filp))
465 break;
466
467 // fall through
468 case RC_NF:
469 continue;
470 case RC_EF:
471 goto end_of_file;
472 default:
473 sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
474 goto err;
475 } // endswitch rc
476
477 /*******************************************************************/
478 /* Get and Store the file position of the last read record for */
479 /* future direct access. */
480 /*******************************************************************/
481 if (nkey == n) {
482 sprintf(g->Message, MSG(TOO_MANY_KEYS), nkey);
483 return true;
484 } else
485 To_Rec[nkey] = Tdbp->GetRecpos();
486
487 if (trace(2))
488 htrc("Make: To_Rec[%d]=%d\n", nkey, To_Rec[nkey]);
489
490 /*******************************************************************/
491 /* Get the keys and place them in the key blocks. */
492 /*******************************************************************/
493 for (k = 0, kcp = To_KeyCol;
494 k < nk && kcp;
495 k++, kcp = kcp->Next) {
496 // colp = To_Cols[k];
497 colp = kcp->Colp;
498
499 if (!colp->GetStatus(BUF_READ))
500 colp->ReadColumn(g);
501 else
502 colp->Reset();
503
504 kcp->SetValue(colp, nkey);
505 } // endfor k
506
507 nkey++; // A new valid key was found
508 } // endfor i
509
510 end_of_file:
511
512 // Update progress information
513 //dup->ProgCur = Tdbp->GetProgMax(g);
514
515 /*********************************************************************/
516 /* Record the Index size and eventually resize memory allocation. */
517 /*********************************************************************/
518 if ((Num_K = nkey) < n) {
519 PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int));
520
521 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
522 kcp->ReAlloc(g, Num_K);
523
524 } // endif Num_K
525
526 /*********************************************************************/
527 /* Sort the index so we can use an optimized Find algorithm. */
528 /* Note: for a unique index we use the non conservative sort */
529 /* version because normally all index values are different. */
530 /* This was set at CSORT class construction. */
531 /* For all indexes, an offset array is made so we can check the */
532 /* uniqueness of unique indexes. */
533 /*********************************************************************/
534 Index.Size = Num_K * sizeof(int);
535
536 if (!PlgDBalloc(g, NULL, Index)) {
537 sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
538 goto err; // Error
539 } // endif alloc
540
541 Offset.Size = (Num_K + 1) * sizeof(int);
542
543 if (!PlgDBalloc(g, NULL, Offset)) {
544 sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1);
545 goto err; // Error
546 } // endif alloc
547
548 // We must separate keys and added columns before sorting
549 addcolp = To_LastCol->Next;
550 To_LastCol->Next = NULL;
551
552 // Call the sort program, it returns the number of distinct values
553 if ((Ndif = Qsort(g, Num_K)) < 0)
554 goto err; // Error during sort
555
556 if (trace(1))
557 htrc("Make: Nk=%d n=%d Num_K=%d Ndif=%d addcolp=%p BlkFil=%p X=%p\n",
558 Nk, n, Num_K, Ndif, addcolp, Tdbp->To_BlkFil, X);
559
560 // Check whether the unique index is unique indeed
561 if (!Mul)
562 {
563 if (Ndif < Num_K) {
564 strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
565 brc = true;
566 goto err;
567 } else
568 PlgDBfree(Offset); // Not used anymore
569 }
570
571 // Restore kcp list
572 To_LastCol->Next = addcolp;
573
574 // Use the index to physically reorder the xindex
575 Srtd = Reorder(g);
576
577 if (Ndif < Num_K) {
578 // Resize the offset array
579 PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int));
580
581 // Initial value of MaxSame
582 MaxSame = Pof[1] - Pof[0];
583
584 // Resize the Key array by only keeping the distinct values
585 for (i = 1; i < Ndif; i++) {
586 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
587 kcp->Move(i, Pof[i]);
588
589 MaxSame = MY_MAX(MaxSame, Pof[i + 1] - Pof[i]);
590 } // endfor i
591
592 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
593 kcp->ReAlloc(g, Ndif);
594
595 } else {
596 Mul = false; // Current index is unique
597 PlgDBfree(Offset); // Not used anymore
598 MaxSame = 1; // Reset it when remaking an index
599 } // endif Ndif
600
601 /*********************************************************************/
602 /* Now do the reduction of the index. Indeed a multi-column index */
603 /* can be used for only some of the first columns. For instance if */
604 /* an index is defined for column A, B, C PlugDB can use it for */
605 /* only the column A or the columns A, B. */
606 /* What we do here is to reduce the data so column A will contain */
607 /* only the sorted distinct values of A, B will contain data such */
608 /* as only distinct values of A,B are stored etc. */
609 /* This implies that for each column set an offset array is made */
610 /* except if the subset originally contains unique values. */
611 /*********************************************************************/
612 // Update progress information
613 //dup->Step = STEP(REDUCE_INDEX);
614
615 ndf = Ndif;
616 To_LastCol->Mxs = MaxSame;
617
618 for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) {
619 if (!(bof = kcp->MakeOffset(g, ndf)))
620 goto err;
621 else
622 *bof = 0;
623
624 for (n = 0, i = j = 1; i < ndf; i++)
625 for (kp = kcp; kp; kp = kp->Previous)
626 if (kp->Compare(n, i)) {
627 // Values are not equal to last ones
628 bof[j++] = n = i;
629 break;
630 } // endif Compare
631
632 if (j < ndf) {
633 // Sub-index is multiple
634 bof[j] = ndf;
635 ndf = j; // New number of distinct values
636
637 // Resize the Key array by only keeping the distinct values
638 for (kp = kcp; kp; kp = kp->Previous) {
639 for (i = 1; i < ndf; i++)
640 kp->Move(i, bof[i]);
641
642 kp->ReAlloc(g, ndf);
643 } // endif kcp
644
645 // Resize the offset array
646 kcp->MakeOffset(g, ndf);
647
648 // Calculate the max same value for this column
649 kcp->Mxs = ColMaxSame(kcp);
650 } else {
651 // Current sub-index is unique
652 kcp->MakeOffset(g, 0); // The offset is not used anymore
653 kcp->Mxs = 1; // Unique
654 } // endif j
655
656 } // endfor kcp
657
658 /*********************************************************************/
659 /* For sorted columns and fixed record size, file position can be */
660 /* calculated, so the Record array can be discarted. */
661 /* Not true for DBF tables because of eventual soft deleted lines. */
662 /* Note: for Num_K = 1 any non null value is Ok. */
663 /*********************************************************************/
664 if (Srtd && !filp && Tdbp->Ftype != RECFM_VAR && Tdbp->Ftype != RECFM_CSV
665 && Tdbp->Txfp->GetAmType() != TYPE_AM_DBF) {
666 Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
667 PlgDBfree(Record);
668 } // endif Srtd
669
670 /*********************************************************************/
671 /* Check whether a two-tier find algorithm can be implemented. */
672 /* It is currently implemented only for single key indexes. */
673 /*********************************************************************/
674 if (Nk == 1 && ndf >= 65536) {
675 // Implement a two-tier find algorithm
676 for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ;
677
678 Nblk = (ndf -1) / Sblk + 1;
679
680 if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk))
681 goto err; // Error
682
683 } // endif Num_K
684
685 nox:
686 /*********************************************************************/
687 /* No valid record read yet for secondary file. */
688 /*********************************************************************/
689 Cur_K = Num_K;
690
691 /*********************************************************************/
692 /* Save the xindex so it has not to be recalculated. */
693 /*********************************************************************/
694 if (X) {
695 if (SaveIndex(g, sxp))
696 brc = true;
697
698 } else { // Dynamic index
699 // Indicate that key column values can be found from KEYCOL's
700 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
701 kcp->Colp->SetKcol(kcp);
702
703 Tdbp->SetFilter(NULL); // Not used anymore
704 } // endif X
705
706 err:
707 // We don't need the index anymore
708 if (X || brc)
709 Close();
710
711 if (brc)
712 printf("%s\n", g->Message);
713
714 return brc;
715 } // end of Make
716
717 /***********************************************************************/
718 /* Return the max size of the intermediate column. */
719 /***********************************************************************/
ColMaxSame(PXCOL kp)720 int XINDEX::ColMaxSame(PXCOL kp)
721 {
722 int *kof, i, ck1, ck2, ckn = 1;
723 PXCOL kcp;
724
725 // Calculate the max same value for this column
726 for (i = 0; i < kp->Ndf; i++) {
727 ck1 = i;
728 ck2 = i + 1;
729
730 for (kcp = kp; kcp; kcp = kcp->Next) {
731 if (!(kof = (kcp->Next) ? kcp->Kof : Pof))
732 break;
733
734 ck1 = kof[ck1];
735 ck2 = kof[ck2];
736 } // endfor kcp
737
738 ckn = MY_MAX(ckn, ck2 - ck1);
739 } // endfor i
740
741 return ckn;
742 } // end of ColMaxSame
743
744 /***********************************************************************/
745 /* Reorder: use the sort index to reorder the data in storage so */
746 /* it will be physically sorted and sort index can be removed. */
747 /***********************************************************************/
Reorder(PGLOBAL g)748 bool XINDEX::Reorder(PGLOBAL g __attribute__((unused)))
749 {
750 int i, j, k, n;
751 bool sorted = true;
752 PXCOL kcp;
753 #if 0
754 PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
755
756 if (Num_K > 500000) {
757 // Update progress information
758 dup->Step = STEP(REORDER_INDEX);
759 dup->ProgMax = Num_K;
760 dup->ProgCur = 0;
761 } else
762 dup = NULL;
763 #endif // 0
764
765 if (!Pex)
766 return Srtd;
767
768 for (i = 0; i < Num_K; i++) {
769 if (Pex[i] == Num_K) { // Already moved
770 continue;
771 } else if (Pex[i] == i) { // Already placed
772 // if (dup)
773 // dup->ProgCur++;
774
775 continue;
776 } // endif's Pex
777
778 sorted = false;
779
780 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
781 kcp->Save(i);
782
783 n = To_Rec[i];
784
785 for (j = i;; j = k) {
786 k = Pex[j];
787 Pex[j] = Num_K; // Mark position as set
788
789 if (k == i) {
790 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
791 kcp->Restore(j);
792
793 To_Rec[j] = n;
794 break; // end of loop
795 } else {
796 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
797 kcp->Move(j, k); // Move k to j
798
799 To_Rec[j] = To_Rec[k];
800 } // endif k
801
802 // if (dup)
803 // dup->ProgCur++;
804
805 } // endfor j
806
807 } // endfor i
808
809 // The index is not used anymore
810 PlgDBfree(Index);
811 return sorted;
812 } // end of Reorder
813
814 /***********************************************************************/
815 /* Save the index values for this table. */
816 /* The problem here is to avoid name duplication, because more than */
817 /* one data file can have the same name (but different types) and/or */
818 /* the same data file can be used with different block sizes. This is */
819 /* why we use Ofn that defaults to the file name but can be set to a */
820 /* different name if necessary. */
821 /***********************************************************************/
SaveIndex(PGLOBAL g,PIXDEF sxp)822 bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
823 {
824 PCSZ ftype;
825 char fn[_MAX_PATH];
826 int n[NZ], nof = (Mul) ? (Ndif + 1) : 0;
827 int id = -1, size = 0;
828 bool sep, rc = false;
829 PXCOL kcp = To_KeyCol;
830 PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
831 //PDBUSER dup = PlgGetUser(g);
832
833 //dup->Step = STEP(SAVING_INDEX);
834 //dup->ProgMax = 15 + 16 * Nk;
835 //dup->ProgCur = 0;
836
837 switch (Tdbp->Ftype) {
838 case RECFM_VAR: ftype = ".dnx"; break;
839 case RECFM_FIX: ftype = ".fnx"; break;
840 case RECFM_BIN: ftype = ".bnx"; break;
841 case RECFM_VCT: ftype = ".vnx"; break;
842 case RECFM_CSV: ftype = ".cnx"; break;
843 case RECFM_DBF: ftype = ".dbx"; break;
844 default:
845 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
846 return true;
847 } // endswitch Ftype
848
849 if ((sep = defp->GetBoolCatInfo("SepIndex", false))) {
850 // Index is saved in a separate file
851 #if defined(_WIN32)
852 char drive[_MAX_DRIVE];
853 #else
854 char *drive = NULL;
855 #endif
856 char direc[_MAX_DIR];
857 char fname[_MAX_FNAME];
858
859 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
860 strcat(strcat(fname, "_"), Xdp->GetName());
861 _makepath(fn, drive, direc, fname, ftype);
862 sxp = NULL;
863 } else {
864 id = ID;
865 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
866 } // endif sep
867
868 PlugSetPath(fn, fn, Tdbp->GetPath());
869
870 if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
871 printf("%s\n", g->Message);
872 return true;
873 } // endif Open
874
875 if (!Ndif)
876 goto end; // Void index
877
878 /*********************************************************************/
879 /* Write the index values on the index file. */
880 /*********************************************************************/
881 n[0] = ID + MAX_INDX; // To check validity
882 n[1] = Nk; // The number of indexed columns
883 n[2] = nof; // The offset array size or 0
884 n[3] = Num_K; // The index size
885 n[4] = Incr; // Increment of record positions
886 n[5] = Nblk; n[6] = Sblk;
887 n[7] = Srtd ? 1 : 0; // Values are sorted in the file
888
889 if (trace(1)) {
890 htrc("Saving index %s\n", Xdp->GetName());
891 htrc("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d Srtd=%d\n",
892 ID, Nk, nof, Num_K, Incr, Nblk, Sblk, Srtd);
893 } // endif trace
894
895 size = X->Write(g, n, NZ, sizeof(int), rc);
896 //dup->ProgCur = 1;
897
898 if (Mul) // Write the offset array
899 size += X->Write(g, Pof, nof, sizeof(int), rc);
900
901 //dup->ProgCur = 5;
902
903 if (!Incr) // Write the record position array(s)
904 size += X->Write(g, To_Rec, Num_K, sizeof(int), rc);
905
906 //dup->ProgCur = 15;
907
908 for (; kcp; kcp = kcp->Next) {
909 n[0] = kcp->Ndf; // Number of distinct sub-values
910 n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique
911 n[2] = (kcp == To_KeyCol) ? Nblk : 0;
912 n[3] = kcp->Klen; // To be checked later
913 n[4] = kcp->Type; // To be checked later
914
915 size += X->Write(g, n, NW, sizeof(int), rc);
916 // dup->ProgCur += 1;
917
918 if (n[2])
919 size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
920
921 // dup->ProgCur += 5;
922
923 size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
924 // dup->ProgCur += 5;
925
926 if (n[1])
927 size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
928
929 // dup->ProgCur += 5;
930 } // endfor kcp
931
932 if (trace(1))
933 htrc("Index %s saved, Size=%d\n", Xdp->GetName(), size);
934
935 end:
936 X->Close(fn, id);
937 return rc;
938 } // end of SaveIndex
939
940 /***********************************************************************/
941 /* Init: Open and Initialize a Key Index. */
942 /***********************************************************************/
Init(PGLOBAL g)943 bool XINDEX::Init(PGLOBAL g)
944 {
945 #if defined(XMAP)
946 if (xmap)
947 return MapInit(g);
948 #endif // XMAP
949
950 /*********************************************************************/
951 /* Table will be accessed through an index table. */
952 /* If sorting is required, this will be done later. */
953 /*********************************************************************/
954 PCSZ ftype;
955 char fn[_MAX_PATH];
956 int k, n, nv[NZ], id = -1;
957 bool estim = false;
958 PCOL colp;
959 PXCOL prev = NULL, kcp = NULL;
960 PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
961
962 /*********************************************************************/
963 /* Get the estimated table size. */
964 /* Note: for fixed tables we must use cardinality to avoid the call */
965 /* to MaxBlkSize that could reduce the cardinality value. */
966 /*********************************************************************/
967 if (Tdbp->Cardinality(NULL)) {
968 // For DBF tables, Cardinality includes bad or soft deleted lines
969 // that are not included in the index, and can be larger then the
970 // index size.
971 estim = (Tdbp->Ftype == RECFM_DBF || Tdbp->Txfp->GetAmType() == TYPE_AM_ZIP);
972 n = Tdbp->Cardinality(g); // n is exact table size
973 } else {
974 // Variable table not optimized
975 estim = true; // n is an estimate of the size
976 n = Tdbp->GetMaxSize(g);
977 } // endif Cardinality
978
979 if (n <= 0)
980 return !(n == 0); // n < 0 error, n = 0 void table
981
982 /*********************************************************************/
983 /* Get the first key column. */
984 /*********************************************************************/
985 if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
986 strcpy(g->Message, MSG(NO_KEY_COL));
987 return true; // Error
988 } else
989 colp = To_Cols[0];
990
991 switch (Tdbp->Ftype) {
992 case RECFM_VAR: ftype = ".dnx"; break;
993 case RECFM_FIX: ftype = ".fnx"; break;
994 case RECFM_BIN: ftype = ".bnx"; break;
995 case RECFM_VCT: ftype = ".vnx"; break;
996 case RECFM_CSV: ftype = ".cnx"; break;
997 case RECFM_DBF: ftype = ".dbx"; break;
998 default:
999 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
1000 return true;
1001 } // endswitch Ftype
1002
1003 if (defp->SepIndex()) {
1004 // Index was saved in a separate file
1005 #if defined(_WIN32)
1006 char drive[_MAX_DRIVE];
1007 #else
1008 char *drive = NULL;
1009 #endif
1010 char direc[_MAX_DIR];
1011 char fname[_MAX_FNAME];
1012
1013 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
1014 strcat(strcat(fname, "_"), Xdp->GetName());
1015 _makepath(fn, drive, direc, fname, ftype);
1016 } else {
1017 id = ID;
1018 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
1019 } // endif sep
1020
1021 PlugSetPath(fn, fn, Tdbp->GetPath());
1022
1023 if (trace(1))
1024 htrc("Index %s file: %s\n", Xdp->GetName(), fn);
1025
1026 /*********************************************************************/
1027 /* Open the index file and check its validity. */
1028 /*********************************************************************/
1029 if (X->Open(g, fn, id, MODE_READ))
1030 goto err; // No saved values
1031
1032 // Now start the reading process.
1033 if (X->Read(g, nv, NZ - 1, sizeof(int)))
1034 goto err;
1035
1036 if (nv[0] >= MAX_INDX) {
1037 // New index format
1038 if (X->Read(g, nv + 7, 1, sizeof(int)))
1039 goto err;
1040
1041 Srtd = nv[7] != 0;
1042 nv[0] -= MAX_INDX;
1043 } else
1044 Srtd = false;
1045
1046 if (trace(1))
1047 htrc("nv=%d %d %d %d %d %d %d (%d)\n",
1048 nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd);
1049
1050 // The test on ID was suppressed because MariaDB can change an index ID
1051 // when other indexes are added or deleted
1052 if (/*nv[0] != ID ||*/ nv[1] != Nk) {
1053 sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
1054
1055 if (trace(1))
1056 htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
1057
1058 goto err;
1059 } // endif
1060
1061 if (nv[2]) {
1062 Mul = true;
1063 Ndif = nv[2];
1064
1065 // Allocate the storage that will contain the offset array
1066 Offset.Size = Ndif * sizeof(int);
1067
1068 if (!PlgDBalloc(g, NULL, Offset)) {
1069 sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif);
1070 goto err;
1071 } // endif
1072
1073 if (X->Read(g, Pof, Ndif, sizeof(int)))
1074 goto err;
1075
1076 Ndif--; // nv[2] is offset size, equal to Ndif + 1
1077 } else {
1078 Mul = false;
1079 Ndif = nv[3];
1080 } // endif nv[2]
1081
1082 if (nv[3] < n && estim)
1083 n = nv[3]; // n was just an evaluated max value
1084
1085 if (nv[3] != n) {
1086 sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
1087 goto err;
1088 } // endif
1089
1090 Num_K = nv[3];
1091 Incr = nv[4];
1092 Nblk = nv[5];
1093 Sblk = nv[6];
1094
1095 if (!Incr) {
1096 /*******************************************************************/
1097 /* Allocate the storage that will contain the file positions. */
1098 /*******************************************************************/
1099 Record.Size = Num_K * sizeof(int);
1100
1101 if (!PlgDBalloc(g, NULL, Record)) {
1102 sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
1103 goto err;
1104 } // endif
1105
1106 if (X->Read(g, To_Rec, Num_K, sizeof(int)))
1107 goto err;
1108
1109 } else
1110 Srtd = true; // Sorted positions can be calculated
1111
1112 /*********************************************************************/
1113 /* Allocate the KXYCOL blocks used to store column values. */
1114 /*********************************************************************/
1115 for (k = 0; k < Nk; k++) {
1116 if (k == Nval)
1117 To_LastVal = prev;
1118
1119 if (X->Read(g, nv, NW, sizeof(int)))
1120 goto err;
1121
1122 colp = To_Cols[k];
1123
1124 if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
1125 (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
1126 sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
1127 goto err; // Error
1128 } // endif GetKey
1129
1130 kcp = new(g) KXYCOL(this);
1131
1132 if (kcp->Init(g, colp, nv[0], true, (int)nv[3]))
1133 goto err; // Error
1134
1135 /*******************************************************************/
1136 /* Read the index values from the index file. */
1137 /*******************************************************************/
1138 if (k == 0 && Nblk) {
1139 if (kcp->MakeBlockArray(g, Nblk, 0))
1140 goto err;
1141
1142 // Read block values
1143 if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen))
1144 goto err;
1145
1146 } // endif Nblk
1147
1148 // Read the entire (small) index
1149 if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen))
1150 goto err;
1151
1152 if (nv[1]) {
1153 if (!kcp->MakeOffset(g, nv[1] - 1))
1154 goto err;
1155
1156 // Read the offset array
1157 if (X->Read(g, kcp->Kof, nv[1], sizeof(int)))
1158 goto err;
1159
1160 } // endif n[1]
1161
1162 if (!kcp->Prefix)
1163 // Indicate that the key column value can be found from KXYCOL
1164 colp->SetKcol(kcp);
1165
1166 if (prev) {
1167 kcp->Previous = prev;
1168 prev->Next = kcp;
1169 } else
1170 To_KeyCol = kcp;
1171
1172 prev = kcp;
1173 } // endfor k
1174
1175 To_LastCol = prev;
1176
1177 if (Mul && prev) {
1178 // Last key offset is the index offset
1179 kcp->Koff = Offset;
1180 kcp->Koff.Sub = true;
1181 } // endif Mul
1182
1183 X->Close();
1184
1185 /*********************************************************************/
1186 /* No valid record read yet for secondary file. */
1187 /*********************************************************************/
1188 Cur_K = Num_K;
1189 return false;
1190
1191 err:
1192 Close();
1193 return true;
1194 } // end of Init
1195
1196 #if defined(XMAP)
1197 /***********************************************************************/
1198 /* Init: Open and Initialize a Key Index. */
1199 /***********************************************************************/
MapInit(PGLOBAL g)1200 bool XINDEX::MapInit(PGLOBAL g)
1201 {
1202 /*********************************************************************/
1203 /* Table will be accessed through an index table. */
1204 /* If sorting is required, this will be done later. */
1205 /*********************************************************************/
1206 const char *ftype;
1207 BYTE *mbase;
1208 char fn[_MAX_PATH];
1209 int *nv, nv0, k, n, id = -1;
1210 bool estim;
1211 PCOL colp;
1212 PXCOL prev = NULL, kcp = NULL;
1213 PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
1214
1215 /*********************************************************************/
1216 /* Get the estimated table size. */
1217 /* Note: for fixed tables we must use cardinality to avoid the call */
1218 /* to MaxBlkSize that could reduce the cardinality value. */
1219 /*********************************************************************/
1220 if (Tdbp->Cardinality(NULL)) {
1221 // For DBF tables, Cardinality includes bad or soft deleted lines
1222 // that are not included in the index, and can be larger then the
1223 // index size.
1224 estim = (Tdbp->Ftype == RECFM_DBF);
1225 n = Tdbp->Cardinality(g); // n is exact table size
1226 } else {
1227 // Variable table not optimized
1228 estim = true; // n is an estimate of the size
1229 n = Tdbp->GetMaxSize(g);
1230 } // endif Cardinality
1231
1232 if (n <= 0)
1233 return !(n == 0); // n < 0 error, n = 0 void table
1234
1235 /*********************************************************************/
1236 /* Get the first key column. */
1237 /*********************************************************************/
1238 if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
1239 strcpy(g->Message, MSG(NO_KEY_COL));
1240 return true; // Error
1241 } else
1242 colp = To_Cols[0];
1243
1244 switch (Tdbp->Ftype) {
1245 case RECFM_VAR: ftype = ".dnx"; break;
1246 case RECFM_FIX: ftype = ".fnx"; break;
1247 case RECFM_BIN: ftype = ".bnx"; break;
1248 case RECFM_VCT: ftype = ".vnx"; break;
1249 case RECFM_CSV: ftype = ".cnx"; break;
1250 case RECFM_DBF: ftype = ".dbx"; break;
1251 default:
1252 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
1253 return true;
1254 } // endswitch Ftype
1255
1256 if (defp->SepIndex()) {
1257 // Index was save in a separate file
1258 #if defined(_WIN32)
1259 char drive[_MAX_DRIVE];
1260 #else
1261 char *drive = NULL;
1262 #endif
1263 char direc[_MAX_DIR];
1264 char fname[_MAX_FNAME];
1265
1266 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
1267 strcat(strcat(fname, "_"), Xdp->GetName());
1268 _makepath(fn, drive, direc, fname, ftype);
1269 } else {
1270 id = ID;
1271 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
1272 } // endif SepIndex
1273
1274 PlugSetPath(fn, fn, Tdbp->GetPath());
1275
1276 if (trace(1))
1277 htrc("Index %s file: %s\n", Xdp->GetName(), fn);
1278
1279 /*********************************************************************/
1280 /* Get a view on the part of the index file containing this index. */
1281 /*********************************************************************/
1282 if (!(mbase = (BYTE*)X->FileView(g, fn)))
1283 goto err;
1284
1285 if (id >= 0) {
1286 // Get offset from the header
1287 IOFF *noff = (IOFF*)mbase;
1288
1289 // Position the memory base at the offset of this index
1290 mbase += noff[id].v.Low;
1291 } // endif id
1292
1293 // Now start the mapping process.
1294 nv = (int*)mbase;
1295
1296 if (nv[0] >= MAX_INDX) {
1297 // New index format
1298 Srtd = nv[7] != 0;
1299 nv0 = nv[0] - MAX_INDX;
1300 mbase += NZ * sizeof(int);
1301 } else {
1302 Srtd = false;
1303 mbase += (NZ - 1) * sizeof(int);
1304 nv0 = nv[0];
1305 } // endif nv
1306
1307 if (trace(1))
1308 htrc("nv=%d %d %d %d %d %d %d %d\n",
1309 nv0, nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd);
1310
1311 // The test on ID was suppressed because MariaDB can change an index ID
1312 // when other indexes are added or deleted
1313 if (/*nv0 != ID ||*/ nv[1] != Nk) {
1314 // Not this index
1315 sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
1316
1317 if (trace(1))
1318 htrc("nv0=%d ID=%d nv[1]=%d Nk=%d\n", nv0, ID, nv[1], Nk);
1319
1320 goto err;
1321 } // endif nv
1322
1323 if (nv[2]) {
1324 // Set the offset array memory block
1325 Offset.Memp = mbase;
1326 Offset.Size = nv[2] * sizeof(int);
1327 Offset.Sub = true;
1328 Mul = true;
1329 Ndif = nv[2] - 1;
1330 mbase += Offset.Size;
1331 } else {
1332 Mul = false;
1333 Ndif = nv[3];
1334 } // endif nv[2]
1335
1336 if (nv[3] < n && estim)
1337 n = nv[3]; // n was just an evaluated max value
1338
1339 if (nv[3] != n) {
1340 sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
1341 goto err;
1342 } // endif
1343
1344 Num_K = nv[3];
1345 Incr = nv[4];
1346 Nblk = nv[5];
1347 Sblk = nv[6];
1348
1349 if (!Incr) {
1350 /*******************************************************************/
1351 /* Point to the storage that contains the file positions. */
1352 /*******************************************************************/
1353 Record.Size = Num_K * sizeof(int);
1354 Record.Memp = mbase;
1355 Record.Sub = true;
1356 mbase += Record.Size;
1357 } else
1358 Srtd = true; // Sorted positions can be calculated
1359
1360 /*********************************************************************/
1361 /* Allocate the KXYCOL blocks used to store column values. */
1362 /*********************************************************************/
1363 for (k = 0; k < Nk; k++) {
1364 if (k == Nval)
1365 To_LastVal = prev;
1366
1367 nv = (int*)mbase;
1368 mbase += (NW * sizeof(int));
1369
1370 colp = To_Cols[k];
1371
1372 if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
1373 (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
1374 sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
1375 goto err; // Error
1376 } // endif GetKey
1377
1378 kcp = new(g) KXYCOL(this);
1379
1380 if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
1381 goto err;
1382
1383 if (!kcp->Prefix)
1384 // Indicate that the key column value can be found from KXYCOL
1385 colp->SetKcol(kcp);
1386
1387 if (prev) {
1388 kcp->Previous = prev;
1389 prev->Next = kcp;
1390 } else
1391 To_KeyCol = kcp;
1392
1393 prev = kcp;
1394 } // endfor k
1395
1396 To_LastCol = prev;
1397
1398 if (Mul && prev)
1399 // Last key offset is the index offset
1400 kcp->Koff = Offset;
1401
1402 /*********************************************************************/
1403 /* No valid record read yet for secondary file. */
1404 /*********************************************************************/
1405 Cur_K = Num_K;
1406 return false;
1407
1408 err:
1409 Close();
1410 return true;
1411 } // end of MapInit
1412 #endif // XMAP
1413
1414 /***********************************************************************/
1415 /* Get Ndif and Num_K from the index file. */
1416 /***********************************************************************/
GetAllSizes(PGLOBAL g,int & numk)1417 bool XINDEX::GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk)
1418 {
1419 PCSZ ftype;
1420 char fn[_MAX_PATH];
1421 int nv[NZ], id = -1; // n
1422 //bool estim = false;
1423 bool rc = true;
1424 PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
1425
1426 // ndif = numk = 0;
1427 numk = 0;
1428
1429 #if 0
1430 /*********************************************************************/
1431 /* Get the estimated table size. */
1432 /* Note: for fixed tables we must use cardinality to avoid the call */
1433 /* to MaxBlkSize that could reduce the cardinality value. */
1434 /*********************************************************************/
1435 if (Tdbp->Cardinality(NULL)) {
1436 // For DBF tables, Cardinality includes bad or soft deleted lines
1437 // that are not included in the index, and can be larger then the
1438 // index size.
1439 estim = (Tdbp->Ftype == RECFM_DBF);
1440 n = Tdbp->Cardinality(g); // n is exact table size
1441 } else {
1442 // Variable table not optimized
1443 estim = true; // n is an estimate of the size
1444 n = Tdbp->GetMaxSize(g);
1445 } // endif Cardinality
1446
1447 if (n <= 0)
1448 return !(n == 0); // n < 0 error, n = 0 void table
1449
1450 /*********************************************************************/
1451 /* Check the key part number. */
1452 /*********************************************************************/
1453 if (!Nk) {
1454 strcpy(g->Message, MSG(NO_KEY_COL));
1455 return true; // Error
1456 } // endif Nk
1457 #endif // 0
1458
1459 switch (Tdbp->Ftype) {
1460 case RECFM_VAR: ftype = ".dnx"; break;
1461 case RECFM_FIX: ftype = ".fnx"; break;
1462 case RECFM_BIN: ftype = ".bnx"; break;
1463 case RECFM_VCT: ftype = ".vnx"; break;
1464 case RECFM_CSV: ftype = ".cnx"; break;
1465 case RECFM_DBF: ftype = ".dbx"; break;
1466 default:
1467 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
1468 return true;
1469 } // endswitch Ftype
1470
1471 if (defp->SepIndex()) {
1472 // Index was saved in a separate file
1473 #if defined(_WIN32)
1474 char drive[_MAX_DRIVE];
1475 #else
1476 char *drive = NULL;
1477 #endif
1478 char direc[_MAX_DIR];
1479 char fname[_MAX_FNAME];
1480
1481 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
1482 strcat(strcat(fname, "_"), Xdp->GetName());
1483 _makepath(fn, drive, direc, fname, ftype);
1484 } else {
1485 id = ID;
1486 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
1487 } // endif sep
1488
1489 PlugSetPath(fn, fn, Tdbp->GetPath());
1490
1491 if (trace(1))
1492 htrc("Index %s file: %s\n", Xdp->GetName(), fn);
1493
1494 /*********************************************************************/
1495 /* Open the index file and check its validity. */
1496 /*********************************************************************/
1497 if (X->Open(g, fn, id, MODE_READ))
1498 goto err; // No saved values
1499
1500 // Get offset from XDB file
1501 //if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
1502 // goto err;
1503
1504 // Now start the reading process.
1505 if (X->Read(g, nv, NZ, sizeof(int)))
1506 goto err;
1507
1508 if (trace(1))
1509 htrc("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
1510
1511 // The test on ID was suppressed because MariaDB can change an index ID
1512 // when other indexes are added or deleted
1513 if (/*nv[0] != ID ||*/ nv[1] != Nk) {
1514 sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
1515
1516 if (trace(1))
1517 htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
1518
1519 goto err;
1520 } // endif
1521
1522 #if 0
1523 if (nv[2]) {
1524 Mul = true;
1525 Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
1526 } else {
1527 Mul = false;
1528 Ndif = nv[3];
1529 } // endif nv[2]
1530
1531 if (nv[3] < n && estim)
1532 n = nv[3]; // n was just an evaluated max value
1533
1534 if (nv[3] != n) {
1535 sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
1536 goto err;
1537 } // endif
1538 #endif // 0
1539
1540 Num_K = nv[3];
1541
1542 #if 0
1543 if (Nk > 1) {
1544 if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
1545 goto err;
1546
1547 if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
1548 goto err;
1549
1550 if (X->Read(g, nv, NW, sizeof(int)))
1551 goto err;
1552
1553 PCOL colp = *To_Cols;
1554
1555 if (nv[4] != colp->GetResultType() ||
1556 (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
1557 sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
1558 goto err; // Error
1559 } // endif GetKey
1560
1561 Ndif = nv[0];
1562 } // endif Nk
1563 #endif // 0
1564
1565 /*********************************************************************/
1566 /* Set size values. */
1567 /*********************************************************************/
1568 //ndif = Ndif;
1569 numk = Num_K;
1570 rc = false;
1571
1572 err:
1573 X->Close();
1574 return rc;
1575 } // end of GetAllSizes
1576
1577 /***********************************************************************/
1578 /* RANGE: Tell how many records exist for a given value, for an array */
1579 /* of values, or in a given value range. */
1580 /***********************************************************************/
Range(PGLOBAL g,int limit,bool incl)1581 int XINDEX::Range(PGLOBAL g, int limit, bool incl)
1582 {
1583 int i, k, n = 0;
1584 PXOB *xp = To_Vals;
1585 PXCOL kp = To_KeyCol;
1586 OPVAL op = Op;
1587
1588 switch (limit) {
1589 case 1: Op = (incl) ? OP_GE : OP_GT; break;
1590 case 2: Op = (incl) ? OP_GT : OP_GE; break;
1591 default: return 0;
1592 } // endswitch limit
1593
1594 /*********************************************************************/
1595 /* Currently only range of constant values with an EQ operator is */
1596 /* implemented. Find the number of rows for each given values. */
1597 /*********************************************************************/
1598 if (xp[0]->GetType() == TYPE_CONST) {
1599 for (i = 0; kp; kp = kp->Next) {
1600 kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
1601 if (++i == Nval) break;
1602 } // endfor kp
1603
1604 if ((k = FastFind()) < Num_K)
1605 n = k;
1606 // if (limit)
1607 // n = (Mul) ? k : kp->Val_K;
1608 // else
1609 // n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
1610
1611 } else {
1612 strcpy(g->Message, MSG(RANGE_NO_JOIN));
1613 n = -1; // Logical error
1614 } // endif'f Type
1615
1616 Op = op;
1617 return n;
1618 } // end of Range
1619
1620 /***********************************************************************/
1621 /* Return the size of the group (equal values) of the current value. */
1622 /***********************************************************************/
GroupSize(void)1623 int XINDEX::GroupSize(void)
1624 {
1625 #if defined(_DEBUG)
1626 assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
1627 #endif // _DEBUG
1628
1629 if (Nval == Nk)
1630 return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
1631 : 1;
1632
1633 #if defined(_DEBUG)
1634 assert(To_LastVal);
1635 #endif // _DEBUG
1636
1637 // Index whose only some columns are used
1638 int ck1, ck2;
1639
1640 ck1 = To_LastVal->Val_K;
1641 ck2 = ck1 + 1;
1642
1643 #if defined(_DEBUG)
1644 assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
1645 #endif // _DEBUG
1646
1647 for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
1648 ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
1649 ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
1650 } // endfor kcp
1651
1652 return ck2 - ck1;
1653 } // end of GroupSize
1654
1655 /***********************************************************************/
1656 /* Find Cur_K and Val_K's of the next distinct value of the index. */
1657 /* Returns false if Ok, true if there are no more different values. */
1658 /***********************************************************************/
NextValDif(void)1659 bool XINDEX::NextValDif(void)
1660 {
1661 int curk;
1662 PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
1663
1664 if (++kcp->Val_K < kcp->Ndf) {
1665 Cur_K = curk = kcp->Val_K;
1666
1667 // (Cur_K return is currently not used by SQLGBX)
1668 for (PXCOL kp = kcp; kp; kp = kp->Next)
1669 Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
1670
1671 } else
1672 return true;
1673
1674 for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
1675 if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
1676 break; // all previous columns have same value
1677
1678 curk = ++kcp->Val_K; // This is a break, get new column value
1679 } // endfor kcp
1680
1681 return false;
1682 } // end of NextValDif
1683
1684 /***********************************************************************/
1685 /* XINDEX: Find Cur_K and Val_K's of next index entry. */
1686 /* If eq is true next values must be equal to last ones up to Nval. */
1687 /* Returns false if Ok, true if there are no more (equal) values. */
1688 /***********************************************************************/
NextVal(bool eq)1689 bool XINDEX::NextVal(bool eq)
1690 {
1691 int n, neq = Nk + 1, curk;
1692 PXCOL kcp;
1693
1694 if (Cur_K == Num_K)
1695 return true;
1696 else
1697 curk = ++Cur_K;
1698
1699 for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
1700 if (kcp->Kof) {
1701 if (curk == kcp->Kof[kcp->Val_K + 1])
1702 neq = n;
1703
1704 } else {
1705 #ifdef _DEBUG
1706 assert(curk == kcp->Val_K + 1);
1707 #endif // _DEBUG
1708 neq = n;
1709 } // endif Kof
1710
1711 #ifdef _DEBUG
1712 assert(kcp->Val_K < kcp->Ndf);
1713 #endif // _DEBUG
1714
1715 // If this is not a break...
1716 if (neq > n)
1717 break; // all previous columns have same value
1718
1719 curk = ++kcp->Val_K; // This is a break, get new column value
1720 } // endfor kcp
1721
1722 // Return true if no more values or, in case of "equal" values,
1723 // if the last used column value has changed
1724 return (Cur_K == Num_K || (eq && neq <= Nval));
1725 } // end of NextVal
1726
1727 /***********************************************************************/
1728 /* XINDEX: Find Cur_K and Val_K's of previous index entry. */
1729 /* Returns false if Ok, true if there are no more values. */
1730 /***********************************************************************/
PrevVal(void)1731 bool XINDEX::PrevVal(void)
1732 {
1733 int n, neq = Nk + 1, curk;
1734 PXCOL kcp;
1735
1736 if (Cur_K == 0)
1737 return true;
1738 else
1739 curk = --Cur_K;
1740
1741 for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
1742 if (kcp->Kof) {
1743 if (curk < kcp->Kof[kcp->Val_K])
1744 neq = n;
1745
1746 } else {
1747 #ifdef _DEBUG
1748 assert(curk == kcp->Val_K -1);
1749 #endif // _DEBUG
1750 neq = n;
1751 } // endif Kof
1752
1753 #ifdef _DEBUG
1754 assert(kcp->Val_K >= 0);
1755 #endif // _DEBUG
1756
1757 // If this is not a break...
1758 if (neq > n)
1759 break; // all previous columns have same value
1760
1761 curk = --kcp->Val_K; // This is a break, get new column value
1762 } // endfor kcp
1763
1764 return false;
1765 } // end of PrevVal
1766
1767 /***********************************************************************/
1768 /* XINDEX: Fetch a physical or logical record. */
1769 /***********************************************************************/
Fetch(PGLOBAL g)1770 int XINDEX::Fetch(PGLOBAL g)
1771 {
1772 int n;
1773 PXCOL kp;
1774
1775 if (Num_K == 0)
1776 return -1; // means end of file
1777
1778 if (trace(2))
1779 htrc("XINDEX Fetch: Op=%d\n", Op);
1780
1781 /*********************************************************************/
1782 /* Table read through a sorted index. */
1783 /*********************************************************************/
1784 switch (Op) {
1785 case OP_NEXT: // Read next
1786 if (NextVal(false))
1787 return -1; // End of indexed file
1788
1789 break;
1790 case OP_FIRST: // Read first
1791 for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
1792 kp->Val_K = 0;
1793
1794 Op = OP_NEXT;
1795 break;
1796 case OP_SAME: // Read next same
1797 // Logically the key values should be the same as before
1798 if (NextVal(true)) {
1799 Op = OP_EQ;
1800 return -2; // no more equal values
1801 } // endif NextVal
1802
1803 break;
1804 case OP_NXTDIF: // Read next dif
1805 // while (!NextVal(true)) ;
1806
1807 // if (Cur_K >= Num_K)
1808 // return -1; // End of indexed file
1809 if (NextValDif())
1810 return -1; // End of indexed file
1811
1812 break;
1813 case OP_FSTDIF: // Read first diff
1814 for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
1815 kp->Val_K = 0;
1816
1817 Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
1818 break;
1819 case OP_LAST: // Read last key
1820 for (Cur_K = Num_K - 1, kp = To_KeyCol; kp; kp = kp->Next)
1821 kp->Val_K = kp->Kblp->GetNval() - 1;
1822
1823 Op = OP_NEXT;
1824 break;
1825 case OP_PREV: // Read previous
1826 if (PrevVal())
1827 return -1; // End of indexed file
1828
1829 break;
1830 default: // Should be OP_EQ
1831 // if (Tbxp->Key_Rank < 0) {
1832 /***************************************************************/
1833 /* Look for the first key equal to the link column values */
1834 /* and return its rank whithin the index table. */
1835 /***************************************************************/
1836 for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
1837 if (kp->InitFind(g, To_Vals[n]))
1838 return -1; // No more constant values
1839
1840 Nth++;
1841
1842 if (trace(2))
1843 htrc("Fetch: Looking for new value Nth=%d\n", Nth);
1844
1845 Cur_K = FastFind();
1846
1847 if (Cur_K >= Num_K)
1848 /*************************************************************/
1849 /* Rank not whithin index table, signal record not found. */
1850 /*************************************************************/
1851 return -2;
1852
1853 else if (Mul || Nval < Nk)
1854 Op = OP_SAME;
1855
1856 } // endswitch Op
1857
1858 /*********************************************************************/
1859 /* If rank is equal to stored rank, record is already there. */
1860 /*********************************************************************/
1861 if (Cur_K == Old_K)
1862 return -3; // Means record already there
1863 else
1864 Old_K = Cur_K; // Store rank of newly read record
1865
1866 /*********************************************************************/
1867 /* Return the position of the required record. */
1868 /*********************************************************************/
1869 return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
1870 } // end of Fetch
1871
1872 /***********************************************************************/
1873 /* FastFind: Returns the index of matching record in a join using an */
1874 /* optimized algorithm based on dichotomie and optimized comparing. */
1875 /***********************************************************************/
FastFind(void)1876 int XINDEX::FastFind(void)
1877 {
1878 int curk, sup, inf, i= 0, k, n = 2;
1879 PXCOL kp, kcp;
1880
1881 //assert((int)nv == Nval);
1882
1883 if (Nblk && Op == OP_EQ) {
1884 // Look in block values to find in which block to search
1885 sup = Nblk;
1886 inf = -1;
1887
1888 while (n && sup - inf > 1) {
1889 i = (inf + sup) >> 1;
1890
1891 n = To_KeyCol->CompBval(i);
1892
1893 if (n < 0)
1894 sup = i;
1895 else
1896 inf = i;
1897
1898 } // endwhile
1899
1900 if (inf < 0)
1901 return Num_K;
1902
1903 // i = inf;
1904 inf *= Sblk;
1905
1906 if ((sup = inf + Sblk) > To_KeyCol->Ndf)
1907 sup = To_KeyCol->Ndf;
1908
1909 inf--;
1910 } else {
1911 inf = -1;
1912 sup = To_KeyCol->Ndf;
1913 } // endif Nblk
1914
1915 if (trace(4))
1916 htrc("XINDEX FastFind: Nblk=%d Op=%d inf=%d sup=%d\n",
1917 Nblk, Op, inf, sup);
1918
1919 for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
1920 while (sup - inf > 1) {
1921 i = (inf + sup) >> 1;
1922
1923 n = kcp->CompVal(i);
1924
1925 if (n < 0)
1926 sup = i;
1927 else if (n > 0)
1928 inf = i;
1929 else
1930 break;
1931
1932 } // endwhile
1933
1934 if (n) {
1935 if (Op != OP_EQ) {
1936 // Currently only OP_GT or OP_GE
1937 kcp->Val_K = curk = sup;
1938
1939 // Check for value changes in previous key parts
1940 for (kp = kcp->Previous; kp; kp = kp->Previous)
1941 if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
1942 break;
1943 else
1944 curk = ++kp->Val_K;
1945
1946 n = 0;
1947 } // endif Op
1948
1949 break;
1950 } // endif n
1951
1952 kcp->Val_K = i;
1953
1954 if (++k == Nval) {
1955 if (Op == OP_GT) { // n is always 0
1956 curk = ++kcp->Val_K; // Increment value by 1
1957
1958 // Check for value changes in previous key parts
1959 for (kp = kcp->Previous; kp; kp = kp->Previous)
1960 if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
1961 break; // Not changed
1962 else
1963 curk = ++kp->Val_K;
1964
1965 } // endif Op
1966
1967 break; // So kcp remains pointing the last tested block
1968 } // endif k
1969
1970 if (kcp->Kof) {
1971 inf = kcp->Kof[i] - 1;
1972 sup = kcp->Kof[i + 1];
1973 } else {
1974 inf = i - 1;
1975 sup = i + 1;
1976 } // endif Kof
1977
1978 } // endfor k, kcp
1979
1980 if (n) {
1981 // Record not found
1982 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
1983 kcp->Val_K = kcp->Ndf; // Not a valid value
1984
1985 return Num_K;
1986 } // endif n
1987
1988 for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
1989 kcp->Val_K = curk;
1990 curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
1991 } // endfor kcp
1992
1993 if (trace(4))
1994 htrc("XINDEX FastFind: curk=%d\n", curk);
1995
1996 return curk;
1997 } // end of FastFind
1998
1999 /* -------------------------- XINDXS Class --------------------------- */
2000
2001 /***********************************************************************/
2002 /* XINDXS public constructor. */
2003 /***********************************************************************/
XINDXS(PTDBDOS tdbp,PIXDEF xdp,PXLOAD pxp,PCOL * cp,PXOB * xp)2004 XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
2005 : XINDEX(tdbp, xdp, pxp, cp, xp)
2006 {
2007 Srtd = To_Cols[0]->GetOpt() == 2;
2008 } // end of XINDXS constructor
2009
2010 /***********************************************************************/
2011 /* XINDXS compare routine for C Quick/Insertion sort. */
2012 /***********************************************************************/
Qcompare(int * i1,int * i2)2013 int XINDXS::Qcompare(int *i1, int *i2)
2014 {
2015 //num_comp++;
2016 return To_KeyCol->Compare(*i1, *i2);
2017 } // end of Qcompare
2018
2019 /***********************************************************************/
2020 /* Range: Tell how many records exist for given value(s): */
2021 /* If limit=0 return range for these values. */
2022 /* If limit=1 return the start of range. */
2023 /* If limit=2 return the end of range. */
2024 /***********************************************************************/
Range(PGLOBAL g,int limit,bool incl)2025 int XINDXS::Range(PGLOBAL g, int limit, bool incl)
2026 {
2027 int k, n = 0;
2028 PXOB xp = To_Vals[0];
2029 PXCOL kp = To_KeyCol;
2030 OPVAL op = Op;
2031
2032 switch (limit) {
2033 case 1: Op = (incl) ? OP_GE : OP_GT; break;
2034 case 2: Op = (incl) ? OP_GT : OP_GE; break;
2035 default: Op = OP_EQ;
2036 } // endswitch limit
2037
2038 /*********************************************************************/
2039 /* Currently only range of constant values with an EQ operator is */
2040 /* implemented. Find the number of rows for each given values. */
2041 /*********************************************************************/
2042 if (xp->GetType() == TYPE_CONST) {
2043 kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
2044 k = FastFind();
2045
2046 if (k < Num_K || Op != OP_EQ)
2047 {
2048 if (limit)
2049 n = (Mul) ? k : kp->Val_K;
2050 else
2051 n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
2052 }
2053 } else {
2054 strcpy(g->Message, MSG(RANGE_NO_JOIN));
2055 n = -1; // Logical error
2056 } // endif'f Type
2057
2058 Op = op;
2059 return n;
2060 } // end of Range
2061
2062 /***********************************************************************/
2063 /* Return the size of the group (equal values) of the current value. */
2064 /***********************************************************************/
GroupSize(void)2065 int XINDXS::GroupSize(void)
2066 {
2067 #if defined(_DEBUG)
2068 assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
2069 #endif // _DEBUG
2070 return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K] : 1;
2071 } // end of GroupSize
2072
2073 /***********************************************************************/
2074 /* XINDXS: Find Cur_K and Val_K of previous index value. */
2075 /* Returns false if Ok, true if there are no more values. */
2076 /***********************************************************************/
PrevVal(void)2077 bool XINDXS::PrevVal(void)
2078 {
2079 if (--Cur_K < 0)
2080 return true;
2081
2082 if (Mul) {
2083 if (Cur_K < Pof[To_KeyCol->Val_K])
2084 To_KeyCol->Val_K--;
2085
2086 } else
2087 To_KeyCol->Val_K = Cur_K;
2088
2089 return false;
2090 } // end of PrevVal
2091
2092 /***********************************************************************/
2093 /* XINDXS: Find Cur_K and Val_K of next index value. */
2094 /* If b is true next value must be equal to last one. */
2095 /* Returns false if Ok, true if there are no more (equal) values. */
2096 /***********************************************************************/
NextVal(bool eq)2097 bool XINDXS::NextVal(bool eq)
2098 {
2099 bool rc;
2100
2101 if (To_KeyCol->Val_K == Ndif)
2102 return true;
2103
2104 if (Mul) {
2105 int limit = Pof[To_KeyCol->Val_K + 1];
2106
2107 #ifdef _DEBUG
2108 assert(Cur_K < limit);
2109 assert(To_KeyCol->Val_K < Ndif);
2110 #endif // _DEBUG
2111
2112 if (++Cur_K == limit) {
2113 To_KeyCol->Val_K++;
2114 rc = (eq || limit == Num_K);
2115 } else
2116 rc = false;
2117
2118 } else
2119 rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
2120
2121 return rc;
2122 } // end of NextVal
2123
2124 /***********************************************************************/
2125 /* XINDXS: Fetch a physical or logical record. */
2126 /***********************************************************************/
Fetch(PGLOBAL g)2127 int XINDXS::Fetch(PGLOBAL g)
2128 {
2129 if (Num_K == 0)
2130 return -1; // means end of file
2131
2132 if (trace(2))
2133 htrc("XINDXS Fetch: Op=%d\n", Op);
2134
2135 /*********************************************************************/
2136 /* Table read through a sorted index. */
2137 /*********************************************************************/
2138 switch (Op) {
2139 case OP_NEXT: // Read next
2140 if (NextVal(false))
2141 return -1; // End of indexed file
2142
2143 break;
2144 case OP_FIRST: // Read first
2145 To_KeyCol->Val_K = Cur_K = 0;
2146 Op = OP_NEXT;
2147 break;
2148 case OP_SAME: // Read next same
2149 if (!Mul || NextVal(true)) {
2150 Op = OP_EQ;
2151 return -2; // No more equal values
2152 } // endif Mul
2153
2154 break;
2155 case OP_NXTDIF: // Read next dif
2156 if (++To_KeyCol->Val_K == Ndif)
2157 return -1; // End of indexed file
2158
2159 Cur_K = Pof[To_KeyCol->Val_K];
2160 break;
2161 case OP_FSTDIF: // Read first diff
2162 To_KeyCol->Val_K = Cur_K = 0;
2163 Op = (Mul) ? OP_NXTDIF : OP_NEXT;
2164 break;
2165 case OP_LAST: // Read first
2166 Cur_K = Num_K - 1;
2167 To_KeyCol->Val_K = Ndif - 1;
2168 Op = OP_PREV;
2169 break;
2170 case OP_PREV: // Read previous
2171 if (PrevVal())
2172 return -1; // End of indexed file
2173
2174 break;
2175 default: // Should be OP_EQ
2176 /*****************************************************************/
2177 /* Look for the first key equal to the link column values */
2178 /* and return its rank whithin the index table. */
2179 /*****************************************************************/
2180 if (To_KeyCol->InitFind(g, To_Vals[0]))
2181 return -1; // No more constant values
2182 else
2183 Nth++;
2184
2185 if (trace(2))
2186 htrc("Fetch: Looking for new value Nth=%d\n", Nth);
2187
2188 Cur_K = FastFind();
2189
2190 if (Cur_K >= Num_K)
2191 // Rank not whithin index table, signal record not found
2192 return -2;
2193 else if (Mul)
2194 Op = OP_SAME;
2195
2196 } // endswitch Op
2197
2198 /*********************************************************************/
2199 /* If rank is equal to stored rank, record is already there. */
2200 /*********************************************************************/
2201 if (Cur_K == Old_K)
2202 return -3; // Means record already there
2203 else
2204 Old_K = Cur_K; // Store rank of newly read record
2205
2206 /*********************************************************************/
2207 /* Return the position of the required record. */
2208 /*********************************************************************/
2209 return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
2210 } // end of Fetch
2211
2212 /***********************************************************************/
2213 /* FastFind: Returns the index of matching indexed record using an */
2214 /* optimized algorithm based on dichotomie and optimized comparing. */
2215 /***********************************************************************/
FastFind(void)2216 int XINDXS::FastFind(void)
2217 {
2218 int sup, inf, i= 0, n = 2;
2219 PXCOL kcp = To_KeyCol;
2220
2221 if (Nblk && Op == OP_EQ) {
2222 // Look in block values to find in which block to search
2223 sup = Nblk;
2224 inf = -1;
2225
2226 while (n && sup - inf > 1) {
2227 i = (inf + sup) >> 1;
2228
2229 n = kcp->CompBval(i);
2230
2231 if (n < 0)
2232 sup = i;
2233 else
2234 inf = i;
2235
2236 } // endwhile
2237
2238 if (inf < 0)
2239 return Num_K;
2240
2241 inf *= Sblk;
2242
2243 if ((sup = inf + Sblk) > Ndif)
2244 sup = Ndif;
2245
2246 inf--;
2247 } else {
2248 inf = -1;
2249 sup = Ndif;
2250 } // endif Nblk
2251
2252 if (trace(4))
2253 htrc("XINDXS FastFind: Nblk=%d Op=%d inf=%d sup=%d\n",
2254 Nblk, Op, inf, sup);
2255
2256 while (sup - inf > 1) {
2257 i = (inf + sup) >> 1;
2258
2259 n = kcp->CompVal(i);
2260
2261 if (n < 0)
2262 sup = i;
2263 else if (n > 0)
2264 inf = i;
2265 else
2266 break;
2267
2268 } // endwhile
2269
2270 if (!n && Op == OP_GT) {
2271 ++i;
2272 } else if (n && Op != OP_EQ) {
2273 // Currently only OP_GT or OP_GE
2274 i = sup;
2275 n = 0;
2276 } // endif sup
2277
2278 if (trace(4))
2279 htrc("XINDXS FastFind: n=%d i=%d\n", n, i);
2280
2281 // Loop on kcp because of dynamic indexing
2282 for (; kcp; kcp = kcp->Next)
2283 kcp->Val_K = i; // Used by FillValue
2284
2285 return ((n) ? Num_K : (Mul) ? Pof[i] : i);
2286 } // end of FastFind
2287
2288 /* -------------------------- XLOAD Class --------------------------- */
2289
2290 /***********************************************************************/
2291 /* XLOAD constructor. */
2292 /***********************************************************************/
XLOAD(void)2293 XLOAD::XLOAD(void)
2294 {
2295 Hfile = INVALID_HANDLE_VALUE;
2296 NewOff.Val = 0LL;
2297 } // end of XLOAD constructor
2298
2299 /***********************************************************************/
2300 /* Close the index huge file. */
2301 /***********************************************************************/
Close(void)2302 void XLOAD::Close(void)
2303 {
2304 if (Hfile != INVALID_HANDLE_VALUE) {
2305 CloseFileHandle(Hfile);
2306 Hfile = INVALID_HANDLE_VALUE;
2307 } // endif Hfile
2308
2309 } // end of Close
2310
2311 /* --------------------------- XFILE Class --------------------------- */
2312
2313 /***********************************************************************/
2314 /* XFILE constructor. */
2315 /***********************************************************************/
XFILE(void)2316 XFILE::XFILE(void) : XLOAD()
2317 {
2318 Xfile = NULL;
2319 #if defined(XMAP)
2320 Mmp = NULL;
2321 #endif // XMAP
2322 } // end of XFILE constructor
2323
2324 /***********************************************************************/
2325 /* Xopen function: opens a file using native API's. */
2326 /***********************************************************************/
Open(PGLOBAL g,char * filename,int id,MODE mode)2327 bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
2328 {
2329 PCSZ pmod;
2330 bool rc;
2331 IOFF noff[MAX_INDX];
2332
2333 /*********************************************************************/
2334 /* Open the index file according to mode. */
2335 /*********************************************************************/
2336 switch (mode) {
2337 case MODE_READ: pmod = "rb"; break;
2338 case MODE_WRITE: pmod = "wb"; break;
2339 case MODE_INSERT: pmod = "ab"; break;
2340 default:
2341 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2342 return true;
2343 } // endswitch mode
2344
2345 if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
2346 if (trace(1))
2347 htrc("Open: %s\n", g->Message);
2348
2349 return true;
2350 } // endif Xfile
2351
2352 if (mode == MODE_INSERT) {
2353 /*******************************************************************/
2354 /* Position the cursor at end of file so ftell returns file size. */
2355 /*******************************************************************/
2356 if (fseek(Xfile, 0, SEEK_END)) {
2357 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2358 return true;
2359 } // endif
2360
2361 NewOff.v.Low = (int)ftell(Xfile);
2362
2363 if (trace(1))
2364 htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
2365
2366 } else if (mode == MODE_WRITE) {
2367 if (id >= 0) {
2368 // New not sep index file. Write the header.
2369 memset(noff, 0, sizeof(noff));
2370 Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
2371 fseek(Xfile, 0, SEEK_END);
2372 NewOff.v.Low = (int)ftell(Xfile);
2373
2374 if (trace(1))
2375 htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
2376
2377 } // endif id
2378
2379 } else if (mode == MODE_READ && id >= 0) {
2380 // Get offset from the header
2381 if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
2382 sprintf(g->Message, MSG(XFILE_READERR), errno);
2383 return true;
2384 } // endif MAX_INDX
2385
2386 if (trace(1))
2387 htrc("XFILE Open: noff[%d].v.Low=%d\n", id, noff[id].v.Low);
2388
2389 // Position the cursor at the offset of this index
2390 if (fseek(Xfile, noff[id].v.Low, SEEK_SET)) {
2391 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2392 return true;
2393 } // endif
2394
2395 } // endif mode
2396
2397 return false;
2398 } // end of Open
2399
2400 /***********************************************************************/
2401 /* Move into an index file. */
2402 /***********************************************************************/
Seek(PGLOBAL g,int low,int high,int origin)2403 bool XFILE::Seek(PGLOBAL g, int low, int high __attribute__((unused)),
2404 int origin)
2405 {
2406 #if defined(_DEBUG)
2407 assert(high == 0);
2408 #endif // !_DEBUG
2409
2410 if (fseek(Xfile, low, origin)) {
2411 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2412 return true;
2413 } // endif
2414
2415 return false;
2416 } // end of Seek
2417
2418 /***********************************************************************/
2419 /* Read from the index file. */
2420 /***********************************************************************/
Read(PGLOBAL g,void * buf,int n,int size)2421 bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
2422 {
2423 if (fread(buf, size, n, Xfile) != (size_t)n) {
2424 sprintf(g->Message, MSG(XFILE_READERR), errno);
2425 return true;
2426 } // endif size
2427
2428 return false;
2429 } // end of Read
2430
2431 /***********************************************************************/
2432 /* Write on index file, set rc and return the number of bytes written */
2433 /***********************************************************************/
Write(PGLOBAL g,void * buf,int n,int size,bool & rc)2434 int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
2435 {
2436 int niw = (int)fwrite(buf, size, n, Xfile);
2437
2438 if (niw != n) {
2439 sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
2440 rc = true;
2441 } // endif size
2442
2443 return niw * size;
2444 } // end of Write
2445
2446 /***********************************************************************/
2447 /* Update the file header and close the index file. */
2448 /***********************************************************************/
Close(char * fn,int id)2449 void XFILE::Close(char *fn, int id)
2450 {
2451 if (id >= 0 && fn && Xfile) {
2452 fclose(Xfile);
2453
2454 if ((Xfile = fopen(fn, "r+b")))
2455 if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
2456 fwrite(&NewOff, sizeof(int), 2, Xfile);
2457
2458 } // endif id
2459
2460 Close();
2461 } // end of Close
2462
2463 /***********************************************************************/
2464 /* Close the index file. */
2465 /***********************************************************************/
Close(void)2466 void XFILE::Close(void)
2467 {
2468 XLOAD::Close();
2469
2470 if (Xfile) {
2471 fclose(Xfile);
2472 Xfile = NULL;
2473 } // endif Xfile
2474
2475 #if defined(XMAP)
2476 if (Mmp && CloseMemMap(Mmp->memory, Mmp->lenL))
2477 printf("Error closing mapped index\n");
2478 #endif // XMAP
2479 } // end of Close
2480
2481 #if defined(XMAP)
2482 /*********************************************************************/
2483 /* Map the entire index file. */
2484 /*********************************************************************/
FileView(PGLOBAL g,char * fn)2485 void *XFILE::FileView(PGLOBAL g, char *fn)
2486 {
2487 HANDLE h;
2488
2489 Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
2490 h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
2491
2492 if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
2493 if (!(*g->Message))
2494 strcpy(g->Message, MSG(FILE_MAP_ERR));
2495
2496 CloseFileHandle(h); // Not used anymore
2497 return NULL; // No saved values
2498 } // endif h
2499
2500 CloseFileHandle(h); // Not used anymore
2501 return Mmp->memory;
2502 } // end of FileView
2503 #endif // XMAP
2504
2505 /* -------------------------- XHUGE Class --------------------------- */
2506
2507 /***********************************************************************/
2508 /* Xopen function: opens a file using native API's. */
2509 /***********************************************************************/
Open(PGLOBAL g,char * filename,int id,MODE mode)2510 bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
2511 {
2512 IOFF noff[MAX_INDX];
2513
2514 if (Hfile != INVALID_HANDLE_VALUE) {
2515 sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
2516 return true;
2517 } // endif
2518
2519 if (trace(1))
2520 htrc(" Xopen: filename=%s id=%d mode=%d\n", filename, id, mode);
2521
2522 #if defined(_WIN32)
2523 LONG high = 0;
2524 DWORD rc, drc, access, share, creation;
2525
2526 /*********************************************************************/
2527 /* Create the file object according to access mode */
2528 /*********************************************************************/
2529 switch (mode) {
2530 case MODE_READ:
2531 access = GENERIC_READ;
2532 share = FILE_SHARE_READ;
2533 creation = OPEN_EXISTING;
2534 break;
2535 case MODE_WRITE:
2536 access = GENERIC_WRITE;
2537 share = 0;
2538 creation = CREATE_ALWAYS;
2539 break;
2540 case MODE_INSERT:
2541 access = GENERIC_WRITE;
2542 share = 0;
2543 creation = OPEN_EXISTING;
2544 break;
2545 default:
2546 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2547 return true;
2548 } // endswitch
2549
2550 Hfile = CreateFile(filename, access, share, NULL, creation,
2551 FILE_ATTRIBUTE_NORMAL, NULL);
2552
2553 if (Hfile == INVALID_HANDLE_VALUE) {
2554 rc = GetLastError();
2555 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
2556 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2557 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
2558 (LPTSTR)filename, sizeof(filename), NULL);
2559 strcat(g->Message, filename);
2560 return true;
2561 } // endif Hfile
2562
2563 if (trace(1))
2564 htrc(" access=%p share=%p creation=%d handle=%p fn=%s\n",
2565 access, share, creation, Hfile, filename);
2566
2567 if (mode == MODE_INSERT) {
2568 /*******************************************************************/
2569 /* In Insert mode we must position the cursor at end of file. */
2570 /*******************************************************************/
2571 rc = SetFilePointer(Hfile, 0, &high, FILE_END);
2572
2573 if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
2574 sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
2575 CloseHandle(Hfile);
2576 Hfile = INVALID_HANDLE_VALUE;
2577 return true;
2578 } // endif
2579
2580 NewOff.v.Low = (int)rc;
2581 NewOff.v.High = (int)high;
2582 } else if (mode == MODE_WRITE) {
2583 if (id >= 0) {
2584 // New not sep index file. Write the header.
2585 memset(noff, 0, sizeof(noff));
2586 rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
2587 NewOff.v.Low = (int)drc;
2588 } // endif id
2589
2590 } else if (mode == MODE_READ && id >= 0) {
2591 // Get offset from the header
2592 rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
2593
2594 if (!rc) {
2595 sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
2596 return true;
2597 } // endif rc
2598
2599 // Position the cursor at the offset of this index
2600 rc = SetFilePointer(Hfile, noff[id].v.Low,
2601 (PLONG)&noff[id].v.High, FILE_BEGIN);
2602
2603 if (rc == INVALID_SET_FILE_POINTER) {
2604 sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
2605 return true;
2606 } // endif
2607
2608 } // endif Mode
2609
2610 #else // UNIX
2611 int oflag = O_LARGEFILE; // Enable file size > 2G
2612 mode_t pmod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
2613
2614 /*********************************************************************/
2615 /* Create the file object according to access mode */
2616 /*********************************************************************/
2617 switch (mode) {
2618 case MODE_READ:
2619 oflag |= O_RDONLY;
2620 break;
2621 case MODE_WRITE:
2622 oflag |= O_WRONLY | O_CREAT | O_TRUNC;
2623 // pmod = S_IREAD | S_IWRITE;
2624 break;
2625 case MODE_INSERT:
2626 oflag |= (O_WRONLY | O_APPEND);
2627 break;
2628 default:
2629 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2630 return true;
2631 } // endswitch
2632
2633 Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
2634
2635 if (Hfile == INVALID_HANDLE_VALUE) {
2636 /*rc = errno;*/
2637 if (trace(1))
2638 htrc("Open: %s\n", g->Message);
2639
2640 return true;
2641 } // endif Hfile
2642
2643 if (trace(1))
2644 htrc(" oflag=%p mode=%d handle=%d fn=%s\n",
2645 oflag, mode, Hfile, filename);
2646
2647 if (mode == MODE_INSERT) {
2648 /*******************************************************************/
2649 /* Position the cursor at end of file so ftell returns file size. */
2650 /*******************************************************************/
2651 if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
2652 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
2653 return true;
2654 } // endif
2655
2656 if (trace(1))
2657 htrc("INSERT: NewOff=%lld\n", NewOff.Val);
2658
2659 } else if (mode == MODE_WRITE) {
2660 if (id >= 0) {
2661 // New not sep index file. Write the header.
2662 memset(noff, 0, sizeof(noff));
2663 NewOff.v.Low = write(Hfile, &noff, sizeof(noff));
2664 } // endif id
2665
2666 if (trace(1))
2667 htrc("WRITE: NewOff=%lld\n", NewOff.Val);
2668
2669 } else if (mode == MODE_READ && id >= 0) {
2670 // Get offset from the header
2671 if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
2672 sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
2673 return true;
2674 } // endif read
2675
2676 if (trace(1))
2677 htrc("noff[%d]=%lld\n", id, noff[id].Val);
2678
2679 // Position the cursor at the offset of this index
2680 if (lseek64(Hfile, noff[id].Val, SEEK_SET) < 0) {
2681 sprintf(g->Message, "(XHUGE)lseek64: %s (%lld)", strerror(errno), noff[id].Val);
2682 printf("%s\n", g->Message);
2683 // sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
2684 return true;
2685 } // endif lseek64
2686
2687 } // endif mode
2688 #endif // UNIX
2689
2690 return false;
2691 } // end of Open
2692
2693 /***********************************************************************/
2694 /* Go to position in a huge file. */
2695 /***********************************************************************/
Seek(PGLOBAL g,int low,int high,int origin)2696 bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
2697 {
2698 #if defined(_WIN32)
2699 LONG hi = high;
2700 DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
2701
2702 if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
2703 sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
2704 return true;
2705 } // endif
2706
2707 #else // UNIX
2708 off64_t pos = (off64_t)low
2709 + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
2710
2711 if (lseek64(Hfile, pos, origin) < 0) {
2712 sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
2713
2714 if (trace(1))
2715 htrc("lseek64 error %d\n", errno);
2716
2717 return true;
2718 } // endif lseek64
2719
2720 if (trace(1))
2721 htrc("Seek: low=%d high=%d\n", low, high);
2722 #endif // UNIX
2723
2724 return false;
2725 } // end of Seek
2726
2727 /***********************************************************************/
2728 /* Read from a huge index file. */
2729 /***********************************************************************/
Read(PGLOBAL g,void * buf,int n,int size)2730 bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
2731 {
2732 bool rc = false;
2733
2734 #if defined(_WIN32)
2735 bool brc;
2736 DWORD nbr, count = (DWORD)(n * size);
2737
2738 brc = ReadFile(Hfile, buf, count, &nbr, NULL);
2739
2740 if (brc) {
2741 if (nbr != count) {
2742 strcpy(g->Message, MSG(EOF_INDEX_FILE));
2743 rc = true;
2744 } // endif nbr
2745
2746 } else {
2747 char buf[256];
2748 DWORD drc = GetLastError();
2749
2750 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2751 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
2752 (LPTSTR)buf, sizeof(buf), NULL);
2753 sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
2754 rc = true;
2755 } // endif brc
2756 #else // UNIX
2757 ssize_t count = (ssize_t)(n * size);
2758
2759 if (trace(1))
2760 htrc("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
2761
2762 if (read(Hfile, buf, count) != count) {
2763 sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
2764
2765 if (trace(1))
2766 htrc("read error %d\n", errno);
2767
2768 rc = true;
2769 } // endif nbr
2770 #endif // UNIX
2771
2772 return rc;
2773 } // end of Read
2774
2775 /***********************************************************************/
2776 /* Write on a huge index file. */
2777 /***********************************************************************/
Write(PGLOBAL g,void * buf,int n,int size,bool & rc)2778 int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
2779 {
2780 #if defined(_WIN32)
2781 bool brc;
2782 DWORD nbw, count = (DWORD)n * (DWORD) size;
2783
2784 brc = WriteFile(Hfile, buf, count, &nbw, NULL);
2785
2786 if (!brc) {
2787 char msg[256];
2788 DWORD drc = GetLastError();
2789
2790 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2791 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
2792 (LPTSTR)msg, sizeof(msg), NULL);
2793 sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
2794 rc = true;
2795 } // endif size
2796
2797 return (int)nbw;
2798 #else // UNIX
2799 ssize_t nbw;
2800 size_t count = (size_t)n * (size_t)size;
2801
2802 nbw = write(Hfile, buf, count);
2803
2804 if (nbw != (signed)count) {
2805 sprintf(g->Message, MSG(WRITING_ERROR),
2806 "index file", strerror(errno));
2807 rc = true;
2808 } // endif nbw
2809
2810 return (int)nbw;
2811 #endif // UNIX
2812 } // end of Write
2813
2814 /***********************************************************************/
2815 /* Update the file header and close the index file. */
2816 /***********************************************************************/
Close(char * fn,int id)2817 void XHUGE::Close(char *fn, int id)
2818 {
2819 if (trace(1))
2820 htrc("XHUGE::Close: fn=%s id=%d NewOff=%lld\n", fn, id, NewOff.Val);
2821
2822 #if defined(_WIN32)
2823 if (id >= 0 && fn) {
2824 CloseFileHandle(Hfile);
2825 Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2826 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2827
2828 if (Hfile != INVALID_HANDLE_VALUE)
2829 if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
2830 != INVALID_SET_FILE_POINTER) {
2831 DWORD nbw;
2832
2833 WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
2834 } // endif SetFilePointer
2835
2836 } // endif id
2837 #else // !_WIN32
2838 if (id >= 0 && fn) {
2839 if (Hfile != INVALID_HANDLE_VALUE) {
2840 if (lseek64(Hfile, id * sizeof(IOFF), SEEK_SET) >= 0) {
2841 ssize_t nbw = write(Hfile, &NewOff, sizeof(IOFF));
2842
2843 if (nbw != (signed)sizeof(IOFF))
2844 htrc("Error writing index file header: %s\n", strerror(errno));
2845
2846 } else
2847 htrc("(XHUGE::Close)lseek64: %s (%d)\n", strerror(errno), id);
2848
2849 } else
2850 htrc("(XHUGE)error reopening %s: %s\n", fn, strerror(errno));
2851
2852 } // endif id
2853 #endif // !_WIN32
2854
2855 XLOAD::Close();
2856 } // end of Close
2857
2858 #if defined(XMAP)
2859 /***********************************************************************/
2860 /* Don't know whether this is possible for huge files. */
2861 /***********************************************************************/
FileView(PGLOBAL g,char *)2862 void *XHUGE::FileView(PGLOBAL g, char *)
2863 {
2864 strcpy(g->Message, MSG(NO_PART_MAP));
2865 return NULL;
2866 } // end of FileView
2867 #endif // XMAP
2868
2869 /* -------------------------- XXROW Class --------------------------- */
2870
2871 /***********************************************************************/
2872 /* XXROW Public Constructor. */
2873 /***********************************************************************/
XXROW(PTDBDOS tdbp)2874 XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
2875 {
2876 Srtd = true;
2877 Tdbp = tdbp;
2878 Valp = NULL;
2879 } // end of XXROW constructor
2880
2881 /***********************************************************************/
2882 /* XXROW Reset: re-initialize a Kindex block. */
2883 /***********************************************************************/
Reset(void)2884 void XXROW::Reset(void)
2885 {
2886 #if defined(_DEBUG)
2887 assert(Tdbp->GetLink()); // This a join index
2888 #endif // _DEBUG
2889 } // end of Reset
2890
2891 /***********************************************************************/
2892 /* Init: Open and Initialize a Key Index. */
2893 /***********************************************************************/
Init(PGLOBAL g)2894 bool XXROW::Init(PGLOBAL g)
2895 {
2896 /*********************************************************************/
2897 /* Table will be accessed through an index table. */
2898 /* To_Link should not be NULL. */
2899 /*********************************************************************/
2900 if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
2901 return true;
2902
2903 if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
2904 strcpy(g->Message, MSG(TYPE_MISMATCH));
2905 return true;
2906 } else
2907 Valp = (*Tdbp->GetLink())->GetValue();
2908
2909 if ((Num_K = Tbxp->Cardinality(g)) < 0)
2910 return true; // Not a fixed file
2911
2912 /*********************************************************************/
2913 /* The entire table is indexed, no need to construct the index. */
2914 /*********************************************************************/
2915 Cur_K = Num_K;
2916 return false;
2917 } // end of Init
2918
2919 /***********************************************************************/
2920 /* RANGE: Tell how many record exist in a given value range. */
2921 /***********************************************************************/
Range(PGLOBAL,int limit,bool incl)2922 int XXROW::Range(PGLOBAL, int limit, bool incl)
2923 {
2924 int n = Valp->GetIntValue();
2925
2926 switch (limit) {
2927 case 1: n += ((incl) ? 0 : 1); break;
2928 case 2: n += ((incl) ? 1 : 0); break;
2929 default: n = 1;
2930 } // endswitch limit
2931
2932 return n;
2933 } // end of Range
2934
2935 /***********************************************************************/
2936 /* XXROW: Fetch a physical or logical record. */
2937 /***********************************************************************/
Fetch(PGLOBAL)2938 int XXROW::Fetch(PGLOBAL)
2939 {
2940 if (Num_K == 0)
2941 return -1; // means end of file
2942
2943 /*********************************************************************/
2944 /* Look for a key equal to the link column of previous table, */
2945 /* and return its rank whithin the index table. */
2946 /*********************************************************************/
2947 Cur_K = FastFind();
2948
2949 if (Cur_K >= Num_K)
2950 /*******************************************************************/
2951 /* Rank not whithin index table, signal record not found. */
2952 /*******************************************************************/
2953 return -2; // Means record not found
2954
2955 /*********************************************************************/
2956 /* If rank is equal to stored rank, record is already there. */
2957 /*********************************************************************/
2958 if (Cur_K == Old_K)
2959 return -3; // Means record already there
2960 else
2961 Old_K = Cur_K; // Store rank of newly read record
2962
2963 return Cur_K;
2964 } // end of Fetch
2965
2966 /***********************************************************************/
2967 /* FastFind: Returns the index of matching record in a join. */
2968 /***********************************************************************/
FastFind(void)2969 int XXROW::FastFind(void)
2970 {
2971 int n = Valp->GetIntValue();
2972
2973 if (n < 0)
2974 return (Op == OP_EQ) ? (-1) : 0;
2975 else if (n > Num_K)
2976 return Num_K;
2977 else
2978 return (Op == OP_GT) ? n : (n - 1);
2979
2980 } // end of FastFind
2981
2982 /* ------------------------- KXYCOL Classes -------------------------- */
2983
2984 /***********************************************************************/
2985 /* KXYCOL public constructor. */
2986 /***********************************************************************/
KXYCOL(PKXBASE kp)2987 KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
2988 To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
2989 {
2990 Next = NULL;
2991 Previous = NULL;
2992 Kxp = kp;
2993 Colp = NULL;
2994 IsSorted = false;
2995 Asc = true;
2996 Keys = Nmblk;
2997 Kblp = NULL;
2998 Bkeys = Nmblk;
2999 Blkp = NULL;
3000 Valp = NULL;
3001 Klen = 0;
3002 Kprec = 0;
3003 Type = TYPE_ERROR;
3004 Prefix = false;
3005 Koff = Nmblk;
3006 Val_K = 0;
3007 Ndf = 0;
3008 Mxs = 0;
3009 } // end of KXYCOL constructor
3010
3011 /***********************************************************************/
3012 /* KXYCOL Init: initialize and allocate storage. */
3013 /* Key length kln can be smaller than column length for CHAR columns. */
3014 /***********************************************************************/
Init(PGLOBAL g,PCOL colp,int n,bool sm,int kln)3015 bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
3016 {
3017 int len = colp->GetLength(), prec = colp->GetScale();
3018 bool un = colp->IsUnsigned();
3019
3020 // Currently no indexing on NULL columns
3021 if (colp->IsNullable() && kln) {
3022 sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
3023 return true;
3024 } // endif nullable
3025
3026 if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
3027 len = kln;
3028 Prefix = true;
3029 } // endif kln
3030
3031 if (trace(1))
3032 htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
3033 this, colp->GetName(), n, colp->GetResultType(), sm);
3034
3035 // Allocate the Value object used when moving items
3036 Type = colp->GetResultType();
3037
3038 if (!(Valp = AllocateValue(g, Type, len, prec, un)))
3039 return true;
3040
3041 Klen = Valp->GetClen();
3042 Keys.Size = (size_t)n * (size_t)Klen;
3043
3044 if (!PlgDBalloc(g, NULL, Keys)) {
3045 sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
3046 return true; // Error
3047 } // endif
3048
3049 // Allocate the Valblock. The last parameter is to have rows filled
3050 // by blanks (if true) or keep the zero ending char (if false).
3051 // Currently we set it to true to be compatible with QRY blocks,
3052 // and the one before last is to enable length/type checking, set to
3053 // true if not a prefix key.
3054 Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true, un);
3055 Asc = sm; // Sort mode: Asc=true Desc=false
3056 Ndf = n;
3057
3058 // Store this information to avoid sorting when already done
3059 if (Asc)
3060 IsSorted = colp->GetOpt() == 2;
3061
3062 //SetNulls(colp->IsNullable()); for when null columns will be indexable
3063 Colp = colp;
3064 return false;
3065 } // end of Init
3066
3067 #if defined(XMAP)
3068 /***********************************************************************/
3069 /* KXYCOL MapInit: initialize and address storage. */
3070 /* Key length kln can be smaller than column length for CHAR columns. */
3071 /***********************************************************************/
MapInit(PGLOBAL g,PCOL colp,int * n,BYTE * m)3072 BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
3073 {
3074 int len = colp->GetLength(), prec = colp->GetScale();
3075 bool un = colp->IsUnsigned();
3076
3077 if (n[3] && colp->GetLength() > n[3]
3078 && colp->GetResultType() == TYPE_STRING) {
3079 len = n[3];
3080 Prefix = true;
3081 } // endif kln
3082
3083 Type = colp->GetResultType();
3084
3085 if (trace(1))
3086 htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
3087 this, colp, Type, n[0], len, m);
3088
3089 // Allocate the Value object used when moving items
3090 Valp = AllocateValue(g, Type, len, prec, un);
3091 Klen = Valp->GetClen();
3092
3093 if (n[2]) {
3094 Bkeys.Size = n[2] * Klen;
3095 Bkeys.Memp = m;
3096 Bkeys.Sub = true;
3097
3098 // Allocate the Valblk containing initial block key values
3099 Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true, un);
3100 } // endif nb
3101
3102 Keys.Size = n[0] * Klen;
3103 Keys.Memp = m + Bkeys.Size;
3104 Keys.Sub = true;
3105
3106 // Allocate the Valblock. Last two parameters are to have rows filled
3107 // by blanks (if true) or keep the zero ending char (if false).
3108 // Currently we set it to true to be compatible with QRY blocks,
3109 // and last one to enable type checking (no conversion).
3110 Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, !Prefix, true, un);
3111
3112 if (n[1]) {
3113 Koff.Size = n[1] * sizeof(int);
3114 Koff.Memp = m + Bkeys.Size + Keys.Size;
3115 Koff.Sub = true;
3116 } // endif n[1]
3117
3118 Ndf = n[0];
3119 //IsSorted = colp->GetOpt() < 0;
3120 IsSorted = false;
3121 Colp = colp;
3122 return m + Bkeys.Size + Keys.Size + Koff.Size;
3123 } // end of MapInit
3124 #endif // XMAP
3125
3126 /***********************************************************************/
3127 /* Allocate the offset block used by intermediate key columns. */
3128 /***********************************************************************/
MakeOffset(PGLOBAL g,int n)3129 int *KXYCOL::MakeOffset(PGLOBAL g, int n)
3130 {
3131 if (!Kof) {
3132 // Calculate the initial size of the offset
3133 Koff.Size = (n + 1) * sizeof(int);
3134
3135 // Allocate the required memory
3136 if (!PlgDBalloc(g, NULL, Koff)) {
3137 strcpy(g->Message, MSG(KEY_ALLOC_ERR));
3138 return NULL; // Error
3139 } // endif
3140
3141 } else if (n) {
3142 // This is a reallocation call
3143 PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
3144 } else
3145 PlgDBfree(Koff);
3146
3147 return (int*)Kof;
3148 } // end of MakeOffset
3149
3150 /***********************************************************************/
3151 /* Make a front end array of key values that are the first value of */
3152 /* each blocks (of size n). This to reduce paging in FastFind. */
3153 /***********************************************************************/
MakeBlockArray(PGLOBAL g,int nb,int size)3154 bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
3155 {
3156 int i, k;
3157
3158 // Calculate the size of the block array in the index
3159 Bkeys.Size = nb * Klen;
3160
3161 // Allocate the required memory
3162 if (!PlgDBalloc(g, NULL, Bkeys)) {
3163 sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
3164 return true; // Error
3165 } // endif
3166
3167 // Allocate the Valblk used to contains initial block key values
3168 Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
3169
3170 // Populate the array with values
3171 for (i = k = 0; i < nb; i++, k += size)
3172 Blkp->SetValue(Kblp, i, k);
3173
3174 return false;
3175 } // end of MakeBlockArray
3176
3177 /***********************************************************************/
3178 /* KXYCOL SetValue: read column value for nth array element. */
3179 /***********************************************************************/
SetValue(PCOL colp,int i)3180 void KXYCOL::SetValue(PCOL colp, int i)
3181 {
3182 #if defined(_DEBUG)
3183 assert (Kblp != NULL);
3184 #endif
3185
3186 Kblp->SetValue(colp->GetValue(), i);
3187 } // end of SetValue
3188
3189 /***********************************************************************/
3190 /* InitFind: initialize finding the rank of column value in index. */
3191 /***********************************************************************/
InitFind(PGLOBAL g,PXOB xp)3192 bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
3193 {
3194 if (xp->GetType() == TYPE_CONST) {
3195 if (Kxp->Nth)
3196 return true;
3197
3198 Valp->SetValue_pval(xp->GetValue(), !Prefix);
3199 } else {
3200 xp->Reset();
3201 xp->Eval(g);
3202 Valp->SetValue_pval(xp->GetValue(), false);
3203 } // endif Type
3204
3205 if (trace(2)) {
3206 char buf[32];
3207
3208 htrc("KCOL InitFind: value=%s\n", Valp->GetCharString(buf));
3209 } // endif trace
3210
3211 return false;
3212 } // end of InitFind
3213
3214 #if 0
3215 /***********************************************************************/
3216 /* InitBinFind: initialize Value to the value pointed by vp. */
3217 /***********************************************************************/
3218 void KXYCOL::InitBinFind(void *vp)
3219 {
3220 Valp->SetBinValue(vp);
3221 } // end of InitBinFind
3222 #endif // 0
3223
3224 /***********************************************************************/
3225 /* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
3226 /* already in storage in the corresponding KXYCOL. */
3227 /***********************************************************************/
FillValue(PVAL valp)3228 void KXYCOL::FillValue(PVAL valp)
3229 {
3230 valp->SetValue_pvblk(Kblp, Val_K);
3231
3232 // Set null when applicable (NIY)
3233 //if (valp->GetNullable())
3234 // valp->SetNull(valp->IsZero());
3235
3236 } // end of FillValue
3237
3238 /***********************************************************************/
3239 /* KXYCOL: Compare routine for one numeric value. */
3240 /***********************************************************************/
Compare(int i1,int i2)3241 int KXYCOL::Compare(int i1, int i2)
3242 {
3243 // Do the actual comparison between values.
3244 int k = Kblp->CompVal(i1, i2);
3245
3246 if (trace(4))
3247 htrc("Compare done result=%d\n", k);
3248
3249 return (Asc) ? k : -k;
3250 } // end of Compare
3251
3252 /***********************************************************************/
3253 /* KXYCOL: Compare the ith key to the stored Value. */
3254 /***********************************************************************/
CompVal(int i)3255 int KXYCOL::CompVal(int i)
3256 {
3257 // Do the actual comparison between numerical values.
3258 if (trace(4)) {
3259 int k = (int)Kblp->CompVal(Valp, (int)i);
3260
3261 htrc("Compare done result=%d\n", k);
3262 return k;
3263 } else
3264 return Kblp->CompVal(Valp, i);
3265
3266 } // end of CompVal
3267
3268 /***********************************************************************/
3269 /* KXYCOL: Compare the key to the stored block value. */
3270 /***********************************************************************/
CompBval(int i)3271 int KXYCOL::CompBval(int i)
3272 {
3273 // Do the actual comparison between key values.
3274 return Blkp->CompVal(Valp, i);
3275 } // end of CompBval
3276
3277 /***********************************************************************/
3278 /* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
3279 /***********************************************************************/
ReAlloc(PGLOBAL g,int n)3280 void KXYCOL::ReAlloc(PGLOBAL g, int n)
3281 {
3282 PlgDBrealloc(g, NULL, Keys, n * Klen);
3283 Kblp->ReAlloc(To_Keys, n);
3284 Ndf = n;
3285 } // end of ReAlloc
3286
3287 /***********************************************************************/
3288 /* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
3289 /***********************************************************************/
FreeData(void)3290 void KXYCOL::FreeData(void)
3291 {
3292 PlgDBfree(Keys);
3293 Kblp = NULL;
3294 PlgDBfree(Bkeys);
3295 Blkp = NULL;
3296 PlgDBfree(Koff);
3297 Ndf = 0;
3298 } // end of FreeData
3299