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