diff options
Diffstat (limited to 'storage/connect/tabfix.cpp')
-rw-r--r-- | storage/connect/tabfix.cpp | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp new file mode 100644 index 00000000..c965b638 --- /dev/null +++ b/storage/connect/tabfix.cpp @@ -0,0 +1,681 @@ +/************* 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. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant section of system dependant header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(_WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !_WIN32 +#if defined(UNIX) +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !_WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/***********************************************************************/ +#include "global.h" // global declares +#include "plgdbsem.h" // DB application declares +#include "filamfix.h" +#include "filamdbf.h" +#include "tabfix.h" // TDBFIX, FIXCOL classes declares +#include "array.h" +#include "blkfil.h" + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +extern int num_read, num_there, num_eq[2]; // Statistics +char BINCOL::Endian = 'H'; + +/***********************************************************************/ +/* External function. */ +/***********************************************************************/ +USETEMP UseTemp(void); + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* 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 + +// Method +PTDB TDBFIX::Clone(PTABS t) + { + PTDB tp; + PGLOBAL g = t->G; + + tp = new(g) TDBFIX(g, this); + + 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) + return new(g) BINCOL(g, cdp, this, cprec, n); + else + return new(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; + + if (Cardinal >= 0) + Txfp->Block = (Cardinal > 0) + ? (Cardinal + Txfp->Nrec - 1) / Txfp->Nrec : 0; + + } // endif Padded + + } // end of RestoreNrec + +/***********************************************************************/ +/* 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())); + return 0; + } // 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); + return false; + } // 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) + return true; + + /*********************************************************************/ + /* Open according to required logical input/output mode. */ + /* Use conventionnal input/output functions. */ + /* Treat fixed length text files as binary. */ + /*********************************************************************/ + if (Txfp->OpenTableFile(g)) + return true; + + 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); + + /*********************************************************************/ + /* Reset statistics values. */ + /*********************************************************************/ + num_read = num_there = num_eq[0] = num_eq[1] = 0; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for FIX access method. */ +/***********************************************************************/ +int TDBFIX::WriteDB(PGLOBAL g) + { + return Txfp->WriteBuffer(g); + } // end of WriteDB + +// ------------------------ BINCOL functions ---------------------------- + +/***********************************************************************/ +/* BINCOL public constructor. */ +/***********************************************************************/ +BINCOL::BINCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PCSZ am) + : DOSCOL(g, cdp, tp, cp, i, am) + { + char c, *fmt = cdp->GetFmt(); + + Fmt = GetDomain() ? 'C' : 'X'; + Buff = NULL; + Eds = ((PTDBFIX)tp)->Teds; + N = 0; + M = GetTypeSize(Buf_Type, sizeof(longlong)); + Lim = 0; + + if (fmt) { + for (N = 0, i = 0; fmt[i]; i++) { + c = toupper(fmt[i]); + + if (isdigit(c)) + N = (N * 10 + (c - '0')); + else if (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); + throw 11; + } // endswitch Fmt + + } else if (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 + +/***********************************************************************/ +/* Set Endian according to the host setting. */ +/***********************************************************************/ +void BINCOL::SetEndian(void) + { + union { + short S; + char C[sizeof(short)]; + }; + + S = 1; + Endian = (C[0] == 1) ? 'L' : 'B'; + } // end of SetEndian + +/***********************************************************************/ +/* 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); + + throw 11; + } // 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]; + else if (Eds == 'L' && Endian == 'B') + Buff[M - i - 1] = p[i]; + else if (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 + + break; + default: + snprintf(g->Message, sizeof(g->Message), MSG(BAD_BIN_FMT), Fmt, Name); + throw 11; + } // endswitch Fmt + + // 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; + + if (trace(1)) { + htrc("BIN WriteColumn: col %s R%d coluse=%.4X status=%.4X", + Name, tdbp->GetTdb_No(), ColUse, Status); + htrc(" Lrecl=%d\n", tdbp->Lrecl); + htrc("Long=%d deplac=%d coltype=%d ftype=%c\n", + Long, Deplac, Buf_Type, *Format.Type); + } // endif trace + + /*********************************************************************/ + /* 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()); + } else if (Value->GetBinValue(p, Long, Status)) { + snprintf(g->Message, sizeof(g->Message), MSG(BIN_F_TOO_LONG), + Name, Value->GetSize(), Long); + throw 31; + } // 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); + throw 31; + } else if (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); + throw 31; + } else if (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); + throw 31; + } else if (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); + throw 31; + } // endif n + + if (Status) { + s = Value->GetCharString(Buf); + memset(p, ' ', Long); + memcpy(p, s, strlen(s)); + } // endif Status + + break; + default: + snprintf(g->Message, sizeof(g->Message), MSG(BAD_BIN_FMT), Fmt, Name); + throw 31; + } // endswitch Fmt + + if (Eds && Status) { + p = tdbp->To_Line + Deplac; + + for (int i = 0; i < Lim; i++) + if (Eds == 'B' && Endian == 'L') + p[N - i - 1] = Buff[i]; + else if (Eds == 'L' && Endian == 'B') + p[i] = Buff[M - i - 1]; + else if (Endian == 'B') + p[N - i - 1] = Buff[M - i - 1]; + else + p[i] = Buff[i]; + + } // endif Eds + + } // end of WriteColumn + +/* ------------------------ End of TabFix ---------------------------- */ |