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 PDBUSER dup __attribute__((unused))= PlgGetUser(g);
1215
1216 /*********************************************************************/
1217 /* Get the estimated table size. */
1218 /* Note: for fixed tables we must use cardinality to avoid the call */
1219 /* to MaxBlkSize that could reduce the cardinality value. */
1220 /*********************************************************************/
1221 if (Tdbp->Cardinality(NULL)) {
1222 // For DBF tables, Cardinality includes bad or soft deleted lines
1223 // that are not included in the index, and can be larger then the
1224 // index size.
1225 estim = (Tdbp->Ftype == RECFM_DBF);
1226 n = Tdbp->Cardinality(g); // n is exact table size
1227 } else {
1228 // Variable table not optimized
1229 estim = true; // n is an estimate of the size
1230 n = Tdbp->GetMaxSize(g);
1231 } // endif Cardinality
1232
1233 if (n <= 0)
1234 return !(n == 0); // n < 0 error, n = 0 void table
1235
1236 /*********************************************************************/
1237 /* Get the first key column. */
1238 /*********************************************************************/
1239 if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
1240 strcpy(g->Message, MSG(NO_KEY_COL));
1241 return true; // Error
1242 } else
1243 colp = To_Cols[0];
1244
1245 switch (Tdbp->Ftype) {
1246 case RECFM_VAR: ftype = ".dnx"; break;
1247 case RECFM_FIX: ftype = ".fnx"; break;
1248 case RECFM_BIN: ftype = ".bnx"; break;
1249 case RECFM_VCT: ftype = ".vnx"; break;
1250 case RECFM_CSV: ftype = ".cnx"; break;
1251 case RECFM_DBF: ftype = ".dbx"; break;
1252 default:
1253 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
1254 return true;
1255 } // endswitch Ftype
1256
1257 if (defp->SepIndex()) {
1258 // Index was save in a separate file
1259 #if defined(_WIN32)
1260 char drive[_MAX_DRIVE];
1261 #else
1262 char *drive = NULL;
1263 #endif
1264 char direc[_MAX_DIR];
1265 char fname[_MAX_FNAME];
1266
1267 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
1268 strcat(strcat(fname, "_"), Xdp->GetName());
1269 _makepath(fn, drive, direc, fname, ftype);
1270 } else {
1271 id = ID;
1272 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
1273 } // endif SepIndex
1274
1275 PlugSetPath(fn, fn, Tdbp->GetPath());
1276
1277 if (trace(1))
1278 htrc("Index %s file: %s\n", Xdp->GetName(), fn);
1279
1280 /*********************************************************************/
1281 /* Get a view on the part of the index file containing this index. */
1282 /*********************************************************************/
1283 if (!(mbase = (BYTE*)X->FileView(g, fn)))
1284 goto err;
1285
1286 if (id >= 0) {
1287 // Get offset from the header
1288 IOFF *noff = (IOFF*)mbase;
1289
1290 // Position the memory base at the offset of this index
1291 mbase += noff[id].v.Low;
1292 } // endif id
1293
1294 // Now start the mapping process.
1295 nv = (int*)mbase;
1296
1297 if (nv[0] >= MAX_INDX) {
1298 // New index format
1299 Srtd = nv[7] != 0;
1300 nv0 = nv[0] - MAX_INDX;
1301 mbase += NZ * sizeof(int);
1302 } else {
1303 Srtd = false;
1304 mbase += (NZ - 1) * sizeof(int);
1305 nv0 = nv[0];
1306 } // endif nv
1307
1308 if (trace(1))
1309 htrc("nv=%d %d %d %d %d %d %d %d\n",
1310 nv0, nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd);
1311
1312 // The test on ID was suppressed because MariaDB can change an index ID
1313 // when other indexes are added or deleted
1314 if (/*nv0 != ID ||*/ nv[1] != Nk) {
1315 // Not this index
1316 sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
1317
1318 if (trace(1))
1319 htrc("nv0=%d ID=%d nv[1]=%d Nk=%d\n", nv0, ID, nv[1], Nk);
1320
1321 goto err;
1322 } // endif nv
1323
1324 if (nv[2]) {
1325 // Set the offset array memory block
1326 Offset.Memp = mbase;
1327 Offset.Size = nv[2] * sizeof(int);
1328 Offset.Sub = true;
1329 Mul = true;
1330 Ndif = nv[2] - 1;
1331 mbase += Offset.Size;
1332 } else {
1333 Mul = false;
1334 Ndif = nv[3];
1335 } // endif nv[2]
1336
1337 if (nv[3] < n && estim)
1338 n = nv[3]; // n was just an evaluated max value
1339
1340 if (nv[3] != n) {
1341 sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
1342 goto err;
1343 } // endif
1344
1345 Num_K = nv[3];
1346 Incr = nv[4];
1347 Nblk = nv[5];
1348 Sblk = nv[6];
1349
1350 if (!Incr) {
1351 /*******************************************************************/
1352 /* Point to the storage that contains the file positions. */
1353 /*******************************************************************/
1354 Record.Size = Num_K * sizeof(int);
1355 Record.Memp = mbase;
1356 Record.Sub = true;
1357 mbase += Record.Size;
1358 } else
1359 Srtd = true; // Sorted positions can be calculated
1360
1361 /*********************************************************************/
1362 /* Allocate the KXYCOL blocks used to store column values. */
1363 /*********************************************************************/
1364 for (k = 0; k < Nk; k++) {
1365 if (k == Nval)
1366 To_LastVal = prev;
1367
1368 nv = (int*)mbase;
1369 mbase += (NW * sizeof(int));
1370
1371 colp = To_Cols[k];
1372
1373 if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
1374 (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
1375 sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
1376 goto err; // Error
1377 } // endif GetKey
1378
1379 kcp = new(g) KXYCOL(this);
1380
1381 if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
1382 goto err;
1383
1384 if (!kcp->Prefix)
1385 // Indicate that the key column value can be found from KXYCOL
1386 colp->SetKcol(kcp);
1387
1388 if (prev) {
1389 kcp->Previous = prev;
1390 prev->Next = kcp;
1391 } else
1392 To_KeyCol = kcp;
1393
1394 prev = kcp;
1395 } // endfor k
1396
1397 To_LastCol = prev;
1398
1399 if (Mul && prev)
1400 // Last key offset is the index offset
1401 kcp->Koff = Offset;
1402
1403 /*********************************************************************/
1404 /* No valid record read yet for secondary file. */
1405 /*********************************************************************/
1406 Cur_K = Num_K;
1407 return false;
1408
1409 err:
1410 Close();
1411 return true;
1412 } // end of MapInit
1413 #endif // XMAP
1414
1415 /***********************************************************************/
1416 /* Get Ndif and Num_K from the index file. */
1417 /***********************************************************************/
GetAllSizes(PGLOBAL g,int & numk)1418 bool XINDEX::GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk)
1419 {
1420 PCSZ ftype;
1421 char fn[_MAX_PATH];
1422 int nv[NZ], id = -1; // n
1423 //bool estim = false;
1424 bool rc = true;
1425 PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
1426
1427 // ndif = numk = 0;
1428 numk = 0;
1429
1430 #if 0
1431 /*********************************************************************/
1432 /* Get the estimated table size. */
1433 /* Note: for fixed tables we must use cardinality to avoid the call */
1434 /* to MaxBlkSize that could reduce the cardinality value. */
1435 /*********************************************************************/
1436 if (Tdbp->Cardinality(NULL)) {
1437 // For DBF tables, Cardinality includes bad or soft deleted lines
1438 // that are not included in the index, and can be larger then the
1439 // index size.
1440 estim = (Tdbp->Ftype == RECFM_DBF);
1441 n = Tdbp->Cardinality(g); // n is exact table size
1442 } else {
1443 // Variable table not optimized
1444 estim = true; // n is an estimate of the size
1445 n = Tdbp->GetMaxSize(g);
1446 } // endif Cardinality
1447
1448 if (n <= 0)
1449 return !(n == 0); // n < 0 error, n = 0 void table
1450
1451 /*********************************************************************/
1452 /* Check the key part number. */
1453 /*********************************************************************/
1454 if (!Nk) {
1455 strcpy(g->Message, MSG(NO_KEY_COL));
1456 return true; // Error
1457 } // endif Nk
1458 #endif // 0
1459
1460 switch (Tdbp->Ftype) {
1461 case RECFM_VAR: ftype = ".dnx"; break;
1462 case RECFM_FIX: ftype = ".fnx"; break;
1463 case RECFM_BIN: ftype = ".bnx"; break;
1464 case RECFM_VCT: ftype = ".vnx"; break;
1465 case RECFM_CSV: ftype = ".cnx"; break;
1466 case RECFM_DBF: ftype = ".dbx"; break;
1467 default:
1468 sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
1469 return true;
1470 } // endswitch Ftype
1471
1472 if (defp->SepIndex()) {
1473 // Index was saved in a separate file
1474 #if defined(_WIN32)
1475 char drive[_MAX_DRIVE];
1476 #else
1477 char *drive = NULL;
1478 #endif
1479 char direc[_MAX_DIR];
1480 char fname[_MAX_FNAME];
1481
1482 _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
1483 strcat(strcat(fname, "_"), Xdp->GetName());
1484 _makepath(fn, drive, direc, fname, ftype);
1485 } else {
1486 id = ID;
1487 strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
1488 } // endif sep
1489
1490 PlugSetPath(fn, fn, Tdbp->GetPath());
1491
1492 if (trace(1))
1493 htrc("Index %s file: %s\n", Xdp->GetName(), fn);
1494
1495 /*********************************************************************/
1496 /* Open the index file and check its validity. */
1497 /*********************************************************************/
1498 if (X->Open(g, fn, id, MODE_READ))
1499 goto err; // No saved values
1500
1501 // Get offset from XDB file
1502 //if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
1503 // goto err;
1504
1505 // Now start the reading process.
1506 if (X->Read(g, nv, NZ, sizeof(int)))
1507 goto err;
1508
1509 if (trace(1))
1510 htrc("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
1511
1512 // The test on ID was suppressed because MariaDB can change an index ID
1513 // when other indexes are added or deleted
1514 if (/*nv[0] != ID ||*/ nv[1] != Nk) {
1515 sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
1516
1517 if (trace(1))
1518 htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
1519
1520 goto err;
1521 } // endif
1522
1523 #if 0
1524 if (nv[2]) {
1525 Mul = true;
1526 Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
1527 } else {
1528 Mul = false;
1529 Ndif = nv[3];
1530 } // endif nv[2]
1531
1532 if (nv[3] < n && estim)
1533 n = nv[3]; // n was just an evaluated max value
1534
1535 if (nv[3] != n) {
1536 sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
1537 goto err;
1538 } // endif
1539 #endif // 0
1540
1541 Num_K = nv[3];
1542
1543 #if 0
1544 if (Nk > 1) {
1545 if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
1546 goto err;
1547
1548 if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
1549 goto err;
1550
1551 if (X->Read(g, nv, NW, sizeof(int)))
1552 goto err;
1553
1554 PCOL colp = *To_Cols;
1555
1556 if (nv[4] != colp->GetResultType() ||
1557 (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
1558 sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
1559 goto err; // Error
1560 } // endif GetKey
1561
1562 Ndif = nv[0];
1563 } // endif Nk
1564 #endif // 0
1565
1566 /*********************************************************************/
1567 /* Set size values. */
1568 /*********************************************************************/
1569 //ndif = Ndif;
1570 numk = Num_K;
1571 rc = false;
1572
1573 err:
1574 X->Close();
1575 return rc;
1576 } // end of GetAllSizes
1577
1578 /***********************************************************************/
1579 /* RANGE: Tell how many records exist for a given value, for an array */
1580 /* of values, or in a given value range. */
1581 /***********************************************************************/
Range(PGLOBAL g,int limit,bool incl)1582 int XINDEX::Range(PGLOBAL g, int limit, bool incl)
1583 {
1584 int i, k, n = 0;
1585 PXOB *xp = To_Vals;
1586 PXCOL kp = To_KeyCol;
1587 OPVAL op = Op;
1588
1589 switch (limit) {
1590 case 1: Op = (incl) ? OP_GE : OP_GT; break;
1591 case 2: Op = (incl) ? OP_GT : OP_GE; break;
1592 default: return 0;
1593 } // endswitch limit
1594
1595 /*********************************************************************/
1596 /* Currently only range of constant values with an EQ operator is */
1597 /* implemented. Find the number of rows for each given values. */
1598 /*********************************************************************/
1599 if (xp[0]->GetType() == TYPE_CONST) {
1600 for (i = 0; kp; kp = kp->Next) {
1601 kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
1602 if (++i == Nval) break;
1603 } // endfor kp
1604
1605 if ((k = FastFind()) < Num_K)
1606 n = k;
1607 // if (limit)
1608 // n = (Mul) ? k : kp->Val_K;
1609 // else
1610 // n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
1611
1612 } else {
1613 strcpy(g->Message, MSG(RANGE_NO_JOIN));
1614 n = -1; // Logical error
1615 } // endif'f Type
1616
1617 Op = op;
1618 return n;
1619 } // end of Range
1620
1621 /***********************************************************************/
1622 /* Return the size of the group (equal values) of the current value. */
1623 /***********************************************************************/
GroupSize(void)1624 int XINDEX::GroupSize(void)
1625 {
1626 #if defined(_DEBUG)
1627 assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
1628 #endif // _DEBUG
1629
1630 if (Nval == Nk)
1631 return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
1632 : 1;
1633
1634 #if defined(_DEBUG)
1635 assert(To_LastVal);
1636 #endif // _DEBUG
1637
1638 // Index whose only some columns are used
1639 int ck1, ck2;
1640
1641 ck1 = To_LastVal->Val_K;
1642 ck2 = ck1 + 1;
1643
1644 #if defined(_DEBUG)
1645 assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
1646 #endif // _DEBUG
1647
1648 for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
1649 ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
1650 ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
1651 } // endfor kcp
1652
1653 return ck2 - ck1;
1654 } // end of GroupSize
1655
1656 /***********************************************************************/
1657 /* Find Cur_K and Val_K's of the next distinct value of the index. */
1658 /* Returns false if Ok, true if there are no more different values. */
1659 /***********************************************************************/
NextValDif(void)1660 bool XINDEX::NextValDif(void)
1661 {
1662 int curk;
1663 PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
1664
1665 if (++kcp->Val_K < kcp->Ndf) {
1666 Cur_K = curk = kcp->Val_K;
1667
1668 // (Cur_K return is currently not used by SQLGBX)
1669 for (PXCOL kp = kcp; kp; kp = kp->Next)
1670 Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
1671
1672 } else
1673 return true;
1674
1675 for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
1676 if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
1677 break; // all previous columns have same value
1678
1679 curk = ++kcp->Val_K; // This is a break, get new column value
1680 } // endfor kcp
1681
1682 return false;
1683 } // end of NextValDif
1684
1685 /***********************************************************************/
1686 /* XINDEX: Find Cur_K and Val_K's of next index entry. */
1687 /* If eq is true next values must be equal to last ones up to Nval. */
1688 /* Returns false if Ok, true if there are no more (equal) values. */
1689 /***********************************************************************/
NextVal(bool eq)1690 bool XINDEX::NextVal(bool eq)
1691 {
1692 int n, neq = Nk + 1, curk;
1693 PXCOL kcp;
1694
1695 if (Cur_K == Num_K)
1696 return true;
1697 else
1698 curk = ++Cur_K;
1699
1700 for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
1701 if (kcp->Kof) {
1702 if (curk == kcp->Kof[kcp->Val_K + 1])
1703 neq = n;
1704
1705 } else {
1706 #ifdef _DEBUG
1707 assert(curk == kcp->Val_K + 1);
1708 #endif // _DEBUG
1709 neq = n;
1710 } // endif Kof
1711
1712 #ifdef _DEBUG
1713 assert(kcp->Val_K < kcp->Ndf);
1714 #endif // _DEBUG
1715
1716 // If this is not a break...
1717 if (neq > n)
1718 break; // all previous columns have same value
1719
1720 curk = ++kcp->Val_K; // This is a break, get new column value
1721 } // endfor kcp
1722
1723 // Return true if no more values or, in case of "equal" values,
1724 // if the last used column value has changed
1725 return (Cur_K == Num_K || (eq && neq <= Nval));
1726 } // end of NextVal
1727
1728 /***********************************************************************/
1729 /* XINDEX: Find Cur_K and Val_K's of previous index entry. */
1730 /* Returns false if Ok, true if there are no more values. */
1731 /***********************************************************************/
PrevVal(void)1732 bool XINDEX::PrevVal(void)
1733 {
1734 int n, neq = Nk + 1, curk;
1735 PXCOL kcp;
1736
1737 if (Cur_K == 0)
1738 return true;
1739 else
1740 curk = --Cur_K;
1741
1742 for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
1743 if (kcp->Kof) {
1744 if (curk < kcp->Kof[kcp->Val_K])
1745 neq = n;
1746
1747 } else {
1748 #ifdef _DEBUG
1749 assert(curk == kcp->Val_K -1);
1750 #endif // _DEBUG
1751 neq = n;
1752 } // endif Kof
1753
1754 #ifdef _DEBUG
1755 assert(kcp->Val_K >= 0);
1756 #endif // _DEBUG
1757
1758 // If this is not a break...
1759 if (neq > n)
1760 break; // all previous columns have same value
1761
1762 curk = --kcp->Val_K; // This is a break, get new column value
1763 } // endfor kcp
1764
1765 return false;
1766 } // end of PrevVal
1767
1768 /***********************************************************************/
1769 /* XINDEX: Fetch a physical or logical record. */
1770 /***********************************************************************/
Fetch(PGLOBAL g)1771 int XINDEX::Fetch(PGLOBAL g)
1772 {
1773 int n;
1774 PXCOL kp;
1775
1776 if (Num_K == 0)
1777 return -1; // means end of file
1778
1779 if (trace(2))
1780 htrc("XINDEX Fetch: Op=%d\n", Op);
1781
1782 /*********************************************************************/
1783 /* Table read through a sorted index. */
1784 /*********************************************************************/
1785 switch (Op) {
1786 case OP_NEXT: // Read next
1787 if (NextVal(false))
1788 return -1; // End of indexed file
1789
1790 break;
1791 case OP_FIRST: // Read first
1792 for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
1793 kp->Val_K = 0;
1794
1795 Op = OP_NEXT;
1796 break;
1797 case OP_SAME: // Read next same
1798 // Logically the key values should be the same as before
1799 if (NextVal(true)) {
1800 Op = OP_EQ;
1801 return -2; // no more equal values
1802 } // endif NextVal
1803
1804 break;
1805 case OP_NXTDIF: // Read next dif
1806 // while (!NextVal(true)) ;
1807
1808 // if (Cur_K >= Num_K)
1809 // return -1; // End of indexed file
1810 if (NextValDif())
1811 return -1; // End of indexed file
1812
1813 break;
1814 case OP_FSTDIF: // Read first diff
1815 for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
1816 kp->Val_K = 0;
1817
1818 Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
1819 break;
1820 case OP_LAST: // Read last key
1821 for (Cur_K = Num_K - 1, kp = To_KeyCol; kp; kp = kp->Next)
1822 kp->Val_K = kp->Kblp->GetNval() - 1;
1823
1824 Op = OP_NEXT;
1825 break;
1826 case OP_PREV: // Read previous
1827 if (PrevVal())
1828 return -1; // End of indexed file
1829
1830 break;
1831 default: // Should be OP_EQ
1832 // if (Tbxp->Key_Rank < 0) {
1833 /***************************************************************/
1834 /* Look for the first key equal to the link column values */
1835 /* and return its rank whithin the index table. */
1836 /***************************************************************/
1837 for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
1838 if (kp->InitFind(g, To_Vals[n]))
1839 return -1; // No more constant values
1840
1841 Nth++;
1842
1843 if (trace(2))
1844 htrc("Fetch: Looking for new value Nth=%d\n", Nth);
1845
1846 Cur_K = FastFind();
1847
1848 if (Cur_K >= Num_K)
1849 /*************************************************************/
1850 /* Rank not whithin index table, signal record not found. */
1851 /*************************************************************/
1852 return -2;
1853
1854 else if (Mul || Nval < Nk)
1855 Op = OP_SAME;
1856
1857 } // endswitch Op
1858
1859 /*********************************************************************/
1860 /* If rank is equal to stored rank, record is already there. */
1861 /*********************************************************************/
1862 if (Cur_K == Old_K)
1863 return -3; // Means record already there
1864 else
1865 Old_K = Cur_K; // Store rank of newly read record
1866
1867 /*********************************************************************/
1868 /* Return the position of the required record. */
1869 /*********************************************************************/
1870 return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
1871 } // end of Fetch
1872
1873 /***********************************************************************/
1874 /* FastFind: Returns the index of matching record in a join using an */
1875 /* optimized algorithm based on dichotomie and optimized comparing. */
1876 /***********************************************************************/
FastFind(void)1877 int XINDEX::FastFind(void)
1878 {
1879 int curk, sup, inf, i= 0, k, n = 2;
1880 PXCOL kp, kcp;
1881
1882 //assert((int)nv == Nval);
1883
1884 if (Nblk && Op == OP_EQ) {
1885 // Look in block values to find in which block to search
1886 sup = Nblk;
1887 inf = -1;
1888
1889 while (n && sup - inf > 1) {
1890 i = (inf + sup) >> 1;
1891
1892 n = To_KeyCol->CompBval(i);
1893
1894 if (n < 0)
1895 sup = i;
1896 else
1897 inf = i;
1898
1899 } // endwhile
1900
1901 if (inf < 0)
1902 return Num_K;
1903
1904 // i = inf;
1905 inf *= Sblk;
1906
1907 if ((sup = inf + Sblk) > To_KeyCol->Ndf)
1908 sup = To_KeyCol->Ndf;
1909
1910 inf--;
1911 } else {
1912 inf = -1;
1913 sup = To_KeyCol->Ndf;
1914 } // endif Nblk
1915
1916 if (trace(4))
1917 htrc("XINDEX FastFind: Nblk=%d Op=%d inf=%d sup=%d\n",
1918 Nblk, Op, inf, sup);
1919
1920 for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
1921 while (sup - inf > 1) {
1922 i = (inf + sup) >> 1;
1923
1924 n = kcp->CompVal(i);
1925
1926 if (n < 0)
1927 sup = i;
1928 else if (n > 0)
1929 inf = i;
1930 else
1931 break;
1932
1933 } // endwhile
1934
1935 if (n) {
1936 if (Op != OP_EQ) {
1937 // Currently only OP_GT or OP_GE
1938 kcp->Val_K = curk = sup;
1939
1940 // Check for value changes in previous key parts
1941 for (kp = kcp->Previous; kp; kp = kp->Previous)
1942 if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
1943 break;
1944 else
1945 curk = ++kp->Val_K;
1946
1947 n = 0;
1948 } // endif Op
1949
1950 break;
1951 } // endif n
1952
1953 kcp->Val_K = i;
1954
1955 if (++k == Nval) {
1956 if (Op == OP_GT) { // n is always 0
1957 curk = ++kcp->Val_K; // Increment value by 1
1958
1959 // Check for value changes in previous key parts
1960 for (kp = kcp->Previous; kp; kp = kp->Previous)
1961 if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
1962 break; // Not changed
1963 else
1964 curk = ++kp->Val_K;
1965
1966 } // endif Op
1967
1968 break; // So kcp remains pointing the last tested block
1969 } // endif k
1970
1971 if (kcp->Kof) {
1972 inf = kcp->Kof[i] - 1;
1973 sup = kcp->Kof[i + 1];
1974 } else {
1975 inf = i - 1;
1976 sup = i + 1;
1977 } // endif Kof
1978
1979 } // endfor k, kcp
1980
1981 if (n) {
1982 // Record not found
1983 for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
1984 kcp->Val_K = kcp->Ndf; // Not a valid value
1985
1986 return Num_K;
1987 } // endif n
1988
1989 for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
1990 kcp->Val_K = curk;
1991 curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
1992 } // endfor kcp
1993
1994 if (trace(4))
1995 htrc("XINDEX FastFind: curk=%d\n", curk);
1996
1997 return curk;
1998 } // end of FastFind
1999
2000 /* -------------------------- XINDXS Class --------------------------- */
2001
2002 /***********************************************************************/
2003 /* XINDXS public constructor. */
2004 /***********************************************************************/
XINDXS(PTDBDOS tdbp,PIXDEF xdp,PXLOAD pxp,PCOL * cp,PXOB * xp)2005 XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
2006 : XINDEX(tdbp, xdp, pxp, cp, xp)
2007 {
2008 Srtd = To_Cols[0]->GetOpt() == 2;
2009 } // end of XINDXS constructor
2010
2011 /***********************************************************************/
2012 /* XINDXS compare routine for C Quick/Insertion sort. */
2013 /***********************************************************************/
Qcompare(int * i1,int * i2)2014 int XINDXS::Qcompare(int *i1, int *i2)
2015 {
2016 //num_comp++;
2017 return To_KeyCol->Compare(*i1, *i2);
2018 } // end of Qcompare
2019
2020 /***********************************************************************/
2021 /* Range: Tell how many records exist for given value(s): */
2022 /* If limit=0 return range for these values. */
2023 /* If limit=1 return the start of range. */
2024 /* If limit=2 return the end of range. */
2025 /***********************************************************************/
Range(PGLOBAL g,int limit,bool incl)2026 int XINDXS::Range(PGLOBAL g, int limit, bool incl)
2027 {
2028 int k, n = 0;
2029 PXOB xp = To_Vals[0];
2030 PXCOL kp = To_KeyCol;
2031 OPVAL op = Op;
2032
2033 switch (limit) {
2034 case 1: Op = (incl) ? OP_GE : OP_GT; break;
2035 case 2: Op = (incl) ? OP_GT : OP_GE; break;
2036 default: Op = OP_EQ;
2037 } // endswitch limit
2038
2039 /*********************************************************************/
2040 /* Currently only range of constant values with an EQ operator is */
2041 /* implemented. Find the number of rows for each given values. */
2042 /*********************************************************************/
2043 if (xp->GetType() == TYPE_CONST) {
2044 kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
2045 k = FastFind();
2046
2047 if (k < Num_K || Op != OP_EQ)
2048 {
2049 if (limit)
2050 n = (Mul) ? k : kp->Val_K;
2051 else
2052 n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
2053 }
2054 } else {
2055 strcpy(g->Message, MSG(RANGE_NO_JOIN));
2056 n = -1; // Logical error
2057 } // endif'f Type
2058
2059 Op = op;
2060 return n;
2061 } // end of Range
2062
2063 /***********************************************************************/
2064 /* Return the size of the group (equal values) of the current value. */
2065 /***********************************************************************/
GroupSize(void)2066 int XINDXS::GroupSize(void)
2067 {
2068 #if defined(_DEBUG)
2069 assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
2070 #endif // _DEBUG
2071 return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K] : 1;
2072 } // end of GroupSize
2073
2074 /***********************************************************************/
2075 /* XINDXS: Find Cur_K and Val_K of previous index value. */
2076 /* Returns false if Ok, true if there are no more values. */
2077 /***********************************************************************/
PrevVal(void)2078 bool XINDXS::PrevVal(void)
2079 {
2080 if (--Cur_K < 0)
2081 return true;
2082
2083 if (Mul) {
2084 if (Cur_K < Pof[To_KeyCol->Val_K])
2085 To_KeyCol->Val_K--;
2086
2087 } else
2088 To_KeyCol->Val_K = Cur_K;
2089
2090 return false;
2091 } // end of PrevVal
2092
2093 /***********************************************************************/
2094 /* XINDXS: Find Cur_K and Val_K of next index value. */
2095 /* If b is true next value must be equal to last one. */
2096 /* Returns false if Ok, true if there are no more (equal) values. */
2097 /***********************************************************************/
NextVal(bool eq)2098 bool XINDXS::NextVal(bool eq)
2099 {
2100 bool rc;
2101
2102 if (To_KeyCol->Val_K == Ndif)
2103 return true;
2104
2105 if (Mul) {
2106 int limit = Pof[To_KeyCol->Val_K + 1];
2107
2108 #ifdef _DEBUG
2109 assert(Cur_K < limit);
2110 assert(To_KeyCol->Val_K < Ndif);
2111 #endif // _DEBUG
2112
2113 if (++Cur_K == limit) {
2114 To_KeyCol->Val_K++;
2115 rc = (eq || limit == Num_K);
2116 } else
2117 rc = false;
2118
2119 } else
2120 rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
2121
2122 return rc;
2123 } // end of NextVal
2124
2125 /***********************************************************************/
2126 /* XINDXS: Fetch a physical or logical record. */
2127 /***********************************************************************/
Fetch(PGLOBAL g)2128 int XINDXS::Fetch(PGLOBAL g)
2129 {
2130 if (Num_K == 0)
2131 return -1; // means end of file
2132
2133 if (trace(2))
2134 htrc("XINDXS Fetch: Op=%d\n", Op);
2135
2136 /*********************************************************************/
2137 /* Table read through a sorted index. */
2138 /*********************************************************************/
2139 switch (Op) {
2140 case OP_NEXT: // Read next
2141 if (NextVal(false))
2142 return -1; // End of indexed file
2143
2144 break;
2145 case OP_FIRST: // Read first
2146 To_KeyCol->Val_K = Cur_K = 0;
2147 Op = OP_NEXT;
2148 break;
2149 case OP_SAME: // Read next same
2150 if (!Mul || NextVal(true)) {
2151 Op = OP_EQ;
2152 return -2; // No more equal values
2153 } // endif Mul
2154
2155 break;
2156 case OP_NXTDIF: // Read next dif
2157 if (++To_KeyCol->Val_K == Ndif)
2158 return -1; // End of indexed file
2159
2160 Cur_K = Pof[To_KeyCol->Val_K];
2161 break;
2162 case OP_FSTDIF: // Read first diff
2163 To_KeyCol->Val_K = Cur_K = 0;
2164 Op = (Mul) ? OP_NXTDIF : OP_NEXT;
2165 break;
2166 case OP_LAST: // Read first
2167 Cur_K = Num_K - 1;
2168 To_KeyCol->Val_K = Ndif - 1;
2169 Op = OP_PREV;
2170 break;
2171 case OP_PREV: // Read previous
2172 if (PrevVal())
2173 return -1; // End of indexed file
2174
2175 break;
2176 default: // Should be OP_EQ
2177 /*****************************************************************/
2178 /* Look for the first key equal to the link column values */
2179 /* and return its rank whithin the index table. */
2180 /*****************************************************************/
2181 if (To_KeyCol->InitFind(g, To_Vals[0]))
2182 return -1; // No more constant values
2183 else
2184 Nth++;
2185
2186 if (trace(2))
2187 htrc("Fetch: Looking for new value Nth=%d\n", Nth);
2188
2189 Cur_K = FastFind();
2190
2191 if (Cur_K >= Num_K)
2192 // Rank not whithin index table, signal record not found
2193 return -2;
2194 else if (Mul)
2195 Op = OP_SAME;
2196
2197 } // endswitch Op
2198
2199 /*********************************************************************/
2200 /* If rank is equal to stored rank, record is already there. */
2201 /*********************************************************************/
2202 if (Cur_K == Old_K)
2203 return -3; // Means record already there
2204 else
2205 Old_K = Cur_K; // Store rank of newly read record
2206
2207 /*********************************************************************/
2208 /* Return the position of the required record. */
2209 /*********************************************************************/
2210 return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
2211 } // end of Fetch
2212
2213 /***********************************************************************/
2214 /* FastFind: Returns the index of matching indexed record using an */
2215 /* optimized algorithm based on dichotomie and optimized comparing. */
2216 /***********************************************************************/
FastFind(void)2217 int XINDXS::FastFind(void)
2218 {
2219 int sup, inf, i= 0, n = 2;
2220 PXCOL kcp = To_KeyCol;
2221
2222 if (Nblk && Op == OP_EQ) {
2223 // Look in block values to find in which block to search
2224 sup = Nblk;
2225 inf = -1;
2226
2227 while (n && sup - inf > 1) {
2228 i = (inf + sup) >> 1;
2229
2230 n = kcp->CompBval(i);
2231
2232 if (n < 0)
2233 sup = i;
2234 else
2235 inf = i;
2236
2237 } // endwhile
2238
2239 if (inf < 0)
2240 return Num_K;
2241
2242 inf *= Sblk;
2243
2244 if ((sup = inf + Sblk) > Ndif)
2245 sup = Ndif;
2246
2247 inf--;
2248 } else {
2249 inf = -1;
2250 sup = Ndif;
2251 } // endif Nblk
2252
2253 if (trace(4))
2254 htrc("XINDXS FastFind: Nblk=%d Op=%d inf=%d sup=%d\n",
2255 Nblk, Op, inf, sup);
2256
2257 while (sup - inf > 1) {
2258 i = (inf + sup) >> 1;
2259
2260 n = kcp->CompVal(i);
2261
2262 if (n < 0)
2263 sup = i;
2264 else if (n > 0)
2265 inf = i;
2266 else
2267 break;
2268
2269 } // endwhile
2270
2271 if (!n && Op == OP_GT) {
2272 ++i;
2273 } else if (n && Op != OP_EQ) {
2274 // Currently only OP_GT or OP_GE
2275 i = sup;
2276 n = 0;
2277 } // endif sup
2278
2279 if (trace(4))
2280 htrc("XINDXS FastFind: n=%d i=%d\n", n, i);
2281
2282 // Loop on kcp because of dynamic indexing
2283 for (; kcp; kcp = kcp->Next)
2284 kcp->Val_K = i; // Used by FillValue
2285
2286 return ((n) ? Num_K : (Mul) ? Pof[i] : i);
2287 } // end of FastFind
2288
2289 /* -------------------------- XLOAD Class --------------------------- */
2290
2291 /***********************************************************************/
2292 /* XLOAD constructor. */
2293 /***********************************************************************/
XLOAD(void)2294 XLOAD::XLOAD(void)
2295 {
2296 Hfile = INVALID_HANDLE_VALUE;
2297 NewOff.Val = 0LL;
2298 } // end of XLOAD constructor
2299
2300 /***********************************************************************/
2301 /* Close the index huge file. */
2302 /***********************************************************************/
Close(void)2303 void XLOAD::Close(void)
2304 {
2305 if (Hfile != INVALID_HANDLE_VALUE) {
2306 CloseFileHandle(Hfile);
2307 Hfile = INVALID_HANDLE_VALUE;
2308 } // endif Hfile
2309
2310 } // end of Close
2311
2312 /* --------------------------- XFILE Class --------------------------- */
2313
2314 /***********************************************************************/
2315 /* XFILE constructor. */
2316 /***********************************************************************/
XFILE(void)2317 XFILE::XFILE(void) : XLOAD()
2318 {
2319 Xfile = NULL;
2320 #if defined(XMAP)
2321 Mmp = NULL;
2322 #endif // XMAP
2323 } // end of XFILE constructor
2324
2325 /***********************************************************************/
2326 /* Xopen function: opens a file using native API's. */
2327 /***********************************************************************/
Open(PGLOBAL g,char * filename,int id,MODE mode)2328 bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
2329 {
2330 PCSZ pmod;
2331 bool rc;
2332 IOFF noff[MAX_INDX];
2333
2334 /*********************************************************************/
2335 /* Open the index file according to mode. */
2336 /*********************************************************************/
2337 switch (mode) {
2338 case MODE_READ: pmod = "rb"; break;
2339 case MODE_WRITE: pmod = "wb"; break;
2340 case MODE_INSERT: pmod = "ab"; break;
2341 default:
2342 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2343 return true;
2344 } // endswitch mode
2345
2346 if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
2347 if (trace(1))
2348 htrc("Open: %s\n", g->Message);
2349
2350 return true;
2351 } // endif Xfile
2352
2353 if (mode == MODE_INSERT) {
2354 /*******************************************************************/
2355 /* Position the cursor at end of file so ftell returns file size. */
2356 /*******************************************************************/
2357 if (fseek(Xfile, 0, SEEK_END)) {
2358 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2359 return true;
2360 } // endif
2361
2362 NewOff.v.Low = (int)ftell(Xfile);
2363
2364 if (trace(1))
2365 htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
2366
2367 } else if (mode == MODE_WRITE) {
2368 if (id >= 0) {
2369 // New not sep index file. Write the header.
2370 memset(noff, 0, sizeof(noff));
2371 Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
2372 fseek(Xfile, 0, SEEK_END);
2373 NewOff.v.Low = (int)ftell(Xfile);
2374
2375 if (trace(1))
2376 htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
2377
2378 } // endif id
2379
2380 } else if (mode == MODE_READ && id >= 0) {
2381 // Get offset from the header
2382 if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
2383 sprintf(g->Message, MSG(XFILE_READERR), errno);
2384 return true;
2385 } // endif MAX_INDX
2386
2387 if (trace(1))
2388 htrc("XFILE Open: noff[%d].v.Low=%d\n", id, noff[id].v.Low);
2389
2390 // Position the cursor at the offset of this index
2391 if (fseek(Xfile, noff[id].v.Low, SEEK_SET)) {
2392 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2393 return true;
2394 } // endif
2395
2396 } // endif mode
2397
2398 return false;
2399 } // end of Open
2400
2401 /***********************************************************************/
2402 /* Move into an index file. */
2403 /***********************************************************************/
Seek(PGLOBAL g,int low,int high,int origin)2404 bool XFILE::Seek(PGLOBAL g, int low, int high __attribute__((unused)),
2405 int origin)
2406 {
2407 #if defined(_DEBUG)
2408 assert(high == 0);
2409 #endif // !_DEBUG
2410
2411 if (fseek(Xfile, low, origin)) {
2412 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
2413 return true;
2414 } // endif
2415
2416 return false;
2417 } // end of Seek
2418
2419 /***********************************************************************/
2420 /* Read from the index file. */
2421 /***********************************************************************/
Read(PGLOBAL g,void * buf,int n,int size)2422 bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
2423 {
2424 if (fread(buf, size, n, Xfile) != (size_t)n) {
2425 sprintf(g->Message, MSG(XFILE_READERR), errno);
2426 return true;
2427 } // endif size
2428
2429 return false;
2430 } // end of Read
2431
2432 /***********************************************************************/
2433 /* Write on index file, set rc and return the number of bytes written */
2434 /***********************************************************************/
Write(PGLOBAL g,void * buf,int n,int size,bool & rc)2435 int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
2436 {
2437 int niw = (int)fwrite(buf, size, n, Xfile);
2438
2439 if (niw != n) {
2440 sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
2441 rc = true;
2442 } // endif size
2443
2444 return niw * size;
2445 } // end of Write
2446
2447 /***********************************************************************/
2448 /* Update the file header and close the index file. */
2449 /***********************************************************************/
Close(char * fn,int id)2450 void XFILE::Close(char *fn, int id)
2451 {
2452 if (id >= 0 && fn && Xfile) {
2453 fclose(Xfile);
2454
2455 if ((Xfile = fopen(fn, "r+b")))
2456 if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
2457 fwrite(&NewOff, sizeof(int), 2, Xfile);
2458
2459 } // endif id
2460
2461 Close();
2462 } // end of Close
2463
2464 /***********************************************************************/
2465 /* Close the index file. */
2466 /***********************************************************************/
Close(void)2467 void XFILE::Close(void)
2468 {
2469 XLOAD::Close();
2470
2471 if (Xfile) {
2472 fclose(Xfile);
2473 Xfile = NULL;
2474 } // endif Xfile
2475
2476 #if defined(XMAP)
2477 if (Mmp && CloseMemMap(Mmp->memory, Mmp->lenL))
2478 printf("Error closing mapped index\n");
2479 #endif // XMAP
2480 } // end of Close
2481
2482 #if defined(XMAP)
2483 /*********************************************************************/
2484 /* Map the entire index file. */
2485 /*********************************************************************/
FileView(PGLOBAL g,char * fn)2486 void *XFILE::FileView(PGLOBAL g, char *fn)
2487 {
2488 HANDLE h;
2489
2490 Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
2491 h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
2492
2493 if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
2494 if (!(*g->Message))
2495 strcpy(g->Message, MSG(FILE_MAP_ERR));
2496
2497 CloseFileHandle(h); // Not used anymore
2498 return NULL; // No saved values
2499 } // endif h
2500
2501 CloseFileHandle(h); // Not used anymore
2502 return Mmp->memory;
2503 } // end of FileView
2504 #endif // XMAP
2505
2506 /* -------------------------- XHUGE Class --------------------------- */
2507
2508 /***********************************************************************/
2509 /* Xopen function: opens a file using native API's. */
2510 /***********************************************************************/
Open(PGLOBAL g,char * filename,int id,MODE mode)2511 bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
2512 {
2513 IOFF noff[MAX_INDX];
2514
2515 if (Hfile != INVALID_HANDLE_VALUE) {
2516 sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
2517 return true;
2518 } // endif
2519
2520 if (trace(1))
2521 htrc(" Xopen: filename=%s id=%d mode=%d\n", filename, id, mode);
2522
2523 #if defined(_WIN32)
2524 LONG high = 0;
2525 DWORD rc, drc, access, share, creation;
2526
2527 /*********************************************************************/
2528 /* Create the file object according to access mode */
2529 /*********************************************************************/
2530 switch (mode) {
2531 case MODE_READ:
2532 access = GENERIC_READ;
2533 share = FILE_SHARE_READ;
2534 creation = OPEN_EXISTING;
2535 break;
2536 case MODE_WRITE:
2537 access = GENERIC_WRITE;
2538 share = 0;
2539 creation = CREATE_ALWAYS;
2540 break;
2541 case MODE_INSERT:
2542 access = GENERIC_WRITE;
2543 share = 0;
2544 creation = OPEN_EXISTING;
2545 break;
2546 default:
2547 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2548 return true;
2549 } // endswitch
2550
2551 Hfile = CreateFile(filename, access, share, NULL, creation,
2552 FILE_ATTRIBUTE_NORMAL, NULL);
2553
2554 if (Hfile == INVALID_HANDLE_VALUE) {
2555 rc = GetLastError();
2556 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
2557 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2558 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
2559 (LPTSTR)filename, sizeof(filename), NULL);
2560 strcat(g->Message, filename);
2561 return true;
2562 } // endif Hfile
2563
2564 if (trace(1))
2565 htrc(" access=%p share=%p creation=%d handle=%p fn=%s\n",
2566 access, share, creation, Hfile, filename);
2567
2568 if (mode == MODE_INSERT) {
2569 /*******************************************************************/
2570 /* In Insert mode we must position the cursor at end of file. */
2571 /*******************************************************************/
2572 rc = SetFilePointer(Hfile, 0, &high, FILE_END);
2573
2574 if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
2575 sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
2576 CloseHandle(Hfile);
2577 Hfile = INVALID_HANDLE_VALUE;
2578 return true;
2579 } // endif
2580
2581 NewOff.v.Low = (int)rc;
2582 NewOff.v.High = (int)high;
2583 } else if (mode == MODE_WRITE) {
2584 if (id >= 0) {
2585 // New not sep index file. Write the header.
2586 memset(noff, 0, sizeof(noff));
2587 rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
2588 NewOff.v.Low = (int)drc;
2589 } // endif id
2590
2591 } else if (mode == MODE_READ && id >= 0) {
2592 // Get offset from the header
2593 rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
2594
2595 if (!rc) {
2596 sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
2597 return true;
2598 } // endif rc
2599
2600 // Position the cursor at the offset of this index
2601 rc = SetFilePointer(Hfile, noff[id].v.Low,
2602 (PLONG)&noff[id].v.High, FILE_BEGIN);
2603
2604 if (rc == INVALID_SET_FILE_POINTER) {
2605 sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
2606 return true;
2607 } // endif
2608
2609 } // endif Mode
2610
2611 #else // UNIX
2612 int oflag = O_LARGEFILE; // Enable file size > 2G
2613 mode_t pmod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
2614
2615 /*********************************************************************/
2616 /* Create the file object according to access mode */
2617 /*********************************************************************/
2618 switch (mode) {
2619 case MODE_READ:
2620 oflag |= O_RDONLY;
2621 break;
2622 case MODE_WRITE:
2623 oflag |= O_WRONLY | O_CREAT | O_TRUNC;
2624 // pmod = S_IREAD | S_IWRITE;
2625 break;
2626 case MODE_INSERT:
2627 oflag |= (O_WRONLY | O_APPEND);
2628 break;
2629 default:
2630 sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
2631 return true;
2632 } // endswitch
2633
2634 Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
2635
2636 if (Hfile == INVALID_HANDLE_VALUE) {
2637 /*rc = errno;*/
2638 if (trace(1))
2639 htrc("Open: %s\n", g->Message);
2640
2641 return true;
2642 } // endif Hfile
2643
2644 if (trace(1))
2645 htrc(" oflag=%p mode=%d handle=%d fn=%s\n",
2646 oflag, mode, Hfile, filename);
2647
2648 if (mode == MODE_INSERT) {
2649 /*******************************************************************/
2650 /* Position the cursor at end of file so ftell returns file size. */
2651 /*******************************************************************/
2652 if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
2653 sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
2654 return true;
2655 } // endif
2656
2657 if (trace(1))
2658 htrc("INSERT: NewOff=%lld\n", NewOff.Val);
2659
2660 } else if (mode == MODE_WRITE) {
2661 if (id >= 0) {
2662 // New not sep index file. Write the header.
2663 memset(noff, 0, sizeof(noff));
2664 NewOff.v.Low = write(Hfile, &noff, sizeof(noff));
2665 } // endif id
2666
2667 if (trace(1))
2668 htrc("WRITE: NewOff=%lld\n", NewOff.Val);
2669
2670 } else if (mode == MODE_READ && id >= 0) {
2671 // Get offset from the header
2672 if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
2673 sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
2674 return true;
2675 } // endif read
2676
2677 if (trace(1))
2678 htrc("noff[%d]=%lld\n", id, noff[id].Val);
2679
2680 // Position the cursor at the offset of this index
2681 if (lseek64(Hfile, noff[id].Val, SEEK_SET) < 0) {
2682 sprintf(g->Message, "(XHUGE)lseek64: %s (%lld)", strerror(errno), noff[id].Val);
2683 printf("%s\n", g->Message);
2684 // sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
2685 return true;
2686 } // endif lseek64
2687
2688 } // endif mode
2689 #endif // UNIX
2690
2691 return false;
2692 } // end of Open
2693
2694 /***********************************************************************/
2695 /* Go to position in a huge file. */
2696 /***********************************************************************/
Seek(PGLOBAL g,int low,int high,int origin)2697 bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
2698 {
2699 #if defined(_WIN32)
2700 LONG hi = high;
2701 DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
2702
2703 if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
2704 sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
2705 return true;
2706 } // endif
2707
2708 #else // UNIX
2709 off64_t pos = (off64_t)low
2710 + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
2711
2712 if (lseek64(Hfile, pos, origin) < 0) {
2713 sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
2714
2715 if (trace(1))
2716 htrc("lseek64 error %d\n", errno);
2717
2718 return true;
2719 } // endif lseek64
2720
2721 if (trace(1))
2722 htrc("Seek: low=%d high=%d\n", low, high);
2723 #endif // UNIX
2724
2725 return false;
2726 } // end of Seek
2727
2728 /***********************************************************************/
2729 /* Read from a huge index file. */
2730 /***********************************************************************/
Read(PGLOBAL g,void * buf,int n,int size)2731 bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
2732 {
2733 bool rc = false;
2734
2735 #if defined(_WIN32)
2736 bool brc;
2737 DWORD nbr, count = (DWORD)(n * size);
2738
2739 brc = ReadFile(Hfile, buf, count, &nbr, NULL);
2740
2741 if (brc) {
2742 if (nbr != count) {
2743 strcpy(g->Message, MSG(EOF_INDEX_FILE));
2744 rc = true;
2745 } // endif nbr
2746
2747 } else {
2748 char buf[256];
2749 DWORD drc = GetLastError();
2750
2751 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2752 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
2753 (LPTSTR)buf, sizeof(buf), NULL);
2754 sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
2755 rc = true;
2756 } // endif brc
2757 #else // UNIX
2758 ssize_t count = (ssize_t)(n * size);
2759
2760 if (trace(1))
2761 htrc("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
2762
2763 if (read(Hfile, buf, count) != count) {
2764 sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
2765
2766 if (trace(1))
2767 htrc("read error %d\n", errno);
2768
2769 rc = true;
2770 } // endif nbr
2771 #endif // UNIX
2772
2773 return rc;
2774 } // end of Read
2775
2776 /***********************************************************************/
2777 /* Write on a huge index file. */
2778 /***********************************************************************/
Write(PGLOBAL g,void * buf,int n,int size,bool & rc)2779 int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
2780 {
2781 #if defined(_WIN32)
2782 bool brc;
2783 DWORD nbw, count = (DWORD)n * (DWORD) size;
2784
2785 brc = WriteFile(Hfile, buf, count, &nbw, NULL);
2786
2787 if (!brc) {
2788 char msg[256];
2789 DWORD drc = GetLastError();
2790
2791 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2792 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
2793 (LPTSTR)msg, sizeof(msg), NULL);
2794 sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
2795 rc = true;
2796 } // endif size
2797
2798 return (int)nbw;
2799 #else // UNIX
2800 ssize_t nbw;
2801 size_t count = (size_t)n * (size_t)size;
2802
2803 nbw = write(Hfile, buf, count);
2804
2805 if (nbw != (signed)count) {
2806 sprintf(g->Message, MSG(WRITING_ERROR),
2807 "index file", strerror(errno));
2808 rc = true;
2809 } // endif nbw
2810
2811 return (int)nbw;
2812 #endif // UNIX
2813 } // end of Write
2814
2815 /***********************************************************************/
2816 /* Update the file header and close the index file. */
2817 /***********************************************************************/
Close(char * fn,int id)2818 void XHUGE::Close(char *fn, int id)
2819 {
2820 if (trace(1))
2821 htrc("XHUGE::Close: fn=%s id=%d NewOff=%lld\n", fn, id, NewOff.Val);
2822
2823 #if defined(_WIN32)
2824 if (id >= 0 && fn) {
2825 CloseFileHandle(Hfile);
2826 Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2827 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2828
2829 if (Hfile != INVALID_HANDLE_VALUE)
2830 if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
2831 != INVALID_SET_FILE_POINTER) {
2832 DWORD nbw;
2833
2834 WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
2835 } // endif SetFilePointer
2836
2837 } // endif id
2838 #else // !_WIN32
2839 if (id >= 0 && fn) {
2840 if (Hfile != INVALID_HANDLE_VALUE) {
2841 if (lseek64(Hfile, id * sizeof(IOFF), SEEK_SET) >= 0) {
2842 ssize_t nbw = write(Hfile, &NewOff, sizeof(IOFF));
2843
2844 if (nbw != (signed)sizeof(IOFF))
2845 htrc("Error writing index file header: %s\n", strerror(errno));
2846
2847 } else
2848 htrc("(XHUGE::Close)lseek64: %s (%d)\n", strerror(errno), id);
2849
2850 } else
2851 htrc("(XHUGE)error reopening %s: %s\n", fn, strerror(errno));
2852
2853 } // endif id
2854 #endif // !_WIN32
2855
2856 XLOAD::Close();
2857 } // end of Close
2858
2859 #if defined(XMAP)
2860 /***********************************************************************/
2861 /* Don't know whether this is possible for huge files. */
2862 /***********************************************************************/
FileView(PGLOBAL g,char *)2863 void *XHUGE::FileView(PGLOBAL g, char *)
2864 {
2865 strcpy(g->Message, MSG(NO_PART_MAP));
2866 return NULL;
2867 } // end of FileView
2868 #endif // XMAP
2869
2870 /* -------------------------- XXROW Class --------------------------- */
2871
2872 /***********************************************************************/
2873 /* XXROW Public Constructor. */
2874 /***********************************************************************/
XXROW(PTDBDOS tdbp)2875 XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
2876 {
2877 Srtd = true;
2878 Tdbp = tdbp;
2879 Valp = NULL;
2880 } // end of XXROW constructor
2881
2882 /***********************************************************************/
2883 /* XXROW Reset: re-initialize a Kindex block. */
2884 /***********************************************************************/
Reset(void)2885 void XXROW::Reset(void)
2886 {
2887 #if defined(_DEBUG)
2888 assert(Tdbp->GetLink()); // This a join index
2889 #endif // _DEBUG
2890 } // end of Reset
2891
2892 /***********************************************************************/
2893 /* Init: Open and Initialize a Key Index. */
2894 /***********************************************************************/
Init(PGLOBAL g)2895 bool XXROW::Init(PGLOBAL g)
2896 {
2897 /*********************************************************************/
2898 /* Table will be accessed through an index table. */
2899 /* To_Link should not be NULL. */
2900 /*********************************************************************/
2901 if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
2902 return true;
2903
2904 if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
2905 strcpy(g->Message, MSG(TYPE_MISMATCH));
2906 return true;
2907 } else
2908 Valp = (*Tdbp->GetLink())->GetValue();
2909
2910 if ((Num_K = Tbxp->Cardinality(g)) < 0)
2911 return true; // Not a fixed file
2912
2913 /*********************************************************************/
2914 /* The entire table is indexed, no need to construct the index. */
2915 /*********************************************************************/
2916 Cur_K = Num_K;
2917 return false;
2918 } // end of Init
2919
2920 /***********************************************************************/
2921 /* RANGE: Tell how many record exist in a given value range. */
2922 /***********************************************************************/
Range(PGLOBAL,int limit,bool incl)2923 int XXROW::Range(PGLOBAL, int limit, bool incl)
2924 {
2925 int n = Valp->GetIntValue();
2926
2927 switch (limit) {
2928 case 1: n += ((incl) ? 0 : 1); break;
2929 case 2: n += ((incl) ? 1 : 0); break;
2930 default: n = 1;
2931 } // endswitch limit
2932
2933 return n;
2934 } // end of Range
2935
2936 /***********************************************************************/
2937 /* XXROW: Fetch a physical or logical record. */
2938 /***********************************************************************/
Fetch(PGLOBAL)2939 int XXROW::Fetch(PGLOBAL)
2940 {
2941 if (Num_K == 0)
2942 return -1; // means end of file
2943
2944 /*********************************************************************/
2945 /* Look for a key equal to the link column of previous table, */
2946 /* and return its rank whithin the index table. */
2947 /*********************************************************************/
2948 Cur_K = FastFind();
2949
2950 if (Cur_K >= Num_K)
2951 /*******************************************************************/
2952 /* Rank not whithin index table, signal record not found. */
2953 /*******************************************************************/
2954 return -2; // Means record not found
2955
2956 /*********************************************************************/
2957 /* If rank is equal to stored rank, record is already there. */
2958 /*********************************************************************/
2959 if (Cur_K == Old_K)
2960 return -3; // Means record already there
2961 else
2962 Old_K = Cur_K; // Store rank of newly read record
2963
2964 return Cur_K;
2965 } // end of Fetch
2966
2967 /***********************************************************************/
2968 /* FastFind: Returns the index of matching record in a join. */
2969 /***********************************************************************/
FastFind(void)2970 int XXROW::FastFind(void)
2971 {
2972 int n = Valp->GetIntValue();
2973
2974 if (n < 0)
2975 return (Op == OP_EQ) ? (-1) : 0;
2976 else if (n > Num_K)
2977 return Num_K;
2978 else
2979 return (Op == OP_GT) ? n : (n - 1);
2980
2981 } // end of FastFind
2982
2983 /* ------------------------- KXYCOL Classes -------------------------- */
2984
2985 /***********************************************************************/
2986 /* KXYCOL public constructor. */
2987 /***********************************************************************/
KXYCOL(PKXBASE kp)2988 KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
2989 To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
2990 {
2991 Next = NULL;
2992 Previous = NULL;
2993 Kxp = kp;
2994 Colp = NULL;
2995 IsSorted = false;
2996 Asc = true;
2997 Keys = Nmblk;
2998 Kblp = NULL;
2999 Bkeys = Nmblk;
3000 Blkp = NULL;
3001 Valp = NULL;
3002 Klen = 0;
3003 Kprec = 0;
3004 Type = TYPE_ERROR;
3005 Prefix = false;
3006 Koff = Nmblk;
3007 Val_K = 0;
3008 Ndf = 0;
3009 Mxs = 0;
3010 } // end of KXYCOL constructor
3011
3012 /***********************************************************************/
3013 /* KXYCOL Init: initialize and allocate storage. */
3014 /* Key length kln can be smaller than column length for CHAR columns. */
3015 /***********************************************************************/
Init(PGLOBAL g,PCOL colp,int n,bool sm,int kln)3016 bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
3017 {
3018 int len = colp->GetLength(), prec = colp->GetScale();
3019 bool un = colp->IsUnsigned();
3020
3021 // Currently no indexing on NULL columns
3022 if (colp->IsNullable() && kln) {
3023 sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
3024 return true;
3025 } // endif nullable
3026
3027 if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
3028 len = kln;
3029 Prefix = true;
3030 } // endif kln
3031
3032 if (trace(1))
3033 htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
3034 this, colp->GetName(), n, colp->GetResultType(), sm);
3035
3036 // Allocate the Value object used when moving items
3037 Type = colp->GetResultType();
3038
3039 if (!(Valp = AllocateValue(g, Type, len, prec, un)))
3040 return true;
3041
3042 Klen = Valp->GetClen();
3043 Keys.Size = (size_t)n * (size_t)Klen;
3044
3045 if (!PlgDBalloc(g, NULL, Keys)) {
3046 sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
3047 return true; // Error
3048 } // endif
3049
3050 // Allocate the Valblock. The last parameter is to have rows filled
3051 // by blanks (if true) or keep the zero ending char (if false).
3052 // Currently we set it to true to be compatible with QRY blocks,
3053 // and the one before last is to enable length/type checking, set to
3054 // true if not a prefix key.
3055 Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true, un);
3056 Asc = sm; // Sort mode: Asc=true Desc=false
3057 Ndf = n;
3058
3059 // Store this information to avoid sorting when already done
3060 if (Asc)
3061 IsSorted = colp->GetOpt() == 2;
3062
3063 //SetNulls(colp->IsNullable()); for when null columns will be indexable
3064 Colp = colp;
3065 return false;
3066 } // end of Init
3067
3068 #if defined(XMAP)
3069 /***********************************************************************/
3070 /* KXYCOL MapInit: initialize and address storage. */
3071 /* Key length kln can be smaller than column length for CHAR columns. */
3072 /***********************************************************************/
MapInit(PGLOBAL g,PCOL colp,int * n,BYTE * m)3073 BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
3074 {
3075 int len = colp->GetLength(), prec = colp->GetScale();
3076 bool un = colp->IsUnsigned();
3077
3078 if (n[3] && colp->GetLength() > n[3]
3079 && colp->GetResultType() == TYPE_STRING) {
3080 len = n[3];
3081 Prefix = true;
3082 } // endif kln
3083
3084 Type = colp->GetResultType();
3085
3086 if (trace(1))
3087 htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
3088 this, colp, Type, n[0], len, m);
3089
3090 // Allocate the Value object used when moving items
3091 Valp = AllocateValue(g, Type, len, prec, un);
3092 Klen = Valp->GetClen();
3093
3094 if (n[2]) {
3095 Bkeys.Size = n[2] * Klen;
3096 Bkeys.Memp = m;
3097 Bkeys.Sub = true;
3098
3099 // Allocate the Valblk containing initial block key values
3100 Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true, un);
3101 } // endif nb
3102
3103 Keys.Size = n[0] * Klen;
3104 Keys.Memp = m + Bkeys.Size;
3105 Keys.Sub = true;
3106
3107 // Allocate the Valblock. Last two parameters are to have rows filled
3108 // by blanks (if true) or keep the zero ending char (if false).
3109 // Currently we set it to true to be compatible with QRY blocks,
3110 // and last one to enable type checking (no conversion).
3111 Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, !Prefix, true, un);
3112
3113 if (n[1]) {
3114 Koff.Size = n[1] * sizeof(int);
3115 Koff.Memp = m + Bkeys.Size + Keys.Size;
3116 Koff.Sub = true;
3117 } // endif n[1]
3118
3119 Ndf = n[0];
3120 //IsSorted = colp->GetOpt() < 0;
3121 IsSorted = false;
3122 Colp = colp;
3123 return m + Bkeys.Size + Keys.Size + Koff.Size;
3124 } // end of MapInit
3125 #endif // XMAP
3126
3127 /***********************************************************************/
3128 /* Allocate the offset block used by intermediate key columns. */
3129 /***********************************************************************/
MakeOffset(PGLOBAL g,int n)3130 int *KXYCOL::MakeOffset(PGLOBAL g, int n)
3131 {
3132 if (!Kof) {
3133 // Calculate the initial size of the offset
3134 Koff.Size = (n + 1) * sizeof(int);
3135
3136 // Allocate the required memory
3137 if (!PlgDBalloc(g, NULL, Koff)) {
3138 strcpy(g->Message, MSG(KEY_ALLOC_ERR));
3139 return NULL; // Error
3140 } // endif
3141
3142 } else if (n) {
3143 // This is a reallocation call
3144 PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
3145 } else
3146 PlgDBfree(Koff);
3147
3148 return (int*)Kof;
3149 } // end of MakeOffset
3150
3151 /***********************************************************************/
3152 /* Make a front end array of key values that are the first value of */
3153 /* each blocks (of size n). This to reduce paging in FastFind. */
3154 /***********************************************************************/
MakeBlockArray(PGLOBAL g,int nb,int size)3155 bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
3156 {
3157 int i, k;
3158
3159 // Calculate the size of the block array in the index
3160 Bkeys.Size = nb * Klen;
3161
3162 // Allocate the required memory
3163 if (!PlgDBalloc(g, NULL, Bkeys)) {
3164 sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
3165 return true; // Error
3166 } // endif
3167
3168 // Allocate the Valblk used to contains initial block key values
3169 Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
3170
3171 // Populate the array with values
3172 for (i = k = 0; i < nb; i++, k += size)
3173 Blkp->SetValue(Kblp, i, k);
3174
3175 return false;
3176 } // end of MakeBlockArray
3177
3178 /***********************************************************************/
3179 /* KXYCOL SetValue: read column value for nth array element. */
3180 /***********************************************************************/
SetValue(PCOL colp,int i)3181 void KXYCOL::SetValue(PCOL colp, int i)
3182 {
3183 #if defined(_DEBUG)
3184 assert (Kblp != NULL);
3185 #endif
3186
3187 Kblp->SetValue(colp->GetValue(), i);
3188 } // end of SetValue
3189
3190 /***********************************************************************/
3191 /* InitFind: initialize finding the rank of column value in index. */
3192 /***********************************************************************/
InitFind(PGLOBAL g,PXOB xp)3193 bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
3194 {
3195 if (xp->GetType() == TYPE_CONST) {
3196 if (Kxp->Nth)
3197 return true;
3198
3199 Valp->SetValue_pval(xp->GetValue(), !Prefix);
3200 } else {
3201 xp->Reset();
3202 xp->Eval(g);
3203 Valp->SetValue_pval(xp->GetValue(), false);
3204 } // endif Type
3205
3206 if (trace(2)) {
3207 char buf[32];
3208
3209 htrc("KCOL InitFind: value=%s\n", Valp->GetCharString(buf));
3210 } // endif trace
3211
3212 return false;
3213 } // end of InitFind
3214
3215 #if 0
3216 /***********************************************************************/
3217 /* InitBinFind: initialize Value to the value pointed by vp. */
3218 /***********************************************************************/
3219 void KXYCOL::InitBinFind(void *vp)
3220 {
3221 Valp->SetBinValue(vp);
3222 } // end of InitBinFind
3223 #endif // 0
3224
3225 /***********************************************************************/
3226 /* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
3227 /* already in storage in the corresponding KXYCOL. */
3228 /***********************************************************************/
FillValue(PVAL valp)3229 void KXYCOL::FillValue(PVAL valp)
3230 {
3231 valp->SetValue_pvblk(Kblp, Val_K);
3232
3233 // Set null when applicable (NIY)
3234 //if (valp->GetNullable())
3235 // valp->SetNull(valp->IsZero());
3236
3237 } // end of FillValue
3238
3239 /***********************************************************************/
3240 /* KXYCOL: Compare routine for one numeric value. */
3241 /***********************************************************************/
Compare(int i1,int i2)3242 int KXYCOL::Compare(int i1, int i2)
3243 {
3244 // Do the actual comparison between values.
3245 int k = Kblp->CompVal(i1, i2);
3246
3247 if (trace(4))
3248 htrc("Compare done result=%d\n", k);
3249
3250 return (Asc) ? k : -k;
3251 } // end of Compare
3252
3253 /***********************************************************************/
3254 /* KXYCOL: Compare the ith key to the stored Value. */
3255 /***********************************************************************/
CompVal(int i)3256 int KXYCOL::CompVal(int i)
3257 {
3258 // Do the actual comparison between numerical values.
3259 if (trace(4)) {
3260 int k = (int)Kblp->CompVal(Valp, (int)i);
3261
3262 htrc("Compare done result=%d\n", k);
3263 return k;
3264 } else
3265 return Kblp->CompVal(Valp, i);
3266
3267 } // end of CompVal
3268
3269 /***********************************************************************/
3270 /* KXYCOL: Compare the key to the stored block value. */
3271 /***********************************************************************/
CompBval(int i)3272 int KXYCOL::CompBval(int i)
3273 {
3274 // Do the actual comparison between key values.
3275 return Blkp->CompVal(Valp, i);
3276 } // end of CompBval
3277
3278 /***********************************************************************/
3279 /* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
3280 /***********************************************************************/
ReAlloc(PGLOBAL g,int n)3281 void KXYCOL::ReAlloc(PGLOBAL g, int n)
3282 {
3283 PlgDBrealloc(g, NULL, Keys, n * Klen);
3284 Kblp->ReAlloc(To_Keys, n);
3285 Ndf = n;
3286 } // end of ReAlloc
3287
3288 /***********************************************************************/
3289 /* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
3290 /***********************************************************************/
FreeData(void)3291 void KXYCOL::FreeData(void)
3292 {
3293 PlgDBfree(Keys);
3294 Kblp = NULL;
3295 PlgDBfree(Bkeys);
3296 Blkp = NULL;
3297 PlgDBfree(Koff);
3298 Ndf = 0;
3299 } // end of FreeData
3300