/***************** Xindex C++ Class Xindex Code (.CPP) *****************/ /* Name: XINDEX.CPP Version 3.0 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2004-2017 */ /* */ /* This file contains the class XINDEX implementation code. */ /***********************************************************************/
/***********************************************************************/ /* DB external variables. */ /***********************************************************************/ extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ #ifdefined(XMAP) extern my_bool xmap; #endif// XMAP
/***********************************************************************/ /* 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;
PTDB 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 = cat->GetTable(g, tablep)))
rc = RC_NF; elseif (!tdbp->GetDef()->Indexable()) {
snprintf(g->Message, sizeof(g->Message), MSG(TABLE_NO_INDEX), name);
rc = RC_NF;
} elseif ((rc = ((PTDBASE)tdbp)->MakeIndex(g, pxdf, add)) == RC_INFO)
rc = RC_OK; // No or remote index
return rc;
} // end of PlgMakeIndex
/* -------------------------- Class INDEXDEF ------------------------- */
/***********************************************************************/ /* Set the max same values for each column 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;
Srtd = false;
Dynamic = 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::Printf(PGLOBAL, 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 Printf
/***********************************************************************/ /* Make string output of XINDEX contents. */ /***********************************************************************/ void XXBASE::Prints(PGLOBAL, char *ps, uint z)
{
*ps = '\0';
strncat(ps, "Xindex", z);
} // end of Prints
/* -------------------------- 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
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 values that are used at return to make. */ /***********************************************************************/ void XINDEX::Close(void)
{ // Close file or view of file if (X)
X->Close();
// De-allocate data
PlgDBfree(Record);
PlgDBfree(Index);
PlgDBfree(Offset);
for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next) { // Column values cannot be retrieved from key anymore if (kcp->Colp)
kcp->Colp->SetKcol(NULL);
// De-allocate Key data
kcp->FreeData();
} // endfor kcp
} // end of Close
/***********************************************************************/ /* XINDEX compare routine for C Quick/Insertion sort. */ /***********************************************************************/ int XINDEX::Qcompare(int *i1, int *i2)
{ int k;
PXCOL kcp;
for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next) if ((k = kcp->Compare(*i1, *i2))) break;
//num_comp++; return k;
} // end of Qcompare
/***********************************************************************/ /* AddColumns: here we try to determine whether it is worthwhile to */ /* add to the keys the values of the columns selected for this table. */ /* Sure enough, it is done while records are read and permit to avoid */ /* reading the table while doing the join (Dynamic index only) */ /***********************************************************************/ bool XINDEX::AddColumns(void)
{ if (!Dynamic) returnfalse; // Not applying to static index elseif (IsMul()) returnfalse; // Not done yet for multiple index #ifdefined(VCT_SUPPORT) elseif (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit()) returnfalse; // This would require to read additional files #endif// VCT_SUPPORT else returntrue;
} // end of AddColumns
/***********************************************************************/ /* Make: Make and index on key column(s). */ /***********************************************************************/ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
{ /*********************************************************************/ /* Table can be accessed through an index. */ /*********************************************************************/ int k, nk = Nk, rc = RC_OK; int *bof, i, j, n, ndf, nkey;
PKPDEF kdfp = Xdp->GetToKeyParts(); bool brc = false;
PCOL colp;
PFIL filp = Tdbp->GetFilter();
PXCOL kp, addcolp, prev = NULL, kcp = NULL; //PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
#ifdefined(_DEBUG)
assert(X || Nk == 1); #endif// _DEBUG
/*********************************************************************/ /* Allocate the storage that will contain the keys and the file */ /* positions corresponding to them. */ /*********************************************************************/ if ((n = Tdbp->GetMaxSize(g)) < 0) returntrue; elseif (!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
if (trace(1))
htrc("XINDEX Make: n=%d\n", n);
// File position must be stored
Record.Size = n * sizeof(int);
/*********************************************************************/ /* Allocate the KXYCOL blocks used to store column values. */ /*********************************************************************/ for (k = 0; k < Nk; k++) {
colp = To_Cols[k];
if (AddColumns()) {
PCOL kolp = To_Cols[0]; // Temporary while imposing Nk = 1
i = 0;
// Allocate the accompanying for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) { // Count how many columns to add // for (k = 0; k < Nk; k++) // if (colp == To_Cols[k]) // break;
// if (k == nk) if (colp != kolp)
i++;
} // endfor colp
if (i && i < 10) // Should be a parameter for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) { // for (k = 0; k < Nk; k++) // if (colp == To_Cols[k]) // break;
// if (k < nk) if (colp == kolp) continue; // This is a key column
kcp = new(g) KXYCOL(this);
if (kcp->Init(g, colp, n, true, 0)) returntrue;
if (trace(1))
htrc("Adding colp=%p Buf_Type=%d size=%d\n",
colp, colp->GetResultType(), n);
#if0 /*********************************************************************/ /* Get the starting information for progress. */ /*********************************************************************/
dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
snprintf((char*)dup->Step, 128, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
dup->ProgMax = Tdbp->GetProgMax(g);
dup->ProgCur = 0; #endif// 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; rc != RC_EF; i++) { #if0 if (!dup->Step) {
strcpy(g->Message, MSG(QUERY_CANCELLED)); throw99;
} // endif Step #endif// 0
/*******************************************************************/ /* 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: if (ApplyFilter(g, filp)) break;
// fall through case RC_NF: continue; case RC_EF: goto end_of_file; default:
snprintf(g->Message, sizeof(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. */ /*******************************************************************/ if (nkey == n) {
snprintf(g->Message, sizeof(g->Message), MSG(TOO_MANY_KEYS), nkey); returntrue;
} else
To_Rec[nkey] = Tdbp->GetRecpos();
if (trace(2))
htrc("Make: To_Rec[%d]=%d\n", nkey, To_Rec[nkey]);
/*******************************************************************/ /* 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 = kcp->Colp;
if (!colp->GetStatus(BUF_READ))
colp->ReadColumn(g); else
colp->Reset();
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);
// We must separate keys and added columns before sorting
addcolp = To_LastCol->Next;
To_LastCol->Next = NULL;
// Call the sort program, it returns the number of distinct values if ((Ndif = Qsort(g, Num_K)) < 0) goto err; // Error during sort
if (trace(1))
htrc("Make: Nk=%d n=%d Num_K=%d Ndif=%d addcolp=%p BlkFil=%p X=%p\n",
Nk, n, Num_K, Ndif, addcolp, Tdbp->To_BlkFil, X);
// Check whether the unique index is unique indeed if (!Mul)
{ if (Ndif < Num_K) {
strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
brc = true; goto err;
} else
PlgDBfree(Offset); // Not used anymore
}
// Restore kcp list
To_LastCol->Next = addcolp;
// 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]);
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. */ /* Not true for DBF tables because of eventual soft deleted lines. */ /* Note: for Num_K = 1 any non null value is Ok. */ /*********************************************************************/ if (Srtd && !filp && Tdbp->Ftype != RECFM_VAR && Tdbp->Ftype != RECFM_CSV
&& Tdbp->Txfp->GetAmType() != TYPE_AM_DBF) {
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 xindex so it has not to be recalculated. */ /*********************************************************************/ if (X) { if (SaveIndex(g, sxp))
brc = true;
} else { // Dynamic index // Indicate that key column values can be found from KEYCOL's for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
kcp->Colp->SetKcol(kcp);
Tdbp->SetFilter(NULL); // Not used anymore
} // endif X
err: // We don't need the index anymore if (X || brc)
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 = MY_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 __attribute__((unused)))
{ int i, j, k, n; bool sorted = true;
PXCOL kcp; #if0
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
for (i = 0; i < Num_K; i++) { if (Pex[i] == Num_K) { // Already moved continue;
} elseif (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)
{
PCSZ 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);
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_CSV: ftype = ".cnx"; break; case RECFM_DBF: ftype = ".dbx"; break; default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Tdbp->Ftype); returntrue;
} // endswitch Ftype
if ((sep = defp->GetBoolCatInfo("SepIndex", false))) { // Index is saved in a separate file #ifdefined(_WIN32) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME];
if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
printf("%s\n", g->Message); returntrue;
} // endif Open
if (!Ndif) goto end; // Void index
/*********************************************************************/ /* Write the index values on the index file. */ /*********************************************************************/
n[0] = ID + MAX_INDX; // 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;
n[7] = Srtd ? 1 : 0; // Values are sorted in the file
if (n[1])
size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
// dup->ProgCur += 5;
} // endfor kcp
if (trace(1))
htrc("Index %s saved, Size=%d\n", Xdp->GetName(), size);
end:
X->Close(fn, id); return rc;
} // end of SaveIndex
/***********************************************************************/ /* Init: Open and Initialize a Key Index. */ /***********************************************************************/ bool XINDEX::Init(PGLOBAL g)
{ #ifdefined(XMAP) if (xmap) return MapInit(g); #endif// XMAP
/*********************************************************************/ /* Table will be accessed through an index table. */ /* If sorting is required, this will be done later. */ /*********************************************************************/
PCSZ 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 || Tdbp->Txfp->GetAmType() == TYPE_AM_ZIP);
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)); returntrue; // 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_CSV: ftype = ".cnx"; break; case RECFM_DBF: ftype = ".dbx"; break; default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Tdbp->Ftype); returntrue;
} // endswitch Ftype
if (defp->SepIndex()) { // Index was saved in a separate file #ifdefined(_WIN32) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME];
if (trace(1))
htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/ /* 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 - 1, sizeof(int))) goto err;
if (nv[0] >= MAX_INDX) { // New index format if (X->Read(g, nv + 7, 1, sizeof(int))) goto err;
// 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) {
snprintf(g->Message, sizeof(g->Message), MSG(BAD_INDEX_FILE), fn);
if (trace(1))
htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
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 (!Incr) { /*******************************************************************/ /* Allocate the storage that will contain the file positions. */ /*******************************************************************/
Record.Size = Num_K * sizeof(int);
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 (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;
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; returnfalse;
err:
Close(); returntrue;
} // end of Init
#ifdefined(XMAP) /***********************************************************************/ /* Init: Open and Initialize a Key Index. */ /***********************************************************************/ bool XINDEX::MapInit(PGLOBAL g)
{ /*********************************************************************/ /* Table will be accessed through an index table. */ /* If sorting is required, this will be done later. */ /*********************************************************************/ constchar *ftype;
BYTE *mbase; char fn[_MAX_PATH]; int *nv, nv0, k, n, id = -1; bool estim;
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)); returntrue; // 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_CSV: ftype = ".cnx"; break; case RECFM_DBF: ftype = ".dbx"; break; default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Tdbp->Ftype); returntrue;
} // endswitch Ftype
if (defp->SepIndex()) { // Index was save in a separate file #ifdefined(_WIN32) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME];
if (trace(1))
htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/ /* 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].v.Low;
} // endif id
// Now start the mapping process.
nv = (int*)mbase;
// The test on ID was suppressed because MariaDB can change an index ID // when other indexes are added or deleted if (/*nv0 != ID ||*/ nv[1] != Nk) { // Not this index
snprintf(g->Message, sizeof(g->Message), MSG(BAD_INDEX_FILE), fn);
if (trace(1))
htrc("nv0=%d ID=%d nv[1]=%d Nk=%d\n", nv0, ID, nv[1], Nk);
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 (!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;
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; returnfalse;
err:
Close(); returntrue;
} // end of MapInit #endif// XMAP
/***********************************************************************/ /* Get Ndif and Num_K from the index file. */ /***********************************************************************/ bool XINDEX::GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk)
{
PCSZ ftype; char fn[_MAX_PATH]; int nv[NZ], id = -1; // n //bool estim = false; bool rc = true;
PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
// ndif = numk = 0;
numk = 0;
#if0 /*********************************************************************/ /* 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)); returntrue; // Error
} // endif Nk #endif// 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_CSV: ftype = ".cnx"; break; case RECFM_DBF: ftype = ".dbx"; break; default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Tdbp->Ftype); returntrue;
} // endswitch Ftype
if (defp->SepIndex()) { // Index was saved in a separate file #ifdefined(_WIN32) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME];
if (trace(1))
htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/ /* 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 (trace(1))
htrc("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
// 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) {
snprintf(g->Message, sizeof(g->Message), MSG(BAD_INDEX_FILE), fn);
if (trace(1))
htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
goto err;
} // endif
#if0 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
err:
X->Close(); return rc;
} // 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;
/*********************************************************************/ /* 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()) < 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)
{ #ifdefined(_DEBUG)
assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif); #endif// _DEBUG
/***********************************************************************/ /* 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 returntrue;
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
returnfalse;
} // 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) returntrue; 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;
// 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: Find Cur_K and Val_K's of previous index entry. */ /* Returns false if Ok, true if there are no more values. */ /***********************************************************************/ bool XINDEX::PrevVal(void)
{ int n, neq = Nk + 1, curk;
PXCOL kcp;
if (Cur_K == 0) returntrue; 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])
neq = n;
// 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
returnfalse;
} // end of PrevVal
/***********************************************************************/ /* 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
if (trace(2))
htrc("XINDEX Fetch: Op=%d\n", Op);
/*********************************************************************/ /* 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 (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; case OP_LAST: // Read last key for (Cur_K = Num_K - 1, kp = To_KeyCol; kp; kp = kp->Next)
kp->Val_K = kp->Kblp->GetNval() - 1;
Op = OP_NEXT; break; case OP_PREV: // Read previous if (PrevVal()) return -1; // End of indexed file
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 (trace(2))
htrc("Fetch: Looking for new value Nth=%d\n", Nth);
Cur_K = FastFind();
if (Cur_K >= Num_K) /*************************************************************/ /* Rank not whithin index table, signal record not found. */ /*************************************************************/ return -2;
elseif (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(void)
{ int curk, sup, inf, i= 0, k, n = 2;
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;
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
/***********************************************************************/ /* XINDXS compare routine for C Quick/Insertion sort. */ /***********************************************************************/ int XINDXS::Qcompare(int *i1, int *i2)
{ //num_comp++; 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;
// In case single column index doesn't exist return if (!kp) return0;
switch (limit) { case1: Op = (incl) ? OP_GE : OP_GT; break; case2: 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();
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)
{ #ifdefined(_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 previous index value. */ /* Returns false if Ok, true if there are no more values. */ /***********************************************************************/ bool XINDXS::PrevVal(void)
{ if (--Cur_K < 0) returntrue;
if (Mul) { if (Cur_K < Pof[To_KeyCol->Val_K])
To_KeyCol->Val_K--;
} else
To_KeyCol->Val_K = Cur_K;
returnfalse;
} // end of PrevVal
/***********************************************************************/ /* 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;
/***********************************************************************/ /* XINDXS: Fetch a physical or logical record. */ /***********************************************************************/ int XINDXS::Fetch(PGLOBAL g)
{ if (Num_K == 0) return -1; // means end of file
if (trace(2))
htrc("XINDXS Fetch: Op=%d\n", Op);
/*********************************************************************/ /* 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 (!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; case OP_LAST: // Read first
Cur_K = Num_K - 1;
To_KeyCol->Val_K = Ndif - 1;
Op = OP_PREV; break; case OP_PREV: // Read previous if (PrevVal()) return -1; // End of indexed file
break; default: // Should be 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 (trace(2))
htrc("Fetch: Looking for new value Nth=%d\n", Nth);
Cur_K = FastFind();
if (Cur_K >= Num_K) // Rank not whithin index table, signal record not found return -2; elseif (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(void)
{ int sup, inf, i= 0, n = 2;
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;
/***********************************************************************/ /* Xopen function: opens a file using native API's. */ /***********************************************************************/ bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
{
PCSZ 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:
snprintf(g->Message, sizeof(g->Message), MSG(BAD_FUNC_MODE), "Xopen", mode); returntrue;
} // endswitch mode
if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) { if (trace(1))
htrc("Open: %s\n", g->Message);
returntrue;
} // endif Xfile
if (mode == MODE_INSERT) { /*******************************************************************/ /* Position the cursor at end of file so ftell returns file size. */ /*******************************************************************/ if (fseek(Xfile, 0, SEEK_END)) {
snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERRNO), errno, "Xseek"); returntrue;
} // endif
NewOff.v.Low = (int)ftell(Xfile);
if (trace(1))
htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
} elseif (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.v.Low = (int)ftell(Xfile);
if (trace(1))
htrc("XFILE Open: NewOff.v.Low=%d\n", NewOff.v.Low);
} // endif id
} elseif (mode == MODE_READ && id >= 0) { // Get offset from the header if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
snprintf(g->Message, sizeof(g->Message), MSG(XFILE_READERR), errno); returntrue;
} // endif MAX_INDX
if (trace(1))
htrc("XFILE Open: noff[%d].v.Low=%d\n", id, noff[id].v.Low);
// Position the cursor at the offset of this index if (fseek(Xfile, noff[id].v.Low, SEEK_SET)) {
snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERRNO), errno, "Xseek"); returntrue;
} // endif
} // endif mode
returnfalse;
} // end of Open
/***********************************************************************/ /* Move into an index file. */ /***********************************************************************/ bool XFILE::Seek(PGLOBAL g, int low, int high __attribute__((unused)), int origin)
{ #ifdefined(_DEBUG)
assert(high == 0); #endif// !_DEBUG
/***********************************************************************/ /* 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) {
snprintf(g->Message, sizeof(g->Message), MSG(XFILE_READERR), errno); returntrue;
} // endif size
returnfalse;
} // 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);
/***********************************************************************/ /* 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 (mode == MODE_INSERT) { /*******************************************************************/ /* In Insert mode we must position the cursor at end of file. */ /*******************************************************************/
rc = SetFilePointer(Hfile, 0, &high, FILE_END);
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))) {
snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERRNO), errno, "Seek"); returntrue;
} // endif
if (trace(1))
htrc("INSERT: NewOff=%lld\n", NewOff.Val);
} elseif (mode == MODE_WRITE) { if (id >= 0) { // New not sep index file. Write the header.
memset(noff, 0, sizeof(noff));
NewOff.v.Low = write(Hfile, &noff, sizeof(noff));
} // endif id
if (trace(1))
htrc("WRITE: NewOff=%lld\n", NewOff.Val);
} elseif (mode == MODE_READ && id >= 0) { // Get offset from the header if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), "Index file", strerror(errno)); returntrue;
} // endif read
if (trace(1))
htrc("noff[%d]=%lld\n", id, noff[id].Val);
// Position the cursor at the offset of this index if (lseek64(Hfile, noff[id].Val, SEEK_SET) < 0) {
snprintf(g->Message, sizeof(g->Message), "(XHUGE)lseek64: %s (%lld)", strerror(errno), noff[id].Val);
printf("%s\n", g->Message); // snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERRNO), errno, "Hseek"); returntrue;
} // endif lseek64
} // endif mode #endif// UNIX
returnfalse;
} // end of Open
/***********************************************************************/ /* Go to position in a huge file. */ /***********************************************************************/ bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
{ #ifdefined(_WIN32) LONG hi = high;
DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
if (trace(1))
htrc("Seek: low=%d high=%d\n", low, high); #endif// UNIX
returnfalse;
} // end of Seek
/***********************************************************************/ /* Read from a huge index file. */ /***********************************************************************/ bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
{ bool rc = false;
/***********************************************************************/ /* Write on a huge index file. */ /***********************************************************************/ int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
{ #ifdefined(_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();
/***********************************************************************/ /* Update the file header and close the index file. */ /***********************************************************************/ void XHUGE::Close(char *fn, int id)
{ if (trace(1))
htrc("XHUGE::Close: fn=%s id=%d NewOff=%lld\n", fn, id, NewOff.Val);
#ifdefined(_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;
#ifdefined(XMAP) /***********************************************************************/ /* Don't know whether this is possible for huge files. */ /***********************************************************************/ void *XHUGE::FileView(PGLOBAL g, char *)
{
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)
{
Srtd = true;
Tdbp = tdbp;
Valp = NULL;
} // end of XXROW constructor
/***********************************************************************/ /* XXROW Reset: re-initialize a Kindex block. */ /***********************************************************************/ void XXROW::Reset(void)
{ #ifdefined(_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) returntrue;
if ((Num_K = Tbxp->Cardinality(g)) < 0) returntrue; // Not a fixed file
/*********************************************************************/ /* The entire table is indexed, no need to construct the index. */ /*********************************************************************/
Cur_K = Num_K; returnfalse;
} // end of Init
/***********************************************************************/ /* RANGE: Tell how many record exist in a given value range. */ /***********************************************************************/ int XXROW::Range(PGLOBAL, int limit, bool incl)
{ int n = Valp->GetIntValue();
switch (limit) { case1: n += ((incl) ? 0 : 1); break; case2: 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)
{ 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();
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(void)
{ int n = Valp->GetIntValue();
/***********************************************************************/ /* 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(); bool un = colp->IsUnsigned();
// Currently no indexing on NULL columns if (colp->IsNullable() && kln) {
snprintf(g->Message, sizeof(g->Message), "Cannot index nullable column %s", colp->GetName()); returntrue;
} // endif nullable
if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
len = kln;
Prefix = true;
} // endif kln
if (trace(1))
htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n", this, colp->GetName(), n, colp->GetResultType(), sm);
// Allocate the Value object used when moving items
Type = colp->GetResultType();
if (!(Valp = AllocateValue(g, Type, len, prec, un))) returntrue;
// 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, un);
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
Colp = colp; returnfalse;
} // end of Init
#ifdefined(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->GetScale(); bool un = colp->IsUnsigned();
// 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, !Prefix, true, un);
Ndf = n[0]; //IsSorted = colp->GetOpt() < 0;
IsSorted = false;
Colp = colp; 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
} elseif (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 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);
returnfalse;
} // end of MakeBlockArray
/***********************************************************************/ /* KXYCOL SetValue: read column value for nth array element. */ /***********************************************************************/ void KXYCOL::SetValue(PCOL colp, int i)
{ #ifdefined(_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) returntrue;
#if0 /***********************************************************************/ /* InitBinFind: initialize Value to the value pointed by vp. */ /***********************************************************************/ void KXYCOL::InitBinFind(void *vp)
{
Valp->SetBinValue(vp);
} // end of InitBinFind #endif// 0
/***********************************************************************/ /* 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. int k = Kblp->CompVal(i1, i2);
if (trace(4))
htrc("Compare done result=%d\n", k);
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. if (trace(4)) { int k = (int)Kblp->CompVal(Valp, (int)i);
/***********************************************************************/ /* 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
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.48 Sekunden
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.