summaryrefslogtreecommitdiffstats
path: root/storage/connect/tabfix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/tabfix.cpp')
-rw-r--r--storage/connect/tabfix.cpp681
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 ---------------------------- */