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