summaryrefslogtreecommitdiff
path: root/storage/connect/xindex.cpp
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
committerOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
commit7b400a088d049661b9a4dded385ac78923bb0017 (patch)
treebb3ee7bac4454f66ec6f38cf6683f24bc6c065d4 /storage/connect/xindex.cpp
parentd67ad26b33ea16a3b59215ef967bdd9b89345e04 (diff)
parente5729127b8a50a0e553fd8b87b2683e4a684dfcc (diff)
downloadmariadb-git-7b400a088d049661b9a4dded385ac78923bb0017.tar.gz
- MRR + Block Indexing
modified: storage/connect/array.h storage/connect/catalog.h storage/connect/colblk.cpp storage/connect/colblk.h storage/connect/connect.cc storage/connect/connect.h storage/connect/domdoc.h storage/connect/filamap.cpp storage/connect/filamap.h storage/connect/filamdbf.h storage/connect/filamfix.cpp storage/connect/filamfix.h storage/connect/filamtxt.cpp storage/connect/filamtxt.h storage/connect/filamvct.cpp storage/connect/filamvct.h storage/connect/filamzip.cpp storage/connect/filamzip.h storage/connect/filter.cpp storage/connect/filter.h storage/connect/global.h storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/myconn.h storage/connect/plgcnx.h storage/connect/plgdbsem.h storage/connect/plgdbutl.cpp storage/connect/plugutil.c storage/connect/preparse.h storage/connect/reldef.cpp storage/connect/reldef.h storage/connect/tabcol.h storage/connect/tabdos.cpp storage/connect/tabdos.h storage/connect/tabfix.cpp storage/connect/tabfmt.cpp storage/connect/tabfmt.h storage/connect/table.cpp storage/connect/tabmysql.cpp storage/connect/tabmysql.h storage/connect/taboccur.h storage/connect/tabodbc.h storage/connect/tabsys.h storage/connect/tabtbl.h storage/connect/tabutil.h storage/connect/tabvct.cpp storage/connect/tabvct.h storage/connect/tabwmi.h storage/connect/tabxml.h storage/connect/user_connect.cc storage/connect/user_connect.h storage/connect/valblk.cpp storage/connect/valblk.h storage/connect/value.cpp storage/connect/value.h storage/connect/xindex.cpp storage/connect/xindex.h storage/connect/xobject.cpp storage/connect/xobject.h storage/connect/xtable.h
Diffstat (limited to 'storage/connect/xindex.cpp')
-rwxr-xr-xstorage/connect/xindex.cpp6056
1 files changed, 3028 insertions, 3028 deletions
diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp
index 601374ef5d7..3037b0a829a 100755
--- a/storage/connect/xindex.cpp
+++ b/storage/connect/xindex.cpp
@@ -1,3028 +1,3028 @@
-/***************** Xindex C++ Class Xindex Code (.CPP) *****************/
-/* Name: XINDEX.CPP Version 2.8 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
-/* */
-/* This file contains the class XINDEX implementation code. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#else // !UNIX
-#include <io.h>
-#endif // !UNIX
-#include <fcntl.h>
-#endif // !WIN32
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/* kindex.h is header containing the KINDEX class definition. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "osutil.h"
-#include "maputil.h"
-//nclude "filter.h"
-#include "tabcol.h"
-#include "xindex.h"
-#include "xobject.h"
-//nclude "scalfnc.h"
-//nclude "array.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-
-/***********************************************************************/
-/* Macro or external routine definition */
-/***********************************************************************/
-#define NZ 7
-#define NW 5
-#define MAX_INDX 10
-#ifndef INVALID_SET_FILE_POINTER
-#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
-#endif
-
-/***********************************************************************/
-/* DB static external variables. */
-/***********************************************************************/
-extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
-
-/***********************************************************************/
-/* Last two parameters are true to enable type checking, and last one */
-/* to have rows filled by blanks to be compatible with QRY blocks. */
-/***********************************************************************/
-PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
- bool check = true, bool blank = true, bool un = false);
-
-/***********************************************************************/
-/* Check whether we have to create/update permanent indexes. */
-/***********************************************************************/
-int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
- {
- int rc;
- PTABLE tablep;
- PTDBDOS tdbp;
- PCATLG cat = PlgGetCatalog(g, true);
-
- /*********************************************************************/
- /* Open a new table in mode read and with only the keys columns. */
- /*********************************************************************/
- tablep = new(g) XTAB(name);
-
- if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep)))
- rc = RC_NF;
- else if (!tdbp->GetDef()->Indexable()) {
- sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
- rc = RC_NF;
- } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
- rc = RC_OK; // No index
-
- return rc;
- } // end of PlgMakeIndex
-
-/* -------------------------- Class INDEXDEF ------------------------- */
-
-/***********************************************************************/
-/* INDEXDEF Constructor. */
-/***********************************************************************/
-INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
- {
-//To_Def = NULL;
- Next = NULL;
- ToKeyParts = NULL;
- Name = name;
- Unique = uniq;
- Invalid = false;
- AutoInc = false;
- Nparts = 0;
- ID = n;
-//Offset = 0;
-//Offhigh = 0;
-//Size = 0;
- MaxSame = 1;
- } // end of INDEXDEF constructor
-
-/***********************************************************************/
-/* Set the max same values for each colum after making the index. */
-/***********************************************************************/
-void INDEXDEF::SetMxsame(PXINDEX x)
- {
- PKPDEF kdp;
- PXCOL xcp;
-
- for (kdp = ToKeyParts, xcp = x->To_KeyCol;
- kdp && xcp; kdp = kdp->Next, xcp = xcp->Next)
- kdp->Mxsame = xcp->Mxs;
- } // end of SetMxsame
-
-/* -------------------------- Class KPARTDEF ------------------------- */
-
-/***********************************************************************/
-/* KPARTDEF Constructor. */
-/***********************************************************************/
-KPARTDEF::KPARTDEF(PSZ name, int n)
- {
- Next = NULL;
- Name = name;
- Mxsame = 0;
- Ncol = n;
- Klen = 0;
- } // end of KPARTDEF constructor
-
-/* -------------------------- XXBASE Class --------------------------- */
-
-/***********************************************************************/
-/* XXBASE public constructor. */
-/***********************************************************************/
-XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
- To_Rec((int*&)Record.Memp)
- {
- Tbxp = tbxp;
- Record = Nmblk;
- Cur_K = -1;
- Old_K = -1;
- Num_K = 0;
- Ndif = 0;
- Bot = Top = Inf = Sup = 0;
- Op = OP_EQ;
- To_KeyCol = NULL;
- Mul = false;
- Val_K = -1;
- Nblk = Sblk = 0;
- Thresh = 7;
- ID = -1;
- Nth = 0;
- } // end of XXBASE constructor
-
-/***********************************************************************/
-/* Make file output of XINDEX contents. */
-/***********************************************************************/
-void XXBASE::Print(PGLOBAL g, FILE *f, uint n)
- {
- char m[64];
-
- memset(m, ' ', n); // Make margin string
- m[n] = '\0';
- fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K);
- } // end of Print
-
-/***********************************************************************/
-/* Make string output of XINDEX contents. */
-/***********************************************************************/
-void XXBASE::Print(PGLOBAL g, char *ps, uint z)
- {
- *ps = '\0';
- strncat(ps, "Xindex", z);
- } // end of Print
-
-/* -------------------------- XINDEX Class --------------------------- */
-
-/***********************************************************************/
-/* XINDEX public constructor. */
-/***********************************************************************/
-XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k)
- : XXBASE(tdbp, !xdp->IsUnique())
- {
- Xdp = xdp;
- ID = xdp->GetID();
- Tdbp = tdbp;
- X = pxp;
- To_LastCol = NULL;
- To_LastVal = NULL;
- To_Cols = cp;
- To_Vals = xp;
- Mul = !xdp->IsUnique();
- Srtd = false;
- Nk = xdp->GetNparts();
- Nval = (k) ? k : Nk;
- Incr = 0;
-//Defoff = xdp->GetOffset();
-//Defhigh = xdp->GetOffhigh();
-//Size = xdp->GetSize();
- MaxSame = xdp->GetMaxSame();
- } // end of XINDEX constructor
-
-/***********************************************************************/
-/* XINDEX Reset: re-initialize a Xindex block. */
-/***********************************************************************/
-void XINDEX::Reset(void)
- {
- for (PXCOL kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = kp->Ndf;
-
- Cur_K = Num_K;
- Old_K = -1; // Needed to avoid not setting CurBlk for Update
- Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST :
- (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ;
- Nth = 0;
- } // end of Reset
-
-/***********************************************************************/
-/* XINDEX Close: terminate index and free all allocated data. */
-/* Do not reset other values that are used at return to make. */
-/***********************************************************************/
-void XINDEX::Close(void)
- {
- // Close file or view of file
- X->Close();
-
- // De-allocate data
- PlgDBfree(Record);
- PlgDBfree(Index);
- PlgDBfree(Offset);
-
- // De-allocate Key data
- for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->FreeData();
-
- // Column values cannot be retrieved from key anymore
- for (int k = 0; k < Nk; k++)
- To_Cols[k]->SetKcol(NULL);
-
- } // end of Close
-
-/***********************************************************************/
-/* XINDEX compare routine for C Quick/Insertion sort. */
-/***********************************************************************/
-int XINDEX::Qcompare(int *i1, int *i2)
- {
- register int k;
- register PXCOL kcp;
-
- for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next)
- if ((k = kcp->Compare(*i1, *i2)))
- break;
-
-#ifdef DEBTRACE
- num_comp++;
-#endif
-
- return k;
- } // end of Qcompare
-
-/***********************************************************************/
-/* Make: Make and index on key column(s). */
-/***********************************************************************/
-bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
- {
- /*********************************************************************/
- /* Table can be accessed through an index. */
- /*********************************************************************/
- int k, rc = RC_OK;
- int *bof, i, j, n, ndf, nkey;
- PKPDEF kdfp = Xdp->GetToKeyParts();
- bool brc = true;
- PCOL colp;
- PXCOL kp, prev = NULL, kcp = NULL;
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- /*********************************************************************/
- /* Allocate the storage that will contain the keys and the file */
- /* positions corresponding to them. */
- /*********************************************************************/
- if ((n = Tdbp->GetMaxSize(g)) < 0)
- return true;
- else if (!n) {
- Num_K = Ndif = 0;
- MaxSame = 1;
-
- // The if condition was suppressed because this may be an existing
- // index that is now void because all table lines were deleted.
-// if (sxp)
- goto nox; // Truncate eventually existing index file
-// else
-// return false;
-
- } // endif n
-
- // File position must be stored
- Record.Size = n * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Record)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n);
- goto err; // Error
- } // endif
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- colp = To_Cols[k];
-
- if (!kdfp) {
- sprintf(g->Message, MSG(INT_COL_ERROR),
- (colp) ? colp->GetName() : "???");
- goto err; // Error
- } // endif kdfp
-
- kcp = new(g) KXYCOL(this);
-
- if (kcp->Init(g, colp, n, true, kdfp->Klen))
- goto err; // Error
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- kdfp = kdfp->Next;
- } // endfor k
-
- To_LastCol = prev;
-
- /*********************************************************************/
- /* Get the starting information for progress. */
- /*********************************************************************/
- dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
- sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
- dup->ProgMax = Tdbp->GetProgMax(g);
- dup->ProgCur = 0;
-
- /*********************************************************************/
- /* Standard init: read the file and construct the index table. */
- /* Note: reading will be sequential as To_Kindex is not set. */
- /*********************************************************************/
- for (i = nkey = 0; i < n && rc != RC_EF; i++) {
-#if defined(THREAD)
- if (!dup->Step) {
- strcpy(g->Message, MSG(QUERY_CANCELLED));
- longjmp(g->jumper[g->jump_level], 99);
- } // endif Step
-#endif // THREAD
-
- /*******************************************************************/
- /* Read a valid record from table file. */
- /*******************************************************************/
- rc = Tdbp->ReadDB(g);
-
- // Update progress information
- dup->ProgCur = Tdbp->GetProgCur();
-
- // Check return code and do whatever must be done according to it
- switch (rc) {
- case RC_OK:
- break;
- case RC_EF:
- goto end_of_file;
- case RC_NF:
- continue;
- default:
- sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
- goto err;
- } // endswitch rc
-
- /*******************************************************************/
- /* Get and Store the file position of the last read record for */
- /* future direct access. */
- /*******************************************************************/
- To_Rec[nkey] = Tdbp->GetRecpos();
-
- /*******************************************************************/
- /* Get the keys and place them in the key blocks. */
- /*******************************************************************/
- for (k = 0, kcp = To_KeyCol;
- k < Nk && kcp;
- k++, kcp = kcp->Next) {
- colp = To_Cols[k];
- colp->Reset();
-
- colp->ReadColumn(g);
-// if (colp->ReadColumn(g))
-// goto err;
-
- kcp->SetValue(colp, nkey);
- } // endfor k
-
- nkey++; // A new valid key was found
- } // endfor i
-
- end_of_file:
-
- // Update progress information
- dup->ProgCur = Tdbp->GetProgMax(g);
-
- /*********************************************************************/
- /* Record the Index size and eventually resize memory allocation. */
- /*********************************************************************/
- if ((Num_K = nkey) < n) {
- PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int));
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->ReAlloc(g, Num_K);
-
- } // endif Num_K
-
- /*********************************************************************/
- /* Sort the index so we can use an optimized Find algorithm. */
- /* Note: for a unique index we use the non conservative sort */
- /* version because normally all index values are different. */
- /* This was set at CSORT class construction. */
- /* For all indexes, an offset array is made so we can check the */
- /* uniqueness of unique indexes. */
- /*********************************************************************/
- Index.Size = Num_K * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Index)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
- goto err; // Error
- } // endif alloc
-
- Offset.Size = (Num_K + 1) * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Offset)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1);
- goto err; // Error
- } // endif alloc
-
- // Call the sort program, it returns the number of distinct values
- if ((Ndif = Qsort(g, Num_K)) < 0)
- goto err; // Error during sort
-
- // Check whether the unique index is unique indeed
- if (!Mul)
- if (Ndif < Num_K) {
- strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
- goto err;
- } else
- PlgDBfree(Offset); // Not used anymore
-
- // Use the index to physically reorder the xindex
- Srtd = Reorder(g);
-
- if (Ndif < Num_K) {
- // Resize the offset array
- PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int));
-
- // Initial value of MaxSame
- MaxSame = Pof[1] - Pof[0];
-
- // Resize the Key array by only keeping the distinct values
- for (i = 1; i < Ndif; i++) {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Move(i, Pof[i]);
-
- MaxSame = max(MaxSame, Pof[i + 1] - Pof[i]);
- } // endfor i
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->ReAlloc(g, Ndif);
-
- } else {
- Mul = false; // Current index is unique
- PlgDBfree(Offset); // Not used anymore
- MaxSame = 1; // Reset it when remaking an index
- } // endif Ndif
-
- /*********************************************************************/
- /* Now do the reduction of the index. Indeed a multi-column index */
- /* can be used for only some of the first columns. For instance if */
- /* an index is defined for column A, B, C PlugDB can use it for */
- /* only the column A or the columns A, B. */
- /* What we do here is to reduce the data so column A will contain */
- /* only the sorted distinct values of A, B will contain data such */
- /* as only distinct values of A,B are stored etc. */
- /* This implies that for each column set an offset array is made */
- /* except if the subset originally contains unique values. */
- /*********************************************************************/
- // Update progress information
- dup->Step = STEP(REDUCE_INDEX);
-
- ndf = Ndif;
- To_LastCol->Mxs = MaxSame;
-
- for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) {
- if (!(bof = kcp->MakeOffset(g, ndf)))
- goto err;
- else
- *bof = 0;
-
- for (n = 0, i = j = 1; i < ndf; i++)
- for (kp = kcp; kp; kp = kp->Previous)
- if (kp->Compare(n, i)) {
- // Values are not equal to last ones
- bof[j++] = n = i;
- break;
- } // endif Compare
-
- if (j < ndf) {
- // Sub-index is multiple
- bof[j] = ndf;
- ndf = j; // New number of distinct values
-
- // Resize the Key array by only keeping the distinct values
- for (kp = kcp; kp; kp = kp->Previous) {
- for (i = 1; i < ndf; i++)
- kp->Move(i, bof[i]);
-
- kp->ReAlloc(g, ndf);
- } // endif kcp
-
- // Resize the offset array
- kcp->MakeOffset(g, ndf);
-
- // Calculate the max same value for this column
- kcp->Mxs = ColMaxSame(kcp);
- } else {
- // Current sub-index is unique
- kcp->MakeOffset(g, 0); // The offset is not used anymore
- kcp->Mxs = 1; // Unique
- } // endif j
-
- } // endfor kcp
-
- /*********************************************************************/
- /* For sorted columns and fixed record size, file position can be */
- /* calculated, so the Record array can be discarted. */
- /* Note: for Num_K = 1 any non null value is Ok. */
- /*********************************************************************/
- if (Srtd && Tdbp->Ftype != RECFM_VAR) {
- Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
- PlgDBfree(Record);
- } // endif Srtd
-
- /*********************************************************************/
- /* Check whether a two-tier find algorithm can be implemented. */
- /* It is currently implemented only for single key indexes. */
- /*********************************************************************/
- if (Nk == 1 && ndf >= 65536) {
- // Implement a two-tier find algorithm
- for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ;
-
- Nblk = (ndf -1) / Sblk + 1;
-
- if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk))
- goto err; // Error
-
- } // endif Num_K
-
- nox:
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
-
- /*********************************************************************/
- /* Save the index so it has not to be recalculated. */
- /*********************************************************************/
- if (!SaveIndex(g, sxp))
- brc = false;
-
- err:
- // We don't need the index anymore
- Close();
-
- if (brc)
- printf("%s\n", g->Message);
-
- return brc;
- } // end of Make
-
-/***********************************************************************/
-/* Return the max size of the intermediate column. */
-/***********************************************************************/
-int XINDEX::ColMaxSame(PXCOL kp)
- {
- int *kof, i, ck1, ck2, ckn = 1;
- PXCOL kcp;
-
- // Calculate the max same value for this column
- for (i = 0; i < kp->Ndf; i++) {
- ck1 = i;
- ck2 = i + 1;
-
- for (kcp = kp; kcp; kcp = kcp->Next) {
- if (!(kof = (kcp->Next) ? kcp->Kof : Pof))
- break;
-
- ck1 = kof[ck1];
- ck2 = kof[ck2];
- } // endfor kcp
-
- ckn = max(ckn, ck2 - ck1);
- } // endfor i
-
- return ckn;
- } // end of ColMaxSame
-
-/***********************************************************************/
-/* Reorder: use the sort index to reorder the data in storage so */
-/* it will be physically sorted and sort index can be removed. */
-/***********************************************************************/
-bool XINDEX::Reorder(PGLOBAL g)
- {
- register int i, j, k, n;
- bool sorted = true;
- PXCOL kcp;
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- if (Num_K > 500000) {
- // Update progress information
- dup->Step = STEP(REORDER_INDEX);
- dup->ProgMax = Num_K;
- dup->ProgCur = 0;
- } else
- dup = NULL;
-
- if (!Pex)
- return Srtd;
-
- for (i = 0; i < Num_K; i++) {
- if (Pex[i] == Num_K) { // Already moved
- continue;
- } else if (Pex[i] == i) { // Already placed
- if (dup)
- dup->ProgCur++;
-
- continue;
- } // endif's Pex
-
- sorted = false;
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Save(i);
-
- n = To_Rec[i];
-
- for (j = i;; j = k) {
- k = Pex[j];
- Pex[j] = Num_K; // Mark position as set
-
- if (k == i) {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Restore(j);
-
- To_Rec[j] = n;
- break; // end of loop
- } else {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Move(j, k); // Move k to j
-
- To_Rec[j] = To_Rec[k];
- } // endif k
-
- if (dup)
- dup->ProgCur++;
-
- } // endfor j
-
- } // endfor i
-
- // The index is not used anymore
- PlgDBfree(Index);
- return sorted;
- } // end of Reorder
-
-/***********************************************************************/
-/* Save the index values for this table. */
-/* The problem here is to avoid name duplication, because more than */
-/* one data file can have the same name (but different types) and/or */
-/* the same data file can be used with different block sizes. This is */
-/* why we use Ofn that defaults to the file name but can be set to a */
-/* different name if necessary. */
-/***********************************************************************/
-bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
- {
- char *ftype;
- char fn[_MAX_PATH];
- int n[NZ], nof = (Mul) ? (Ndif + 1) : 0;
- int id = -1, size = 0;
- bool sep, rc = false;
- PXCOL kcp = To_KeyCol;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- PDBUSER dup = PlgGetUser(g);
-
- dup->Step = STEP(SAVING_INDEX);
- dup->ProgMax = 15 + 16 * Nk;
- dup->ProgCur = 0;
-
- switch (Tdbp->Ftype) {
- case RECFM_VAR: ftype = ".dnx"; break;
- case RECFM_FIX: ftype = ".fnx"; break;
- case RECFM_BIN: ftype = ".bnx"; break;
- case RECFM_VCT: ftype = ".vnx"; break;
- case RECFM_DBF: ftype = ".dbx"; break;
- default:
- sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) {
- // Index is saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- sxp = NULL;
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
- if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
- printf("%s\n", g->Message);
- return true;
- } // endif Open
-
- if (!Ndif)
- goto end; // Void index
-
- /*********************************************************************/
- /* Write the index values on the index file. */
- /*********************************************************************/
- n[0] = ID; // To check validity
- n[1] = Nk; // The number of indexed columns
- n[2] = nof; // The offset array size or 0
- n[3] = Num_K; // The index size
- n[4] = Incr; // Increment of record positions
- n[5] = Nblk; n[6] = Sblk;
-
-#if defined(TRACE)
- printf("Saving index %s\n", Xdp->GetName());
- printf("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d\n",
- ID, Nk, nof, Num_K, Incr, Nblk, Sblk);
-#endif // TRACE
-
- size = X->Write(g, n, NZ, sizeof(int), rc);
- dup->ProgCur = 1;
-
- if (Mul) // Write the offset array
- size += X->Write(g, Pof, nof, sizeof(int), rc);
-
- dup->ProgCur = 5;
-
- if (!Incr) // Write the record position array(s)
- size += X->Write(g, To_Rec, Num_K, sizeof(int), rc);
-
- dup->ProgCur = 15;
-
- for (; kcp; kcp = kcp->Next) {
- n[0] = kcp->Ndf; // Number of distinct sub-values
- n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique
- n[2] = (kcp == To_KeyCol) ? Nblk : 0;
- n[3] = kcp->Klen; // To be checked later
- n[4] = kcp->Type; // To be checked later
-
- size += X->Write(g, n, NW, sizeof(int), rc);
- dup->ProgCur += 1;
-
- if (n[2])
- size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
-
- dup->ProgCur += 5;
-
- size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
- dup->ProgCur += 5;
-
- if (n[1])
- size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
-
- dup->ProgCur += 5;
- } // endfor kcp
-
-#if defined(TRACE)
- printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size);
-#endif // TRACE
-
- end:
- X->Close(fn, id);
- return rc;
- } // end of SaveIndex
-
-#if !defined(XMAP)
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XINDEX::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* If sorting is required, this will be done later. */
- /*********************************************************************/
- char *ftype;
- char fn[_MAX_PATH];
- int k, n, nv[NZ], id = -1;
- bool estim = false;
- PCOL colp;
- PXCOL prev = NULL, kcp = NULL;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Get the first key column. */
- /*********************************************************************/
- if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } else
- colp = To_Cols[0];
-
- switch (Tdbp->Ftype) {
- case RECFM_VAR: ftype = ".dnx"; break;
- case RECFM_FIX: ftype = ".fnx"; break;
- case RECFM_BIN: ftype = ".bnx"; break;
- case RECFM_VCT: ftype = ".vnx"; break;
- case RECFM_DBF: ftype = ".dbx"; break;
- default:
- sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Open the index file and check its validity. */
- /*********************************************************************/
- if (X->Open(g, fn, id, MODE_READ))
- goto err; // No saved values
-
- // Now start the reading process.
- if (X->Read(g, nv, NZ, sizeof(int)))
- goto err;
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d %d %d %d\n",
- nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif
-
- if (nv[2]) {
- Mul = true;
- Ndif = nv[2];
-
- // Allocate the storage that will contain the offset array
- Offset.Size = Ndif * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Offset)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif);
- goto err;
- } // endif
-
- if (X->Read(g, Pof, Ndif, sizeof(int)))
- goto err;
-
- Ndif--; // nv[2] is offset size, equal to Ndif + 1
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
- Incr = nv[4];
- Nblk = nv[5];
- Sblk = nv[6];
-
- if (!Incr) {
- /*******************************************************************/
- /* Allocate the storage that will contain the file positions. */
- /*******************************************************************/
- Record.Size = Num_K * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Record)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
- goto err;
- } // endif
-
- if (X->Read(g, To_Rec, Num_K, sizeof(int)))
- goto err;
-
- } else
- Srtd = true; // Sorted positions can be calculated
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- if (k == Nval)
- To_LastVal = prev;
-
- if (X->Read(g, nv, NW, sizeof(int)))
- goto err;
-
- colp = To_Cols[k];
-
- if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- kcp = new(g) KXYCOL(this);
-
- if (kcp->Init(g, colp, nv[0], true, (int)nv[3]))
- goto err; // Error
-
- /*******************************************************************/
- /* Read the index values from the index file. */
- /*******************************************************************/
- if (k == 0 && Nblk) {
- if (kcp->MakeBlockArray(g, Nblk, 0))
- goto err;
-
- // Read block values
- if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen))
- goto err;
-
- } // endif Nblk
-
- // Read the entire (small) index
- if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen))
- goto err;
-
- if (nv[1]) {
- if (!kcp->MakeOffset(g, nv[1] - 1))
- goto err;
-
- // Read the offset array
- if (X->Read(g, kcp->Kof, nv[1], sizeof(int)))
- goto err;
-
- } // endif n[1]
-
- if (!kcp->Prefix)
- // Indicate that the key column value can be found from KXYCOL
- colp->SetKcol(kcp);
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- } // endfor k
-
- To_LastCol = prev;
-
- if (Mul && prev) {
- // Last key offset is the index offset
- kcp->Koff = Offset;
- kcp->Koff.Sub = true;
- } // endif Mul
-
- X->Close();
-
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
-
-err:
- Close();
- return true;
- } // end of Init
-
-#else // XMAP
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XINDEX::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* If sorting is required, this will be done later. */
- /*********************************************************************/
- const char *ftype;
- BYTE *mbase;
- char fn[_MAX_PATH];
- int *nv, k, n, id = -1;
- bool estim;
- PCOL colp;
- PXCOL prev = NULL, kcp = NULL;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- PDBUSER dup = PlgGetUser(g);
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Get the first key column. */
- /*********************************************************************/
- if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } else
- colp = To_Cols[0];
-
- switch (Tdbp->Ftype) {
- case RECFM_VAR: ftype = ".dnx"; break;
- case RECFM_FIX: ftype = ".fnx"; break;
- case RECFM_BIN: ftype = ".bnx"; break;
- case RECFM_VCT: ftype = ".vnx"; break;
- case RECFM_DBF: ftype = ".dbx"; break;
- default:
- sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was save in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif SepIndex
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Get a view on the part of the index file containing this index. */
- /*********************************************************************/
- if (!(mbase = (BYTE*)X->FileView(g, fn)))
- goto err;
-
- if (id >= 0) {
- // Get offset from the header
- IOFF *noff = (IOFF*)mbase;
-
- // Position the memory base at the offset of this index
- mbase += noff[id].Low;
- } // endif id
-
- // Now start the mapping process.
- nv = (int*)mbase;
- mbase += NZ * sizeof(int);
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d %d %d %d\n",
- nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- // Not this index
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif nv
-
- if (nv[2]) {
- // Set the offset array memory block
- Offset.Memp = mbase;
- Offset.Size = nv[2] * sizeof(int);
- Offset.Sub = true;
- Mul = true;
- Ndif = nv[2] - 1;
- mbase += Offset.Size;
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
- Incr = nv[4];
- Nblk = nv[5];
- Sblk = nv[6];
-
- if (!Incr) {
- /*******************************************************************/
- /* Point to the storage that contains the file positions. */
- /*******************************************************************/
- Record.Size = Num_K * sizeof(int);
- Record.Memp = mbase;
- Record.Sub = true;
- mbase += Record.Size;
- } else
- Srtd = true; // Sorted positions can be calculated
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- if (k == Nval)
- To_LastVal = prev;
-
- nv = (int*)mbase;
- mbase += (NW * sizeof(int));
-
- colp = To_Cols[k];
-
- if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- kcp = new(g) KXYCOL(this);
-
- if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
- goto err;
-
- if (!kcp->Prefix)
- // Indicate that the key column value can be found from KXYCOL
- colp->SetKcol(kcp);
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- } // endfor k
-
- To_LastCol = prev;
-
- if (Mul && prev)
- // Last key offset is the index offset
- kcp->Koff = Offset;
-
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
-
-err:
- Close();
- return true;
- } // end of Init
-#endif // XMAP
-
-/***********************************************************************/
-/* Get Ndif and Num_K from the index file. */
-/***********************************************************************/
-bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
- {
- char *ftype;
- char fn[_MAX_PATH];
- int n, nv[NZ], id = -1;
- bool estim = false;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
-
- ndif = numk = 0;
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Check the key part number. */
- /*********************************************************************/
- if (!Nk) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } // endif Nk
-
- switch (Tdbp->Ftype) {
- case RECFM_VAR: ftype = ".dnx"; break;
- case RECFM_FIX: ftype = ".fnx"; break;
- case RECFM_BIN: ftype = ".bnx"; break;
- case RECFM_VCT: ftype = ".vnx"; break;
- case RECFM_DBF: ftype = ".dbx"; break;
- default:
- sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Open the index file and check its validity. */
- /*********************************************************************/
- if (X->Open(g, fn, id, MODE_READ))
- goto err; // No saved values
-
- // Get offset from XDB file
-//if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
-// goto err;
-
- // Now start the reading process.
- if (X->Read(g, nv, NZ, sizeof(int)))
- goto err;
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif
-
- if (nv[2]) {
- Mul = true;
- Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
-
- if (Nk > 1) {
- if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
- goto err;
-
- if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
- goto err;
-
- if (X->Read(g, nv, NW, sizeof(int)))
- goto err;
-
- PCOL colp = *To_Cols;
-
- if (nv[4] != colp->GetResultType() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- Ndif = nv[0];
- } // endif Nk
-
- /*********************************************************************/
- /* Set size values. */
- /*********************************************************************/
- ndif = Ndif;
- numk = Num_K;
- return false;
-
-err:
- X->Close();
- return true;
- } // end of GetAllSizes
-
-/***********************************************************************/
-/* RANGE: Tell how many records exist for a given value, for an array */
-/* of values, or in a given value range. */
-/***********************************************************************/
-int XINDEX::Range(PGLOBAL g, int limit, bool incl)
- {
- int i, k, n = 0;
- PXOB *xp = To_Vals;
- PXCOL kp = To_KeyCol;
- OPVAL op = Op;
-
- switch (limit) {
- case 1: Op = (incl) ? OP_GE : OP_GT; break;
- case 2: Op = (incl) ? OP_GT : OP_GE; break;
- default: return 0;
- } // endswitch limit
-
- /*********************************************************************/
- /* Currently only range of constant values with an EQ operator is */
- /* implemented. Find the number of rows for each given values. */
- /*********************************************************************/
- if (xp[0]->GetType() == TYPE_CONST) {
- for (i = 0; kp; kp = kp->Next) {
- kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
- if (++i == Nval) break;
- } // endfor kp
-
- if ((k = FastFind(Nval)) < Num_K)
- n = k;
-// if (limit)
-// n = (Mul) ? k : kp->Val_K;
-// else
-// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
-
- } else {
- strcpy(g->Message, MSG(RANGE_NO_JOIN));
- n = -1; // Logical error
- } // endif'f Type
-
- Op = op;
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* Return the size of the group (equal values) of the current value. */
-/***********************************************************************/
-int XINDEX::GroupSize(void)
- {
-#if defined(_DEBUG)
- assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
-#endif // _DEBUG
-
- if (Nval == Nk)
- return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
- : 1;
-
-#if defined(_DEBUG)
- assert(To_LastVal);
-#endif // _DEBUG
-
- // Index whose only some columns are used
- int ck1, ck2;
-
- ck1 = To_LastVal->Val_K;
- ck2 = ck1 + 1;
-
-#if defined(_DEBUG)
- assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
-#endif // _DEBUG
-
- for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
- ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
- ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
- } // endfor kcp
-
- return ck2 - ck1;
- } // end of GroupSize
-
-/***********************************************************************/
-/* Find Cur_K and Val_K's of the next distinct value of the index. */
-/* Returns false if Ok, true if there are no more different values. */
-/***********************************************************************/
-bool XINDEX::NextValDif(void)
- {
- int curk;
- PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
-
- if (++kcp->Val_K < kcp->Ndf) {
- Cur_K = curk = kcp->Val_K;
-
- // (Cur_K return is currently not used by SQLGBX)
- for (PXCOL kp = kcp; kp; kp = kp->Next)
- Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
-
- } else
- return true;
-
- for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
- if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
- break; // all previous columns have same value
-
- curk = ++kcp->Val_K; // This is a break, get new column value
- } // endfor kcp
-
- return false;
- } // end of NextValDif
-
-/***********************************************************************/
-/* XINDEX: Find Cur_K and Val_K's of next index entry. */
-/* If eq is true next values must be equal to last ones up to Nval. */
-/* Returns false if Ok, true if there are no more (equal) values. */
-/***********************************************************************/
-bool XINDEX::NextVal(bool eq)
- {
- int n, neq = Nk + 1, curk;
- PXCOL kcp;
-
- if (Cur_K == Num_K)
- return true;
- else
- curk = ++Cur_K;
-
- for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
- if (kcp->Kof) {
- if (curk == kcp->Kof[kcp->Val_K + 1])
- neq = n;
-
- } else {
-#ifdef _DEBUG
- assert(curk == kcp->Val_K + 1);
-#endif // _DEBUG
- neq = n;
- } // endif Kof
-
-#ifdef _DEBUG
- assert(kcp->Val_K < kcp->Ndf);
-#endif // _DEBUG
-
- // If this is not a break...
- if (neq > n)
- break; // all previous columns have same value
-
- curk = ++kcp->Val_K; // This is a break, get new column value
- } // endfor kcp
-
- // Return true if no more values or, in case of "equal" values,
- // if the last used column value has changed
- return (Cur_K == Num_K || (eq && neq <= Nval));
- } // end of NextVal
-
-/***********************************************************************/
-/* XINDEX: Fetch a physical or logical record. */
-/***********************************************************************/
-int XINDEX::Fetch(PGLOBAL g)
- {
- int n;
- PXCOL kp;
-
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Table read through a sorted index. */
- /*********************************************************************/
- switch (Op) {
- case OP_NEXT: // Read next
- if (NextVal(false))
- return -1; // End of indexed file
-
- break;
- case OP_FIRST: // Read first
- for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = 0;
-
- Op = OP_NEXT;
- break;
- case OP_SAME: // Read next same
- // Logically the key values should be the same as before
-#if defined(TRACE)
- printf("looking for next same value\n");
-#endif // TRACE
-
- if (NextVal(true)) {
- Op = OP_EQ;
- return -2; // no more equal values
- } // endif NextVal
-
- break;
- case OP_NXTDIF: // Read next dif
-// while (!NextVal(true)) ;
-
-// if (Cur_K >= Num_K)
-// return -1; // End of indexed file
- if (NextValDif())
- return -1; // End of indexed file
-
- break;
- case OP_FSTDIF: // Read first diff
- for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = 0;
-
- Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
- break;
- default: // Should be OP_EQ
-// if (Tbxp->Key_Rank < 0) {
- /***************************************************************/
- /* Look for the first key equal to the link column values */
- /* and return its rank whithin the index table. */
- /***************************************************************/
- for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
- if (kp->InitFind(g, To_Vals[n]))
- return -1; // No more constant values
-
- Nth++;
-
-#if defined(TRACE)
- printf("Fetch: Looking for new value\n");
-#endif // TRACE
- Cur_K = FastFind(Nval);
-
- if (Cur_K >= Num_K)
- /*************************************************************/
- /* Rank not whithin index table, signal record not found. */
- /*************************************************************/
- return -2;
-
- else if (Mul || Nval < Nk)
- Op = OP_SAME;
-
- } // endswitch Op
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- /*********************************************************************/
- /* Return the position of the required record. */
- /*********************************************************************/
- return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching record in a join using an */
-/* optimized algorithm based on dichotomie and optimized comparing. */
-/***********************************************************************/
-int XINDEX::FastFind(int nv)
- {
- register int curk, sup, inf, i= 0, k, n = 2;
- register PXCOL kp, kcp;
-
- assert((int)nv == Nval);
-
- if (Nblk && Op == OP_EQ) {
- // Look in block values to find in which block to search
- sup = Nblk;
- inf = -1;
-
- while (n && sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = To_KeyCol->CompBval(i);
-
- if (n < 0)
- sup = i;
- else
- inf = i;
-
- } // endwhile
-
- if (inf < 0)
- return Num_K;
-
-// i = inf;
- inf *= Sblk;
-
- if ((sup = inf + Sblk) > To_KeyCol->Ndf)
- sup = To_KeyCol->Ndf;
-
- inf--;
- } else {
- inf = -1;
- sup = To_KeyCol->Ndf;
- } // endif Nblk
-
- for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
- while (sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompVal(i);
-
- if (n < 0)
- sup = i;
- else if (n > 0)
- inf = i;
- else
- break;
-
- } // endwhile
-
- if (n) {
- if (Op != OP_EQ) {
- // Currently only OP_GT or OP_GE
- kcp->Val_K = curk = sup;
-
- // Check for value changes in previous key parts
- for (kp = kcp->Previous; kp; kp = kp->Previous)
- if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
- break;
- else
- curk = ++kp->Val_K;
-
- n = 0;
- } // endif Op
-
- break;
- } // endif n
-
- kcp->Val_K = i;
-
- if (++k == Nval) {
- if (Op == OP_GT) { // n is always 0
- curk = ++kcp->Val_K; // Increment value by 1
-
- // Check for value changes in previous key parts
- for (kp = kcp->Previous; kp; kp = kp->Previous)
- if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
- break; // Not changed
- else
- curk = ++kp->Val_K;
-
- } // endif Op
-
- break; // So kcp remains pointing the last tested block
- } // endif k
-
- if (kcp->Kof) {
- inf = kcp->Kof[i] - 1;
- sup = kcp->Kof[i + 1];
- } else {
- inf = i - 1;
- sup = i + 1;
- } // endif Kof
-
- } // endfor k, kcp
-
- if (n) {
- // Record not found
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Val_K = kcp->Ndf; // Not a valid value
-
- return Num_K;
- } // endif n
-
- for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
- kcp->Val_K = curk;
- curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
- } // endfor kcp
-
- return curk;
- } // end of FastFind
-
-/* -------------------------- XINDXS Class --------------------------- */
-
-/***********************************************************************/
-/* XINDXS public constructor. */
-/***********************************************************************/
-XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
- : XINDEX(tdbp, xdp, pxp, cp, xp)
- {
- Srtd = To_Cols[0]->GetOpt() < 0; // ?????
- } // end of XINDXS constructor
-
-/***********************************************************************/
-/* XINDXS compare routine for C Quick/Insertion sort. */
-/***********************************************************************/
-int XINDXS::Qcompare(int *i1, int *i2)
- {
-#ifdef DEBTRACE
- num_comp++;
-#endif
-
- return To_KeyCol->Compare(*i1, *i2);
- } // end of Qcompare
-
-/***********************************************************************/
-/* Range: Tell how many records exist for given value(s): */
-/* If limit=0 return range for these values. */
-/* If limit=1 return the start of range. */
-/* If limit=2 return the end of range. */
-/***********************************************************************/
-int XINDXS::Range(PGLOBAL g, int limit, bool incl)
- {
- int k, n = 0;
- PXOB xp = To_Vals[0];
- PXCOL kp = To_KeyCol;
- OPVAL op = Op;
-
- switch (limit) {
- case 1: Op = (incl) ? OP_GE : OP_GT; break;
- case 2: Op = (incl) ? OP_GT : OP_GE; break;
- default: Op = OP_EQ;
- } // endswitch limit
-
- /*********************************************************************/
- /* Currently only range of constant values with an EQ operator is */
- /* implemented. Find the number of rows for each given values. */
- /*********************************************************************/
- if (xp->GetType() == TYPE_CONST) {
- kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
- k = FastFind(Nval);
-
- if (k < Num_K || Op != OP_EQ)
- if (limit)
- n = (Mul) ? k : kp->Val_K;
- else
- n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
-
- } else {
- strcpy(g->Message, MSG(RANGE_NO_JOIN));
- n = -1; // Logical error
- } // endif'f Type
-
- Op = op;
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* Return the size of the group (equal values) of the current value. */
-/***********************************************************************/
-int XINDXS::GroupSize(void)
- {
-#if defined(_DEBUG)
- assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
-#endif // _DEBUG
- return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K]
- : 1;
- } // end of GroupSize
-
-/***********************************************************************/
-/* XINDXS: Find Cur_K and Val_K of next index value. */
-/* If b is true next value must be equal to last one. */
-/* Returns false if Ok, true if there are no more (equal) values. */
-/***********************************************************************/
-bool XINDXS::NextVal(bool eq)
- {
- bool rc;
-
- if (To_KeyCol->Val_K == Ndif)
- return true;
-
- if (Mul) {
- int limit = Pof[To_KeyCol->Val_K + 1];
-
-#ifdef _DEBUG
- assert(Cur_K < limit);
- assert(To_KeyCol->Val_K < Ndif);
-#endif // _DEBUG
-
- if (++Cur_K == limit) {
- To_KeyCol->Val_K++;
- rc = (eq || limit == Num_K);
- } else
- rc = false;
-
- } else
- rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
-
- return rc;
- } // end of NextVal
-
-/***********************************************************************/
-/* XINDXS: Fetch a physical or logical record. */
-/***********************************************************************/
-int XINDXS::Fetch(PGLOBAL g)
- {
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Table read through a sorted index. */
- /*********************************************************************/
- switch (Op) {
- case OP_NEXT: // Read next
- if (NextVal(false))
- return -1; // End of indexed file
-
- break;
- case OP_FIRST: // Read first
- To_KeyCol->Val_K = Cur_K = 0;
- Op = OP_NEXT;
- break;
- case OP_SAME: // Read next same
-#if defined(TRACE)
-// printf("looking for next same value\n");
-#endif // TRACE
-
- if (!Mul || NextVal(true)) {
- Op = OP_EQ;
- return -2; // No more equal values
- } // endif Mul
-
- break;
- case OP_NXTDIF: // Read next dif
- if (++To_KeyCol->Val_K == Ndif)
- return -1; // End of indexed file
-
- Cur_K = Pof[To_KeyCol->Val_K];
- break;
- case OP_FSTDIF: // Read first diff
- To_KeyCol->Val_K = Cur_K = 0;
- Op = (Mul) ? OP_NXTDIF : OP_NEXT;
- break;
- default: // Should OP_EQ
- /*****************************************************************/
- /* Look for the first key equal to the link column values */
- /* and return its rank whithin the index table. */
- /*****************************************************************/
- if (To_KeyCol->InitFind(g, To_Vals[0]))
- return -1; // No more constant values
- else
- Nth++;
-
-#if defined(TRACE)
- printf("Fetch: Looking for new value\n");
-#endif // TRACE
-
- Cur_K = FastFind(1);
-
- if (Cur_K >= Num_K)
- // Rank not whithin index table, signal record not found
- return -2;
- else if (Mul)
- Op = OP_SAME;
-
- } // endswitch Op
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- /*********************************************************************/
- /* Return the position of the required record. */
- /*********************************************************************/
- return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching indexed record using an */
-/* optimized algorithm based on dichotomie and optimized comparing. */
-/***********************************************************************/
-int XINDXS::FastFind(int nk)
- {
- register int sup, inf, i= 0, n = 2;
- register PXCOL kcp = To_KeyCol;
-
- if (Nblk && Op == OP_EQ) {
- // Look in block values to find in which block to search
- sup = Nblk;
- inf = -1;
-
- while (n && sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompBval(i);
-
- if (n < 0)
- sup = i;
- else
- inf = i;
-
- } // endwhile
-
- if (inf < 0)
- return Num_K;
-
-// i = inf;
- inf *= Sblk;
-
- if ((sup = inf + Sblk) > Ndif)
- sup = Ndif;
-
- inf--;
- } else {
- inf = -1;
- sup = Ndif;
- } // endif Nblk
-
- while (sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompVal(i);
-
- if (n < 0)
- sup = i;
- else if (n > 0)
- inf = i;
- else
- break;
-
- } // endwhile
-
- if (!n && Op == OP_GT) {
- ++i;
- } else if (n && Op != OP_EQ) {
- // Currently only OP_GT or OP_GE
- i = sup;
- n = 0;
- } // endif sup
-
- kcp->Val_K = i; // Used by FillValue
- return ((n) ? Num_K : (Mul) ? Pof[i] : i);
- } // end of FastFind
-
-/* -------------------------- XLOAD Class --------------------------- */
-
-/***********************************************************************/
-/* XLOAD constructor. */
-/***********************************************************************/
-XLOAD::XLOAD(void)
- {
- Hfile = INVALID_HANDLE_VALUE;
-#if defined(WIN32) && defined(XMAP)
- ViewBase = NULL;
-#endif // WIN32 && XMAP
- NewOff.Val = 0LL;
-} // end of XLOAD constructor
-
-/***********************************************************************/
-/* Close the index huge file. */
-/***********************************************************************/
-void XLOAD::Close(void)
- {
- if (Hfile != INVALID_HANDLE_VALUE) {
- CloseFileHandle(Hfile);
- Hfile = INVALID_HANDLE_VALUE;
- } // endif Hfile
-
-#if defined(WIN32) && defined(XMAP)
- if (ViewBase) {
- if (!UnmapViewOfFile(ViewBase))
- printf("Error %d closing Viewmap\n", GetLastError());
-
- ViewBase = NULL;
- } // endif ViewBase
-#endif // WIN32 && XMAP
-
- } // end of Close
-
-/* --------------------------- XFILE Class --------------------------- */
-
-/***********************************************************************/
-/* XFILE constructor. */
-/***********************************************************************/
-XFILE::XFILE(void) : XLOAD()
- {
- Xfile = NULL;
-#if defined(XMAP) && !defined(WIN32)
- Mmp = NULL;
-#endif // XMAP && !WIN32
- } // end of XFILE constructor
-
-/***********************************************************************/
-/* Xopen function: opens a file using native API's. */
-/***********************************************************************/
-bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
- {
- char *pmod;
- bool rc;
- IOFF noff[MAX_INDX];
-
- /*********************************************************************/
- /* Open the index file according to mode. */
- /*********************************************************************/
- switch (mode) {
- case MODE_READ: pmod = "rb"; break;
- case MODE_WRITE: pmod = "wb"; break;
- case MODE_INSERT: pmod = "ab"; break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch mode
-
- if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
-#if defined(TRACE)
- printf("Open: %s\n", g->Message);
-#endif // TRACE
- return true;
- } // endif Xfile
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Position the cursor at end of file so ftell returns file size. */
- /*******************************************************************/
- if (fseek(Xfile, 0, SEEK_END)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
- NewOff.Low = (int)ftell(Xfile);
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
- fseek(Xfile, 0, SEEK_END);
- NewOff.Low = (int)ftell(Xfile);
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
- sprintf(g->Message, MSG(XFILE_READERR), errno);
- return true;
- } // endif MAX_INDX
-
- // Position the cursor at the offset of this index
- if (fseek(Xfile, noff[id].Low, SEEK_SET)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
- } // endif mode
-
- return false;
- } // end of Open
-
-/***********************************************************************/
-/* Move into an index file. */
-/***********************************************************************/
-bool XFILE::Seek(PGLOBAL g, int low, int high, int origin)
- {
-#if defined(_DEBUG)
- assert(high == 0);
-#endif // !_DEBUG
-
- if (fseek(Xfile, low, origin)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
-//ftell(Xfile);
- return false;
- } // end of Seek
-
-/***********************************************************************/
-/* Read from the index file. */
-/***********************************************************************/
-bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
- {
- if (fread(buf, size, n, Xfile) != (size_t)n) {
- sprintf(g->Message, MSG(XFILE_READERR), errno);
- return true;
- } // endif size
-
- return false;
- } // end of Read
-
-/***********************************************************************/
-/* Write on index file, set rc and return the number of bytes written */
-/***********************************************************************/
-int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
- {
- int niw = (int)fwrite(buf, size, n, Xfile);
-
- if (niw != n) {
- sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
- rc = true;
- } // endif size
-
- return niw * size;
- } // end of Write
-
-/***********************************************************************/
-/* Update the file header and close the index file. */
-/***********************************************************************/
-void XFILE::Close(char *fn, int id)
- {
- if (id >= 0 && fn && Xfile) {
- fclose(Xfile);
-
- if ((Xfile = fopen(fn, "r+b")))
- if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
- fwrite(&NewOff, sizeof(int), 2, Xfile);
-
- } // endif id
-
- Close();
- } // end of Close
-
-/***********************************************************************/
-/* Close the index file. */
-/***********************************************************************/
-void XFILE::Close(void)
- {
- XLOAD::Close();
-
- if (Xfile) {
- fclose(Xfile);
- Xfile = NULL;
- } // endif Xfile
-
-#if defined(XMAP) && !defined(WIN32)
- if (Mmp) {
- CloseMemMap(Mmp->memory, Mmp->lenL);
- Mmp = NULL;
- } // endif Mmp
-#endif // XMAP
- } // end of Close
-
-#if defined(XMAP)
- /*********************************************************************/
- /* Map the entire index file. */
- /*********************************************************************/
-void *XFILE::FileView(PGLOBAL g, char *fn)
- {
- HANDLE h;
-
- Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
- h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
-
- if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
- if (!(*g->Message))
- strcpy(g->Message, MSG(FILE_MAP_ERR));
-
- CloseFileHandle(h); // Not used anymore
- return NULL; // No saved values
- } // endif h
-
- CloseFileHandle(h); // Not used anymore
- return Mmp->memory;
- } // end of FileView
-#endif // XMAP
-
-/* -------------------------- XHUGE Class --------------------------- */
-
-/***********************************************************************/
-/* Xopen function: opens a file using native API's. */
-/***********************************************************************/
-bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
- {
- IOFF noff[MAX_INDX];
-
- if (Hfile != INVALID_HANDLE_VALUE) {
- sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
- return true;
- } // endif
-
-#if defined(TRACE)
- printf( "Xopen: filename=%s mode=%d\n", filename, mode);
-#endif // TRACE
-
-#if defined(WIN32)
- LONG high = 0;
- DWORD rc, drc, access, share, creation;
-
- /*********************************************************************/
- /* Create the file object according to access mode */
- /*********************************************************************/
- switch (mode) {
- case MODE_READ:
- access = GENERIC_READ;
- share = FILE_SHARE_READ;
- creation = OPEN_EXISTING;
- break;
- case MODE_WRITE:
- access = GENERIC_WRITE;
- share = 0;
- creation = CREATE_ALWAYS;
- break;
- case MODE_INSERT:
- access = GENERIC_WRITE;
- share = 0;
- creation = OPEN_EXISTING;
- break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch
-
- Hfile = CreateFile(filename, access, share, NULL, creation,
- FILE_ATTRIBUTE_NORMAL, NULL);
-
- if (Hfile == INVALID_HANDLE_VALUE) {
- rc = GetLastError();
- sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
- (LPTSTR)filename, sizeof(filename), NULL);
- strcat(g->Message, filename);
- return true;
- } // endif Hfile
-
-#ifdef DEBTRACE
- fprintf(debug,
- " access=%p share=%p creation=%d handle=%p fn=%s\n",
- access, share, creation, Hfile, filename);
-#endif
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* In Insert mode we must position the cursor at end of file. */
- /*******************************************************************/
- rc = SetFilePointer(Hfile, 0, &high, FILE_END);
-
- if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
- sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
- CloseHandle(Hfile);
- Hfile = INVALID_HANDLE_VALUE;
- return true;
- } // endif
-
- NewOff.Low = (int)rc;
- NewOff.High = (int)high;
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
- NewOff.Low = (int)drc;
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
-
- if (!rc) {
- sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
- return true;
- } // endif rc
-
- // Position the cursor at the offset of this index
- rc = SetFilePointer(Hfile, noff[id].Low,
- (PLONG)&noff[id].High, FILE_BEGIN);
-
- if (rc == INVALID_SET_FILE_POINTER) {
- sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
- return true;
- } // endif
-
- } // endif Mode
-
-#else // UNIX
- int oflag = O_LARGEFILE; // Enable file size > 2G
- mode_t pmod = 0;
-
- /*********************************************************************/
- /* Create the file object according to access mode */
- /*********************************************************************/
- switch (mode) {
- case MODE_READ:
- oflag |= O_RDONLY;
- break;
- case MODE_WRITE:
- oflag |= O_WRONLY | O_CREAT | O_TRUNC;
- pmod = S_IREAD | S_IWRITE;
- break;
- case MODE_INSERT:
- oflag |= (O_WRONLY | O_APPEND);
- break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch
-
- Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
-
- if (Hfile == INVALID_HANDLE_VALUE) {
- /*rc = errno;*/
-#if defined(TRACE)
- printf("Open: %s\n", g->Message);
-#endif // TRACE
- return true;
- } // endif Hfile
-
-#if defined(TRACE)
- printf(" rc=%d oflag=%p mode=%d handle=%d fn=%s\n",
- rc, oflag, mode, Hfile, filename);
-#endif // TRACE
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Position the cursor at end of file so ftell returns file size. */
- /*******************************************************************/
- if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
- return true;
- } // endif
-
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- NewOff.Low = write(Hfile, &noff, sizeof(noff));
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
- sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
- return true;
- } // endif MAX_INDX
-
- // Position the cursor at the offset of this index
- if (!lseek64(Hfile, noff[id].Val, SEEK_SET)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
- return true;
- } // endif
-
- } // endif mode
-#endif // UNIX
-
- return false;
- } // end of Open
-
-/***********************************************************************/
-/* Go to position in a huge file. */
-/***********************************************************************/
-bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
- {
-#if defined(WIN32)
- LONG hi = high;
- DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
-
- if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
- sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
- return true;
- } // endif
-
-#else // UNIX
- off64_t pos = (off64_t)low
- + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
-
- if (lseek64(Hfile, pos, origin) < 0) {
- sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
-#if defined(TRACE)
- printf("lseek64 error %d\n", errno);
-#endif // TRACE
- return true;
- } // endif lseek64
-
-#if defined(TRACE)
- printf("Seek: low=%d high=%d\n", low, high);
-#endif // TRACE
-#endif // UNIX
-
- return false;
- } // end of Seek
-
-/***********************************************************************/
-/* Read from a huge index file. */
-/***********************************************************************/
-bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
- {
- bool rc = false;
-
-#if defined(WIN32)
- bool brc;
- DWORD nbr, count = (DWORD)(n * size);
-
- brc = ReadFile(Hfile, buf, count, &nbr, NULL);
-
- if (brc) {
- if (nbr != count) {
- strcpy(g->Message, MSG(EOF_INDEX_FILE));
- rc = true;
- } // endif nbr
-
- } else {
- char *buf[256];
- DWORD drc = GetLastError();
-
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)buf, sizeof(buf), NULL);
- sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
- rc = true;
- } // endif brc
-#else // UNIX
- ssize_t count = (ssize_t)(n * size);
-
-#if defined(TRACE)
- printf("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
-#endif // TRACE
-
- if (read(Hfile, buf, count) != count) {
- sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
-#if defined(TRACE)
- printf("read error %d\n", errno);
-#endif // TRACE
- rc = true;
- } // endif nbr
-#endif // UNIX
-
- return rc;
- } // end of Read
-
-/***********************************************************************/
-/* Write on a huge index file. */
-/***********************************************************************/
-int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
- {
-#if defined(WIN32)
- bool brc;
- DWORD nbw, count = (DWORD)n * (DWORD) size;
-
- brc = WriteFile(Hfile, buf, count, &nbw, NULL);
-
- if (!brc) {
- char msg[256];
- DWORD drc = GetLastError();
-
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)msg, sizeof(msg), NULL);
- sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
- rc = true;
- } // endif size
-
- return (int)nbw;
-#else // UNIX
- ssize_t nbw;
- size_t count = (size_t)n * (size_t)size;
-
- nbw = write(Hfile, buf, count);
-
- if (nbw != (signed)count) {
- sprintf(g->Message, MSG(WRITING_ERROR),
- "index file", strerror(errno));
- rc = true;
- } // endif nbw
-
- return (int)nbw;
-#endif // UNIX
- } // end of Write
-
-/***********************************************************************/
-/* Update the file header and close the index file. */
-/***********************************************************************/
-void XHUGE::Close(char *fn, int id)
- {
-#if defined(WIN32)
- if (id >= 0 && fn) {
- CloseFileHandle(Hfile);
- Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
- if (Hfile != INVALID_HANDLE_VALUE)
- if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
- != INVALID_SET_FILE_POINTER) {
- DWORD nbw;
-
- WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
- } // endif SetFilePointer
-
- } // endif id
-#else // !WIN32
- if (id >= 0 && fn) {
- fcntl(Hfile, F_SETFD, O_WRONLY);
-
- if (lseek(Hfile, id * sizeof(IOFF), SEEK_SET))
- write(Hfile, &NewOff, sizeof(IOFF));
-
- } // endif id
-#endif // !WIN32
-
- XLOAD::Close();
- } // end of Close
-
-#if defined(XMAP)
-/***********************************************************************/
-/* Don't know whether this is possible for huge files. */
-/***********************************************************************/
-void *XHUGE::FileView(PGLOBAL g, char *fn)
- {
- strcpy(g->Message, MSG(NO_PART_MAP));
- return NULL;
- } // end of FileView
-#endif // XMAP
-
-/* -------------------------- XXROW Class --------------------------- */
-
-/***********************************************************************/
-/* XXROW Public Constructor. */
-/***********************************************************************/
-XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
- {
- Tdbp = tdbp;
- Valp = NULL;
- } // end of XXROW constructor
-
-/***********************************************************************/
-/* XXROW Reset: re-initialize a Kindex block. */
-/***********************************************************************/
-void XXROW::Reset(void)
- {
-#if defined(_DEBUG)
- assert(Tdbp->GetLink()); // This a join index
-#endif // _DEBUG
- } // end of Reset
-
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XXROW::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* To_Link should not be NULL. */
- /*********************************************************************/
- if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
- return true;
-
- if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
- strcpy(g->Message, MSG(TYPE_MISMATCH));
- return true;
- } else
- Valp = (*Tdbp->GetLink())->GetValue();
-
- if ((Num_K = Tbxp->Cardinality(g)) < 0)
- return true; // Not a fixed file
-
- /*********************************************************************/
- /* The entire table is indexed, no need to construct the index. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
- } // end of Init
-
-/***********************************************************************/
-/* RANGE: Tell how many record exist in a given value range. */
-/***********************************************************************/
-int XXROW::Range(PGLOBAL g, int limit, bool incl)
- {
- int n = Valp->GetIntValue();
-
- switch (limit) {
- case 1: n += ((incl) ? 0 : 1); break;
- case 2: n += ((incl) ? 1 : 0); break;
- default: n = 1;
- } // endswitch limit
-
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* XXROW: Fetch a physical or logical record. */
-/***********************************************************************/
-int XXROW::Fetch(PGLOBAL g)
- {
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Look for a key equal to the link column of previous table, */
- /* and return its rank whithin the index table. */
- /*********************************************************************/
- Cur_K = FastFind(1);
-
- if (Cur_K >= Num_K)
- /*******************************************************************/
- /* Rank not whithin index table, signal record not found. */
- /*******************************************************************/
- return -2; // Means record not found
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- return Cur_K;
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching record in a join. */
-/***********************************************************************/
-int XXROW::FastFind(int nk)
- {
- int n = Valp->GetIntValue();
-
- if (n < 0)
- return (Op == OP_EQ) ? (-1) : 0;
- else if (n > Num_K)
- return Num_K;
- else
- return (Op == OP_GT) ? n : (n - 1);
-
- } // end of FastFind
-
-/* ------------------------- KXYCOL Classes -------------------------- */
-
-/***********************************************************************/
-/* KXYCOL public constructor. */
-/***********************************************************************/
-KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
- To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
- {
- Next = NULL;
- Previous = NULL;
- Kxp = kp;
- Colp = NULL;
- IsSorted = false;
- Asc = true;
- Keys = Nmblk;
- Kblp = NULL;
- Bkeys = Nmblk;
- Blkp = NULL;
- Valp = NULL;
- Klen = 0;
- Kprec = 0;
- Type = TYPE_ERROR;
- Prefix = false;
- Koff = Nmblk;
- Val_K = 0;
- Ndf = 0;
- Mxs = 0;
- } // end of KXYCOL constructor
-
-/***********************************************************************/
-/* KXYCOL Init: initialize and allocate storage. */
-/* Key length kln can be smaller than column length for CHAR columns. */
-/***********************************************************************/
-bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
- {
- int len = colp->GetLength(), prec = colp->GetScale();
-
- // Currently no indexing on NULL columns
- if (colp->IsNullable()) {
- sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
- return true;
- } // endif nullable
-
- if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
- len = kln;
- Prefix = true;
- } // endif kln
-
-#ifdef DEBTRACE
- htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
- this, colp->GetName(), n, colp->GetResultType(), sm);
-#endif
-
- // Allocate the Value object used when moving items
- Type = colp->GetResultType();
-
- if (!(Valp = AllocateValue(g, Type, len, colp->GetScale(),
- colp->IsUnsigned())))
- return true;
-
- Klen = Valp->GetClen();
- Keys.Size = n * Klen;
-
- if (!PlgDBalloc(g, NULL, Keys)) {
- sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
- return true; // Error
- } // endif
-
- // Allocate the Valblock. The last parameter is to have rows filled
- // by blanks (if true) or keep the zero ending char (if false).
- // Currently we set it to true to be compatible with QRY blocks,
- // and the one before last is to enable length/type checking, set to
- // true if not a prefix key.
- Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true);
- Asc = sm; // Sort mode: Asc=true Desc=false
- Ndf = n;
-
- // Store this information to avoid sorting when already done
- if (Asc)
- IsSorted = colp->GetOpt() < 0;
-
-//SetNulls(colp->IsNullable()); for when null columns will be indexable
- return false;
- } // end of Init
-
-#if defined(XMAP)
-/***********************************************************************/
-/* KXYCOL MapInit: initialize and address storage. */
-/* Key length kln can be smaller than column length for CHAR columns. */
-/***********************************************************************/
-BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
- {
- int len = colp->GetLength(), prec = colp->GetPrecision();
-
- if (n[3] && colp->GetLength() > n[3]
- && colp->GetResultType() == TYPE_STRING) {
- len = n[3];
- Prefix = true;
- } // endif kln
-
- Type = colp->GetResultType();
-
-#ifdef DEBTRACE
- htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
- this, colp, Type, n[0], len, m);
-#endif
-
- // Allocate the Value object used when moving items
- Valp = AllocateValue(g, Type, len, prec, false, NULL);
- Klen = Valp->GetClen();
-
- if (n[2]) {
- Bkeys.Size = n[2] * Klen;
- Bkeys.Memp = m;
- Bkeys.Sub = true;
-
- // Allocate the Valblk containing initial block key values
- Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true);
- } // endif nb
-
- Keys.Size = n[0] * Klen;
- Keys.Memp = m + Bkeys.Size;
- Keys.Sub = true;
-
- // Allocate the Valblock. Last two parameters are to have rows filled
- // by blanks (if true) or keep the zero ending char (if false).
- // Currently we set it to true to be compatible with QRY blocks,
- // and last one to enable type checking (no conversion).
- Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, true, true);
-
- if (n[1]) {
- Koff.Size = n[1] * sizeof(int);
- Koff.Memp = m + Bkeys.Size + Keys.Size;
- Koff.Sub = true;
- } // endif n[1]
-
- Ndf = n[0];
- IsSorted = colp->GetOpt() < 0;
- return m + Bkeys.Size + Keys.Size + Koff.Size;
- } // end of MapInit
-#endif // XMAP
-
-/***********************************************************************/
-/* Allocate the offset block used by intermediate key columns. */
-/***********************************************************************/
-int *KXYCOL::MakeOffset(PGLOBAL g, int n)
- {
- if (!Kof) {
- // Calculate the initial size of the offset
- Koff.Size = (n + 1) * sizeof(int);
-
- // Allocate the required memory
- if (!PlgDBalloc(g, NULL, Koff)) {
- strcpy(g->Message, MSG(KEY_ALLOC_ERR));
- return NULL; // Error
- } // endif
-
- } else if (n) {
- // This is a reallocation call
- PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
- } else
- PlgDBfree(Koff);
-
- return (int*)Kof;
- } // end of MakeOffset
-
-/***********************************************************************/
-/* Make a front end array of key values that are the first value of */
-/* each blocks (of size n). This to reduce paging in FastFind. */
-/***********************************************************************/
-bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
- {
- int i, k;
-
- // Calculate the size of the block array in the index
- Bkeys.Size = nb * Klen;
-
- // Allocate the required memory
- if (!PlgDBalloc(g, NULL, Bkeys)) {
- sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
- return true; // Error
- } // endif
-
- // Allocate the Valblk used to contains initial block key values
- Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
-
- // Populate the array with values
- for (i = k = 0; i < nb; i++, k += size)
- Blkp->SetValue(Kblp, i, k);
-
- return false;
- } // end of MakeBlockArray
-
-/***********************************************************************/
-/* KXYCOL SetValue: read column value for nth array element. */
-/***********************************************************************/
-void KXYCOL::SetValue(PCOL colp, int i)
- {
-#if defined(_DEBUG)
- assert (Kblp != NULL);
-#endif
-
- Kblp->SetValue(colp->GetValue(), i);
- } // end of SetValue
-
-/***********************************************************************/
-/* InitFind: initialize finding the rank of column value in index. */
-/***********************************************************************/
-bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
- {
- if (xp->GetType() == TYPE_CONST) {
- if (Kxp->Nth)
- return true;
-
- Valp->SetValue_pval(xp->GetValue(), !Prefix);
- } else {
- xp->Reset();
- xp->Eval(g);
- Valp->SetValue_pval(xp->GetValue(), false);
-// Valp->SetValue_pval(xp->GetValue(), !Prefix);
- } // endif Type
-
- return false;
- } // end of InitFind
-
-/***********************************************************************/
-/* InitBinFind: initialize Value to the value pointed by vp. */
-/***********************************************************************/
-void KXYCOL::InitBinFind(void *vp)
- {
- Valp->SetBinValue(vp);
- } // end of InitBinFind
-
-/***********************************************************************/
-/* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
-/* already in storage in the corresponding KXYCOL. */
-/***********************************************************************/
-void KXYCOL::FillValue(PVAL valp)
- {
- valp->SetValue_pvblk(Kblp, Val_K);
-
- // Set null when applicable (NIY)
-//if (valp->GetNullable())
-// valp->SetNull(valp->IsZero());
-
- } // end of FillValue
-
-/***********************************************************************/
-/* KXYCOL: Compare routine for one numeric value. */
-/***********************************************************************/
-int KXYCOL::Compare(int i1, int i2)
- {
- // Do the actual comparison between values.
- register int k = Kblp->CompVal(i1, i2);
-
-#ifdef DEBUG2
- htrc("Compare done result=%d\n", k);
-#endif
-
- return (Asc) ? k : -k;
- } // end of Compare
-
-/***********************************************************************/
-/* KXYCOL: Compare the ith key to the stored Value. */
-/***********************************************************************/
-int KXYCOL::CompVal(int i)
- {
- // Do the actual comparison between numerical values.
-#ifdef DEBUG2
- register int k = (int)Kblp->CompVal(Valp, (int)i);
-
- htrc("Compare done result=%d\n", k);
- return k;
-#endif
- return Kblp->CompVal(Valp, i);
- } // end of CompVal
-
-/***********************************************************************/
-/* KXYCOL: Compare the key to the stored block value. */
-/***********************************************************************/
-int KXYCOL::CompBval(int i)
- {
- // Do the actual comparison between key values.
- return Blkp->CompVal(Valp, i);
- } // end of CompBval
-
-/***********************************************************************/
-/* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
-/***********************************************************************/
-void KXYCOL::ReAlloc(PGLOBAL g, int n)
- {
- PlgDBrealloc(g, NULL, Keys, n * Klen);
- Kblp->ReAlloc(To_Keys, n);
- Ndf = n;
- } // end of ReAlloc
-
-/***********************************************************************/
-/* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
-/***********************************************************************/
-void KXYCOL::FreeData(void)
- {
- PlgDBfree(Keys);
- Kblp = NULL;
- PlgDBfree(Bkeys);
- Blkp = NULL;
- PlgDBfree(Koff);
- Ndf = 0;
- } // end of FreeData
+/***************** Xindex C++ Class Xindex Code (.CPP) *****************/
+/* Name: XINDEX.CPP Version 2.8 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
+/* */
+/* This file contains the class XINDEX implementation code. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#else // !UNIX
+#include <io.h>
+#endif // !UNIX
+#include <fcntl.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* kindex.h is header containing the KINDEX class definition. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "osutil.h"
+#include "maputil.h"
+//nclude "filter.h"
+#include "tabcol.h"
+#include "xindex.h"
+#include "xobject.h"
+//nclude "scalfnc.h"
+//nclude "array.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+
+/***********************************************************************/
+/* Macro or external routine definition */
+/***********************************************************************/
+#define NZ 7
+#define NW 5
+#define MAX_INDX 10
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
+#endif
+
+/***********************************************************************/
+/* DB static external variables. */
+/***********************************************************************/
+extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
+
+/***********************************************************************/
+/* Last two parameters are true to enable type checking, and last one */
+/* to have rows filled by blanks to be compatible with QRY blocks. */
+/***********************************************************************/
+PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
+ bool check = true, bool blank = true, bool un = false);
+
+/***********************************************************************/
+/* Check whether we have to create/update permanent indexes. */
+/***********************************************************************/
+int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
+ {
+ int rc;
+ PTABLE tablep;
+ PTDBDOS tdbp;
+ PCATLG cat = PlgGetCatalog(g, true);
+
+ /*********************************************************************/
+ /* Open a new table in mode read and with only the keys columns. */
+ /*********************************************************************/
+ tablep = new(g) XTAB(name);
+
+ if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep)))
+ rc = RC_NF;
+ else if (!tdbp->GetDef()->Indexable()) {
+ sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
+ rc = RC_NF;
+ } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
+ rc = RC_OK; // No index
+
+ return rc;
+ } // end of PlgMakeIndex
+
+/* -------------------------- Class INDEXDEF ------------------------- */
+
+/***********************************************************************/
+/* INDEXDEF Constructor. */
+/***********************************************************************/
+INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
+ {
+//To_Def = NULL;
+ Next = NULL;
+ ToKeyParts = NULL;
+ Name = name;
+ Unique = uniq;
+ Invalid = false;
+ AutoInc = false;
+ Nparts = 0;
+ ID = n;
+//Offset = 0;
+//Offhigh = 0;
+//Size = 0;
+ MaxSame = 1;
+ } // end of INDEXDEF constructor
+
+/***********************************************************************/
+/* Set the max same values for each colum after making the index. */
+/***********************************************************************/
+void INDEXDEF::SetMxsame(PXINDEX x)
+ {
+ PKPDEF kdp;
+ PXCOL xcp;
+
+ for (kdp = ToKeyParts, xcp = x->To_KeyCol;
+ kdp && xcp; kdp = kdp->Next, xcp = xcp->Next)
+ kdp->Mxsame = xcp->Mxs;
+ } // end of SetMxsame
+
+/* -------------------------- Class KPARTDEF ------------------------- */
+
+/***********************************************************************/
+/* KPARTDEF Constructor. */
+/***********************************************************************/
+KPARTDEF::KPARTDEF(PSZ name, int n)
+ {
+ Next = NULL;
+ Name = name;
+ Mxsame = 0;
+ Ncol = n;
+ Klen = 0;
+ } // end of KPARTDEF constructor
+
+/* -------------------------- XXBASE Class --------------------------- */
+
+/***********************************************************************/
+/* XXBASE public constructor. */
+/***********************************************************************/
+XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
+ To_Rec((int*&)Record.Memp)
+ {
+ Tbxp = tbxp;
+ Record = Nmblk;
+ Cur_K = -1;
+ Old_K = -1;
+ Num_K = 0;
+ Ndif = 0;
+ Bot = Top = Inf = Sup = 0;
+ Op = OP_EQ;
+ To_KeyCol = NULL;
+ Mul = false;
+ Val_K = -1;
+ Nblk = Sblk = 0;
+ Thresh = 7;
+ ID = -1;
+ Nth = 0;
+ } // end of XXBASE constructor
+
+/***********************************************************************/
+/* Make file output of XINDEX contents. */
+/***********************************************************************/
+void XXBASE::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+ fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K);
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of XINDEX contents. */
+/***********************************************************************/
+void XXBASE::Print(PGLOBAL g, char *ps, uint z)
+ {
+ *ps = '\0';
+ strncat(ps, "Xindex", z);
+ } // end of Print
+
+/* -------------------------- XINDEX Class --------------------------- */
+
+/***********************************************************************/
+/* XINDEX public constructor. */
+/***********************************************************************/
+XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k)
+ : XXBASE(tdbp, !xdp->IsUnique())
+ {
+ Xdp = xdp;
+ ID = xdp->GetID();
+ Tdbp = tdbp;
+ X = pxp;
+ To_LastCol = NULL;
+ To_LastVal = NULL;
+ To_Cols = cp;
+ To_Vals = xp;
+ Mul = !xdp->IsUnique();
+ Srtd = false;
+ Nk = xdp->GetNparts();
+ Nval = (k) ? k : Nk;
+ Incr = 0;
+//Defoff = xdp->GetOffset();
+//Defhigh = xdp->GetOffhigh();
+//Size = xdp->GetSize();
+ MaxSame = xdp->GetMaxSame();
+ } // end of XINDEX constructor
+
+/***********************************************************************/
+/* XINDEX Reset: re-initialize a Xindex block. */
+/***********************************************************************/
+void XINDEX::Reset(void)
+ {
+ for (PXCOL kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = kp->Ndf;
+
+ Cur_K = Num_K;
+ Old_K = -1; // Needed to avoid not setting CurBlk for Update
+ Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST :
+ (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ;
+ Nth = 0;
+ } // end of Reset
+
+/***********************************************************************/
+/* XINDEX Close: terminate index and free all allocated data. */
+/* Do not reset other values that are used at return to make. */
+/***********************************************************************/
+void XINDEX::Close(void)
+ {
+ // Close file or view of file
+ X->Close();
+
+ // De-allocate data
+ PlgDBfree(Record);
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+
+ // De-allocate Key data
+ for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->FreeData();
+
+ // Column values cannot be retrieved from key anymore
+ for (int k = 0; k < Nk; k++)
+ To_Cols[k]->SetKcol(NULL);
+
+ } // end of Close
+
+/***********************************************************************/
+/* XINDEX compare routine for C Quick/Insertion sort. */
+/***********************************************************************/
+int XINDEX::Qcompare(int *i1, int *i2)
+ {
+ register int k;
+ register PXCOL kcp;
+
+ for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next)
+ if ((k = kcp->Compare(*i1, *i2)))
+ break;
+
+#ifdef DEBTRACE
+ num_comp++;
+#endif
+
+ return k;
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Make: Make and index on key column(s). */
+/***********************************************************************/
+bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
+ {
+ /*********************************************************************/
+ /* Table can be accessed through an index. */
+ /*********************************************************************/
+ int k, rc = RC_OK;
+ int *bof, i, j, n, ndf, nkey;
+ PKPDEF kdfp = Xdp->GetToKeyParts();
+ bool brc = true;
+ PCOL colp;
+ PXCOL kp, prev = NULL, kcp = NULL;
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ /*********************************************************************/
+ /* Allocate the storage that will contain the keys and the file */
+ /* positions corresponding to them. */
+ /*********************************************************************/
+ if ((n = Tdbp->GetMaxSize(g)) < 0)
+ return true;
+ else if (!n) {
+ Num_K = Ndif = 0;
+ MaxSame = 1;
+
+ // The if condition was suppressed because this may be an existing
+ // index that is now void because all table lines were deleted.
+// if (sxp)
+ goto nox; // Truncate eventually existing index file
+// else
+// return false;
+
+ } // endif n
+
+ // File position must be stored
+ Record.Size = n * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Record)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n);
+ goto err; // Error
+ } // endif
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ colp = To_Cols[k];
+
+ if (!kdfp) {
+ sprintf(g->Message, MSG(INT_COL_ERROR),
+ (colp) ? colp->GetName() : "???");
+ goto err; // Error
+ } // endif kdfp
+
+ kcp = new(g) KXYCOL(this);
+
+ if (kcp->Init(g, colp, n, true, kdfp->Klen))
+ goto err; // Error
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ kdfp = kdfp->Next;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ /*********************************************************************/
+ /* Get the starting information for progress. */
+ /*********************************************************************/
+ dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
+ sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
+ dup->ProgMax = Tdbp->GetProgMax(g);
+ dup->ProgCur = 0;
+
+ /*********************************************************************/
+ /* Standard init: read the file and construct the index table. */
+ /* Note: reading will be sequential as To_Kindex is not set. */
+ /*********************************************************************/
+ for (i = nkey = 0; i < n && rc != RC_EF; i++) {
+#if defined(THREAD)
+ if (!dup->Step) {
+ strcpy(g->Message, MSG(QUERY_CANCELLED));
+ longjmp(g->jumper[g->jump_level], 99);
+ } // endif Step
+#endif // THREAD
+
+ /*******************************************************************/
+ /* Read a valid record from table file. */
+ /*******************************************************************/
+ rc = Tdbp->ReadDB(g);
+
+ // Update progress information
+ dup->ProgCur = Tdbp->GetProgCur();
+
+ // Check return code and do whatever must be done according to it
+ switch (rc) {
+ case RC_OK:
+ break;
+ case RC_EF:
+ goto end_of_file;
+ case RC_NF:
+ continue;
+ default:
+ sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
+ goto err;
+ } // endswitch rc
+
+ /*******************************************************************/
+ /* Get and Store the file position of the last read record for */
+ /* future direct access. */
+ /*******************************************************************/
+ To_Rec[nkey] = Tdbp->GetRecpos();
+
+ /*******************************************************************/
+ /* Get the keys and place them in the key blocks. */
+ /*******************************************************************/
+ for (k = 0, kcp = To_KeyCol;
+ k < Nk && kcp;
+ k++, kcp = kcp->Next) {
+ colp = To_Cols[k];
+ colp->Reset();
+
+ colp->ReadColumn(g);
+// if (colp->ReadColumn(g))
+// goto err;
+
+ kcp->SetValue(colp, nkey);
+ } // endfor k
+
+ nkey++; // A new valid key was found
+ } // endfor i
+
+ end_of_file:
+
+ // Update progress information
+ dup->ProgCur = Tdbp->GetProgMax(g);
+
+ /*********************************************************************/
+ /* Record the Index size and eventually resize memory allocation. */
+ /*********************************************************************/
+ if ((Num_K = nkey) < n) {
+ PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int));
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->ReAlloc(g, Num_K);
+
+ } // endif Num_K
+
+ /*********************************************************************/
+ /* Sort the index so we can use an optimized Find algorithm. */
+ /* Note: for a unique index we use the non conservative sort */
+ /* version because normally all index values are different. */
+ /* This was set at CSORT class construction. */
+ /* For all indexes, an offset array is made so we can check the */
+ /* uniqueness of unique indexes. */
+ /*********************************************************************/
+ Index.Size = Num_K * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Index)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
+ goto err; // Error
+ } // endif alloc
+
+ Offset.Size = (Num_K + 1) * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1);
+ goto err; // Error
+ } // endif alloc
+
+ // Call the sort program, it returns the number of distinct values
+ if ((Ndif = Qsort(g, Num_K)) < 0)
+ goto err; // Error during sort
+
+ // Check whether the unique index is unique indeed
+ if (!Mul)
+ if (Ndif < Num_K) {
+ strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
+ goto err;
+ } else
+ PlgDBfree(Offset); // Not used anymore
+
+ // Use the index to physically reorder the xindex
+ Srtd = Reorder(g);
+
+ if (Ndif < Num_K) {
+ // Resize the offset array
+ PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int));
+
+ // Initial value of MaxSame
+ MaxSame = Pof[1] - Pof[0];
+
+ // Resize the Key array by only keeping the distinct values
+ for (i = 1; i < Ndif; i++) {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Move(i, Pof[i]);
+
+ MaxSame = max(MaxSame, Pof[i + 1] - Pof[i]);
+ } // endfor i
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->ReAlloc(g, Ndif);
+
+ } else {
+ Mul = false; // Current index is unique
+ PlgDBfree(Offset); // Not used anymore
+ MaxSame = 1; // Reset it when remaking an index
+ } // endif Ndif
+
+ /*********************************************************************/
+ /* Now do the reduction of the index. Indeed a multi-column index */
+ /* can be used for only some of the first columns. For instance if */
+ /* an index is defined for column A, B, C PlugDB can use it for */
+ /* only the column A or the columns A, B. */
+ /* What we do here is to reduce the data so column A will contain */
+ /* only the sorted distinct values of A, B will contain data such */
+ /* as only distinct values of A,B are stored etc. */
+ /* This implies that for each column set an offset array is made */
+ /* except if the subset originally contains unique values. */
+ /*********************************************************************/
+ // Update progress information
+ dup->Step = STEP(REDUCE_INDEX);
+
+ ndf = Ndif;
+ To_LastCol->Mxs = MaxSame;
+
+ for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) {
+ if (!(bof = kcp->MakeOffset(g, ndf)))
+ goto err;
+ else
+ *bof = 0;
+
+ for (n = 0, i = j = 1; i < ndf; i++)
+ for (kp = kcp; kp; kp = kp->Previous)
+ if (kp->Compare(n, i)) {
+ // Values are not equal to last ones
+ bof[j++] = n = i;
+ break;
+ } // endif Compare
+
+ if (j < ndf) {
+ // Sub-index is multiple
+ bof[j] = ndf;
+ ndf = j; // New number of distinct values
+
+ // Resize the Key array by only keeping the distinct values
+ for (kp = kcp; kp; kp = kp->Previous) {
+ for (i = 1; i < ndf; i++)
+ kp->Move(i, bof[i]);
+
+ kp->ReAlloc(g, ndf);
+ } // endif kcp
+
+ // Resize the offset array
+ kcp->MakeOffset(g, ndf);
+
+ // Calculate the max same value for this column
+ kcp->Mxs = ColMaxSame(kcp);
+ } else {
+ // Current sub-index is unique
+ kcp->MakeOffset(g, 0); // The offset is not used anymore
+ kcp->Mxs = 1; // Unique
+ } // endif j
+
+ } // endfor kcp
+
+ /*********************************************************************/
+ /* For sorted columns and fixed record size, file position can be */
+ /* calculated, so the Record array can be discarted. */
+ /* Note: for Num_K = 1 any non null value is Ok. */
+ /*********************************************************************/
+ if (Srtd && Tdbp->Ftype != RECFM_VAR) {
+ Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
+ PlgDBfree(Record);
+ } // endif Srtd
+
+ /*********************************************************************/
+ /* Check whether a two-tier find algorithm can be implemented. */
+ /* It is currently implemented only for single key indexes. */
+ /*********************************************************************/
+ if (Nk == 1 && ndf >= 65536) {
+ // Implement a two-tier find algorithm
+ for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ;
+
+ Nblk = (ndf -1) / Sblk + 1;
+
+ if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk))
+ goto err; // Error
+
+ } // endif Num_K
+
+ nox:
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+
+ /*********************************************************************/
+ /* Save the index so it has not to be recalculated. */
+ /*********************************************************************/
+ if (!SaveIndex(g, sxp))
+ brc = false;
+
+ err:
+ // We don't need the index anymore
+ Close();
+
+ if (brc)
+ printf("%s\n", g->Message);
+
+ return brc;
+ } // end of Make
+
+/***********************************************************************/
+/* Return the max size of the intermediate column. */
+/***********************************************************************/
+int XINDEX::ColMaxSame(PXCOL kp)
+ {
+ int *kof, i, ck1, ck2, ckn = 1;
+ PXCOL kcp;
+
+ // Calculate the max same value for this column
+ for (i = 0; i < kp->Ndf; i++) {
+ ck1 = i;
+ ck2 = i + 1;
+
+ for (kcp = kp; kcp; kcp = kcp->Next) {
+ if (!(kof = (kcp->Next) ? kcp->Kof : Pof))
+ break;
+
+ ck1 = kof[ck1];
+ ck2 = kof[ck2];
+ } // endfor kcp
+
+ ckn = max(ckn, ck2 - ck1);
+ } // endfor i
+
+ return ckn;
+ } // end of ColMaxSame
+
+/***********************************************************************/
+/* Reorder: use the sort index to reorder the data in storage so */
+/* it will be physically sorted and sort index can be removed. */
+/***********************************************************************/
+bool XINDEX::Reorder(PGLOBAL g)
+ {
+ register int i, j, k, n;
+ bool sorted = true;
+ PXCOL kcp;
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ if (Num_K > 500000) {
+ // Update progress information
+ dup->Step = STEP(REORDER_INDEX);
+ dup->ProgMax = Num_K;
+ dup->ProgCur = 0;
+ } else
+ dup = NULL;
+
+ if (!Pex)
+ return Srtd;
+
+ for (i = 0; i < Num_K; i++) {
+ if (Pex[i] == Num_K) { // Already moved
+ continue;
+ } else if (Pex[i] == i) { // Already placed
+ if (dup)
+ dup->ProgCur++;
+
+ continue;
+ } // endif's Pex
+
+ sorted = false;
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Save(i);
+
+ n = To_Rec[i];
+
+ for (j = i;; j = k) {
+ k = Pex[j];
+ Pex[j] = Num_K; // Mark position as set
+
+ if (k == i) {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Restore(j);
+
+ To_Rec[j] = n;
+ break; // end of loop
+ } else {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Move(j, k); // Move k to j
+
+ To_Rec[j] = To_Rec[k];
+ } // endif k
+
+ if (dup)
+ dup->ProgCur++;
+
+ } // endfor j
+
+ } // endfor i
+
+ // The index is not used anymore
+ PlgDBfree(Index);
+ return sorted;
+ } // end of Reorder
+
+/***********************************************************************/
+/* Save the index values for this table. */
+/* The problem here is to avoid name duplication, because more than */
+/* one data file can have the same name (but different types) and/or */
+/* the same data file can be used with different block sizes. This is */
+/* why we use Ofn that defaults to the file name but can be set to a */
+/* different name if necessary. */
+/***********************************************************************/
+bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
+ {
+ char *ftype;
+ char fn[_MAX_PATH];
+ int n[NZ], nof = (Mul) ? (Ndif + 1) : 0;
+ int id = -1, size = 0;
+ bool sep, rc = false;
+ PXCOL kcp = To_KeyCol;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+ PDBUSER dup = PlgGetUser(g);
+
+ dup->Step = STEP(SAVING_INDEX);
+ dup->ProgMax = 15 + 16 * Nk;
+ dup->ProgCur = 0;
+
+ switch (Tdbp->Ftype) {
+ case RECFM_VAR: ftype = ".dnx"; break;
+ case RECFM_FIX: ftype = ".fnx"; break;
+ case RECFM_BIN: ftype = ".bnx"; break;
+ case RECFM_VCT: ftype = ".vnx"; break;
+ case RECFM_DBF: ftype = ".dbx"; break;
+ default:
+ sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) {
+ // Index is saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ sxp = NULL;
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+ if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
+ printf("%s\n", g->Message);
+ return true;
+ } // endif Open
+
+ if (!Ndif)
+ goto end; // Void index
+
+ /*********************************************************************/
+ /* Write the index values on the index file. */
+ /*********************************************************************/
+ n[0] = ID; // To check validity
+ n[1] = Nk; // The number of indexed columns
+ n[2] = nof; // The offset array size or 0
+ n[3] = Num_K; // The index size
+ n[4] = Incr; // Increment of record positions
+ n[5] = Nblk; n[6] = Sblk;
+
+#if defined(TRACE)
+ printf("Saving index %s\n", Xdp->GetName());
+ printf("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d\n",
+ ID, Nk, nof, Num_K, Incr, Nblk, Sblk);
+#endif // TRACE
+
+ size = X->Write(g, n, NZ, sizeof(int), rc);
+ dup->ProgCur = 1;
+
+ if (Mul) // Write the offset array
+ size += X->Write(g, Pof, nof, sizeof(int), rc);
+
+ dup->ProgCur = 5;
+
+ if (!Incr) // Write the record position array(s)
+ size += X->Write(g, To_Rec, Num_K, sizeof(int), rc);
+
+ dup->ProgCur = 15;
+
+ for (; kcp; kcp = kcp->Next) {
+ n[0] = kcp->Ndf; // Number of distinct sub-values
+ n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique
+ n[2] = (kcp == To_KeyCol) ? Nblk : 0;
+ n[3] = kcp->Klen; // To be checked later
+ n[4] = kcp->Type; // To be checked later
+
+ size += X->Write(g, n, NW, sizeof(int), rc);
+ dup->ProgCur += 1;
+
+ if (n[2])
+ size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
+
+ dup->ProgCur += 5;
+
+ size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
+ dup->ProgCur += 5;
+
+ if (n[1])
+ size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
+
+ dup->ProgCur += 5;
+ } // endfor kcp
+
+#if defined(TRACE)
+ printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size);
+#endif // TRACE
+
+ end:
+ X->Close(fn, id);
+ return rc;
+ } // end of SaveIndex
+
+#if !defined(XMAP)
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XINDEX::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* If sorting is required, this will be done later. */
+ /*********************************************************************/
+ char *ftype;
+ char fn[_MAX_PATH];
+ int k, n, nv[NZ], id = -1;
+ bool estim = false;
+ PCOL colp;
+ PXCOL prev = NULL, kcp = NULL;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Get the first key column. */
+ /*********************************************************************/
+ if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } else
+ colp = To_Cols[0];
+
+ switch (Tdbp->Ftype) {
+ case RECFM_VAR: ftype = ".dnx"; break;
+ case RECFM_FIX: ftype = ".fnx"; break;
+ case RECFM_BIN: ftype = ".bnx"; break;
+ case RECFM_VCT: ftype = ".vnx"; break;
+ case RECFM_DBF: ftype = ".dbx"; break;
+ default:
+ sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Open the index file and check its validity. */
+ /*********************************************************************/
+ if (X->Open(g, fn, id, MODE_READ))
+ goto err; // No saved values
+
+ // Now start the reading process.
+ if (X->Read(g, nv, NZ, sizeof(int)))
+ goto err;
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d %d %d %d\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif
+
+ if (nv[2]) {
+ Mul = true;
+ Ndif = nv[2];
+
+ // Allocate the storage that will contain the offset array
+ Offset.Size = Ndif * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif);
+ goto err;
+ } // endif
+
+ if (X->Read(g, Pof, Ndif, sizeof(int)))
+ goto err;
+
+ Ndif--; // nv[2] is offset size, equal to Ndif + 1
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+ Incr = nv[4];
+ Nblk = nv[5];
+ Sblk = nv[6];
+
+ if (!Incr) {
+ /*******************************************************************/
+ /* Allocate the storage that will contain the file positions. */
+ /*******************************************************************/
+ Record.Size = Num_K * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Record)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
+ goto err;
+ } // endif
+
+ if (X->Read(g, To_Rec, Num_K, sizeof(int)))
+ goto err;
+
+ } else
+ Srtd = true; // Sorted positions can be calculated
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ if (k == Nval)
+ To_LastVal = prev;
+
+ if (X->Read(g, nv, NW, sizeof(int)))
+ goto err;
+
+ colp = To_Cols[k];
+
+ if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ kcp = new(g) KXYCOL(this);
+
+ if (kcp->Init(g, colp, nv[0], true, (int)nv[3]))
+ goto err; // Error
+
+ /*******************************************************************/
+ /* Read the index values from the index file. */
+ /*******************************************************************/
+ if (k == 0 && Nblk) {
+ if (kcp->MakeBlockArray(g, Nblk, 0))
+ goto err;
+
+ // Read block values
+ if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen))
+ goto err;
+
+ } // endif Nblk
+
+ // Read the entire (small) index
+ if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen))
+ goto err;
+
+ if (nv[1]) {
+ if (!kcp->MakeOffset(g, nv[1] - 1))
+ goto err;
+
+ // Read the offset array
+ if (X->Read(g, kcp->Kof, nv[1], sizeof(int)))
+ goto err;
+
+ } // endif n[1]
+
+ if (!kcp->Prefix)
+ // Indicate that the key column value can be found from KXYCOL
+ colp->SetKcol(kcp);
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ if (Mul && prev) {
+ // Last key offset is the index offset
+ kcp->Koff = Offset;
+ kcp->Koff.Sub = true;
+ } // endif Mul
+
+ X->Close();
+
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+
+err:
+ Close();
+ return true;
+ } // end of Init
+
+#else // XMAP
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XINDEX::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* If sorting is required, this will be done later. */
+ /*********************************************************************/
+ const char *ftype;
+ BYTE *mbase;
+ char fn[_MAX_PATH];
+ int *nv, k, n, id = -1;
+ bool estim;
+ PCOL colp;
+ PXCOL prev = NULL, kcp = NULL;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+ PDBUSER dup = PlgGetUser(g);
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Get the first key column. */
+ /*********************************************************************/
+ if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } else
+ colp = To_Cols[0];
+
+ switch (Tdbp->Ftype) {
+ case RECFM_VAR: ftype = ".dnx"; break;
+ case RECFM_FIX: ftype = ".fnx"; break;
+ case RECFM_BIN: ftype = ".bnx"; break;
+ case RECFM_VCT: ftype = ".vnx"; break;
+ case RECFM_DBF: ftype = ".dbx"; break;
+ default:
+ sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was save in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif SepIndex
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Get a view on the part of the index file containing this index. */
+ /*********************************************************************/
+ if (!(mbase = (BYTE*)X->FileView(g, fn)))
+ goto err;
+
+ if (id >= 0) {
+ // Get offset from the header
+ IOFF *noff = (IOFF*)mbase;
+
+ // Position the memory base at the offset of this index
+ mbase += noff[id].Low;
+ } // endif id
+
+ // Now start the mapping process.
+ nv = (int*)mbase;
+ mbase += NZ * sizeof(int);
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d %d %d %d\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ // Not this index
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif nv
+
+ if (nv[2]) {
+ // Set the offset array memory block
+ Offset.Memp = mbase;
+ Offset.Size = nv[2] * sizeof(int);
+ Offset.Sub = true;
+ Mul = true;
+ Ndif = nv[2] - 1;
+ mbase += Offset.Size;
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+ Incr = nv[4];
+ Nblk = nv[5];
+ Sblk = nv[6];
+
+ if (!Incr) {
+ /*******************************************************************/
+ /* Point to the storage that contains the file positions. */
+ /*******************************************************************/
+ Record.Size = Num_K * sizeof(int);
+ Record.Memp = mbase;
+ Record.Sub = true;
+ mbase += Record.Size;
+ } else
+ Srtd = true; // Sorted positions can be calculated
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ if (k == Nval)
+ To_LastVal = prev;
+
+ nv = (int*)mbase;
+ mbase += (NW * sizeof(int));
+
+ colp = To_Cols[k];
+
+ if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ kcp = new(g) KXYCOL(this);
+
+ if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
+ goto err;
+
+ if (!kcp->Prefix)
+ // Indicate that the key column value can be found from KXYCOL
+ colp->SetKcol(kcp);
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ if (Mul && prev)
+ // Last key offset is the index offset
+ kcp->Koff = Offset;
+
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+
+err:
+ Close();
+ return true;
+ } // end of Init
+#endif // XMAP
+
+/***********************************************************************/
+/* Get Ndif and Num_K from the index file. */
+/***********************************************************************/
+bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
+ {
+ char *ftype;
+ char fn[_MAX_PATH];
+ int n, nv[NZ], id = -1;
+ bool estim = false;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+
+ ndif = numk = 0;
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Check the key part number. */
+ /*********************************************************************/
+ if (!Nk) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } // endif Nk
+
+ switch (Tdbp->Ftype) {
+ case RECFM_VAR: ftype = ".dnx"; break;
+ case RECFM_FIX: ftype = ".fnx"; break;
+ case RECFM_BIN: ftype = ".bnx"; break;
+ case RECFM_VCT: ftype = ".vnx"; break;
+ case RECFM_DBF: ftype = ".dbx"; break;
+ default:
+ sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Open the index file and check its validity. */
+ /*********************************************************************/
+ if (X->Open(g, fn, id, MODE_READ))
+ goto err; // No saved values
+
+ // Get offset from XDB file
+//if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
+// goto err;
+
+ // Now start the reading process.
+ if (X->Read(g, nv, NZ, sizeof(int)))
+ goto err;
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif
+
+ if (nv[2]) {
+ Mul = true;
+ Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+
+ if (Nk > 1) {
+ if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
+ goto err;
+
+ if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
+ goto err;
+
+ if (X->Read(g, nv, NW, sizeof(int)))
+ goto err;
+
+ PCOL colp = *To_Cols;
+
+ if (nv[4] != colp->GetResultType() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ Ndif = nv[0];
+ } // endif Nk
+
+ /*********************************************************************/
+ /* Set size values. */
+ /*********************************************************************/
+ ndif = Ndif;
+ numk = Num_K;
+ return false;
+
+err:
+ X->Close();
+ return true;
+ } // end of GetAllSizes
+
+/***********************************************************************/
+/* RANGE: Tell how many records exist for a given value, for an array */
+/* of values, or in a given value range. */
+/***********************************************************************/
+int XINDEX::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int i, k, n = 0;
+ PXOB *xp = To_Vals;
+ PXCOL kp = To_KeyCol;
+ OPVAL op = Op;
+
+ switch (limit) {
+ case 1: Op = (incl) ? OP_GE : OP_GT; break;
+ case 2: Op = (incl) ? OP_GT : OP_GE; break;
+ default: return 0;
+ } // endswitch limit
+
+ /*********************************************************************/
+ /* Currently only range of constant values with an EQ operator is */
+ /* implemented. Find the number of rows for each given values. */
+ /*********************************************************************/
+ if (xp[0]->GetType() == TYPE_CONST) {
+ for (i = 0; kp; kp = kp->Next) {
+ kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
+ if (++i == Nval) break;
+ } // endfor kp
+
+ if ((k = FastFind(Nval)) < Num_K)
+ n = k;
+// if (limit)
+// n = (Mul) ? k : kp->Val_K;
+// else
+// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
+
+ } else {
+ strcpy(g->Message, MSG(RANGE_NO_JOIN));
+ n = -1; // Logical error
+ } // endif'f Type
+
+ Op = op;
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* Return the size of the group (equal values) of the current value. */
+/***********************************************************************/
+int XINDEX::GroupSize(void)
+ {
+#if defined(_DEBUG)
+ assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
+#endif // _DEBUG
+
+ if (Nval == Nk)
+ return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
+ : 1;
+
+#if defined(_DEBUG)
+ assert(To_LastVal);
+#endif // _DEBUG
+
+ // Index whose only some columns are used
+ int ck1, ck2;
+
+ ck1 = To_LastVal->Val_K;
+ ck2 = ck1 + 1;
+
+#if defined(_DEBUG)
+ assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
+#endif // _DEBUG
+
+ for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
+ ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
+ ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
+ } // endfor kcp
+
+ return ck2 - ck1;
+ } // end of GroupSize
+
+/***********************************************************************/
+/* Find Cur_K and Val_K's of the next distinct value of the index. */
+/* Returns false if Ok, true if there are no more different values. */
+/***********************************************************************/
+bool XINDEX::NextValDif(void)
+ {
+ int curk;
+ PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
+
+ if (++kcp->Val_K < kcp->Ndf) {
+ Cur_K = curk = kcp->Val_K;
+
+ // (Cur_K return is currently not used by SQLGBX)
+ for (PXCOL kp = kcp; kp; kp = kp->Next)
+ Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
+
+ } else
+ return true;
+
+ for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
+ if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
+ break; // all previous columns have same value
+
+ curk = ++kcp->Val_K; // This is a break, get new column value
+ } // endfor kcp
+
+ return false;
+ } // end of NextValDif
+
+/***********************************************************************/
+/* XINDEX: Find Cur_K and Val_K's of next index entry. */
+/* If eq is true next values must be equal to last ones up to Nval. */
+/* Returns false if Ok, true if there are no more (equal) values. */
+/***********************************************************************/
+bool XINDEX::NextVal(bool eq)
+ {
+ int n, neq = Nk + 1, curk;
+ PXCOL kcp;
+
+ if (Cur_K == Num_K)
+ return true;
+ else
+ curk = ++Cur_K;
+
+ for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
+ if (kcp->Kof) {
+ if (curk == kcp->Kof[kcp->Val_K + 1])
+ neq = n;
+
+ } else {
+#ifdef _DEBUG
+ assert(curk == kcp->Val_K + 1);
+#endif // _DEBUG
+ neq = n;
+ } // endif Kof
+
+#ifdef _DEBUG
+ assert(kcp->Val_K < kcp->Ndf);
+#endif // _DEBUG
+
+ // If this is not a break...
+ if (neq > n)
+ break; // all previous columns have same value
+
+ curk = ++kcp->Val_K; // This is a break, get new column value
+ } // endfor kcp
+
+ // Return true if no more values or, in case of "equal" values,
+ // if the last used column value has changed
+ return (Cur_K == Num_K || (eq && neq <= Nval));
+ } // end of NextVal
+
+/***********************************************************************/
+/* XINDEX: Fetch a physical or logical record. */
+/***********************************************************************/
+int XINDEX::Fetch(PGLOBAL g)
+ {
+ int n;
+ PXCOL kp;
+
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Table read through a sorted index. */
+ /*********************************************************************/
+ switch (Op) {
+ case OP_NEXT: // Read next
+ if (NextVal(false))
+ return -1; // End of indexed file
+
+ break;
+ case OP_FIRST: // Read first
+ for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = 0;
+
+ Op = OP_NEXT;
+ break;
+ case OP_SAME: // Read next same
+ // Logically the key values should be the same as before
+#if defined(TRACE)
+ printf("looking for next same value\n");
+#endif // TRACE
+
+ if (NextVal(true)) {
+ Op = OP_EQ;
+ return -2; // no more equal values
+ } // endif NextVal
+
+ break;
+ case OP_NXTDIF: // Read next dif
+// while (!NextVal(true)) ;
+
+// if (Cur_K >= Num_K)
+// return -1; // End of indexed file
+ if (NextValDif())
+ return -1; // End of indexed file
+
+ break;
+ case OP_FSTDIF: // Read first diff
+ for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = 0;
+
+ Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
+ break;
+ default: // Should be OP_EQ
+// if (Tbxp->Key_Rank < 0) {
+ /***************************************************************/
+ /* Look for the first key equal to the link column values */
+ /* and return its rank whithin the index table. */
+ /***************************************************************/
+ for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
+ if (kp->InitFind(g, To_Vals[n]))
+ return -1; // No more constant values
+
+ Nth++;
+
+#if defined(TRACE)
+ printf("Fetch: Looking for new value\n");
+#endif // TRACE
+ Cur_K = FastFind(Nval);
+
+ if (Cur_K >= Num_K)
+ /*************************************************************/
+ /* Rank not whithin index table, signal record not found. */
+ /*************************************************************/
+ return -2;
+
+ else if (Mul || Nval < Nk)
+ Op = OP_SAME;
+
+ } // endswitch Op
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ /*********************************************************************/
+ /* Return the position of the required record. */
+ /*********************************************************************/
+ return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching record in a join using an */
+/* optimized algorithm based on dichotomie and optimized comparing. */
+/***********************************************************************/
+int XINDEX::FastFind(int nv)
+ {
+ register int curk, sup, inf, i= 0, k, n = 2;
+ register PXCOL kp, kcp;
+
+ assert((int)nv == Nval);
+
+ if (Nblk && Op == OP_EQ) {
+ // Look in block values to find in which block to search
+ sup = Nblk;
+ inf = -1;
+
+ while (n && sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = To_KeyCol->CompBval(i);
+
+ if (n < 0)
+ sup = i;
+ else
+ inf = i;
+
+ } // endwhile
+
+ if (inf < 0)
+ return Num_K;
+
+// i = inf;
+ inf *= Sblk;
+
+ if ((sup = inf + Sblk) > To_KeyCol->Ndf)
+ sup = To_KeyCol->Ndf;
+
+ inf--;
+ } else {
+ inf = -1;
+ sup = To_KeyCol->Ndf;
+ } // endif Nblk
+
+ for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
+ while (sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompVal(i);
+
+ if (n < 0)
+ sup = i;
+ else if (n > 0)
+ inf = i;
+ else
+ break;
+
+ } // endwhile
+
+ if (n) {
+ if (Op != OP_EQ) {
+ // Currently only OP_GT or OP_GE
+ kcp->Val_K = curk = sup;
+
+ // Check for value changes in previous key parts
+ for (kp = kcp->Previous; kp; kp = kp->Previous)
+ if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
+ break;
+ else
+ curk = ++kp->Val_K;
+
+ n = 0;
+ } // endif Op
+
+ break;
+ } // endif n
+
+ kcp->Val_K = i;
+
+ if (++k == Nval) {
+ if (Op == OP_GT) { // n is always 0
+ curk = ++kcp->Val_K; // Increment value by 1
+
+ // Check for value changes in previous key parts
+ for (kp = kcp->Previous; kp; kp = kp->Previous)
+ if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
+ break; // Not changed
+ else
+ curk = ++kp->Val_K;
+
+ } // endif Op
+
+ break; // So kcp remains pointing the last tested block
+ } // endif k
+
+ if (kcp->Kof) {
+ inf = kcp->Kof[i] - 1;
+ sup = kcp->Kof[i + 1];
+ } else {
+ inf = i - 1;
+ sup = i + 1;
+ } // endif Kof
+
+ } // endfor k, kcp
+
+ if (n) {
+ // Record not found
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Val_K = kcp->Ndf; // Not a valid value
+
+ return Num_K;
+ } // endif n
+
+ for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
+ kcp->Val_K = curk;
+ curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
+ } // endfor kcp
+
+ return curk;
+ } // end of FastFind
+
+/* -------------------------- XINDXS Class --------------------------- */
+
+/***********************************************************************/
+/* XINDXS public constructor. */
+/***********************************************************************/
+XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
+ : XINDEX(tdbp, xdp, pxp, cp, xp)
+ {
+ Srtd = To_Cols[0]->GetOpt() == 2;
+ } // end of XINDXS constructor
+
+/***********************************************************************/
+/* XINDXS compare routine for C Quick/Insertion sort. */
+/***********************************************************************/
+int XINDXS::Qcompare(int *i1, int *i2)
+ {
+#ifdef DEBTRACE
+ num_comp++;
+#endif
+
+ return To_KeyCol->Compare(*i1, *i2);
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Range: Tell how many records exist for given value(s): */
+/* If limit=0 return range for these values. */
+/* If limit=1 return the start of range. */
+/* If limit=2 return the end of range. */
+/***********************************************************************/
+int XINDXS::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int k, n = 0;
+ PXOB xp = To_Vals[0];
+ PXCOL kp = To_KeyCol;
+ OPVAL op = Op;
+
+ switch (limit) {
+ case 1: Op = (incl) ? OP_GE : OP_GT; break;
+ case 2: Op = (incl) ? OP_GT : OP_GE; break;
+ default: Op = OP_EQ;
+ } // endswitch limit
+
+ /*********************************************************************/
+ /* Currently only range of constant values with an EQ operator is */
+ /* implemented. Find the number of rows for each given values. */
+ /*********************************************************************/
+ if (xp->GetType() == TYPE_CONST) {
+ kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
+ k = FastFind(Nval);
+
+ if (k < Num_K || Op != OP_EQ)
+ if (limit)
+ n = (Mul) ? k : kp->Val_K;
+ else
+ n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
+
+ } else {
+ strcpy(g->Message, MSG(RANGE_NO_JOIN));
+ n = -1; // Logical error
+ } // endif'f Type
+
+ Op = op;
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* Return the size of the group (equal values) of the current value. */
+/***********************************************************************/
+int XINDXS::GroupSize(void)
+ {
+#if defined(_DEBUG)
+ assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
+#endif // _DEBUG
+ return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K]
+ : 1;
+ } // end of GroupSize
+
+/***********************************************************************/
+/* XINDXS: Find Cur_K and Val_K of next index value. */
+/* If b is true next value must be equal to last one. */
+/* Returns false if Ok, true if there are no more (equal) values. */
+/***********************************************************************/
+bool XINDXS::NextVal(bool eq)
+ {
+ bool rc;
+
+ if (To_KeyCol->Val_K == Ndif)
+ return true;
+
+ if (Mul) {
+ int limit = Pof[To_KeyCol->Val_K + 1];
+
+#ifdef _DEBUG
+ assert(Cur_K < limit);
+ assert(To_KeyCol->Val_K < Ndif);
+#endif // _DEBUG
+
+ if (++Cur_K == limit) {
+ To_KeyCol->Val_K++;
+ rc = (eq || limit == Num_K);
+ } else
+ rc = false;
+
+ } else
+ rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
+
+ return rc;
+ } // end of NextVal
+
+/***********************************************************************/
+/* XINDXS: Fetch a physical or logical record. */
+/***********************************************************************/
+int XINDXS::Fetch(PGLOBAL g)
+ {
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Table read through a sorted index. */
+ /*********************************************************************/
+ switch (Op) {
+ case OP_NEXT: // Read next
+ if (NextVal(false))
+ return -1; // End of indexed file
+
+ break;
+ case OP_FIRST: // Read first
+ To_KeyCol->Val_K = Cur_K = 0;
+ Op = OP_NEXT;
+ break;
+ case OP_SAME: // Read next same
+#if defined(TRACE)
+// printf("looking for next same value\n");
+#endif // TRACE
+
+ if (!Mul || NextVal(true)) {
+ Op = OP_EQ;
+ return -2; // No more equal values
+ } // endif Mul
+
+ break;
+ case OP_NXTDIF: // Read next dif
+ if (++To_KeyCol->Val_K == Ndif)
+ return -1; // End of indexed file
+
+ Cur_K = Pof[To_KeyCol->Val_K];
+ break;
+ case OP_FSTDIF: // Read first diff
+ To_KeyCol->Val_K = Cur_K = 0;
+ Op = (Mul) ? OP_NXTDIF : OP_NEXT;
+ break;
+ default: // Should OP_EQ
+ /*****************************************************************/
+ /* Look for the first key equal to the link column values */
+ /* and return its rank whithin the index table. */
+ /*****************************************************************/
+ if (To_KeyCol->InitFind(g, To_Vals[0]))
+ return -1; // No more constant values
+ else
+ Nth++;
+
+#if defined(TRACE)
+ printf("Fetch: Looking for new value\n");
+#endif // TRACE
+
+ Cur_K = FastFind(1);
+
+ if (Cur_K >= Num_K)
+ // Rank not whithin index table, signal record not found
+ return -2;
+ else if (Mul)
+ Op = OP_SAME;
+
+ } // endswitch Op
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ /*********************************************************************/
+ /* Return the position of the required record. */
+ /*********************************************************************/
+ return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching indexed record using an */
+/* optimized algorithm based on dichotomie and optimized comparing. */
+/***********************************************************************/
+int XINDXS::FastFind(int nk)
+ {
+ register int sup, inf, i= 0, n = 2;
+ register PXCOL kcp = To_KeyCol;
+
+ if (Nblk && Op == OP_EQ) {
+ // Look in block values to find in which block to search
+ sup = Nblk;
+ inf = -1;
+
+ while (n && sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompBval(i);
+
+ if (n < 0)
+ sup = i;
+ else
+ inf = i;
+
+ } // endwhile
+
+ if (inf < 0)
+ return Num_K;
+
+// i = inf;
+ inf *= Sblk;
+
+ if ((sup = inf + Sblk) > Ndif)
+ sup = Ndif;
+
+ inf--;
+ } else {
+ inf = -1;
+ sup = Ndif;
+ } // endif Nblk
+
+ while (sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompVal(i);
+
+ if (n < 0)
+ sup = i;
+ else if (n > 0)
+ inf = i;
+ else
+ break;
+
+ } // endwhile
+
+ if (!n && Op == OP_GT) {
+ ++i;
+ } else if (n && Op != OP_EQ) {
+ // Currently only OP_GT or OP_GE
+ i = sup;
+ n = 0;
+ } // endif sup
+
+ kcp->Val_K = i; // Used by FillValue
+ return ((n) ? Num_K : (Mul) ? Pof[i] : i);
+ } // end of FastFind
+
+/* -------------------------- XLOAD Class --------------------------- */
+
+/***********************************************************************/
+/* XLOAD constructor. */
+/***********************************************************************/
+XLOAD::XLOAD(void)
+ {
+ Hfile = INVALID_HANDLE_VALUE;
+#if defined(WIN32) && defined(XMAP)
+ ViewBase = NULL;
+#endif // WIN32 && XMAP
+ NewOff.Val = 0LL;
+} // end of XLOAD constructor
+
+/***********************************************************************/
+/* Close the index huge file. */
+/***********************************************************************/
+void XLOAD::Close(void)
+ {
+ if (Hfile != INVALID_HANDLE_VALUE) {
+ CloseFileHandle(Hfile);
+ Hfile = INVALID_HANDLE_VALUE;
+ } // endif Hfile
+
+#if defined(WIN32) && defined(XMAP)
+ if (ViewBase) {
+ if (!UnmapViewOfFile(ViewBase))
+ printf("Error %d closing Viewmap\n", GetLastError());
+
+ ViewBase = NULL;
+ } // endif ViewBase
+#endif // WIN32 && XMAP
+
+ } // end of Close
+
+/* --------------------------- XFILE Class --------------------------- */
+
+/***********************************************************************/
+/* XFILE constructor. */
+/***********************************************************************/
+XFILE::XFILE(void) : XLOAD()
+ {
+ Xfile = NULL;
+#if defined(XMAP) && !defined(WIN32)
+ Mmp = NULL;
+#endif // XMAP && !WIN32
+ } // end of XFILE constructor
+
+/***********************************************************************/
+/* Xopen function: opens a file using native API's. */
+/***********************************************************************/
+bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
+ {
+ char *pmod;
+ bool rc;
+ IOFF noff[MAX_INDX];
+
+ /*********************************************************************/
+ /* Open the index file according to mode. */
+ /*********************************************************************/
+ switch (mode) {
+ case MODE_READ: pmod = "rb"; break;
+ case MODE_WRITE: pmod = "wb"; break;
+ case MODE_INSERT: pmod = "ab"; break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch mode
+
+ if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
+#if defined(TRACE)
+ printf("Open: %s\n", g->Message);
+#endif // TRACE
+ return true;
+ } // endif Xfile
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Position the cursor at end of file so ftell returns file size. */
+ /*******************************************************************/
+ if (fseek(Xfile, 0, SEEK_END)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+ NewOff.Low = (int)ftell(Xfile);
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
+ fseek(Xfile, 0, SEEK_END);
+ NewOff.Low = (int)ftell(Xfile);
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
+ sprintf(g->Message, MSG(XFILE_READERR), errno);
+ return true;
+ } // endif MAX_INDX
+
+ // Position the cursor at the offset of this index
+ if (fseek(Xfile, noff[id].Low, SEEK_SET)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+ } // endif mode
+
+ return false;
+ } // end of Open
+
+/***********************************************************************/
+/* Move into an index file. */
+/***********************************************************************/
+bool XFILE::Seek(PGLOBAL g, int low, int high, int origin)
+ {
+#if defined(_DEBUG)
+ assert(high == 0);
+#endif // !_DEBUG
+
+ if (fseek(Xfile, low, origin)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+//ftell(Xfile);
+ return false;
+ } // end of Seek
+
+/***********************************************************************/
+/* Read from the index file. */
+/***********************************************************************/
+bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
+ {
+ if (fread(buf, size, n, Xfile) != (size_t)n) {
+ sprintf(g->Message, MSG(XFILE_READERR), errno);
+ return true;
+ } // endif size
+
+ return false;
+ } // end of Read
+
+/***********************************************************************/
+/* Write on index file, set rc and return the number of bytes written */
+/***********************************************************************/
+int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
+ {
+ int niw = (int)fwrite(buf, size, n, Xfile);
+
+ if (niw != n) {
+ sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
+ rc = true;
+ } // endif size
+
+ return niw * size;
+ } // end of Write
+
+/***********************************************************************/
+/* Update the file header and close the index file. */
+/***********************************************************************/
+void XFILE::Close(char *fn, int id)
+ {
+ if (id >= 0 && fn && Xfile) {
+ fclose(Xfile);
+
+ if ((Xfile = fopen(fn, "r+b")))
+ if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
+ fwrite(&NewOff, sizeof(int), 2, Xfile);
+
+ } // endif id
+
+ Close();
+ } // end of Close
+
+/***********************************************************************/
+/* Close the index file. */
+/***********************************************************************/
+void XFILE::Close(void)
+ {
+ XLOAD::Close();
+
+ if (Xfile) {
+ fclose(Xfile);
+ Xfile = NULL;
+ } // endif Xfile
+
+#if defined(XMAP) && !defined(WIN32)
+ if (Mmp) {
+ CloseMemMap(Mmp->memory, Mmp->lenL);
+ Mmp = NULL;
+ } // endif Mmp
+#endif // XMAP
+ } // end of Close
+
+#if defined(XMAP)
+ /*********************************************************************/
+ /* Map the entire index file. */
+ /*********************************************************************/
+void *XFILE::FileView(PGLOBAL g, char *fn)
+ {
+ HANDLE h;
+
+ Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
+ h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
+
+ if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
+ if (!(*g->Message))
+ strcpy(g->Message, MSG(FILE_MAP_ERR));
+
+ CloseFileHandle(h); // Not used anymore
+ return NULL; // No saved values
+ } // endif h
+
+ CloseFileHandle(h); // Not used anymore
+ return Mmp->memory;
+ } // end of FileView
+#endif // XMAP
+
+/* -------------------------- XHUGE Class --------------------------- */
+
+/***********************************************************************/
+/* Xopen function: opens a file using native API's. */
+/***********************************************************************/
+bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
+ {
+ IOFF noff[MAX_INDX];
+
+ if (Hfile != INVALID_HANDLE_VALUE) {
+ sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
+ return true;
+ } // endif
+
+#if defined(TRACE)
+ printf( "Xopen: filename=%s mode=%d\n", filename, mode);
+#endif // TRACE
+
+#if defined(WIN32)
+ LONG high = 0;
+ DWORD rc, drc, access, share, creation;
+
+ /*********************************************************************/
+ /* Create the file object according to access mode */
+ /*********************************************************************/
+ switch (mode) {
+ case MODE_READ:
+ access = GENERIC_READ;
+ share = FILE_SHARE_READ;
+ creation = OPEN_EXISTING;
+ break;
+ case MODE_WRITE:
+ access = GENERIC_WRITE;
+ share = 0;
+ creation = CREATE_ALWAYS;
+ break;
+ case MODE_INSERT:
+ access = GENERIC_WRITE;
+ share = 0;
+ creation = OPEN_EXISTING;
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch
+
+ Hfile = CreateFile(filename, access, share, NULL, creation,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (Hfile == INVALID_HANDLE_VALUE) {
+ rc = GetLastError();
+ sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)filename, sizeof(filename), NULL);
+ strcat(g->Message, filename);
+ return true;
+ } // endif Hfile
+
+#ifdef DEBTRACE
+ fprintf(debug,
+ " access=%p share=%p creation=%d handle=%p fn=%s\n",
+ access, share, creation, Hfile, filename);
+#endif
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* In Insert mode we must position the cursor at end of file. */
+ /*******************************************************************/
+ rc = SetFilePointer(Hfile, 0, &high, FILE_END);
+
+ if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
+ sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
+ CloseHandle(Hfile);
+ Hfile = INVALID_HANDLE_VALUE;
+ return true;
+ } // endif
+
+ NewOff.Low = (int)rc;
+ NewOff.High = (int)high;
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
+ NewOff.Low = (int)drc;
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
+
+ if (!rc) {
+ sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
+ return true;
+ } // endif rc
+
+ // Position the cursor at the offset of this index
+ rc = SetFilePointer(Hfile, noff[id].Low,
+ (PLONG)&noff[id].High, FILE_BEGIN);
+
+ if (rc == INVALID_SET_FILE_POINTER) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
+ return true;
+ } // endif
+
+ } // endif Mode
+
+#else // UNIX
+ int oflag = O_LARGEFILE; // Enable file size > 2G
+ mode_t pmod = 0;
+
+ /*********************************************************************/
+ /* Create the file object according to access mode */
+ /*********************************************************************/
+ switch (mode) {
+ case MODE_READ:
+ oflag |= O_RDONLY;
+ break;
+ case MODE_WRITE:
+ oflag |= O_WRONLY | O_CREAT | O_TRUNC;
+ pmod = S_IREAD | S_IWRITE;
+ break;
+ case MODE_INSERT:
+ oflag |= (O_WRONLY | O_APPEND);
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch
+
+ Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
+
+ if (Hfile == INVALID_HANDLE_VALUE) {
+ /*rc = errno;*/
+#if defined(TRACE)
+ printf("Open: %s\n", g->Message);
+#endif // TRACE
+ return true;
+ } // endif Hfile
+
+#if defined(TRACE)
+ printf(" rc=%d oflag=%p mode=%d handle=%d fn=%s\n",
+ rc, oflag, mode, Hfile, filename);
+#endif // TRACE
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Position the cursor at end of file so ftell returns file size. */
+ /*******************************************************************/
+ if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
+ return true;
+ } // endif
+
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ NewOff.Low = write(Hfile, &noff, sizeof(noff));
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
+ sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
+ return true;
+ } // endif MAX_INDX
+
+ // Position the cursor at the offset of this index
+ if (!lseek64(Hfile, noff[id].Val, SEEK_SET)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
+ return true;
+ } // endif
+
+ } // endif mode
+#endif // UNIX
+
+ return false;
+ } // end of Open
+
+/***********************************************************************/
+/* Go to position in a huge file. */
+/***********************************************************************/
+bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
+ {
+#if defined(WIN32)
+ LONG hi = high;
+ DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
+
+ if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
+ sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
+ return true;
+ } // endif
+
+#else // UNIX
+ off64_t pos = (off64_t)low
+ + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
+
+ if (lseek64(Hfile, pos, origin) < 0) {
+ sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
+#if defined(TRACE)
+ printf("lseek64 error %d\n", errno);
+#endif // TRACE
+ return true;
+ } // endif lseek64
+
+#if defined(TRACE)
+ printf("Seek: low=%d high=%d\n", low, high);
+#endif // TRACE
+#endif // UNIX
+
+ return false;
+ } // end of Seek
+
+/***********************************************************************/
+/* Read from a huge index file. */
+/***********************************************************************/
+bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
+ {
+ bool rc = false;
+
+#if defined(WIN32)
+ bool brc;
+ DWORD nbr, count = (DWORD)(n * size);
+
+ brc = ReadFile(Hfile, buf, count, &nbr, NULL);
+
+ if (brc) {
+ if (nbr != count) {
+ strcpy(g->Message, MSG(EOF_INDEX_FILE));
+ rc = true;
+ } // endif nbr
+
+ } else {
+ char *buf[256];
+ DWORD drc = GetLastError();
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
+ rc = true;
+ } // endif brc
+#else // UNIX
+ ssize_t count = (ssize_t)(n * size);
+
+#if defined(TRACE)
+ printf("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
+#endif // TRACE
+
+ if (read(Hfile, buf, count) != count) {
+ sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
+#if defined(TRACE)
+ printf("read error %d\n", errno);
+#endif // TRACE
+ rc = true;
+ } // endif nbr
+#endif // UNIX
+
+ return rc;
+ } // end of Read
+
+/***********************************************************************/
+/* Write on a huge index file. */
+/***********************************************************************/
+int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
+ {
+#if defined(WIN32)
+ bool brc;
+ DWORD nbw, count = (DWORD)n * (DWORD) size;
+
+ brc = WriteFile(Hfile, buf, count, &nbw, NULL);
+
+ if (!brc) {
+ char msg[256];
+ DWORD drc = GetLastError();
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)msg, sizeof(msg), NULL);
+ sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
+ rc = true;
+ } // endif size
+
+ return (int)nbw;
+#else // UNIX
+ ssize_t nbw;
+ size_t count = (size_t)n * (size_t)size;
+
+ nbw = write(Hfile, buf, count);
+
+ if (nbw != (signed)count) {
+ sprintf(g->Message, MSG(WRITING_ERROR),
+ "index file", strerror(errno));
+ rc = true;
+ } // endif nbw
+
+ return (int)nbw;
+#endif // UNIX
+ } // end of Write
+
+/***********************************************************************/
+/* Update the file header and close the index file. */
+/***********************************************************************/
+void XHUGE::Close(char *fn, int id)
+ {
+#if defined(WIN32)
+ if (id >= 0 && fn) {
+ CloseFileHandle(Hfile);
+ Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (Hfile != INVALID_HANDLE_VALUE)
+ if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
+ != INVALID_SET_FILE_POINTER) {
+ DWORD nbw;
+
+ WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
+ } // endif SetFilePointer
+
+ } // endif id
+#else // !WIN32
+ if (id >= 0 && fn) {
+ fcntl(Hfile, F_SETFD, O_WRONLY);
+
+ if (lseek(Hfile, id * sizeof(IOFF), SEEK_SET))
+ write(Hfile, &NewOff, sizeof(IOFF));
+
+ } // endif id
+#endif // !WIN32
+
+ XLOAD::Close();
+ } // end of Close
+
+#if defined(XMAP)
+/***********************************************************************/
+/* Don't know whether this is possible for huge files. */
+/***********************************************************************/
+void *XHUGE::FileView(PGLOBAL g, char *fn)
+ {
+ strcpy(g->Message, MSG(NO_PART_MAP));
+ return NULL;
+ } // end of FileView
+#endif // XMAP
+
+/* -------------------------- XXROW Class --------------------------- */
+
+/***********************************************************************/
+/* XXROW Public Constructor. */
+/***********************************************************************/
+XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
+ {
+ Tdbp = tdbp;
+ Valp = NULL;
+ } // end of XXROW constructor
+
+/***********************************************************************/
+/* XXROW Reset: re-initialize a Kindex block. */
+/***********************************************************************/
+void XXROW::Reset(void)
+ {
+#if defined(_DEBUG)
+ assert(Tdbp->GetLink()); // This a join index
+#endif // _DEBUG
+ } // end of Reset
+
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XXROW::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* To_Link should not be NULL. */
+ /*********************************************************************/
+ if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
+ return true;
+
+ if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
+ strcpy(g->Message, MSG(TYPE_MISMATCH));
+ return true;
+ } else
+ Valp = (*Tdbp->GetLink())->GetValue();
+
+ if ((Num_K = Tbxp->Cardinality(g)) < 0)
+ return true; // Not a fixed file
+
+ /*********************************************************************/
+ /* The entire table is indexed, no need to construct the index. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+ } // end of Init
+
+/***********************************************************************/
+/* RANGE: Tell how many record exist in a given value range. */
+/***********************************************************************/
+int XXROW::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int n = Valp->GetIntValue();
+
+ switch (limit) {
+ case 1: n += ((incl) ? 0 : 1); break;
+ case 2: n += ((incl) ? 1 : 0); break;
+ default: n = 1;
+ } // endswitch limit
+
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* XXROW: Fetch a physical or logical record. */
+/***********************************************************************/
+int XXROW::Fetch(PGLOBAL g)
+ {
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Look for a key equal to the link column of previous table, */
+ /* and return its rank whithin the index table. */
+ /*********************************************************************/
+ Cur_K = FastFind(1);
+
+ if (Cur_K >= Num_K)
+ /*******************************************************************/
+ /* Rank not whithin index table, signal record not found. */
+ /*******************************************************************/
+ return -2; // Means record not found
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ return Cur_K;
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching record in a join. */
+/***********************************************************************/
+int XXROW::FastFind(int nk)
+ {
+ int n = Valp->GetIntValue();
+
+ if (n < 0)
+ return (Op == OP_EQ) ? (-1) : 0;
+ else if (n > Num_K)
+ return Num_K;
+ else
+ return (Op == OP_GT) ? n : (n - 1);
+
+ } // end of FastFind
+
+/* ------------------------- KXYCOL Classes -------------------------- */
+
+/***********************************************************************/
+/* KXYCOL public constructor. */
+/***********************************************************************/
+KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
+ To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
+ {
+ Next = NULL;
+ Previous = NULL;
+ Kxp = kp;
+ Colp = NULL;
+ IsSorted = false;
+ Asc = true;
+ Keys = Nmblk;
+ Kblp = NULL;
+ Bkeys = Nmblk;
+ Blkp = NULL;
+ Valp = NULL;
+ Klen = 0;
+ Kprec = 0;
+ Type = TYPE_ERROR;
+ Prefix = false;
+ Koff = Nmblk;
+ Val_K = 0;
+ Ndf = 0;
+ Mxs = 0;
+ } // end of KXYCOL constructor
+
+/***********************************************************************/
+/* KXYCOL Init: initialize and allocate storage. */
+/* Key length kln can be smaller than column length for CHAR columns. */
+/***********************************************************************/
+bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
+ {
+ int len = colp->GetLength(), prec = colp->GetScale();
+
+ // Currently no indexing on NULL columns
+ if (colp->IsNullable()) {
+ sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
+ return true;
+ } // endif nullable
+
+ if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
+ len = kln;
+ Prefix = true;
+ } // endif kln
+
+#ifdef DEBTRACE
+ htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
+ this, colp->GetName(), n, colp->GetResultType(), sm);
+#endif
+
+ // Allocate the Value object used when moving items
+ Type = colp->GetResultType();
+
+ if (!(Valp = AllocateValue(g, Type, len, colp->GetScale(),
+ colp->IsUnsigned())))
+ return true;
+
+ Klen = Valp->GetClen();
+ Keys.Size = n * Klen;
+
+ if (!PlgDBalloc(g, NULL, Keys)) {
+ sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
+ return true; // Error
+ } // endif
+
+ // Allocate the Valblock. The last parameter is to have rows filled
+ // by blanks (if true) or keep the zero ending char (if false).
+ // Currently we set it to true to be compatible with QRY blocks,
+ // and the one before last is to enable length/type checking, set to
+ // true if not a prefix key.
+ Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true);
+ Asc = sm; // Sort mode: Asc=true Desc=false
+ Ndf = n;
+
+ // Store this information to avoid sorting when already done
+ if (Asc)
+ IsSorted = colp->GetOpt() == 2;
+
+//SetNulls(colp->IsNullable()); for when null columns will be indexable
+ return false;
+ } // end of Init
+
+#if defined(XMAP)
+/***********************************************************************/
+/* KXYCOL MapInit: initialize and address storage. */
+/* Key length kln can be smaller than column length for CHAR columns. */
+/***********************************************************************/
+BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
+ {
+ int len = colp->GetLength(), prec = colp->GetPrecision();
+
+ if (n[3] && colp->GetLength() > n[3]
+ && colp->GetResultType() == TYPE_STRING) {
+ len = n[3];
+ Prefix = true;
+ } // endif kln
+
+ Type = colp->GetResultType();
+
+#ifdef DEBTRACE
+ htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
+ this, colp, Type, n[0], len, m);
+#endif
+
+ // Allocate the Value object used when moving items
+ Valp = AllocateValue(g, Type, len, prec, false, NULL);
+ Klen = Valp->GetClen();
+
+ if (n[2]) {
+ Bkeys.Size = n[2] * Klen;
+ Bkeys.Memp = m;
+ Bkeys.Sub = true;
+
+ // Allocate the Valblk containing initial block key values
+ Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true);
+ } // endif nb
+
+ Keys.Size = n[0] * Klen;
+ Keys.Memp = m + Bkeys.Size;
+ Keys.Sub = true;
+
+ // Allocate the Valblock. Last two parameters are to have rows filled
+ // by blanks (if true) or keep the zero ending char (if false).
+ // Currently we set it to true to be compatible with QRY blocks,
+ // and last one to enable type checking (no conversion).
+ Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, true, true);
+
+ if (n[1]) {
+ Koff.Size = n[1] * sizeof(int);
+ Koff.Memp = m + Bkeys.Size + Keys.Size;
+ Koff.Sub = true;
+ } // endif n[1]
+
+ Ndf = n[0];
+ IsSorted = colp->GetOpt() < 0;
+ return m + Bkeys.Size + Keys.Size + Koff.Size;
+ } // end of MapInit
+#endif // XMAP
+
+/***********************************************************************/
+/* Allocate the offset block used by intermediate key columns. */
+/***********************************************************************/
+int *KXYCOL::MakeOffset(PGLOBAL g, int n)
+ {
+ if (!Kof) {
+ // Calculate the initial size of the offset
+ Koff.Size = (n + 1) * sizeof(int);
+
+ // Allocate the required memory
+ if (!PlgDBalloc(g, NULL, Koff)) {
+ strcpy(g->Message, MSG(KEY_ALLOC_ERR));
+ return NULL; // Error
+ } // endif
+
+ } else if (n) {
+ // This is a reallocation call
+ PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
+ } else
+ PlgDBfree(Koff);
+
+ return (int*)Kof;
+ } // end of MakeOffset
+
+/***********************************************************************/
+/* Make a front end array of key values that are the first value of */
+/* each blocks (of size n). This to reduce paging in FastFind. */
+/***********************************************************************/
+bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
+ {
+ int i, k;
+
+ // Calculate the size of the block array in the index
+ Bkeys.Size = nb * Klen;
+
+ // Allocate the required memory
+ if (!PlgDBalloc(g, NULL, Bkeys)) {
+ sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
+ return true; // Error
+ } // endif
+
+ // Allocate the Valblk used to contains initial block key values
+ Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
+
+ // Populate the array with values
+ for (i = k = 0; i < nb; i++, k += size)
+ Blkp->SetValue(Kblp, i, k);
+
+ return false;
+ } // end of MakeBlockArray
+
+/***********************************************************************/
+/* KXYCOL SetValue: read column value for nth array element. */
+/***********************************************************************/
+void KXYCOL::SetValue(PCOL colp, int i)
+ {
+#if defined(_DEBUG)
+ assert (Kblp != NULL);
+#endif
+
+ Kblp->SetValue(colp->GetValue(), i);
+ } // end of SetValue
+
+/***********************************************************************/
+/* InitFind: initialize finding the rank of column value in index. */
+/***********************************************************************/
+bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
+ {
+ if (xp->GetType() == TYPE_CONST) {
+ if (Kxp->Nth)
+ return true;
+
+ Valp->SetValue_pval(xp->GetValue(), !Prefix);
+ } else {
+ xp->Reset();
+ xp->Eval(g);
+ Valp->SetValue_pval(xp->GetValue(), false);
+// Valp->SetValue_pval(xp->GetValue(), !Prefix);
+ } // endif Type
+
+ return false;
+ } // end of InitFind
+
+/***********************************************************************/
+/* InitBinFind: initialize Value to the value pointed by vp. */
+/***********************************************************************/
+void KXYCOL::InitBinFind(void *vp)
+ {
+ Valp->SetBinValue(vp);
+ } // end of InitBinFind
+
+/***********************************************************************/
+/* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
+/* already in storage in the corresponding KXYCOL. */
+/***********************************************************************/
+void KXYCOL::FillValue(PVAL valp)
+ {
+ valp->SetValue_pvblk(Kblp, Val_K);
+
+ // Set null when applicable (NIY)
+//if (valp->GetNullable())
+// valp->SetNull(valp->IsZero());
+
+ } // end of FillValue
+
+/***********************************************************************/
+/* KXYCOL: Compare routine for one numeric value. */
+/***********************************************************************/
+int KXYCOL::Compare(int i1, int i2)
+ {
+ // Do the actual comparison between values.
+ register int k = Kblp->CompVal(i1, i2);
+
+#ifdef DEBUG2
+ htrc("Compare done result=%d\n", k);
+#endif
+
+ return (Asc) ? k : -k;
+ } // end of Compare
+
+/***********************************************************************/
+/* KXYCOL: Compare the ith key to the stored Value. */
+/***********************************************************************/
+int KXYCOL::CompVal(int i)
+ {
+ // Do the actual comparison between numerical values.
+#ifdef DEBUG2
+ register int k = (int)Kblp->CompVal(Valp, (int)i);
+
+ htrc("Compare done result=%d\n", k);
+ return k;
+#endif
+ return Kblp->CompVal(Valp, i);
+ } // end of CompVal
+
+/***********************************************************************/
+/* KXYCOL: Compare the key to the stored block value. */
+/***********************************************************************/
+int KXYCOL::CompBval(int i)
+ {
+ // Do the actual comparison between key values.
+ return Blkp->CompVal(Valp, i);
+ } // end of CompBval
+
+/***********************************************************************/
+/* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
+/***********************************************************************/
+void KXYCOL::ReAlloc(PGLOBAL g, int n)
+ {
+ PlgDBrealloc(g, NULL, Keys, n * Klen);
+ Kblp->ReAlloc(To_Keys, n);
+ Ndf = n;
+ } // end of ReAlloc
+
+/***********************************************************************/
+/* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
+/***********************************************************************/
+void KXYCOL::FreeData(void)
+ {
+ PlgDBfree(Keys);
+ Kblp = NULL;
+ PlgDBfree(Bkeys);
+ Blkp = NULL;
+ PlgDBfree(Koff);
+ Ndf = 0;
+ } // end of FreeData