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