/************* TabDos C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABDOS */ /* ------------- */ /* Version 4.8 */ /* */ /* COPYRIGHT: */ /* ---------- */ /* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ /* This program are the DOS tables classes. */ /* */ /***********************************************************************/ /***********************************************************************/ /* Include relevant sections of the System header files. */ /***********************************************************************/ #include "my_global.h" #if defined(WIN32) #include #include // For testing only #include #include #if defined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif // __BORLANDC__ //#include #else // !WIN32 #if defined(UNIX) #include #include #else // !UNIX #include #endif // !UNIX #include #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" #if defined(ZIP_SUPPORT) #include "filamzip.h" #endif // ZIP_SUPPORT #include "tabdos.h" #include "tabfix.h" #include "tabmul.h" #define PLGINI "plugdb.ini" // Configuration settings file #if defined(UNIX) #define _fileno fileno #define _O_RDONLY O_RDONLY #endif /***********************************************************************/ /* DB static variables. */ /***********************************************************************/ int num_read, num_there, num_eq[2]; // Statistics extern "C" char plgini[_MAX_PATH]; extern "C" int trace; /***********************************************************************/ /* Min and Max blocks contains zero ended fields (blank = false). */ /* No conversion of block values (check = true). */ /***********************************************************************/ PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len = 0, int prec = 0, bool check = true, bool blank = false); /* --------------------------- Class DOSDEF -------------------------- */ /***********************************************************************/ /* Constructor. */ /***********************************************************************/ DOSDEF::DOSDEF(void) { Pseudo = 3; Fn = NULL; Ofn = NULL; To_Indx = NULL; Recfm = RECFM_VAR; Mapped = false; Padded = false; Huge = false; Accept = false; Eof = false; To_Pos = NULL; Compressed = 0; Lrecl = 0; AvgLen = 0; Block = 0; Last = 0; Blksize = 0; Maxerr = 0; ReadMode = 0; Ending = 0; //Mtime = 0; } // end of DOSDEF constructor /***********************************************************************/ /* DeleteTableFile: Delete DOS/UNIX table files using platform API. */ /* If the table file is protected (declared as read/only) we still */ /* erase the the eventual optimize and index files but return true. */ /***********************************************************************/ bool DOSDEF::DeleteTableFile(PGLOBAL g) { char filename[_MAX_PATH]; bool rc = false; // Now delete the table file itself if not protected if (!IsReadOnly()) { rc = Erase(filename); } else rc =true; return rc; // Return true if error } // end of DeleteTableFile /***********************************************************************/ /* Erase: This was made a separate routine because a strange thing */ /* happened when DeleteTablefile was defined for the VCTDEF class: */ /* when called from Catalog, the DOSDEF routine was still called even */ /* when the class was VCTDEF. It also minimizes the specific code. */ /***********************************************************************/ bool DOSDEF::Erase(char *filename) { bool rc; PlugSetPath(filename, Fn, GetPath()); #if defined(WIN32) rc = !DeleteFile(filename); #else // UNIX rc = remove(filename); #endif // UNIX return rc; // Return true if error } // end of Erase /***********************************************************************/ /* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */ /***********************************************************************/ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf) { char *ftype; char filename[_MAX_PATH]; bool sep, rc = false; if (!To_Indx) return false; // No index // If true indexes are in separate files sep = Cat->GetBoolCatInfo("SepIndex", false); if (!sep && pxdf) { strcpy(g->Message, MSG(NO_RECOV_SPACE)); return true; } // endif sep 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_DBF: ftype = ".dbx"; break; default: sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm); return true; } // endswitch Ftype /*********************************************************************/ /* Check for existence of an index file. */ /*********************************************************************/ if (sep) { // Indexes are save in separate files #if !defined(UNIX) char drive[_MAX_DRIVE]; #else char *drive = NULL; #endif char direc[_MAX_DIR]; char fname[_MAX_FNAME]; for (; pxdf; pxdf = pxdf->GetNext()) { _splitpath(Ofn, drive, direc, fname, NULL); strcat(strcat(fname, "_"), pxdf->GetName()); _makepath(filename, drive, direc, fname, ftype); PlugSetPath(filename, filename, GetPath()); #if defined(WIN32) rc |= !DeleteFile(filename); #else // UNIX rc |= remove(filename); #endif // UNIX } // endfor pxdf } else { // !sep // Drop all indexes, delete the common file PlugSetPath(filename, Ofn, GetPath()); strcat(PlugRemoveType(filename, filename), ftype); #if defined(WIN32) rc = !DeleteFile(filename); #else // UNIX rc = remove(filename); #endif // UNIX } // endif sep if (rc) sprintf(g->Message, MSG(DEL_FILE_ERR), filename); return rc; // Return true if error } // end of DeleteIndexFile /***********************************************************************/ /* DefineAM: define specific AM block values from XDB file. */ /***********************************************************************/ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { char buf[8]; bool map = (am && (*am == 'M' || *am == 'm')); LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F" : (am && (*am == 'B' || *am == 'b')) ? "B" : (am && !stricmp(am, "DBF")) ? "D" : "V"; Desc = Fn = Cat->GetStringCatInfo(g, "Filename", NULL); Ofn = Cat->GetStringCatInfo(g, "Optname", Fn); Cat->GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : (toupper(*buf) == 'B') ? RECFM_BIN : (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR; Lrecl = Cat->GetIntCatInfo("Lrecl", 0); if (Recfm != RECFM_DBF) Compressed = Cat->GetIntCatInfo("Compressed", 0); Mapped = Cat->GetBoolCatInfo("Mapped", map); Block = Cat->GetIntCatInfo("Blocks", 0); Last = Cat->GetIntCatInfo("Last", 0); Ending = Cat->GetIntCatInfo("Ending", CRLF); if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) { Huge = Cat->GetBoolCatInfo("Huge", Cat->GetDefHuge()); Padded = Cat->GetBoolCatInfo("Padded", false); Blksize = Cat->GetIntCatInfo("Blksize", 0); Eof = (Cat->GetIntCatInfo("EOF", 0) != 0); } else if (Recfm == RECFM_DBF) { Maxerr = Cat->GetIntCatInfo("Maxerr", 0); Accept = (Cat->GetIntCatInfo("Accept", 0) != 0); ReadMode = Cat->GetIntCatInfo("Readmode", 0); } else // (Recfm == RECFM_VAR) AvgLen = Cat->GetIntCatInfo("Avglen", 0); // Ignore wrong Index definitions for catalog commands return (Cat->GetIndexInfo(g, this) /*&& !Cat->GetCatFnc()*/); } // end of DefineAM /***********************************************************************/ /* InvalidateIndex: mark all indexes as invalid. */ /***********************************************************************/ bool DOSDEF::InvalidateIndex(PGLOBAL g) { if (To_Indx) for (PIXDEF xp = To_Indx; xp; xp = xp->Next) xp->Invalid = true; return false; } // end of InvalidateIndex /***********************************************************************/ /* GetTable: makes a new Table Description Block. */ /***********************************************************************/ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) { // Mapping not used for insert USETEMP tmp = PlgGetUser(g)->UseTemp; bool map = Mapped && mode != MODE_INSERT && !(tmp != TMP_NO && Recfm == RECFM_VAR && mode == MODE_UPDATE) && !(tmp == TMP_FORCE && (mode == MODE_UPDATE || mode == MODE_DELETE)); PTXF txfp; PTDBASE tdbp; /*********************************************************************/ /* Allocate table and file processing class of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ if (Recfm == RECFM_DBF) { if (Catfunc == FNC_NO) { if (map) txfp = new(g) DBMFAM(this); else txfp = new(g) DBFFAM(this); tdbp = new(g) TDBFIX(this, txfp); } else // Catfunc should be 'C' tdbp = new(g) TDBDCL(this); } else if (Recfm != RECFM_VAR && Compressed < 2) { if (Huge) txfp = new(g) BGXFAM(this); else if (map) txfp = new(g) MPXFAM(this); #if defined(ZIP_SUPPORT) else if (Compressed) txfp = new(g) ZIXFAM(this); #endif // ZIP_SUPPORT else txfp = new(g) FIXFAM(this); tdbp = new(g) TDBFIX(this, txfp); } else { #if defined(ZIP_SUPPORT) if (Compressed) { if (Compressed == 1) txfp = new(g) ZIPFAM(this); else { strcpy(g->Message, "Compress 2 not supported yet"); // txfp = new(g) ZLBFAM(defp); return NULL; } // endelse } else #endif // ZIP_SUPPORT if (map) txfp = new(g) MAPFAM(this); else txfp = new(g) DOSFAM(this); // 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); return tdbp; } // end of GetTable /* ------------------------ 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); Lrecl = tdp->Lrecl; AvgLen = tdp->AvgLen; Ftype = tdp->Recfm; To_Line = NULL; Cardinal = -1; } // end of TDBDOS standard constructor TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp) { Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp; Lrecl = tdbp->Lrecl; AvgLen = tdbp->AvgLen; Ftype = tdbp->Ftype; To_Line = tdbp->To_Line; Cardinal = tdbp->Cardinal; } // end of TDBDOS copy constructor // Method PTDB TDBDOS::CopyOne(PTABS t) { PTDB tp; PDOSCOL cp1, cp2; PGLOBAL g = t->G; tp = new(g) TDBDOS(g, this); for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) { cp2 = new(g) DOSCOL(cp1, tp); // Make a copy NewPointer(t, cp1, cp2); } // endfor cp1 return tp; } // end of CopyOne /***********************************************************************/ /* Allocate DOS column description block. */ /***********************************************************************/ PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) { return new(g) DOSCOL(g, cdp, this, cprec, n); } // end of MakeCol /***********************************************************************/ /* Print debug information. */ /***********************************************************************/ void TDBDOS::PrintAM(FILE *f, char *m) { fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); 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 dox) { int rc = RC_OK; MaxSize = -1; // Size must be recalculated Cardinal = -1; // as well as Cardinality if (dox) { // Remake eventual indexes if (Mode != MODE_UPDATE) To_SetCols = NULL; // Only used on Update Columns = NULL; // Not used anymore Txfp->Reset(); // New start Use = USE_READY; // So the table can be reopened Mode = MODE_READ; // New mode if (!(PlgGetUser(g)->Check & CHK_OPT)) { // After the table was modified the indexes // are invalid and we should mark them as such... rc = ((PDOSDEF)To_Def)->InvalidateIndex(g); } else // ... or we should remake them. rc = MakeIndex(g, NULL, false); } // endif dox return rc; } // end of ResetTableOpt /***********************************************************************/ /* Check whether we have to create/update permanent indexes. */ /***********************************************************************/ int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add) { int k, n; bool fixed, doit, sep, b = (pxdf != NULL); PCOL *keycols, colp; PIXDEF xdp, sxp = NULL; PKPDEF kdp; PDOSDEF dfp; //PCOLDEF cdp; PXINDEX x; PXLOAD pxp; PCATLG cat = PlgGetCatalog(g); Mode = MODE_READ; Use = USE_READY; dfp = (PDOSDEF)To_Def; fixed = Cardinality(g) >= 0; // Are we are called from CreateTable or CreateIndex? if (pxdf) { if (!add && dfp->GetIndx()) { strcpy(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())) { sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name); return RC_FX; } else if (!sxp->GetNext()) break; sxp->SetNext(pxdf); // first = false; } else dfp->SetIndx(pxdf); // pxdf->SetDef(dfp); } else if (!(pxdf = dfp->GetIndx())) return RC_INFO; // No index to make // 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))) { sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name); goto err; } // endif colp colp->InitValue(g); n = max(n, xdp->GetNparts()); } // endfor kdp keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL)); sep = cat->GetBoolCatInfo("SepIndex", false); /*********************************************************************/ /* 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; keycols[n++] = ColDB(g, kdp->GetName(), 0); } // endfor kdp // If no indexed columns were updated, don't remake the index // if indexes are in separate files. if (!doit && sep) continue; k = xdp->GetNparts(); // Make the index and save it if (dfp->Huge) pxp = new(g) XHUGE; else pxp = new(g) XFILE; if (k == 1) // Simple index x = new(g) XINDXS(this, xdp, pxp, keycols); else // Multi-Column index x = new(g) XINDEX(this, xdp, pxp, keycols); if (!x->Make(g, sxp)) { // Retreive define values from the index xdp->SetMaxSame(x->GetMaxSame()); // xdp->SetSize(x->GetSize()); // store KXYCOL Mxs in KPARTDEF Mxsame xdp->SetMxsame(x); #if defined(TRACE) printf("Make done...\n"); #endif // TRACE // if (x->GetSize() > 0) sxp = xdp; xdp->SetInvalid(false); } else goto err; } else break; // Physical table does not exist yet or anymore if (Use == USE_OPEN) CloseDB(g); return RC_OK; err: if (sxp) sxp->SetNext(NULL); else dfp->SetIndx(NULL); return RC_FX; } // end of MakeIndex /***********************************************************************/ /* 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 b) { if (To_Kindex) { /*******************************************************************/ /* Don't know how to retrieve RowID from file address. */ /*******************************************************************/ sprintf(g->Message, MSG(NO_ROWID_FOR_AM), GetAmName(g, Txfp->GetAmType())); return 0; } 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) { if (!g) return Txfp->Cardinality(g); if (Cardinal < 0) Cardinal = Txfp->Cardinality(g); 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) { if (trace) 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 minimum line length assuming */ /* only the last column can be of variable length. This will be */ /* a ceiling estimate (as last column is never totally absent). */ /*****************************************************************/ int rec = ((PDOSDEF)To_Def)->Ending; // +2: CRLF +1: LF if (AvgLen <= 0) // No given average estimate rec += EstimatedLength(g); else // A lower estimate was given for the average record length rec += (int)AvgLen; if (trace) htrc(" Filen=%d min_rec=%d\n", len, rec); MaxSize = (len + rec - 1) / rec; if (trace) htrc(" Estimated max_K=%d\n", MaxSize); } // endif len } else MaxSize = Cardinality(g); return MaxSize; } // end of GetMaxSize /***********************************************************************/ /* DOS EstimatedLength. Returns an estimated minimum line length. */ /***********************************************************************/ int TDBDOS::EstimatedLength(PGLOBAL g) { 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 ????? } else for (; cdp; cdp = cdp->GetNext()) dep = max(dep, cdp->GetOffset()); return (int)dep; } // end of Estimated Length /***********************************************************************/ /* DOS tables favor the use temporary files for Update. */ /***********************************************************************/ bool TDBDOS::IsUsingTemp(PGLOBAL g) { USETEMP usetemp = PlgGetUser(g)->UseTemp; return (usetemp == TMP_YES || usetemp == TMP_FORCE || (usetemp == TMP_AUTO && Mode == MODE_UPDATE)); } // end of IsUsingTemp /***********************************************************************/ /* 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) 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. */ /*******************************************************************/ Txfp->Rewind(); // see comment in Work.log if (SkipHeader(g)) return true; return false; } // endif use if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS) { // Delete all lines. Not handled in MAP or block mode Txfp = new(g) DOSFAM((PDOSDEF)To_Def); Txfp->SetTdbp(this); } else if (Txfp->Blocked && (Mode == MODE_DELETE || (Mode == MODE_UPDATE && PlgGetUser(g)->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); #if defined(ZIP_SUPPORT) else if (Txfp->GetAmType() == TYPE_AM_ZIP) Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); #endif // ZIP_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)) return true; Use = USE_OPEN; // Do it now in case we are recursively called /*********************************************************************/ /* Allocate the line buffer plus a null character. */ /*********************************************************************/ To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1); 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, Lrecl + 1); if (trace) htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line); if (SkipHeader(g)) // When called from CSV/FMT files return true; /*********************************************************************/ /* Reset statistics values. */ /*********************************************************************/ num_read = num_there = num_eq[0] = num_eq[1] = 0; return false; } // end of OpenDB /***********************************************************************/ /* ReadDB: Data Base read routine for DOS access method. */ /***********************************************************************/ int TDBDOS::ReadDB(PGLOBAL g) { if (trace > 1) 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 last non null 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 > 1) 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 > 1) htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line); /*********************************************************************/ /* Now start the reading process. */ /*********************************************************************/ return ReadBuffer(g); } // end of ReadDB /***********************************************************************/ /* WriteDB: Data Base write routine for DOS access method. */ /***********************************************************************/ int TDBDOS::WriteDB(PGLOBAL g) { if (trace > 1) htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode); if (!Ftype && (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 if (trace > 1) 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); } // end of CloseDB // ------------------------ DOSCOL functions ---------------------------- /***********************************************************************/ /* DOSCOL public constructor (also called by MAPCOL). */ /***********************************************************************/ DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ 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; OldVal = NULL; // Currently used only in MinMax Ldz = false; Nod = false; Dcm = -1; p = cdp->GetFmt(); 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; } // endswitch p // Set number of decimal digits Dcm = (*p) ? atoi(p) : GetPrecision(); } // endif fmt if (trace) 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; Nod = col1->Nod; Dcm = col1->Dcm; OldVal = col1->OldVal; Buf = col1->Buf; } // 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. return true; else return false; } // 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)) { sprintf(g->Message, MSG(VALUE_ERROR), Name); return true; } else if (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; } else if (Buf_Type == TYPE_FLOAT) // Float values must be written with the correct (column) precision // Note: maybe this should be forced by ShowValue instead of this ? value->SetPrec(GetPrecision()); Value = value; // Directly access the external value } else { // Values are not of the (good) column type if (check) { sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, GetTypeName(Buf_Type), GetTypeName(value->GetType())); return true; } // endif check newval: if (InitValue(g)) // Allocate the matching value block return true; } // endif's Value, Buf_Type // Allocate the buffer used in WriteColumn for numeric columns if (IsTypeNum(Buf_Type)) Buf = (char*)PlugSubAlloc(g, NULL, max(32, Long + Dcm + 1)); // 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; return false; } // 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; int i, rc; int field; double dval; PTDBDOS tdbp = (PTDBDOS)To_Tdb; if (trace > 1) 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) sprintf(g->Message, MSG(INV_DEF_READ), rc); longjmp(g->jumper[g->jump_level], 11); } // endif p = tdbp->To_Line + Deplac; field = Long; switch (tdbp->Ftype) { case RECFM_VAR: /*****************************************************************/ /* For a variable length file, check if the field exists. */ /*****************************************************************/ if (strlen(tdbp->To_Line) < (unsigned)Deplac) field = 0; case RECFM_FIX: // Fixed length text 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: Value->SetValue_char(p, field - Dcm); break; case TYPE_FLOAT: Value->SetValue_char(p, field); dval = Value->GetFloatValue(); for (i = 0; i < Dcm; i++) dval /= 10.0; Value->SetValue(dval); break; default: Value->SetValue_char(p, field); break; } // endswitch Buf_Type else Value->SetValue_char(p, field); break; default: sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype); longjmp(g->jumper[g->jump_level], 34); } // endswitch Ftype } // 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, *p2, fmt[32]; int i, k, len, field; PTDBDOS tdbp = (PTDBDOS)To_Tdb; if (trace > 1) 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 > 1) 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 = min(len - Deplac, Long); } // endif Ftype if (trace > 1) 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: strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd"); i = 0; if (Nod) for (; i < Dcm; i++) strcat(fmt, "0"); len = sprintf(Buf, fmt, field - i, Value->GetShortValue()); break; case TYPE_INT: strcpy(fmt, (Ldz) ? "%0*d" : "%*.d"); i = 0; if (Nod) for (; i < Dcm; i++) strcat(fmt, "0"); len = sprintf(Buf, fmt, field - i, Value->GetIntValue()); break; case TYPE_TINY: strcpy(fmt, (Ldz) ? "%0*d" : "%*.d"); i = 0; if (Nod) for (; i < Dcm; i++) strcat(fmt, "0"); len = sprintf(Buf, fmt, field - i, Value->GetTinyValue()); break; case TYPE_FLOAT: strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf"); sprintf(Buf, fmt, field + ((Nod && Dcm) ? 1 : 0), Dcm, Value->GetFloatValue()); len = strlen(Buf); if (Nod && Dcm) for (i = k = 0; i < len; i++, k++) if (Buf[i] != ' ') { if (Buf[i] == '.' || Buf[i] == ',') k++; Buf[i] = Buf[k]; } // endif Buf(i) len = strlen(Buf); break; } // endswitch BufType p2 = Buf; } else // Standard PlugDB format p2 = Value->ShowValue(Buf, field); if (trace) htrc("new length(%p)=%d\n", p2, strlen(p2)); if ((len = strlen(p2)) > field) { sprintf(g->Message, MSG(VALUE_TOO_LONG), p2, Name, field); longjmp(g->jumper[g->jump_level], 31); } // endif if (trace > 1) htrc("buffer=%s\n", p2); /*******************************************************************/ /* Updating must be done only when not in checking pass. */ /*******************************************************************/ if (Status) { memset(p, ' ', field); memcpy(p, p2, len); if (trace > 1) htrc(" col write: '%.*s'\n", len, p); } // endif Use } 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)) { sprintf(g->Message, MSG(BIN_F_TOO_LONG), Name, Value->GetSize(), Long); longjmp(g->jumper[g->jump_level], 31); } // endif } // end of WriteColumn /***********************************************************************/ /* Make file output of a Dos column descriptor block. */ /***********************************************************************/ void DOSCOL::Print(PGLOBAL g, FILE *f, uint n) { COLBLK::Print(g, f, n); } // end of Print /* ------------------------------------------------------------------- */