/************* TabFix C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABFIX */ /* ------------- */ /* Version 4.9.2 */ /* */ /* COPYRIGHT: */ /* ---------- */ /* (C) Copyright to the author Olivier BERTRAND 1998-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ /* This program are the TDBFIX class DB routines. */ /* */ /***********************************************************************/
/***********************************************************************/ /* Implementation of the TDBFIX class. */ /***********************************************************************/
TDBFIX::TDBFIX(PDOSDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
{
Teds = tdp->Teds; // For BIN tables
} // end of TDBFIX standard constructor
TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp)
{
Teds = tdbp->Teds;
} // end of TDBFIX copy constructor
if (Ftype == RECFM_VAR || Ftype == RECFM_FIX) { // File is text
PDOSCOL cp1, cp2;
for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
cp2 = new(g) DOSCOL(cp1, tp); // Make a copy
NewPointer(t, cp1, cp2);
} // endfor cp1
} else { // File is binary
PBINCOL cp1, cp2;
for (cp1 = (PBINCOL)Columns; cp1; cp1 = (PBINCOL)cp1->GetNext()) {
cp2 = new(g) BINCOL(cp1, tp); // Make a copy
NewPointer(t, cp1, cp2);
} // endfor cp1
} // endif Ftype
return tp;
} // end of Clone
/***********************************************************************/ /* Reset read/write position values. */ /***********************************************************************/ void TDBFIX::ResetDB(void)
{
TDBDOS::ResetDB();
} // end of ResetDB
/***********************************************************************/ /* Allocate FIX (DOS) or BIN column description block. */ /***********************************************************************/
PCOL TDBFIX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{ if (Ftype == RECFM_BIN) returnnew(g) BINCOL(g, cdp, this, cprec, n); else returnnew(g) DOSCOL(g, cdp, this, cprec, n);
} // end of MakeCol
/***********************************************************************/ /* Remake the indexes after the table was modified. */ /***********************************************************************/ int TDBFIX::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{ int prc, rc = RC_OK;
To_Filter = NULL; // Disable filtering //To_BlkIdx = NULL; // and block filtering
To_BlkFil = NULL; // and index filtering
Cardinality(g); // If called by create
RestoreNrec(); // May have been modified
MaxSize = -1; // Size must be recalculated
Cardinal = -1; // as well as Cardinality
// After the table was modified the indexes // are invalid and we should mark them as such...
rc = ((PDOSDEF)To_Def)->InvalidateIndex(g);
if (dop) {
Columns = NULL; // Not used anymore
Txfp->Reset(); // OldBlk = CurBlk = -1; // ReadBlks = CurNum = Rbuf = Modif = 0;
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
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 indexes.
rc = MakeIndex(g, NULL, FALSE);
rc = (rc == RC_INFO) ? prc : rc;
} // endif dox
return rc;
} // end of ResetTableOpt
/***********************************************************************/ /* Reset the Nrec and BlkSize values that can have been modified. */ /***********************************************************************/ void TDBFIX::RestoreNrec(void)
{ if (!Txfp->Padded) {
Txfp->Nrec = (To_Def && To_Def->GetElemt()) ? To_Def->GetElemt()
: DOS_BUFF_LEN;
Txfp->Blksize = Txfp->Nrec * Txfp->Lrecl;
/***********************************************************************/ /* FIX 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 TDBFIX::Cardinality(PGLOBAL g)
{ if (!g) return Txfp->Cardinality(g);
if (Cardinal < 0)
Cardinal = Txfp->Cardinality(g);
return Cardinal;
} // end of Cardinality
/***********************************************************************/ /* FIX GetMaxSize: returns file size in number of lines. */ /***********************************************************************/ int TDBFIX::GetMaxSize(PGLOBAL g)
{ if (MaxSize < 0) {
MaxSize = Cardinality(g);
if (MaxSize > 0 && (To_BlkFil = InitBlockFilter(g, To_Filter))
&& !To_BlkFil->Correlated()) { // Use BlockTest to reduce the estimated size
MaxSize = Txfp->MaxBlkSize(g, MaxSize);
ResetBlockFilter(g);
} // endif To_BlkFil
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/ /* FIX ResetSize: Must reset Headlen for DBF tables only. */ /***********************************************************************/ void TDBFIX::ResetSize(void)
{ if (Txfp->GetAmType() == TYPE_AM_DBF)
Txfp->Headlen = 0;
MaxSize = Cardinal = -1;
} // end of ResetSize
/***********************************************************************/ /* FIX GetProgMax: get the max value for progress information. */ /***********************************************************************/ int TDBFIX::GetProgMax(PGLOBAL g)
{ return Cardinality(g);
} // end of GetProgMax
/***********************************************************************/ /* RowNumber: return the ordinal number of the current row. */ /***********************************************************************/ int TDBFIX::RowNumber(PGLOBAL g, bool b)
{ if (Txfp->GetAmType() == TYPE_AM_DBF) { if (!b && To_Kindex) { /*****************************************************************/ /* Don't know how to retrieve Rows from DBF file address */ /* because of eventual deleted lines still in the file. */ /*****************************************************************/
snprintf(g->Message, sizeof(g->Message), MSG(NO_ROWID_FOR_AM),
GetAmName(g, Txfp->GetAmType())); return0;
} // endif To_Kindex
if (!b) return Txfp->GetRows();
} // endif DBF
return Txfp->GetRowID();
} // end of RowNumber
/***********************************************************************/ /* FIX tables don't use temporary files except if specified as do it. */ /***********************************************************************/ bool TDBFIX::IsUsingTemp(PGLOBAL)
{ // Not ready yet to handle using a temporary file with mapping // or while deleting from DBF files. return ((UseTemp() == TMP_YES && Txfp->GetAmType() != TYPE_AM_MAP &&
!(Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_DBF)) ||
UseTemp() == TMP_FORCE || UseTemp() == TMP_TEST);
} // end of IsUsingTemp
/***********************************************************************/ /* FIX Access Method opening routine (also used by the BIN a.m.) */ /* 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 TDBFIX::OpenDB(PGLOBAL g)
{ if (trace(1))
htrc("FIX OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d Ftype=%d\n", this, Tdb_No, Use, To_Key_Col, Mode, Ftype);
if (Use == USE_OPEN) { /*******************************************************************/ /* Table already open, just replace it at its beginning. */ /*******************************************************************/ if (To_Kindex) /*****************************************************************/ /* Table is to be accessed through a sorted index table. */ /*****************************************************************/
To_Kindex->Reset(); else
Txfp->Rewind(); // see comment in Work.log
ResetBlockFilter(g); returnfalse;
} // endif use
if (Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_MAP &&
(!Next || UseTemp() == TMP_FORCE)) { // Delete all lines or using temp. Not handled in MAP mode
Txfp = new(g) FIXFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
} // endif Mode
/*********************************************************************/ /* Call Cardinality to calculate Block in the case of Func queries. */ /* and also in the case of multiple tables. */ /*********************************************************************/ if (Cardinality(g) < 0) returntrue;
/*********************************************************************/ /* Open according to required logical input/output mode. */ /* Use conventionnal input/output functions. */ /* Treat fixed length text files as binary. */ /*********************************************************************/ if (Txfp->OpenTableFile(g)) returntrue;
Use = USE_OPEN; // Do it now in case we are recursively called
/*********************************************************************/ /* Initialize To_Line at the beginning of the block buffer. */ /*********************************************************************/
To_Line = Txfp->GetBuf(); // For WriteDB
/*********************************************************************/ /* Allocate the block filter tree if evaluation is possible. */ /*********************************************************************/
To_BlkFil = InitBlockFilter(g, To_Filter);
if (trace(1))
htrc("OpenFix: R%hd mode=%d BlkFil=%p\n", Tdb_No, Mode, To_BlkFil);
/*********************************************************************/ /* Reset buffer access according to indexing and to mode. */ /*********************************************************************/
Txfp->ResetBuffer(g);
/***********************************************************************/ /* WriteDB: Data Base write routine for FIX access method. */ /***********************************************************************/ int TDBFIX::WriteDB(PGLOBAL g)
{ return Txfp->WriteBuffer(g);
} // end of WriteDB
if (fmt) { for (N = 0, i = 0; fmt[i]; i++) {
c = toupper(fmt[i]);
if (isdigit(c))
N = (N * 10 + (c - '0')); elseif (c == 'L' || c == 'B' || c == 'H')
Eds = c; else
Fmt = c;
} // endfor i
// M is the size of the source value switch (Fmt) { case'C': Eds = 0; break; case'X': break; case'S': M = sizeof(short); break; case'T': M = sizeof(char); break; case'I': M = sizeof(int); break; case'G': M = sizeof(longlong); break; case'R': // Real case'F': M = sizeof(float); break; case'D': M = sizeof(double); break; default:
snprintf(g->Message, sizeof(g->Message), MSG(BAD_BIN_FMT), Fmt, Name); throw11;
} // endswitch Fmt
} elseif (IsTypeChar(Buf_Type))
Eds = 0;
if (Eds) { // This is a byte order specification if (!N)
N = M;
if (Eds != 'L' && Eds != 'B')
Eds = Endian;
if (N != M || Eds != Endian || IsTypeChar(Buf_Type)) {
Buff = (char*)PlugSubAlloc(g, NULL, M);
memset(Buff, 0, M);
Lim = MY_MIN(N, M);
} else
Eds = 0; // New format is a no op
} // endif Eds
} // end of BINCOL constructor
/***********************************************************************/ /* BINCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/
BINCOL::BINCOL(BINCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
{
Eds = col1->Eds;
Fmt = col1->Fmt;
N = col1->N;
M = col1->M;
Lim = col1->Lim;
} // end of BINCOL copy constructor
/***********************************************************************/ /* ReadColumn: what this routine does is to access the last line */ /* read from the corresponding table and extract from it the field */ /* corresponding to this column. */ /***********************************************************************/ void BINCOL::ReadColumn(PGLOBAL g)
{ char *p = NULL; int rc;
PTDBFIX tdbp = (PTDBFIX)To_Tdb;
if (trace(2))
htrc("BIN 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;
/*********************************************************************/ /* Set Value from the line field. */ /*********************************************************************/ if (Eds) { for (int i = 0; i < Lim; i++) if (Eds == 'B' && Endian == 'L')
Buff[i] = p[N - i - 1]; elseif (Eds == 'L' && Endian == 'B')
Buff[M - i - 1] = p[i]; elseif (Endian == 'B')
Buff[M - i - 1] = p[N - i - 1]; else
Buff[i] = p[i];
p = Buff;
} // endif Eds
switch (Fmt) { case'X': // Standard not converted values if (Eds && IsTypeChar(Buf_Type))
Value->SetValueNonAligned<longlong>(p); else
Value->SetBinValue(p);
break; case'S': // Short integer
Value->SetValueNonAligned<short>(p); break; case'T': // Tiny integer
Value->SetValue(*p); break; case'I': // Integer
Value->SetValueNonAligned<int>(p); break; case'G': // Large (great) integer
Value->SetValueNonAligned<longlong>(p); break; case'F': // Float case'R': // Real
Value->SetValueNonAligned<float>(p); break; case'D': // Double
Value->SetValueNonAligned<double>(p); break; case'C': // Text if (Value->SetValue_char(p, Long)) {
snprintf(g->Message, sizeof(g->Message), "Out of range value for column %s at row %d",
Name, tdbp->RowNumber(g));
PushWarning(g, tdbp);
} // endif SetValue_char
// 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. */ /***********************************************************************/ void BINCOL::WriteColumn(PGLOBAL g)
{ char *p, *s;
longlong n;
PTDBFIX tdbp = (PTDBFIX)To_Tdb;
/*********************************************************************/ /* Check whether the new value has to be converted to Buf_Type. */ /*********************************************************************/ if (Value != To_Val)
Value->SetValue_pval(To_Val, false); // Convert the updated value
p = (Eds) ? Buff : tdbp->To_Line + Deplac;
/*********************************************************************/ /* Check whether updating is Ok, meaning col value is not too long. */ /* Updating will be done only during the second pass (Status=true) */ /* Conversion occurs if the external format Fmt is specified. */ /*********************************************************************/ switch (Fmt) { case'X': // Standard not converted values if (Eds && IsTypeChar(Buf_Type)) { if (Status)
Value->GetValueNonAligned<longlong>(p, Value->GetBigintValue());
} elseif (Value->GetBinValue(p, Long, Status)) {
snprintf(g->Message, sizeof(g->Message), MSG(BIN_F_TOO_LONG),
Name, Value->GetSize(), Long); throw31;
} // endif p
break; case'S': // Short integer
n = Value->GetBigintValue();
if (n > 32767LL || n < -32768LL) {
snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_BIG), n, Name); throw31;
} elseif (Status)
Value->GetValueNonAligned<short>(p, (short)n);
break; case'T': // Tiny integer
n = Value->GetBigintValue();
if (n > 255LL || n < -256LL) {
snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_BIG), n, Name); throw31;
} elseif (Status)
*p = (char)n;
break; case'I': // Integer
n = Value->GetBigintValue();
if (n > INT_MAX || n < INT_MIN) {
snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_BIG), n, Name); throw31;
} elseif (Status)
Value->GetValueNonAligned<int>(p, (int)n);
break; case'G': // Large (great) integer if (Status)
*(longlong *)p = Value->GetBigintValue();
break; case'F': // Float case'R': // Real if (Status)
Value->GetValueNonAligned<float>(p, (float)Value->GetFloatValue());
break; case'D': // Double if (Status)
Value->GetValueNonAligned<double>(p, Value->GetFloatValue());
break; case'C': // Characters if ((n = (signed)strlen(Value->GetCharString(Buf))) > Long) {
snprintf(g->Message, sizeof(g->Message), MSG(BIN_F_TOO_LONG), Name, (int) n, Long); throw31;
} // endif n
if (Status) {
s = Value->GetCharString(Buf);
memset(p, ' ', Long);
memcpy(p, s, strlen(s));
} // endif Status