/************* TabDos C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABDOS */ /* ------------- */ /* Version 4.9.5 */ /* */ /* COPYRIGHT: */ /* ---------- */ /* (C) Copyright to the author Olivier BERTRAND 1998-2020 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ /* This program are the DOS tables classes. */ /* */ /***********************************************************************/
/***********************************************************************/ /* Include relevant sections of the System header files. */ /***********************************************************************/ #include"my_global.h" #ifdefined(_WIN32) #include <io.h> #include <sys\timeb.h> // For testing only #include <fcntl.h> #include <errno.h> #ifdefined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif// __BORLANDC__ //#include <windows.h> #else// !_WIN32 #ifdefined(UNIX) #include <errno.h> #include <unistd.h> #else// !UNIX #include <io.h> #endif// !UNIX #include <fcntl.h> #endif// !_WIN32
/***********************************************************************/ /* Include application header files: */ /* global.h is header containing all global declarations. */ /* plgdbsem.h is header containing the DB application declarations. */ /* filamtxt.h is header containing the file AM classes declarations. */ /***********************************************************************/ #include"global.h" #include"osutil.h" #include"plgdbsem.h" //#include "catalog.h" #include"mycat.h" #include"xindex.h" #include"filamap.h" #include"filamfix.h" #include"filamdbf.h" #ifdefined(GZ_SUPPORT) #include"filamgz.h" #endif// GZ_SUPPORT #ifdefined(ZIP_SUPPORT) #include"filamzip.h" #endif// ZIP_SUPPORT #include"tabdos.h" #include"tabfix.h" #include"tabmul.h" #include"array.h" #include"blkfil.h" #include"m_string.h"
/***********************************************************************/ /* DB static variables. */ /***********************************************************************/ int num_read, num_there, num_eq[2]; // Statistics
// Ignore wrong Index definitions for catalog commands
SetIndexInfo(); returnfalse;
} // end of DefineAM
/***********************************************************************/ /* Get the full path/name of the optimization file. */ /***********************************************************************/ bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename)
{
PCSZ ftype;
switch (Recfm) { case RECFM_VAR: ftype = ".dop"; break; case RECFM_FIX: ftype = ".fop"; break; case RECFM_BIN: ftype = ".bop"; break; case RECFM_VCT: ftype = ".vop"; break; case RECFM_CSV: ftype = ".cop"; break; case RECFM_DBF: ftype = ".dbp"; break; default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Recfm); returntrue;
} // endswitch Ftype
PlugSetPath(filename, Ofn, GetPath());
strcat(PlugRemoveType(filename, filename), ftype); returnfalse;
} // end of GetOptFileName
/***********************************************************************/ /* After an optimize error occurred, remove all set optimize values. */ /***********************************************************************/ void DOSDEF::RemoveOptValues(PGLOBAL g)
{ char filename[_MAX_PATH];
PCOLDEF cdp;
// Delete settings of optimized columns for (cdp = To_Cols; cdp; cdp = cdp->GetNext()) if (cdp->GetOpt()) {
cdp->SetMin(NULL);
cdp->SetMax(NULL);
cdp->SetNdv(0);
cdp->SetNbm(0);
cdp->SetDval(NULL);
cdp->SetBmap(NULL);
} // endif Opt
// Delete block position setting for not fixed tables
To_Pos = NULL;
AllocBlks = 0;
// Delete any eventually ill formed non matching optimization file if (!GetOptFileName(g, filename)) #ifdefined(_WIN32)
DeleteFile(filename); #else// UNIX
remove(filename); #endif// _WIN32
switch (Recfm) { 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(BAD_RECFM_VAL), Recfm); returntrue;
} // endswitch Ftype
/*********************************************************************/ /* Check for existence of an index file. */ /*********************************************************************/ if (sep) { // Indexes are save in separate files #ifdefined(_WIN32) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME]; bool all = !pxdf;
} else { // !sep // Drop all indexes, delete the common file
PlugSetPath(filename, Ofn, GetPath());
safe_strcat(PlugRemoveType(filename, filename), sizeof(filename), ftype); #ifdefined(_WIN32) if (!DeleteFile(filename))
rc = (GetLastError() != ERROR_FILE_NOT_FOUND); #else// UNIX if (remove(filename))
rc = (errno != ENOENT); #endif// UNIX
} // endif sep
if (rc)
snprintf(g->Message, sizeof(g->Message), MSG(DEL_FILE_ERR), filename);
return rc; // Return true if error
} // end of DeleteIndexFile
/***********************************************************************/ /* InvalidateIndex: mark all indexes as invalid. */ /***********************************************************************/ bool DOSDEF::InvalidateIndex(PGLOBAL)
{ if (To_Indx) for (PIXDEF xp = To_Indx; xp; xp = xp->Next)
xp->Invalid = true;
// Txfp must be set even for not multiple tables because // it is needed when calling Cardinality in GetBlockValues.
tdbp = new(g) TDBDOS(this, txfp);
} // endif Recfm
if (Multiple)
tdbp = new(g) TDBMUL(tdbp); else /*******************************************************************/ /* For block tables, get eventually saved optimization values. */ /*******************************************************************/ if (tdbp->GetBlockValues(g)) {
PushWarning(g, tdbp); // return NULL; // causes a crash when deleting index
} elseif (Recfm == RECFM_VAR || Compressed > 1) { if (IsOptimized()) { if (map) {
txfp = new(g) MBKFAM(this);
} elseif (Compressed) { #ifdefined(GZ_SUPPORT) if (Compressed == 1)
txfp = new(g) ZBKFAM(this); else {
txfp->SetBlkPos(To_Pos);
((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL);
} // endelse #else
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; #endif
} else
txfp = new(g) BLKFAM(this);
/* ------------------------ Class TDBDOS ----------------------------- */
/***********************************************************************/ /* Implementation of the TDBDOS class. This is the common class that */ /* contain all that is common between the TDBDOS and TDBMAP classes. */ /***********************************************************************/
TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
{ if ((Txfp = txfp))
Txfp->SetTdbp(this);
if (Txfp->To_File)
fprintf(f, "%s File: %s\n", m, Txfp->To_File);
} // end of PrintAM
/***********************************************************************/ /* Remake the indexes after the table was modified. */ /***********************************************************************/ int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{ int prc = RC_OK, rc = RC_OK;
if (!GetFileLength(g)) { // Void table, delete all opt and index files
PDOSDEF defp = (PDOSDEF)To_Def;
MaxSize = -1; // Size must be recalculated
Cardinal = -1; // as well as Cardinality
To_Filter = NULL; // Disable filtering //To_BlkIdx = NULL; // and index filtering
To_BlkFil = NULL; // and block filtering
// After the table was modified the indexes // are invalid and we should mark them as such...
(void)((PDOSDEF)To_Def)->InvalidateIndex(g);
if (dop) {
Columns = NULL; // Not used anymore
if (Txfp->Blocked) { // MakeBlockValues must be executed in non blocked mode // except for ZLIB access method. if (Txfp->GetAmType() == TYPE_AM_MAP) {
Txfp = new(g) MAPFAM((PDOSDEF)To_Def); #ifdefined(GZ_SUPPORT)
} elseif (Txfp->GetAmType() == TYPE_AM_GZ) {
Txfp = new(g) GZFAM((PDOSDEF)To_Def);
} elseif (Txfp->GetAmType() == TYPE_AM_ZLIB) {
Txfp->Reset();
((PZLBFAM)Txfp)->SetOptimized(false); #endif// GZ_SUPPORT
} elseif (Txfp->GetAmType() == TYPE_AM_BLK)
Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
} else
Txfp->Reset();
Use = USE_READY; // So the table can be reopened
Mode = MODE_ANY; // Just to be clean
rc = MakeBlockValues(g); // Redo optimization
} // endif dop
if (dox && (rc == RC_OK || rc == RC_INFO)) { // Remake eventual indexes // if (Mode != MODE_UPDATE)
To_SetCols = NULL; // Positions are changed
Columns = NULL; // Not used anymore
Txfp->Reset(); // New start
Use = USE_READY; // So the table can be reopened
Mode = MODE_READ; // New mode
prc = rc;
if (PlgGetUser(g)->Check & CHK_OPT) // We must remake all indexes.
rc = MakeIndex(g, NULL, false);
rc = (rc == RC_INFO) ? prc : rc;
} // endif dox
return rc;
} // end of ResetTableOpt
/***********************************************************************/ /* Calculate the block sizes so block I/O can be used and also the */ /* Min/Max values for clustered/sorted table columns. */ /***********************************************************************/ int TDBDOS::MakeBlockValues(PGLOBAL g)
{ int i, lg, nrec, rc, n = 0; int curnum, curblk, block, savndv, savnbm; void *savmin, *savmax; bool blocked, xdb2 = false; //POOLHEADER save;
PCOLDEF cdp;
PDOSDEF defp = (PDOSDEF)To_Def;
PDOSCOL colp = NULL;
PDBUSER dup = PlgGetUser(g); //void *memp = cat->GetDescp();
(void) defp->GetCat(); // XXX Should be removed?
if ((nrec = defp->GetElemt()) < 2) { if (!To_Def->Partitioned()) { // This may be wrong to do in some cases
safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT)); return RC_INFO; // Not to be optimized
} else return RC_OK;
} elseif (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) { // Suppress the opt file firstly if the table is void, // secondly when it was modified with OPTIMIZATION unchecked // because it is no more valid.
defp->RemoveOptValues(g); // Erase opt file return RC_OK; // void table
} elseif (MaxSize < 0) return RC_FX;
defp->SetOptimized(0);
// Estimate the number of needed blocks if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) { // This may be wrong to do in some cases
defp->RemoveOptValues(g);
safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT)); return RC_INFO; // Not to be optimized
} // endif block
// We have to use local variables because Txfp->CurBlk is set // to Rows+1 by unblocked variable length table access methods.
curblk = -1;
curnum = nrec - 1; //last = 0;
Txfp->Block = block; // This is useful mainly for
Txfp->CurBlk = curblk; // blocked tables (ZLBFAM), for
Txfp->CurNum = curnum; // others it is just to be clean.
/*********************************************************************/ /* Allocate the array of block starting positions. */ /*********************************************************************/ //if (memp) // save = *(PPOOLHEADER)memp;
/*********************************************************************/ /* Allocate the blocks for clustered columns. */ /*********************************************************************/
blocked = Txfp->Blocked; // Save
Txfp->Blocked = true; // So column block can be allocated
for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) if (cdp->GetOpt()) {
lg = cdp->GetClen();
if (trace(1))
htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n",
cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg);
// colp will be initialized with proper Dval VALBLK
colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
colp->InitValue(g); // Allocate column value buffer
cdp->SetNbm(savnbm); // cdp->SetBmap(savmin); // Can be reused if the new size
cdp->SetDval(savmax); // is not greater than this one.
cdp->SetNdv(savndv);
} else {
cdp->SetXdb2(false); // Maxbmp may have been reset
savmin = cdp->GetMin();
savmax = cdp->GetMax();
cdp->SetMin(PlugSubAlloc(g, NULL, block * lg));
cdp->SetMax(PlugSubAlloc(g, NULL, block * lg));
// Valgrind complains if there are uninitialised bytes // after the null character ending if (IsTypeChar(cdp->GetType())) {
memset(cdp->GetMin(), 0, block * lg);
memset(cdp->GetMax(), 0, block * lg);
} // endif Type
if (trace(1))
htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n",
cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg);
// colp will be initialized with proper opt VALBLK's
colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
colp->InitValue(g); // Allocate column value buffer
cdp->SetMin(savmin); // Can be reused if the number
cdp->SetMax(savmax); // of blocks does not change.
} // endif Freq
} // endif Clustered
// No optimised columns. Still useful for blocked variable tables. if (!colp && defp->Recfm != RECFM_VAR) {
safe_strcpy(g->Message, sizeof(g->Message), "No optimised columns"); return RC_INFO;
} // endif colp
Txfp->Blocked = blocked;
/*********************************************************************/ /* Now do calculate the optimization values. */ /*********************************************************************/
Mode = MODE_READ;
if (OpenDB(g)) return RC_FX;
if (xdb2) { /*********************************************************************/ /* Retrieve the distinct values of XDB2 columns. */ /*********************************************************************/ if (GetDistinctColumnValues(g, nrec)) return RC_FX;
OpenDB(g); // Rewind the table file
} // endif xdb2
/*********************************************************************/ /* Make block starting pos and min/max values of cluster columns. */ /*********************************************************************/ while ((rc = ReadDB(g)) == RC_OK) { if (blocked) { // A blocked FAM class handles CurNum and CurBlk (ZLBFAM) if (!Txfp->CurNum)
Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos();
// Get block starting position
Txfp->BlkPos[curblk] = Txfp->GetPos();
} // endif CurNum
// last = curnum + 1; // curnum is zero based
Txfp->CurBlk = curblk; // Used in COLDOS::SetMinMax
Txfp->CurNum = curnum; // Used in COLDOS::SetMinMax
} // endif blocked
/*******************************************************************/ /* Now calculate the min and max values for the cluster columns. */ /*******************************************************************/ for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext()) if (colp->Clustered == 2) { if (colp->SetBitMap(g)) goto err;
} else if (colp->SetMinMax(g)) goto err; // Currently: column is not sorted
defp->RemoveOptValues(g);
CloseDB(g); return RC_FX;
} // end of MakeBlockValues
/***********************************************************************/ /* Save the block and Min/Max 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 TDBDOS::SaveBlockValues(PGLOBAL g)
{ char filename[_MAX_PATH]; int lg, n[NZ + 2];
size_t nbk, ndv, nbm, block = Txfp->Block; bool rc = false;
FILE *opfile;
PDOSCOL colp;
PDOSDEF defp = (PDOSDEF)To_Def;
if (defp->GetOptFileName(g, filename)) returntrue;
/*********************************************************************/ /* Write the Min/Max values into the opt file. */ /*********************************************************************/ for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) {
lg = colp->Value->GetClen();
// Now start the writing process if (colp->Clustered == 2) { // New XDB2 block optimization. Will be recognized when reading // because the column index is negated.
ndv = colp->Ndv; nbm = colp->Nbm;
nbk = nbm * block;
n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
n[4] = ndv; n[5] = nbm;
fclose(opfile); return rc;
} // end of SaveBlockValues
/***********************************************************************/ /* Read the Min/Max 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 TDBDOS::GetBlockValues(PGLOBAL g)
{ char filename[_MAX_PATH]; int i, lg, n[NZ]; int nrec, block = 0, last = 0; int len; bool newblk = false;
size_t ndv, nbm, nbk, blk;
FILE *opfile;
PCOLDEF cdp;
PDOSDEF defp = (PDOSDEF)To_Def;
PDBUSER dup = PlgGetUser(g);
if (defp->Optimized || !(dup->Check & CHK_OPT)) returnfalse; // Already done or to be redone
if (Ftype == RECFM_VAR || defp->Compressed == 2) { /*******************************************************************/ /* Variable length file that can be read by block. */ /*******************************************************************/
nrec = (defp->GetElemt()) ? defp->GetElemt() : 1;
if (nrec > 1) { // The table can be declared optimized if it is void. // This is useful to handle Insert in optimized mode. char filename[_MAX_PATH]; int h; int flen = -1;
if (!flen) {
defp->SetOptimized(1); returnfalse;
} // endif flen
} else returnfalse; // Not optimisable
cdp = defp->GetCols();
i = 1;
} else { /*******************************************************************/ /* Fixed length file. Opt file exists only for clustered columns. */ /*******************************************************************/ // Check for existence of clustered columns for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) if (cdp->GetOpt()) break;
if (!cdp) returnfalse; // No optimization needed
if ((len = Cardinality(g)) < 0) returntrue; // Table error elseif (!len) returnfalse; // File does not exist yet
block = Txfp->Block; // Was set in Cardinality
nrec = Txfp->Nrec;
} // endif Ftype
if (defp->GetOptFileName(g, filename)) returntrue;
if (!(opfile = fopen(filename, "rb"))) returnfalse; // No saved values
if (Ftype == RECFM_VAR || defp->Compressed == 2) { /*******************************************************************/ /* Read block starting positions from the opt file. */ /*******************************************************************/
lg = sizeof(int);
/*********************************************************************/ /* Read the Min/Max values from the opt file. */ /*********************************************************************/ for (; cdp; cdp = cdp->GetNext(), i++) if (cdp->GetOpt()) {
lg = cdp->GetClen();
blk = block;
// Now start the reading process. if (fread(n, sizeof(int), NZ, opfile) != NZ) {
snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_RD_ERR), strerror(errno)); goto err;
} // endif size
if (n[0] == -i) { // Read the XDB2 opt values from the opt file if (n[1] != lg || n[2] != nrec || n[3] != block) {
snprintf(g->Message, sizeof(g->Message), MSG(OPT_NOT_MATCH), filename); goto err;
} // endif
defp->SetBlock(block);
defp->Last = last; // For Cardinality
defp->SetAllocBlks(block);
defp->SetOptimized(1);
fclose(opfile);
MaxSize = -1; // Can be refined later returnfalse;
err:
defp->RemoveOptValues(g);
fclose(opfile);
// Ignore error if not in mode CHK_OPT return (PlgGetUser(g)->Check & CHK_OPT) != 0;
} // end of GetBlockValues
/***********************************************************************/ /* This fonction is used while making XDB2 block optimization. */ /* It constructs for each elligible columns, the sorted list of the */ /* distinct values existing in the column. This function uses an */ /* algorithm that permit to get several sets of distinct values by */ /* reading the table only once, which cannot be done using a standard */ /* SQL query. */ /***********************************************************************/ bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
{ char *p; int rc, blk, n = 0;
PDOSCOL colp;
PDBUSER dup = PlgGetUser(g);
while ((rc = ReadDB(g)) == RC_OK) { for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) if (colp->Clustered == 2) if (colp->AddDistinctValue(g)) returntrue; // Too many distinct values
/***********************************************************************/ /* Analyze the filter and construct the Block Evaluation Filter. */ /* This is possible when a filter contains predicates implying a */ /* column marked as "clustered" or "sorted" matched to a constant */ /* argument. It is then possible by comparison against the smallest */ /* and largest column values in each block to determine whether the */ /* filter condition will be always true or always false for the block.*/ /***********************************************************************/
PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp)
{ bool blk = Txfp->Blocked;
if (To_BlkFil) return To_BlkFil; // Already done elseif (!filp) return NULL; elseif (blk) { if (Txfp->GetAmType() == TYPE_AM_DBF) /*****************************************************************/ /* If RowID is used in this query, block optimization cannot be */ /* used because currently the file must be read sequentially. */ /*****************************************************************/ for (PCOL cp = Columns; cp; cp = cp->GetNext()) if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm()) return NULL;
} // endif blk
int i, op = filp->GetOpc(), opm = filp->GetOpm(); bool cnv[2];
PCOL colp;
PXOB arg[2] = {NULL,NULL};
PBF *fp = NULL, bfp = NULL;
switch (op) { case OP_EQ: case OP_NE: case OP_GT: case OP_GE: case OP_LT: case OP_LE: if (! opm) { for (i = 0; i < 2; i++) {
arg[i] = filp->Arg(i);
cnv[i] = filp->Conv(i);
} // endfor i
// if opm, pass thru // fall through case OP_IN: if (filp->GetArgType(0) == TYPE_COLBLK &&
filp->GetArgType(1) == TYPE_ARRAY) {
arg[0] = filp->Arg(0);
arg[1] = filp->Arg(1);
colp = (PCOL)arg[0];
if (colp->GetTo_Tdb() == this) { // Block evaluation is possible for... if (colp->GetAmType() == TYPE_AM_ROWID) { // Special column ROWID and constant array, but // currently we don't know how to retrieve a RowID // from a DBF table that is not sequentially read. // if (Txfp->GetAmType() != TYPE_AM_DBF || // ((RIDBLK*)arg[0])->GetRnm())
bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec);
break; case OP_LIKE: default: break;
} // endswitch op
return bfp;
} // end of InitBlockFilter
/***********************************************************************/ /* Analyze the passed arguments and construct the Block Filter. */ /***********************************************************************/
PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv)
{ //int i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; //bool conv = false, xdb2 = false, ok = false, b[2]; //PXOB *xarg1, *xarg2 = NULL, xp[2]; int i, n = 0, type[2] = {0,0}; bool conv = false, xdb2 = false;
PXOB xp[2];
PCOL colp;
PBF bfp = NULL;
for (i = 0; i < 2; i++) { switch (arg[i]->GetType()) { case TYPE_CONST:
type[i] = 1; // ctype = arg[i]->GetResultType(); break; case TYPE_COLBLK:
conv = cnv[i];
colp = (PCOL)arg[i];
if (colp->GetTo_Tdb() == this) { if (colp->GetAmType() == TYPE_AM_ROWID) { // Currently we don't know how to retrieve a RowID // from a DBF table that is not sequentially read. // if (Txfp->GetAmType() != TYPE_AM_DBF || // ((RIDBLK*)arg[i])->GetRnm())
type[i] = 5;
} elseif (colp->GetColUse(U_CORREL)) { // This is a column pointing to the outer query of a // correlated subquery, it has a constant value during // each execution of the subquery.
type[i] = 1; // ctype = arg[i]->GetResultType();
} // endif this
break; // case TYPE_SCALF: // if (((PSCALF)arg[i])->GetOp() == OP_ROW) { // sfr[i] = (SFROW*)arg[i]; // type[i] = 7; // } // endif Op
// break; default: break;
} // endswitch ArgType
if (!type[i]) break;
n += type[i];
} // endfor i
if (n == 3 || n == 6) { if (conv) { // The constant has not the good type and will not match // the block min/max values. Warn and abort.
snprintf(g->Message, sizeof(g->Message), "Block opt: %s", MSG(VALTYPE_NOMATCH));
PushWarning(g, this); return NULL;
} // endif Conv
if (type[0] == 1) { // Make it always as Column-op-Value
*xp = arg[0];
arg[0] = arg[1];
arg[1] = *xp;
switch (op) { case OP_GT: op = OP_LT; break; case OP_GE: op = OP_LE; break; case OP_LT: op = OP_GT; break; case OP_LE: op = OP_GE; break;
} // endswitch op
#if0
} elseif (n == 8 || n == 14) { if (n == 8 && ctype != TYPE_LIST) { // Should never happen
safe_strcpy(g->Message, sizeof(g->Message), "Block opt: bad constant"); throw99;
} // endif Conv
if (type[0] == 1) { // Make it always as Column-op-Value
sfr[0] = sfr[1];
arg[1] = arg[0];
switch (op) { case OP_GT: op = OP_LT; break; case OP_GE: op = OP_LE; break; case OP_LT: op = OP_GT; break; case OP_LE: op = OP_GE; break;
} // endswitch op
/***********************************************************************/ /* ResetBlkFil: reset the block filter and restore filtering, or make */ /* the block filter if To_Filter was not set when opening the table. */ /***********************************************************************/ void TDBDOS::ResetBlockFilter(PGLOBAL g)
{ if (!To_BlkFil) { if (To_Filter) if ((To_BlkFil = InitBlockFilter(g, To_Filter))) {
htrc("BlkFil=%p\n", To_BlkFil);
MaxSize = -1; // To be recalculated
} // endif To_BlkFil
return;
} // endif To_BlkFil
To_BlkFil->Reset(g);
if (SavFil && !To_Filter) { // Restore filter if it was disabled by optimization
To_Filter = SavFil;
SavFil = NULL;
} // endif
Beval = 0;
} // end of ResetBlockFilter
/***********************************************************************/ /* Block optimization: evaluate the block index filter against */ /* the min and max values of this block and return: */ /* RC_OK: if some records in the block can meet filter criteria. */ /* RC_NF: if no record in the block can meet filter criteria. */ /* RC_EF: if no record in the remaining file can meet filter criteria.*/ /* In addition, temporarily suppress filtering if all the records in */ /* the block meet filter criteria. */ /***********************************************************************/ int TDBDOS::TestBlock(PGLOBAL g)
{ int rc = RC_OK;
if (To_BlkFil && Beval != 2) { // Check for block filtering evaluation if (Beval == 1) { // Filter was removed for last block, restore it
To_Filter = SavFil;
SavFil = NULL;
} // endif Beval
// Check for valid records in new block switch (Beval = To_BlkFil->BlockEval(g)) { case -2: // No more valid values in file
rc = RC_EF; break; case -1: // No valid values in block
rc = RC_NF; break; case1: // All block values are valid case2: // All subsequent file values are Ok // Before suppressing the filter for the block(s) it is // necessary to reset the filtered columns to NOT_READ // so their new values are retrieved by the SELECT list. if (To_Filter) // Can be NULL when externally called (XDB)
To_Filter->Reset();
// Are we are called from CreateTable or CreateIndex? if (pxdf) { if (!add && dfp->GetIndx()) {
safe_strcpy(g->Message, sizeof(g->Message), MSG(INDX_EXIST_YET)); return RC_FX;
} // endif To_Indx
if (add && dfp->GetIndx()) { for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext()) if (!stricmp(sxp->GetName(), pxdf->GetName())) {
snprintf(g->Message, sizeof(g->Message), MSG(INDEX_YET_ON), pxdf->GetName(), Name); return RC_FX;
} elseif (!sxp->GetNext()) break;
sxp->SetNext(pxdf); // first = false;
} else
dfp->SetIndx(pxdf);
// pxdf->SetDef(dfp);
} elseif (!(pxdf = dfp->GetIndx())) return RC_INFO; // No index to make
try { // Allocate all columns that will be used by indexes. // This must be done before opening the table so specific // column initialization can be done (in particular by TDBVCT) for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext()) for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { if (!(colp = ColDB(g, kdp->GetName(), 0))) {
snprintf(g->Message, sizeof(g->Message), MSG(INDX_COL_NOTIN), kdp->GetName(), Name); goto err;
} elseif (colp->GetResultType() == TYPE_DECIM) {
snprintf(g->Message, sizeof(g->Message), "Decimal columns are not indexable yet"); goto err;
} // endif Type
colp->InitValue(g);
n = MY_MAX(n, xdp->GetNparts());
} // endfor kdp
/*********************************************************************/ /* Construct and save the defined indexes. */ /*********************************************************************/ for (xdp = pxdf; xdp; xdp = xdp->GetNext()) if (!OpenDB(g)) { if (xdp->IsAuto() && fixed) // Auto increment key and fixed file: use an XXROW index continue; // XXROW index doesn't need to be made
// On Update, redo only indexes that are modified
doit = !To_SetCols;
n = 0;
if (sxp)
xdp->SetID(sxp->GetID() + 1);
for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { // Check whether this column was updated for (colp = To_SetCols; !doit && colp; colp = colp->GetNext()) if (!stricmp(kdp->GetName(), colp->GetName()))
doit = true;
// Get the key column description list for (k = 0, kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) if (!(colp = ColDB(g, kdp->GetName(), 0)) || colp->InitValue(g)) {
snprintf(g->Message, sizeof(g->Message), "Wrong column %s", kdp->GetName()); returntrue;
} else
To_Key_Col[k++] = colp;
#ifdefined(_DEBUG) if (k != Knum) {
snprintf(g->Message, sizeof(g->Message), "Key part number mismatch for %s",
xdp->GetName()); return0;
} // endif k #endif// _DEBUG
// Allocate the pseudo constants that will contain the key values
To_Link = (PXOB*)PlugSubAlloc(g, NULL, Knum * sizeof(PXOB));
} catch (int n) { if (trace(1))
htrc("Exception %d: %s\n", n, g->Message);
brc = true;
} catch (constchar *msg) {
safe_strcpy(g->Message, sizeof(g->Message), msg);
brc = true;
} // end catch
return brc;
} // end of InitialyzeIndex
/***********************************************************************/ /* DOS GetProgMax: get the max value for progress information. */ /***********************************************************************/ int TDBDOS::GetProgMax(PGLOBAL g)
{ return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g);
} // end of GetProgMax
/***********************************************************************/ /* DOS GetProgCur: get the current value for progress information. */ /***********************************************************************/ int TDBDOS::GetProgCur(void)
{ return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos();
} // end of GetProgCur
/***********************************************************************/ /* RowNumber: return the ordinal number of the current row. */ /***********************************************************************/ int TDBDOS::RowNumber(PGLOBAL g, bool)
{ if (To_Kindex) { /*******************************************************************/ /* Don't know how to retrieve RowID from file address. */ /*******************************************************************/
snprintf(g->Message, sizeof(g->Message), MSG(NO_ROWID_FOR_AM),
GetAmName(g, Txfp->GetAmType())); return0;
} else return Txfp->GetRowID();
} // end of RowNumber
/***********************************************************************/ /* DOS Cardinality: returns table cardinality in number of rows. */ /* This function can be called with a null argument to test the */ /* availability of Cardinality implementation (1 yes, 0 no). */ /***********************************************************************/ int TDBDOS::Cardinality(PGLOBAL g)
{ int n = Txfp->Cardinality(NULL);
if (!g) return (Mode == MODE_ANY) ? 1 : n;
if (Cardinal < 0) { if (!Txfp->Blocked && n == 0) { // Info command, we try to return exact row number
PDOSDEF dfp = (PDOSDEF)To_Def;
PIXDEF xdp = dfp->To_Indx;
if (xdp && xdp->IsValid()) { // Cardinality can be retreived from one index
PXLOAD pxp;
if (!(kxp->GetAllSizes(g, Cardinal))) return Cardinal;
} // endif Mode
if (Mode == MODE_ANY && ExactInfo()) { // Using index impossible or failed, do it the hard way
Mode = MODE_READ;
To_Line = (char*)PlugSubAlloc(g, NULL, (size_t)Lrecl + 1);
if (Txfp->OpenTableFile(g)) return (Cardinal = Txfp->Cardinality(g));
for (Cardinal = 0; n != RC_EF;) if (!(n = Txfp->ReadBuffer(g)))
Cardinal++;
Txfp->CloseTableFile(g, false);
Mode = MODE_ANY;
} else { // Return the best estimate int len = GetFileLength(g);
if (len >= 0) { int rec;
if (trace(1))
htrc("Estimating lines len=%d ending=%d/n",
len, ((PDOSDEF)To_Def)->Ending);
/*************************************************************/ /* Estimate the number of lines in the table (if not known) */ /* by dividing the file length by the average record length. */ /*************************************************************/
rec = ((PDOSDEF)To_Def)->Ending;
if (AvgLen <= 0) // No given average estimate
rec += EstimatedLength(); else// An estimate was given for the average record length
rec += AvgLen;
Cardinal = (len + rec - 1) / rec;
if (trace(1))
htrc("avglen=%d MaxSize%d\n", rec, Cardinal);
} // endif len
} // endif Mode
} else
Cardinal = Txfp->Cardinality(g);
} // endif Cardinal
return Cardinal;
} // end of Cardinality
/***********************************************************************/ /* DOS GetMaxSize: returns file size estimate in number of lines. */ /* This function covers variable record length files. */ /***********************************************************************/ int TDBDOS::GetMaxSize(PGLOBAL g)
{ if (MaxSize >= 0) return MaxSize;
if (!Cardinality(NULL)) { int len = GetFileLength(g);
if (len >= 0) { int rec;
if (trace(1))
htrc("Estimating lines len=%d ending=%d/n",
len, ((PDOSDEF)To_Def)->Ending);
/*****************************************************************/ /* Estimate the number of lines in the table (if not known) by */ /* dividing the file length by minimum record length. */ /*****************************************************************/
rec = EstimatedLength() + ((PDOSDEF)To_Def)->Ending;
MaxSize = (len + rec - 1) / rec;
if (trace(1))
htrc("avglen=%d MaxSize%d\n", rec, MaxSize);
} // endif len
} else
MaxSize = Cardinality(g);
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/ /* DOS EstimatedLength. Returns an estimated minimum line length. */ /***********************************************************************/ int TDBDOS::EstimatedLength(void)
{ int dep = 0;
PCOLDEF cdp = To_Def->GetCols();
if (!cdp->GetNext()) { // One column table, we are going to return a ridiculous // result if we set dep to 1
dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
} elsefor (; cdp; cdp = cdp->GetNext()) if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL)))
dep = MY_MAX(dep, cdp->GetOffset());
return (int)dep;
} // end of Estimated Length
/***********************************************************************/ /* DOS tables favor the use temporary files for Update. */ /***********************************************************************/ bool TDBDOS::IsUsingTemp(PGLOBAL)
{
USETEMP utp = UseTemp();
/***********************************************************************/ /* DOS Access Method opening routine. */ /* New method now that this routine is called recursively (last table */ /* first in reverse order): index blocks are immediately linked to */ /* join block of next table if it exists or else are discarted. */ /***********************************************************************/ bool TDBDOS::OpenDB(PGLOBAL g)
{ if (trace(1))
htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n", this, Tdb_No, Use, Mode);
if (Use == USE_OPEN) { /*******************************************************************/ /* Table already open, just replace it at its beginning. */ /*******************************************************************/ if (!To_Kindex) {
Txfp->Rewind(); // see comment in Work.log
if (SkipHeader(g)) returntrue;
} else /*****************************************************************/ /* Table is to be accessed through a sorted index table. */ /*****************************************************************/
To_Kindex->Reset();
ResetBlockFilter(g); returnfalse;
} // endif use
if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS #ifdefined(BSON_SUPPORT)
&& Txfp->GetAmType() != TYPE_AM_BIN #endif// BSON_SUPPORT
&& Txfp->GetAmType() != TYPE_AM_MGO) { // Delete all lines. Not handled in MAP or block mode
Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
} elseif (Txfp->Blocked && (Mode == MODE_DELETE ||
(Mode == MODE_UPDATE && UseTemp() != TMP_NO))) { /*******************************************************************/ /* Delete is not currently handled in block mode neither Update */ /* when using a temporary file. */ /*******************************************************************/ if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE)
Txfp = new(g) MAPFAM((PDOSDEF)To_Def); #ifdefined(GZ_SUPPORT) elseif (Txfp->GetAmType() == TYPE_AM_GZ)
Txfp = new(g) GZFAM((PDOSDEF)To_Def); #endif// GZ_SUPPORT else// if (Txfp->GetAmType() != TYPE_AM_DOS) ???
Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
} // endif Mode
/*********************************************************************/ /* Open according to logical input/output mode required. */ /* Use conventionnal input/output functions. */ /* Treat files as binary in Delete mode (for line moving) */ /*********************************************************************/ if (Txfp->OpenTableFile(g)) returntrue;
Use = USE_OPEN; // Do it now in case we are recursively called
/*********************************************************************/ /* Allocate the block filter tree if evaluation is possible. */ /*********************************************************************/
To_BlkFil = InitBlockFilter(g, To_Filter);
/*********************************************************************/ /* Lrecl does not include line ending */ /*********************************************************************/
size_t linelen = Lrecl + ((PDOSDEF)To_Def)->Ending + 1;
To_Line = (char*)PlugSubAlloc(g, NULL, linelen);
if (Mode == MODE_INSERT) { // Spaces between fields must be filled with blanks
memset(To_Line, ' ', Lrecl);
To_Line[Lrecl] = '\0';
} else
memset(To_Line, 0, linelen);
if (trace(1))
htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
if (SkipHeader(g)) // When called from CSV/FMT files returntrue;
/***********************************************************************/ /* ReadDB: Data Base read routine for DOS access method. */ /***********************************************************************/ int TDBDOS::ReadDB(PGLOBAL g)
{ if (trace(2))
htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n",
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line);
if (To_Kindex) { /*******************************************************************/ /* Reading is by an index table. */ /*******************************************************************/ int recpos = To_Kindex->Fetch(g);
switch (recpos) { case -1: // End of file reached return RC_EF; case -2: // No match for join return RC_NF; case -3: // Same record as non null last one
num_there++; return RC_OK; default: /***************************************************************/ /* Set the file position according to record to read. */ /***************************************************************/ if (SetRecpos(g, recpos)) return RC_FX;
if (trace(2))
htrc("File position is now %d\n", GetRecpos());
if (Mode == MODE_READ) /*************************************************************/ /* Defer physical reading until one column setting needs it */ /* as it can be a big saving on joins where no other column */ /* than the keys are used, so reading is unnecessary. */ /*************************************************************/ if (Txfp->DeferReading()) return RC_OK;
} // endswitch recpos
} // endif To_Kindex
if (trace(2))
htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line);
/*********************************************************************/ /* Now start the reading process. */ /*********************************************************************/ return ReadBuffer(g);
} // end of ReadDB
/***********************************************************************/ /* PrepareWriting: Prepare the line to write. */ /***********************************************************************/ bool TDBDOS::PrepareWriting(PGLOBAL)
{ if (Ftype == RECFM_VAR && (Mode == MODE_INSERT || Txfp->GetUseTemp())) { char *p;
/*******************************************************************/ /* Suppress trailing blanks. */ /* Also suppress eventual null from last line. */ /*******************************************************************/ for (p = To_Line + Lrecl -1; p >= To_Line; p--) if (*p && *p != ' ') break;
*(++p) = '\0';
} // endif Mode
returnfalse;
} // end of PrepareWriting
/***********************************************************************/ /* WriteDB: Data Base write routine for DOS access method. */ /***********************************************************************/ int TDBDOS::WriteDB(PGLOBAL g)
{ if (trace(2))
htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);
// Make the line to write if (PrepareWriting(g)) return RC_FX;
if (trace(2))
htrc("Write: line is='%s'\n", To_Line);
// Now start the writing process return Txfp->WriteBuffer(g);
} // end of WriteDB
/***********************************************************************/ /* Data Base delete line routine for DOS (and FIX) access method. */ /* RC_FX means delete all. Nothing to do here (was done at open). */ /***********************************************************************/ int TDBDOS::DeleteDB(PGLOBAL g, int irc)
{ return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc);
} // end of DeleteDB
/***********************************************************************/ /* Data Base close routine for DOS access method. */ /***********************************************************************/ void TDBDOS::CloseDB(PGLOBAL g)
{ if (To_Kindex) {
To_Kindex->Close();
To_Kindex = NULL;
} // endif
Txfp->CloseTableFile(g, Abort);
RestoreNrec();
} // end of CloseDB
/***********************************************************************/ /* DOSCOL public constructor (also called by MAPCOL). */ /***********************************************************************/
DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PCSZ am)
: COLBLK(cdp, tp, i)
{ char *p; int prec = Format.Prec;
PTXF txfp = ((PTDBDOS)tp)->Txfp;
assert(cdp);
if (cp) {
Next = cp->GetNext();
cp->SetNext(this);
} else {
Next = tp->GetColumns();
tp->SetColumns(this);
} // endif cprec
// Set additional Dos access method information for column.
Deplac = cdp->GetOffset(); Long = cdp->GetLong();
To_Val = NULL;
Clustered = cdp->GetOpt();
Sorted = (cdp->GetOpt() == 2) ? 1 : 0;
Ndv = 0; // Currently used only for XDB2
Nbm = 0; // Currently used only for XDB2
Min = NULL;
Max = NULL;
Bmap = NULL;
Dval = NULL;
Buf = NULL;
if (txfp && txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) { int nblk = txfp->GetBlock();
Clustered = (cdp->GetXdb2()) ? 2 : 1;
Sorted = (cdp->GetOpt() > 1) ? 1 : 0; // Currently ascending only
if (Clustered == 1) {
Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec);
Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec);
} else { // Clustered == 2 // Ndv is the number of distinct values in Dval. Ndv and Nbm // may be 0 when optimizing because Ndval is not filled yet, // but the size of the passed Dval memory block is Ok.
Ndv = cdp->GetNdv();
Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec);
// Bmap cannot be allocated when optimizing, we must know Nbm first if ((Nbm = cdp->GetNbm()))
Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk);
} // endif Clustered
} // endif Opt
OldVal = NULL; // Currently used only in MinMax
Dsp = 0;
Ldz = false;
Nod = false;
Dcm = -1;
p = cdp->GetFmt();
Buf = NULL;
if (p && IsTypeNum(Buf_Type)) { // Formatted numeric value for (; p && *p && isalpha(*p); p++) switch (toupper(*p)) { case'Z': // Have leading zeros
Ldz = true; break; case'N': // Have no decimal point
Nod = true; break; case'D': // Decimal separator
Dsp = *(++p); break;
} // endswitch p
// Set number of decimal digits
Dcm = (*p) ? atoi(p) : GetScale();
} // endif fmt
if (trace(1))
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
} // end of DOSCOL constructor
/***********************************************************************/ /* DOSCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/
DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
Deplac = col1->Deplac; Long = col1->Long;
To_Val = col1->To_Val;
Ldz = col1->Ldz;
Dsp = col1->Dsp;
Nod = col1->Nod;
Dcm = col1->Dcm;
OldVal = col1->OldVal;
Buf = col1->Buf;
Clustered = col1->Clustered;
Sorted = col1->Sorted;
Min = col1->Min;
Max = col1->Max;
Bmap = col1->Bmap;
Dval = col1->Dval;
Ndv = col1->Ndv;
Nbm = col1->Nbm;
} // end of DOSCOL copy constructor
/***********************************************************************/ /* VarSize: This function tells UpdateDB whether or not the block */ /* optimization file must be redone if this column is updated, even */ /* it is not sorted or clustered. This applies to the last column of */ /* a variable length table that is blocked, because if it is updated */ /* using a temporary file, the block size may be modified. */ /***********************************************************************/ bool DOSCOL::VarSize(void)
{
PTDBDOS tdbp = (PTDBDOS)To_Tdb;
PTXF txfp = tdbp->Txfp;
if (Cdp && !Cdp->GetNext() // Must be the last column
&& tdbp->Ftype == RECFM_VAR // of a DOS variable length
&& txfp->Blocked // blocked table
&& txfp->GetUseTemp()) // using a temporary file. returntrue; else returnfalse;
} // end VarSize
/***********************************************************************/ /* SetBuffer: prepare a column block for write operation. */ /***********************************************************************/ bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{ if (!(To_Val = value)) {
snprintf(g->Message, sizeof(g->Message), MSG(VALUE_ERROR), Name); returntrue;
} elseif (Buf_Type == value->GetType()) { // Values are of the (good) column type if (Buf_Type == TYPE_DATE) { // If any of the date values is formatted // output format must be set for the receiving table if (GetDomain() || ((DTVAL *)value)->IsFormatted()) goto newval; // This will make a new value;
} elseif (Buf_Type == TYPE_DOUBLE) // Float values must be written with the correct (column) precision // Note: maybe this should be forced by ShowValue instead of this ?
value->SetPrec(GetScale());
Value = value; // Directly access the external value
} else { // Values are not of the (good) column type if (check) {
snprintf(g->Message, sizeof(g->Message), MSG(TYPE_VALUE_ERR), Name,
GetTypeName(Buf_Type), GetTypeName(value->GetType())); returntrue;
} // endif check
newval: if (InitValue(g)) // Allocate the matching value block returntrue;
} // endif's Value, Buf_Type
// Allocate the buffer used in WriteColumn for numeric columns if (!Buf && IsTypeNum(Buf_Type))
Buf = (char*)PlugSubAlloc(g, NULL, MY_MAX(64, Long + 1)); else// Text columns do not need additional buffer
Buf = (char*)Value->GetTo_Val();
// Because Colblk's have been made from a copy of the original TDB in // case of Update, we must reset them to point to the original one. if (To_Tdb->GetOrig())
To_Tdb = (PTDB)To_Tdb->GetOrig();
// Set the Column
Status = (ok) ? BUF_EMPTY : BUF_NO; returnfalse;
} // end of SetBuffer
/***********************************************************************/ /* ReadColumn: what this routine does is to access the last line */ /* read from the corresponding table, extract from it the field */ /* corresponding to this column and convert it to buffer type. */ /***********************************************************************/ void DOSCOL::ReadColumn(PGLOBAL g)
{ char *p = NULL; int i, rc; int field; bool err = false; double dval;
PTDBDOS tdbp = (PTDBDOS)To_Tdb;
if (trace(2))
htrc( "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);
/*********************************************************************/ /* If physical reading of the line was deferred, do it now. */ /*********************************************************************/ if (!tdbp->IsRead()) if ((rc = tdbp->ReadBuffer(g)) != RC_OK) { if (rc == RC_EF)
snprintf(g->Message, sizeof(g->Message), MSG(INV_DEF_READ), rc);
throw11;
} // endif
p = tdbp->To_Line + Deplac;
field = Long;
/*********************************************************************/ /* For a variable length file, check if the field exists. */ /*********************************************************************/ if ((tdbp->Ftype == RECFM_VAR || tdbp->Ftype == RECFM_CSV)
&& strlen(tdbp->To_Line) < (unsigned)Deplac)
field = 0; elseif (Dsp) for(i = 0; i < field; i++) if (p[i] == Dsp)
p[i] = '.';
switch (tdbp->Ftype) { case RECFM_VAR: case RECFM_FIX: // Fixed length text file case RECFM_CSV: // Variable length CSV or FMT file case RECFM_DBF: // Fixed length DBase file if (Nod) switch (Buf_Type) { case TYPE_INT: case TYPE_SHORT: case TYPE_TINY: case TYPE_BIGINT:
err = Value->SetValue_char(p, field - Dcm); break; case TYPE_DOUBLE: if (!(err = Value->SetValue_char(p, field))) {
dval = Value->GetFloatValue();
if (err) {
snprintf(g->Message, sizeof(g->Message), "Out of range value for column %s at row %d",
Name, tdbp->RowNumber(g));
PushWarning(g, tdbp);
} // endif err
// Set null when applicable if (Nullable)
Value->SetNull(Value->IsZero());
} // end of ReadColumn
/***********************************************************************/ /* WriteColumn: what this routine does is to access the last line */ /* read from the corresponding table, and rewrite the field */ /* corresponding to this column from the column buffer and type. */ /***********************************************************************/ void DOSCOL::WriteColumn(PGLOBAL g)
{ char *p, fmt[32]; int i, k, n, len, field;
PTDBDOS tdbp = (PTDBDOS)To_Tdb;
if (trace(2))
htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
Name, tdbp->GetTdb_No(), ColUse, Status);
p = tdbp->To_Line + Deplac;
if (trace(2))
htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long);
field = Long;
if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) {
len = (signed)strlen(tdbp->To_Line);
if (tdbp->IsUsingTemp(g)) // Because of eventual missing field(s) the buffer must be reset
memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len); else // The size actually available must be recalculated
field = MY_MIN(len - Deplac, Long);
} // endif Ftype
if (trace(2))
htrc("Long=%d field=%d coltype=%d colval=%p\n", Long, field, Buf_Type, Value);
/*********************************************************************/ /* Get the string representation of Value according to column type. */ /*********************************************************************/ if (Value != To_Val)
Value->SetValue_pval(To_Val, false); // Convert the updated value
/*********************************************************************/ /* This test is only useful for compressed(2) tables. */ /*********************************************************************/ if (tdbp->Ftype != RECFM_BIN) { if (Ldz || Nod || Dcm >= 0) { switch (Buf_Type) { case TYPE_SHORT:
safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*hd" : "%*.hd");
i = 0;
if (Nod) for (; i < Dcm; i++)
safe_strcat(fmt, sizeof(fmt), "0");
len = snprintf(Buf, field + 1, fmt, field - i, Value->GetShortValue()); break; case TYPE_INT:
safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
i = 0;
if (Nod) for (; i < Dcm; i++)
safe_strcat(fmt,sizeof(fmt), "0");
len = snprintf(Buf, field + 1, fmt, field - i, Value->GetIntValue()); break; case TYPE_TINY:
safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
i = 0;
if (Nod) for (; i < Dcm; i++)
safe_strcat(fmt, sizeof(fmt), "0");
len = snprintf(Buf, field + 1, fmt, field - i, Value->GetTinyValue()); break; case TYPE_DOUBLE: case TYPE_DECIM:
safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*.*lf" : "%*.*lf");
len = field + ((Nod && Dcm) ? 1 : 0);
snprintf(Buf, len + 1, fmt, len, Dcm, Value->GetFloatValue());
len = strlen(Buf);
if (Nod && Dcm) for (i = k = 0; i < len; i++, k++) if (Buf[i] != ' ') { if (Buf[i] == '.')
k++;
Buf[i] = Buf[k];
} // endif Buf(i)
len = strlen(Buf); break; default:
snprintf(g->Message, sizeof(g->Message), "Invalid field format for column %s", Name); throw31;
} // endswitch BufType
n = strlen(Buf);
} else// Standard CONNECT format
n = Value->ShowValue(Buf, field);
if (trace(1))
htrc("new length(%p)=%d\n", Buf, n);
snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, field); throw31;
} elseif (Dsp) for (i = 0; i < len; i++) if (Buf[i] == '.')
Buf[i] = Dsp;
if (trace(2))
htrc("buffer=%s\n", Buf);
/*******************************************************************/ /* Updating must be done only when not in checking pass. */ /*******************************************************************/ if (Status) {
memset(p, ' ', field);
memcpy(p, Buf, len);
if (trace(2))
htrc(" col write: '%.*s'\n", len, p);
} // endif Status
} else// BIN compressed table /*******************************************************************/ /* Check if updating is Ok, meaning col value is not too long. */ /* Updating to be done only during the second pass (Status=true) */ /*******************************************************************/ if (Value->GetBinValue(p, Long, Status)) {
snprintf(g->Message, sizeof(g->Message), MSG(BIN_F_TOO_LONG),
Name, Value->GetSize(), Long); throw31;
} // endif
} // end of WriteColumn
/***********************************************************************/ /* SetMinMax: Calculate minimum and maximum values for one block. */ /* Note: TYPE_STRING is stored and processed with zero ended strings */ /* to be matching the way the FILTER Eval function processes them. */ /***********************************************************************/ bool DOSCOL::SetMinMax(PGLOBAL g)
{
PTDBDOS tp = (PTDBDOS)To_Tdb;
ReadColumn(g); // Extract column value from current line
/***********************************************************************/ /* SetBitMap: Calculate the bit map of existing values in one block. */ /* Note: TYPE_STRING is processed with zero ended strings */ /* to be matching the way the FILTER Eval function processes them. */ /***********************************************************************/ bool DOSCOL::SetBitMap(PGLOBAL g)
{ int i, m, n;
uint *bmp;
PTDBDOS tp = (PTDBDOS)To_Tdb;
PDBUSER dup = PlgGetUser(g);
n = tp->Txfp->CurNum;
bmp = (uint*)Bmap->GetValPtr(Nbm * tp->Txfp->CurBlk);
// Extract column value from current line
ReadColumn(g);
if (CheckSorted(g)) returntrue;
if (!n) // New block for (m = 0; m < Nbm; m++)
bmp[m] = 0; // Reset the new bit map
if ((i = Dval->Find(Value)) < 0) { char buf[32];
snprintf(g->Message, sizeof(g->Message), MSG(DVAL_NOTIN_LIST),
Value->GetCharString(buf), Name); returntrue;
} elseif (i >= dup->Maxbmp) {
snprintf(g->Message, sizeof(g->Message), MSG(OPT_LOGIC_ERR), i); returntrue;
} else {
m = i / MAXBMP; #ifdefined(_DEBUG)
assert (m < Nbm); #endif// _DEBUG
bmp[m] |= (1 << (i % MAXBMP));
} // endif's i
returnfalse;
} // end of SetBitMap
/***********************************************************************/ /* Checks whether a column declared as sorted is sorted indeed. */ /***********************************************************************/ bool DOSCOL::CheckSorted(PGLOBAL g)
{ if (Sorted)
{ if (OldVal) { // Verify whether this column is sorted all right if (OldVal->CompareValue(Value) > 0) { // Column is no more in ascending order
snprintf(g->Message, sizeof(g->Message), MSG(COL_NOT_SORTED), Name, To_Tdb->GetName());
Sorted = false; returntrue;
} else
OldVal->SetValue_pval(Value);
} else
OldVal = AllocateValue(g, Value);
} returnfalse;
} // end of CheckSorted
/***********************************************************************/ /* AddDistinctValue: Check whether this value already exist in the */ /* list and if not add it to the distinct values list. */ /***********************************************************************/ bool DOSCOL::AddDistinctValue(PGLOBAL g)
{ bool found = false; int i, m, n;
ReadColumn(g); // Extract column value from current line
// Perhaps a better algorithm can be used when Ndv gets bigger // Here we cannot use Find because we must get the index of where // to insert a new value if it is not found in the array. for (n = 0; n < Ndv; n++) {
m = Dval->CompVal(Value, n);
if (m > 0) continue; elseif (!m)
found = true; // Already there
break;
} // endfor n
if (!found) { // Check whether we have room for an additional value if (Ndv == Freq) { // Too many values because of wrong Freq setting
snprintf(g->Message, sizeof(g->Message), MSG(BAD_FREQ_SET), Name); returntrue;
} // endif Ndv
// New value, add it to the list before the nth value
Dval->SetNval(Ndv + 1);
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.