diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h index 61f151ba794..411660431b9 100644 --- a/storage/connect/catalog.h +++ b/storage/connect/catalog.h @@ -1,122 +1,112 @@ -/*************** Catalog H Declares Source Code File (.H) **************/ -/* Name: CATALOG.H Version 3.2 */ -/* */ -/* (C) Copyright to the author Olivier BERTRAND 2000-2012 */ -/* */ -/* This file contains the CATALOG PlugDB classes definitions. */ -/***********************************************************************/ -#ifndef __CATALOG__H -#define __CATALOG__H - -#include "block.h" - -/***********************************************************************/ -/* Defines the length of a buffer to contain entire table section. */ -/***********************************************************************/ -#define PLG_MAX_PATH 144 /* Must be the same across systems */ -#define PLG_BUFF_LEN 100 /* Number of lines in binary file buffer */ - - -//typedef class INDEXDEF *PIXDEF; - -/***********************************************************************/ -/* Defines the structure used to enumerate tables or views. */ -/***********************************************************************/ -typedef struct _curtab { - PRELDEF CurTdb; - char *Curp; - char *Tabpat; - bool Ispat; - bool NoView; - int Nt; - char *Type[16]; - } CURTAB, *PCURTAB; - -/***********************************************************************/ -/* Defines the structure used to get column catalog info. */ -/***********************************************************************/ -typedef struct _colinfo { - char *Name; - int Type; - int Offset; - int Length; - int Key; - int Precision; - int Scale; - int Opt; - int Freq; - char *Remark; - char *Datefmt; - char *Fieldfmt; - ushort Flags; // Used by MariaDB CONNECT handlers - } COLINFO, *PCOLINFO; - -/***********************************************************************/ -/* CATALOG: base class for catalog classes. */ -/***********************************************************************/ -class DllExport CATALOG { - friend class RELDEF; - friend class TABDEF; - friend class DIRDEF; - friend class OEMDEF; - public: - CATALOG(void); // Constructor - virtual ~CATALOG() { } // Make -Wdelete-non-virtual-dtor happy - - // Implementation - int GetCblen(void) {return Cblen;} - bool GetDefHuge(void) {return DefHuge;} - void SetDefHuge(bool b) {DefHuge = b;} - char *GetCbuf(void) {return Cbuf;} - char *GetDataPath(void) {return (char*)DataPath;} - - // Methods - virtual void Reset(void) {} - virtual void SetDataPath(PGLOBAL g, const char *path) {} - virtual bool GetBoolCatInfo(PSZ what, bool bdef) {return bdef;} - virtual bool SetIntCatInfo(PSZ what, int ival) {return false;} - virtual int GetIntCatInfo(PSZ what, int idef) {return idef;} - virtual int GetSizeCatInfo(PSZ what, PSZ sdef) {return 0;} - virtual int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size) - {strncpy(buf, sdef, size); return size;} - virtual char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef) - {return sdef;} - virtual int GetColCatInfo(PGLOBAL g, PTABDEF defp) {return -1;} - virtual bool GetIndexInfo(PGLOBAL g, PTABDEF defp) {return true;} - virtual bool CheckName(PGLOBAL g, char *name) {return true;} - virtual bool ClearName(PGLOBAL g, PSZ name) {return true;} - virtual PRELDEF MakeOneTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} - virtual PRELDEF GetTableDescEx(PGLOBAL g, PTABLE tablep) {return NULL;} - virtual PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, LPCSTR type, - PRELDEF *prp = NULL) {return NULL;} - virtual PRELDEF GetFirstTable(PGLOBAL g) {return NULL;} - virtual PRELDEF GetNextTable(PGLOBAL g) {return NULL;} - virtual bool TestCond(PGLOBAL g, const char *name, const char *type) - {return true;} - virtual bool DropTable(PGLOBAL g, PSZ name, bool erase) {return true;} - virtual PTDB GetTable(PGLOBAL g, PTABLE tablep, - MODE mode = MODE_READ, LPCSTR type = NULL) - {return NULL;} - virtual void TableNames(PGLOBAL g, char *buffer, int maxbuf, int info[]) {} - virtual void ColumnNames(PGLOBAL g, char *tabname, char *buffer, - int maxbuf, int info[]) {} - virtual void ColumnDefs(PGLOBAL g, char *tabname, char *buffer, - int maxbuf, int info[]) {} - virtual void *DecodeValues(PGLOBAL g, char *tabname, char *colname, - char *buffer, int maxbuf, int info[]) {return NULL;} - virtual int ColumnType(PGLOBAL g, char *tabname, char *colname) {return 0;} - virtual void ClearDB(PGLOBAL g) {} - - protected: - virtual bool ClearSection(PGLOBAL g, const char *key, const char *section) {return true;} - virtual PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} - - // Members - char *Cbuf; /* Buffer used for col section */ - int Cblen; /* Length of suballoc. buffer */ - CURTAB Ctb; /* Used to enumerate tables */ - bool DefHuge; /* true: tables default to huge */ - LPCSTR DataPath; /* Is the Path of DB data dir */ - }; // end of class CATALOG - -#endif // __CATALOG__H +/*************** Catalog H Declares Source Code File (.H) **************/ +/* Name: CATALOG.H Version 3.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2000-2012 */ +/* */ +/* This file contains the CATALOG PlugDB classes definitions. */ +/***********************************************************************/ +#ifndef __CATALOG__H +#define __CATALOG__H + +#include "block.h" + +/***********************************************************************/ +/* Defines the length of a buffer to contain entire table section. */ +/***********************************************************************/ +#define PLG_MAX_PATH 144 /* Must be the same across systems */ +#define PLG_BUFF_LEN 100 /* Number of lines in binary file buffer */ + + +//typedef class INDEXDEF *PIXDEF; + +/***********************************************************************/ +/* Defines the structure used to enumerate tables or views. */ +/***********************************************************************/ +typedef struct _curtab { + PRELDEF CurTdb; + char *Curp; + char *Tabpat; + bool Ispat; + bool NoView; + int Nt; + char *Type[16]; + } CURTAB, *PCURTAB; + +/***********************************************************************/ +/* Defines the structure used to get column catalog info. */ +/***********************************************************************/ +typedef struct _colinfo { + char *Name; + int Type; + int Offset; + int Length; + int Key; + int Precision; + int Scale; + int Opt; + int Freq; + char *Remark; + char *Datefmt; + char *Fieldfmt; + ushort Flags; // Used by MariaDB CONNECT handlers + } COLINFO, *PCOLINFO; + +/***********************************************************************/ +/* CATALOG: base class for catalog classes. */ +/***********************************************************************/ +class DllExport CATALOG { + friend class RELDEF; + friend class TABDEF; + friend class DIRDEF; + friend class OEMDEF; + public: + CATALOG(void); // Constructor + virtual ~CATALOG() { } // Make -Wdelete-non-virtual-dtor happy + + // Implementation + int GetCblen(void) {return Cblen;} + bool GetDefHuge(void) {return DefHuge;} + void SetDefHuge(bool b) {DefHuge = b;} + char *GetCbuf(void) {return Cbuf;} + char *GetDataPath(void) {return (char*)DataPath;} + + // Methods + virtual void Reset(void) {} + virtual void SetDataPath(PGLOBAL g, const char *path) {} + virtual bool CheckName(PGLOBAL g, char *name) {return true;} + virtual bool ClearName(PGLOBAL g, PSZ name) {return true;} + virtual PRELDEF MakeOneTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} + virtual PRELDEF GetTableDescEx(PGLOBAL g, PTABLE tablep) {return NULL;} + virtual PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, LPCSTR type, + PRELDEF *prp = NULL) {return NULL;} + virtual PRELDEF GetFirstTable(PGLOBAL g) {return NULL;} + virtual PRELDEF GetNextTable(PGLOBAL g) {return NULL;} + virtual bool TestCond(PGLOBAL g, const char *name, const char *type) + {return true;} + virtual bool DropTable(PGLOBAL g, PSZ name, bool erase) {return true;} + virtual PTDB GetTable(PGLOBAL g, PTABLE tablep, + MODE mode = MODE_READ, LPCSTR type = NULL) + {return NULL;} + virtual void TableNames(PGLOBAL g, char *buffer, int maxbuf, int info[]) {} + virtual void ColumnNames(PGLOBAL g, char *tabname, char *buffer, + int maxbuf, int info[]) {} + virtual void ColumnDefs(PGLOBAL g, char *tabname, char *buffer, + int maxbuf, int info[]) {} + virtual void *DecodeValues(PGLOBAL g, char *tabname, char *colname, + char *buffer, int maxbuf, int info[]) {return NULL;} + virtual int ColumnType(PGLOBAL g, char *tabname, char *colname) {return 0;} + virtual void ClearDB(PGLOBAL g) {} + + protected: + virtual bool ClearSection(PGLOBAL g, const char *key, const char *section) {return true;} + virtual PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} + + // Members + char *Cbuf; /* Buffer used for col section */ + int Cblen; /* Length of suballoc. buffer */ + CURTAB Ctb; /* Used to enumerate tables */ + bool DefHuge; /* true: tables default to huge */ + LPCSTR DataPath; /* Is the Path of DB data dir */ + }; // end of class CATALOG + +#endif // __CATALOG__H diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp index 586c7bd7c54..d92c9c3a946 100755 --- a/storage/connect/filamvct.cpp +++ b/storage/connect/filamvct.cpp @@ -1,4234 +1,4233 @@ -/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/ -/* PROGRAM NAME: FILAMVCT */ -/* ------------- */ -/* Version 2.5 */ -/* */ -/* COPYRIGHT: */ -/* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */ -/* */ -/* WHAT THIS PROGRAM DOES: */ -/* ----------------------- */ -/* This program are the VCT file access method classes. */ -/* Added in version 2: F */ -/* - Split Vec format. */ -/* - Partial delete. */ -/* - Use of tempfile for update. */ -/* */ -/***********************************************************************/ - -/***********************************************************************/ -/* Include relevant MariaDB header file. */ -/***********************************************************************/ -#include "my_global.h" -#if defined(WIN32) -#include -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif // __BORLAND__ -//#include -#include -#else // !WIN32 F -#if defined(UNIX) -#include -#include -#include -#include -#define NO_ERROR 0 -#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. */ -/* tabdos.h is header containing the TABDOS class declarations. */ -/***********************************************************************/ -#include "global.h" -#include "osutil.h" // Unuseful for WIN32 -#include "plgdbsem.h" -#include "valblk.h" -#include "filamfix.h" -#include "tabdos.h" -#include "tabvct.h" -#include "maputil.h" -#include "filamvct.h" - -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - -extern int num_read, num_there; // Statistics -static int num_write; -extern "C" int trace; - -#if defined(UNIX) -// Add dummy strerror (NGC) -char *strerror(int num); -#endif // UNIX - -/***********************************************************************/ -/* Header containing block info for not split VEC tables. */ -/* Block and last values can be calculated from NumRec and Nrec. */ -/* This is better than directly storing Block and Last because it */ -/* make possible to use the same file with tables having a different */ -/* block size value (Element -> Nrec) */ -/* Note: can be in a separate file if header=1 or a true header (2) */ -/***********************************************************************/ -typedef struct _vecheader { -//int Block; /* The number of used blocks */ -//int Last; /* The number of used records in last block */ - int MaxRec; /* Max number of records (True vector format)*/ - int NumRec; /* Number of valid records in the table */ - } VECHEADER; - -/***********************************************************************/ -/* Char VCT column blocks are right filled with blanks (blank = true) */ -/* Conversion of block values allowed conditionally for insert only. */ -/***********************************************************************/ -PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, - bool check = true, bool blank = true, bool un = false); - -/* -------------------------- Class VCTFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the VCTFAM class. */ -/***********************************************************************/ -VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp) - { - Last = tdp->GetLast(); - MaxBlk = (tdp->GetEstimate() > 0) ? - ((tdp->GetEstimate() - 1) / Nrec + 1) : 0; - NewBlock = NULL; - AddBlock = false; - Split = false; - - if ((Header = (MaxBlk) ? tdp->Header : 0)) - Block = Last = -1; - - Bsize = Nrec; - CurNum = Nrec - 1; - Colfn = NULL; - Tempat = NULL; - Clens = NULL; - Deplac = NULL; - Isnum = NULL; - Ncol = 0; - } // end of VCTFAM standard constructor - -VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp) - { - MaxBlk = txfp->MaxBlk; - NewBlock = NULL; - AddBlock = false; - Split = txfp->Split; - Header = txfp->Header; - Bsize = txfp->Bsize; - Colfn = txfp->Colfn; - Tempat = txfp->Tempat; - Clens = txfp->Clens; - Deplac = txfp->Deplac; - Isnum = txfp->Isnum; - Ncol = txfp->Ncol; - } // end of VCTFAM copy constructor - -/***********************************************************************/ -/* Reset read/write position values. */ -/***********************************************************************/ -void VCTFAM::Reset(void) - { - FIXFAM::Reset(); - NewBlock = NULL; - AddBlock = false; - CurNum = Nrec - 1; - } // end of Reset - -/***********************************************************************/ -/* Get the Headlen, Block and Last info from the file header. */ -/***********************************************************************/ -int VCTFAM::GetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - int h, k, n; - VECHEADER vh; - - if (Header < 1 || Header > 3 || !MaxBlk) { - sprintf(g->Message, "Invalid header value %d", Header); - return -1; - } else - n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header == 2) - strcat(PlugRemoveType(filename, filename), ".blk"); - - if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1 - || !_filelength(h)) { - // Consider this is a void table - Last = Nrec; - Block = 0; - - if (h != -1) - close(h); - - return n; - } else if (Header == 3) - k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END); - - if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) { - sprintf(g->Message, "Error reading header file %s", filename); - n = -1; - } else if (MaxBlk * Nrec != vh.MaxRec) { - sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", - vh.MaxRec, MaxBlk, Nrec); - n = -1; - } else { - Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; - Last = (vh.NumRec + Nrec - 1) % Nrec + 1; - } // endif s - - close(h); - return n; - } // end of GetBlockInfo - -/***********************************************************************/ -/* Get the Headlen, Block and Last info from the file header. */ -/***********************************************************************/ -bool VCTFAM::SetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - bool rc = false; - size_t n; - VECHEADER vh; - FILE *s; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header != 2) { - if (Stream) { - s = Stream; - - if (Header == 1) - /*k =*/ fseek(s, 0, SEEK_SET); - - } else - s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b"); - - } else { // Header == 2 - strcat(PlugRemoveType(filename, filename), ".blk"); - s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb"); - } // endif Header - - if (!s) { - sprintf(g->Message, "Error opening header file %s", filename); - return true; - } else if (Header == 3) - /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END); - - vh.MaxRec = MaxBlk * Bsize; - vh.NumRec = (Block - 1) * Nrec + Last; - - if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) { - sprintf(g->Message, "Error writing header file %s", filename); - rc = true; - } // endif fread - - if (Header == 2 || !Stream) - fclose(s); - - return rc; - } // end of SetBlockInfo - -/***********************************************************************/ -/* Use BlockTest to reduce the table estimated size. */ -/***********************************************************************/ -int VCTFAM::MaxBlkSize(PGLOBAL g, int s) - { - int rc = RC_OK, savcur = CurBlk; - int size; - - // Roughly estimate the table size as the sum of blocks - // that can contain good rows - for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) - if ((rc = Tdbp->TestBlock(g)) == RC_OK) - size += (CurBlk == Block - 1) ? Last : Nrec; - else if (rc == RC_EF) - break; - - CurBlk = savcur; - return size; - } // end of MaxBlkSize - -/***********************************************************************/ -/* VCT 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 VCTFAM::Cardinality(PGLOBAL g) - { - if (!g) - return 1; - - if (Block < 0) - if (Split) { - // Separate column files and no pre setting of Block and Last - // This allows to see a table modified externally, but Block - // and Last must be set from the file cardinality. - // Only happens when called by sub classes. - char filename[_MAX_PATH]; - PSZ savfn = To_File; - int len, clen, card = -1; - PCOLDEF cdp = Tdbp->GetDef()->GetCols(); - - if (!Colfn) { - // Prepare the column file name pattern - Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); - Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); - } // endif Colfn - - // Use the first column file to calculate the cardinality - clen = cdp->GetClen(); - sprintf(filename, Colfn, 1); - To_File = filename; - len = GetFileLength(g); - To_File = savfn; - - if (len >= 0) { - if (!(len % clen)) - card = len / clen; // Fixed length file - else - sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen); - - if (trace) - htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen); - - } else - card = 0; - - // Set number of blocks for later use - Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; - Last = (card + Nrec - 1) % Nrec + 1; - return card; - } else { - // Vector table having Block and Last info in a Header (file) - if ((Headlen = GetBlockInfo(g)) < 0) - return -1; // Error - - } // endif split - - return (int)((Block - 1) * Nrec + Last); - } // end of Cardinality - -/***********************************************************************/ -/* GetRowID: return the RowID of last read record. */ -/***********************************************************************/ -int VCTFAM::GetRowID(void) - { - return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk - : (Block - 1) * Nrec + Last); - } // end of GetRowID - -/***********************************************************************/ -/* VCT Create an empty file for Vector formatted tables. */ -/***********************************************************************/ -bool VCTFAM::MakeEmptyFile(PGLOBAL g, char *fn) - { - // Vector formatted file: this will create an empty file of the - // required length if it does not exists yet. - char filename[_MAX_PATH], c = 0; - int h, n; - - PlugSetPath(filename, fn, Tdbp->GetPath()); -#if defined(WIN32) - h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE); -#else // !WIN32 - h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); -#endif // !WIN32 - - if (h == -1) - return true; - - n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; - - if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) == -1) { - sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); - close(h); - return true; - } // endif h - - write(h, &c, 1); // This actually fills the empty file - close(h); - return false; - } // end of MakeEmptyFile - -/***********************************************************************/ -/* VCT 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 VCTFAM::OpenTableFile(PGLOBAL g) - { - char opmode[4], filename[_MAX_PATH]; - MODE mode = Tdbp->GetMode(); - PDBUSER dbuserp = PlgGetUser(g); - - /*********************************************************************/ - /* Update block info if necessary. */ - /*********************************************************************/ - if (Block < 0) - if ((Headlen = GetBlockInfo(g)) < 0) - return true; - - /*********************************************************************/ - /* Open according to input/output mode required. */ - /*********************************************************************/ - switch (mode) { - case MODE_READ: - strcpy(opmode, "rb"); - break; - case MODE_DELETE: - if (!Tdbp->GetNext()) { - // Store the number of deleted lines - DelRows = Cardinality(g); - - // This will delete the whole file - strcpy(opmode, "wb"); - break; - } // endif - - // Selective delete, pass thru - case MODE_UPDATE: - UseTemp = Tdbp->IsUsingTemp(g); - strcpy(opmode, (UseTemp) ? "rb" : "r+b"); - break; - case MODE_INSERT: - if (MaxBlk) { - if (!Block) - if (MakeEmptyFile(g, To_File)) - return true; - - strcpy(opmode, "r+b"); // Required to update empty blocks - } else if (Last == Nrec) - strcpy(opmode, "ab"); - else - strcpy(opmode, "r+b"); // Required to update the last block - - break; - default: - sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); - return true; - } // endswitch Mode - - /*********************************************************************/ - /* Use conventionnal input/output functions. */ - /*********************************************************************/ - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (!(Stream = PlugOpenFile(g, filename, opmode))) { - if (trace) - htrc("%s\n", g->Message); - - return (mode == MODE_READ && errno == ENOENT) - ? PushWarning(g, Tdbp) : true; - } // endif Stream - - if (trace) - htrc("File %s is open in mode %s\n", filename, opmode); - - To_Fb = dbuserp->Openlist; // Keep track of File block - - if (!strcmp(opmode, "wb")) - // This will stop the process by - // causing GetProgMax to return 0. - return ResetTableSize(g, 0, Nrec); - - num_read = num_there = num_write = 0; - - // Allocate the table and column block buffer - return AllocateBuffer(g); - } // end of OpenTableFile - -/***********************************************************************/ -/* Allocate the block buffers for columns used in the query. */ -/***********************************************************************/ -bool VCTFAM::AllocateBuffer(PGLOBAL g) - { - MODE mode = Tdbp->GetMode(); - PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - PCOLDEF cdp; - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - if (mode == MODE_INSERT) { - bool chk = PlgGetUser(g)->Check & CHK_TYPE; - - NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); - - for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) - memset(NewBlock + Nrec * cdp->GetPoff(), - (IsTypeNum(cdp->GetType()) ? 0 : ' '), - Nrec * cdp->GetClen()); - - for (; cp; cp = (PVCTCOL)cp->Next) - cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, - cp->Buf_Type, Nrec, cp->Format.Length, - cp->Format.Prec, chk); - - return InitInsert(g); // Initialize inserting - } else { - if (UseTemp || mode == MODE_DELETE) { - // Allocate all that is needed to move lines - int i = 0, n = (MaxBlk) ? MaxBlk : 1; - - if (!Ncol) - for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) - Ncol++; - - Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); - - for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { - Clens[i] = cdp->GetClen(); - Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec; - Isnum[i] = IsTypeNum(cdp->GetType()); - Buflen = max(Buflen, cdp->GetClen()); - } // endfor cdp - - if (!UseTemp || MaxBlk) { - Buflen *= Nrec; - To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); - } else - NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); - - } // endif mode - - for (; cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial()) // Not a pseudo column - cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, - cp->Format.Length, cp->Format.Prec); - - } //endif mode - - return false; - } // end of AllocateBuffer - -/***********************************************************************/ -/* Do initial action when inserting. */ -/***********************************************************************/ -bool VCTFAM::InitInsert(PGLOBAL g) - { - // We come here in MODE_INSERT only - if (Last == Nrec) { - CurBlk = Block; - CurNum = 0; - AddBlock = !MaxBlk; - } else { - int rc; - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - // The starting point must be at the end of file as for append. - CurBlk = Block - 1; - CurNum = Last; - - // Prepare error return - if (g->jump_level == MAX_JUMP) { - strcpy(g->Message, MSG(TOO_MANY_JUMPS)); - return true; - } // endif - - if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { - g->jump_level--; - return true; - } // endif - - // Last block must be updated by new values - for (; cp; cp = (PVCTCOL)cp->Next) - cp->ReadBlock(g); - - g->jump_level--; - } // endif Last - - // We are not currently using a temporary file for Insert - T_Stream = Stream; - return false; - } // end of InitInsert - -/***********************************************************************/ -/* ReadBuffer: Read one line for a VCT file. */ -/***********************************************************************/ -int VCTFAM::ReadBuffer(PGLOBAL g) - { - int rc = RC_OK; - MODE mode = Tdbp->GetMode(); - - if (Placed) - Placed = false; - else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) { - /*******************************************************************/ - /* New block. */ - /*******************************************************************/ - CurNum = 0; - - next: - if (++CurBlk == Block) - return RC_EF; // End of file - - /*******************************************************************/ - /* Before reading a new block, check whether block optimizing */ - /* can be done, as well as for join as for local filtering. */ - /*******************************************************************/ - switch (Tdbp->TestBlock(g)) { - case RC_EF: - return RC_EF; - case RC_NF: - goto next; - } // endswitch rc - - num_there++; - } // endif CurNum - - if (OldBlk != CurBlk) { - if (mode == MODE_UPDATE) { - /*****************************************************************/ - /* Flush the eventually modified column buffers in old blocks */ - /* and read the blocks to modify attached to Set columns. */ - /*****************************************************************/ - if (MoveLines(g)) // For VECFAM - return RC_FX; - - for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols(); - colp; colp = (PVCTCOL)colp->Next) { - colp->WriteBlock(g); - colp->ReadBlock(g); - } // endfor colp - - } // endif mode - - OldBlk = CurBlk; // Last block actually read - } // endif oldblk - - if (trace) - htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK); - - return rc; - } // end of ReadBuffer - -/***********************************************************************/ -/* Data Base write routine for VCT access method. */ -/***********************************************************************/ -int VCTFAM::WriteBuffer(PGLOBAL g) - { - if (trace) - htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", - Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); - - if (Tdbp->GetMode() == MODE_UPDATE) { - // Mode Update is done in ReadDB, we just initialize it here - if (!T_Stream) { - if (UseTemp) { - if (OpenTempFile(g)) - return RC_FX; - - // Most of the time, not all table columns are updated. - // This why we must completely pre-fill the temporary file. - Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last - : Block * Nrec; // To write last lock - - if (MoveIntermediateLines(g)) - return RC_FX; - - } else - T_Stream = Stream; - - } // endif T_Stream - - } else { - // Mode Insert - if (MaxBlk && CurBlk == MaxBlk) { - strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); - return RC_EF; // Too many lines for vector formatted table - } // endif MaxBlk - - if (Closing || ++CurNum == Nrec) { - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - if (!AddBlock) { - // Write back the updated last block values - for (; cp; cp = (PVCTCOL)cp->Next) - cp->WriteBlock(g); - - if (!Closing && !MaxBlk) { - // For VCT tables, future blocks must be added - char filename[_MAX_PATH]; - - // Close the file and reopen it in mode Insert - fclose(Stream); - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) { - Closing = true; // Tell CloseDB of error - return RC_FX; - } // endif Stream - - AddBlock = true; - } // endif Closing - - } else { - // Here we must add a new block to the file - if (Closing) - // Reset the overwritten columns for last block extra records - for (; cp; cp = (PVCTCOL)cp->Next) - memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, - (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', - (Nrec - Last) * cp->Clen); - - if ((size_t)Nrec != - fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) { - sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); - return RC_FX; - } // endif - - } // endif AddBlock - - if (!Closing) { - CurBlk++; - CurNum = 0; - } // endif Closing - - } // endif Closing || CurNum - - } // endif Mode - - return RC_OK; - } // end of WriteBuffer - -/***********************************************************************/ -/* Data Base delete line routine for VCT access method. */ -/* Note: lines are moved directly in the files (ooops...) */ -/* Using temp file depends on the Check setting, false by default. */ -/***********************************************************************/ -int VCTFAM::DeleteRecords(PGLOBAL g, int irc) - { - bool eof = false; - - if (trace) - htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", - irc, UseTemp, Fpos, Tpos, Spos); - - if (irc != RC_OK) { - /*******************************************************************/ - /* EOF: position Fpos at the end-of-file position. */ - /*******************************************************************/ - Fpos = (Block - 1) * Nrec + Last; - - if (trace) - htrc("Fpos placed at file end=%d\n", Fpos); - - eof = UseTemp && !MaxBlk; - } else // Fpos is the Deleted line position - Fpos = CurBlk * Nrec + CurNum; - - if (Tpos == Spos) { - if (UseTemp) { - /*****************************************************************/ - /* Open the temporary file, Spos is at the beginning of file. */ - /*****************************************************************/ - if (OpenTempFile(g)) - return RC_FX; - - } else { - /*****************************************************************/ - /* First line to delete. Move of eventual preceeding lines is */ - /* not required here, just the setting of future Spos and Tpos. */ - /*****************************************************************/ - T_Stream = Stream; - Spos = Tpos = Fpos; - } // endif UseTemp - - } // endif Tpos == Spos - - /*********************************************************************/ - /* Move any intermediate lines. */ - /*********************************************************************/ - if (MoveIntermediateLines(g, &eof)) - return RC_FX; - - if (irc == RC_OK) { - /*******************************************************************/ - /* Reposition the file pointer and set Spos. */ - /*******************************************************************/ -#ifdef _DEBUG - assert(Spos == Fpos); -#endif - Spos++; // New start position is on next line - - if (trace) - htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); - - } else { - /*******************************************************************/ - /* Last call after EOF has been reached. */ - /* Update the Block and Last values. */ - /*******************************************************************/ - Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; - Last = (Tpos + Nrec - 1) % Nrec + 1; - - if (!UseTemp) { // The UseTemp case is treated in CloseTableFile - if (!MaxBlk) { - /***************************************************************/ - /* Because the chsize functionality is only accessible with a */ - /* system call we must close the file and reopen it with the */ - /* open function (_fopen for MS ??) this is still to be */ - /* checked for compatibility with Text files and other OS's. */ - /***************************************************************/ - char filename[_MAX_PATH]; - int h; - - /*rc =*/ CleanUnusedSpace(g); // Clean last block - /*rc =*/ PlugCloseFile(g, To_Fb); - Stream = NULL; // For SetBlockInfo - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) - return RC_FX; - - /***************************************************************/ - /* Remove extra blocks. */ - /***************************************************************/ -#if defined(UNIX) - if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) { - sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); - close(h); - return RC_FX; - } // endif -#else - if (chsize(h, Headlen + Block * Blksize)) { - sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); - close(h); - return RC_FX; - } // endif -#endif - - close(h); - - if (trace) - htrc("done, h=%d irc=%d\n", h, irc); - - } else - // Clean the unused space in the file, this is required when - // inserting again with a partial column list. - if (CleanUnusedSpace(g)) - return RC_FX; - - if (ResetTableSize(g, Block, Last)) - return RC_FX; - - } // endif UseTemp - - } // endif irc - - return RC_OK; // All is correct - } // end of DeleteRecords - -/***********************************************************************/ -/* Open a temporary file used while updating or deleting. */ -/***********************************************************************/ -bool VCTFAM::OpenTempFile(PGLOBAL g) - { - char *opmode, tempname[_MAX_PATH]; - bool rc = false; - - /*********************************************************************/ - /* Open the temporary file, Spos is at the beginning of file. */ - /*********************************************************************/ - PlugSetPath(tempname, To_File, Tdbp->GetPath()); - strcat(PlugRemoveType(tempname, tempname), ".t"); - - if (MaxBlk) { - if (MakeEmptyFile(g, tempname)) - return true; - - opmode = "r+b"; - } else - opmode = "wb"; - - if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) { - if (trace) - htrc("%s\n", g->Message); - - rc = true; - } else - To_Fbt = PlgGetUser(g)->Openlist; - - return rc; - } // end of OpenTempFile - -/***********************************************************************/ -/* Move intermediate deleted or updated lines. */ -/***********************************************************************/ -bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b) - { - int i, dep, off; - int n; - bool eof = (b) ? *b : false; - size_t req, len; - - for (n = Fpos - Spos; n > 0 || eof; n -= req) { - /*******************************************************************/ - /* Non consecutive line to delete. Move intermediate lines. */ - /*******************************************************************/ - if (!MaxBlk) - req = (size_t)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); - else - req = (size_t)min(n, Nrec); - - if (req) for (i = 0; i < Ncol; i++) { - if (MaxBlk) { - dep = Deplac[i]; - off = Spos * Clens[i]; - } else { - if (UseTemp) - To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; - - dep = Deplac[i] + (Spos / Nrec) * Blksize; - off = (Spos % Nrec) * Clens[i]; - } // endif MaxBlk - - if (fseek(Stream, dep + off, SEEK_SET)) { - sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); - return true; - } // endif - - len = fread(To_Buf, Clens[i], req, Stream); - - if (trace) - htrc("after read req=%d len=%d\n", req, len); - - if (len != req) { - sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); - return true; - } // endif len - - if (!UseTemp || MaxBlk) { - if (MaxBlk) { - dep = Deplac[i]; - off = Tpos * Clens[i]; - } else { - dep = Deplac[i] + (Tpos / Nrec) * Blksize; - off = (Tpos % Nrec) * Clens[i]; - } // endif MaxBlk - - if (fseek(T_Stream, dep + off, SEEK_SET)) { - sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); - return true; - } // endif - - if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { - sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); - return true; - } // endif - - } // endif UseTemp - - if (trace) - htrc("after write pos=%d\n", ftell(Stream)); - - } // endfor i - - Tpos += (int)req; - Spos += (int)req; - - if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) { - // Write the full or last block to the temporary file - if ((dep = Nrec - (Tpos % Nrec)) < Nrec) - // Clean the last block in case of future insert, - // must be done here because T_Stream was open in write only. - for (i = 0; i < Ncol; i++) { - To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; - memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); - } // endfor i - - // Write a new block in the temporary file - len = (size_t)Blksize; - - if (fwrite(NewBlock, 1, len, T_Stream) != len) { - sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); - return true; - } // endif - - if (Spos == Fpos) - eof = false; - - } // endif UseTemp - - if (trace) - htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); - - } // endfor n - - return false; - } // end of MoveIntermediateLines - -/***********************************************************************/ -/* Clean deleted space in a VCT or Vec table file. */ -/***********************************************************************/ -bool VCTFAM::CleanUnusedSpace(PGLOBAL g) - { - int i, dep; - int n; - size_t req, len; - - if (!MaxBlk) { - /*******************************************************************/ - /* Clean last block of the VCT table file. */ - /*******************************************************************/ - assert(!UseTemp); - - if (!(n = Nrec - Last)) - return false; - - dep = (Block - 1) * Blksize; - req = (size_t)n; - - for (i = 0; i < Ncol; i++) { - memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); - - if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) { - sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); - return true; - } // endif - - if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) { - sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); - return true; - } // endif - - } // endfor i - - } else for (n = Fpos - Tpos; n > 0; n -= req) { - /*******************************************************************/ - /* Fill VEC file remaining lines with 0's. */ - /* Note: this seems to work even column blocks have been made */ - /* with Blanks = true. Perhaps should it be set to false for VEC. */ - /*******************************************************************/ - req = (size_t)min(n, Nrec); - memset(To_Buf, 0, Buflen); - - for (i = 0; i < Ncol; i++) { - if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) { - sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); - return true; - } // endif - - if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { - sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); - return true; - } // endif - - } // endfor i - - Tpos += (int)req; - } // endfor n - - return false; - } // end of CleanUnusedSpace - -/***********************************************************************/ -/* Data Base close routine for VCT access method. */ -/***********************************************************************/ -void VCTFAM::CloseTableFile(PGLOBAL g) - { - int rc = 0, wrc = RC_OK; - MODE mode = Tdbp->GetMode(); - - if (mode == MODE_INSERT) { - if (Closing) - wrc = RC_FX; // Last write was in error - else - if (CurNum) { - // Some more inserted lines remain to be written - Last = CurNum; - Block = CurBlk + 1; - Closing = true; - wrc = WriteBuffer(g); - } else { - Last = Nrec; - Block = CurBlk; - wrc = RC_OK; - } // endif CurNum - - if (wrc != RC_FX) { - rc = ResetTableSize(g, Block, Last); - } else if (AddBlock) { - // Last block was not written - rc = ResetTableSize(g, CurBlk, Nrec); - longjmp(g->jumper[g->jump_level], 44); - } // endif - - } else if (mode == MODE_UPDATE) { - // Write back to file any pending modifications - for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; - colp; colp = (PVCTCOL)colp->Next) - colp->WriteBlock(g); - - if (UseTemp && T_Stream) { - rc = RenameTempFile(g); - - if (Header) { - // Header must be set because it was not set in temp file - Stream = T_Stream = NULL; // For SetBlockInfo - rc = SetBlockInfo(g); - } // endif Header - - } // endif UseTemp - - } else if (mode == MODE_DELETE && UseTemp && T_Stream) { - if (MaxBlk) - rc = CleanUnusedSpace(g); - - if ((rc = RenameTempFile(g)) != RC_FX) { - Stream = T_Stream = NULL; // For SetBlockInfo - rc = ResetTableSize(g, Block, Last); - } // endif rc - - } // endif's mode - - if (!(UseTemp && T_Stream)) - rc = PlugCloseFile(g, To_Fb); - - if (trace) - htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", - To_File, wrc, rc); - - Stream = NULL; - } // end of CloseTableFile - -/***********************************************************************/ -/* Data Base close routine for VCT access method. */ -/***********************************************************************/ -bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last) - { - bool rc = false; - - // Set Block and Last values for TDBVCT::MakeBlockValues - Block = block; - Last = last; - - if (!Split) { - if (!Header) { - // Update catalog values for Block and Last - PVCTDEF defp = (PVCTDEF)Tdbp->GetDef(); - LPCSTR name = Tdbp->GetName(); - PCATLG cat = PlgGetCatalog(g); - - defp->SetBlock(Block); - defp->SetLast(Last); - - if (!cat->SetIntCatInfo("Blocks", Block) || - !cat->SetIntCatInfo("Last", Last)) { - sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); - rc = true; - } // endif - - } else - rc = SetBlockInfo(g); - - } // endif Split - - Tdbp->ResetSize(); - return rc; - } // end of ResetTableSize - -/***********************************************************************/ -/* Rewind routine for VCT access method. */ -/***********************************************************************/ -void VCTFAM::Rewind(void) - { - // In mode update we need to read Set Column blocks - if (Tdbp->GetMode() == MODE_UPDATE) - OldBlk = -1; - - // Initialize so block optimization is called for 1st block - CurBlk = -1; - CurNum = Nrec - 1; -//rewind(Stream); will be placed by fseek - } // end of Rewind - -/***********************************************************************/ -/* ReadBlock: Read column values from current block. */ -/***********************************************************************/ -bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) - { - int len; - size_t n; - - /*********************************************************************/ - /* Calculate the offset and size of the block to read. */ - /*********************************************************************/ - if (MaxBlk) // True vector format - len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk); - else // Blocked vector format - len = Nrec * (colp->Deplac + Lrecl * CurBlk); - - if (trace) - htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n", - len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); - - if (fseek(Stream, len, SEEK_SET)) { - sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); - return true; - } // endif - - n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, - (size_t)Nrec, Stream); - - if (n != (size_t)Nrec) { - if (errno == NO_ERROR) - sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File); - else - sprintf(g->Message, MSG(READ_ERROR), - To_File, strerror(errno)); - - if (trace) - htrc(" Read error: %s\n", g->Message); - - return true; - } // endif - - if (trace) - num_read++; - - return false; - } // end of ReadBlock - -/***********************************************************************/ -/* WriteBlock: Write back current column values for one block. */ -/* Note: the test of Status is meant to prevent physical writing of */ -/* the block during the checking loop in mode Update. It is set to */ -/* BUF_EMPTY when reopening the table between the two loops. */ -/***********************************************************************/ -bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) - { - int len; - size_t n; - - /*********************************************************************/ - /* Calculate the offset and size of the block to write. */ - /*********************************************************************/ - if (MaxBlk) // File has Vector format - len = Headlen - + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk); - else // Old VCT format - len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk); - - if (trace) - htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", - Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk); - - if (fseek(T_Stream, len, SEEK_SET)) { - sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); - return true; - } // endif - - // Here Nrec was changed to CurNum in mode Insert, - // this is the true number of records to write, - // this also avoid writing garbage in the file for true vector tables. - n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec; - - if (n != fwrite(colp->Blk->GetValPointer(), - (size_t)colp->Clen, n, T_Stream)) { - sprintf(g->Message, MSG(WRITE_STRERROR), - (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno)); - - if (trace) - htrc("Write error: %s\n", strerror(errno)); - - return true; - } // endif - -#if defined(UNIX) - fflush(T_Stream); //NGC -#endif - -#ifdef _DEBUG - num_write++; -#endif - - return false; - } // end of WriteBlock - -/* -------------------------- Class VCMFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the VCMFAM class. */ -/***********************************************************************/ -VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) - { - Memory = NULL; - Memcol = NULL; - } // end of VCMFAM standard constructor - -VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp) - { - Memory = txfp->Memory; - Memcol = txfp->Memcol; - } // end of VCMFAM copy constructor - -/***********************************************************************/ -/* Mapped VCT 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 VCMFAM::OpenTableFile(PGLOBAL g) - { - char filename[_MAX_PATH]; - int len; - MODE mode = Tdbp->GetMode(); - PFBLOCK fp = NULL; - PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; - - /*********************************************************************/ - /* Update block info if necessary. */ - /*********************************************************************/ - if (Block < 0) - if ((Headlen = GetBlockInfo(g)) < 0) - return true; - - /*********************************************************************/ - /* We used the file name relative to recorded datapath. */ - /*********************************************************************/ - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - /*********************************************************************/ - /* The whole file will be mapped so we can use it as if it were */ - /* entirely read into virtual memory. */ - /* Firstly we check whether this file have been already mapped. */ - /*********************************************************************/ - if (mode == MODE_READ) { - for (fp = dbuserp->Openlist; fp; fp = fp->Next) - if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) - && fp->Count && fp->Mode == mode) - break; - - if (trace) - htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count); - - } else - fp = NULL; - - if (fp) { - /*******************************************************************/ - /* File already mapped. Just increment use count and get pointer. */ - /*******************************************************************/ - fp->Count++; - Memory = fp->Memory; - len = fp->Length; - } else { - /*******************************************************************/ - /* If required, delete the whole file if no filtering is implied. */ - /*******************************************************************/ - bool del; - HANDLE hFile; - MEMMAP mm; - MODE mapmode = mode; - - if (mode == MODE_INSERT) { - if (MaxBlk) { - if (!Block) - if (MakeEmptyFile(g, To_File)) - return true; - - // Inserting will be like updating the file - mapmode = MODE_UPDATE; - } else { - strcpy(g->Message, "MAP Insert is for VEC Estimate tables only"); - return true; - } // endif MaxBlk - - } // endif mode - - del = mode == MODE_DELETE && !Tdbp->GetNext(); - - if (del) { - DelRows = Cardinality(g); - - // This will stop the process by causing GetProgMax to return 0. -// ResetTableSize(g, 0, Nrec); must be done later - } // endif del - - /*******************************************************************/ - /* Create the mapping file object. */ - /*******************************************************************/ - hFile = CreateFileMap(g, filename, &mm, mapmode, del); - - if (hFile == INVALID_HANDLE_VALUE) { - DWORD rc = GetLastError(); - - if (!(*g->Message)) - sprintf(g->Message, MSG(OPEN_MODE_ERROR), - "map", (int) rc, filename); - - if (trace) - htrc("%s\n", g->Message); - - return (mode == MODE_READ && rc == ENOENT) - ? PushWarning(g, Tdbp) : true; - } // endif hFile - - /*******************************************************************/ - /* Get the file size (assuming file is smaller than 4 GB) */ - /*******************************************************************/ - len = mm.lenL; - Memory = (char *)mm.memory; - - if (!len) { // Empty or deleted file - CloseFileHandle(hFile); - bool rc = ResetTableSize(g, 0, Nrec); - return (mapmode == MODE_UPDATE) ? true : rc; - } // endif len - - if (!Memory) { - CloseFileHandle(hFile); - sprintf(g->Message, MSG(MAP_VIEW_ERROR), - filename, GetLastError()); - return true; - } // endif Memory - - if (mode != MODE_DELETE) { - CloseFileHandle(hFile); // Not used anymore - hFile = INVALID_HANDLE_VALUE; // For Fblock - } // endif Mode - - /*******************************************************************/ - /* Link a Fblock. This make possible to reuse already opened maps */ - /* and also to automatically unmap them in case of error g->jump. */ - /* Note: block can already exist for previously closed file. */ - /*******************************************************************/ - fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); - fp->Type = TYPE_FB_MAP; - fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); - strcpy((char*)fp->Fname, filename); - fp->Next = dbuserp->Openlist; - dbuserp->Openlist = fp; - fp->Count = 1; - fp->Length = len; - fp->Memory = Memory; - fp->Mode = mode; - fp->File = NULL; - fp->Handle = hFile; // Used for Delete - } // endif fp - - To_Fb = fp; // Useful when closing - - if (trace) - htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n", - fp, fp->Count, Memory, len); - - return AllocateBuffer(g); - } // end of OpenTableFile - -/***********************************************************************/ -/* Allocate the block buffers for columns used in the query. */ -/* Give a dummy value (1) to prevent allocating the value block. */ -/* It will be set pointing into the memory map of the file. */ -/* Note: Memcol must be set for all columns because it can be used */ -/* for set columns in Update. Clens values are used only in Delete. */ -/***********************************************************************/ -bool VCMFAM::AllocateBuffer(PGLOBAL g) - { - int m, i = 0; - bool b = Tdbp->GetMode() == MODE_DELETE; - PVCTCOL cp; - PCOLDEF cdp; - PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - - // Calculate the number of columns - if (!Ncol) - for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) - Ncol++; - - // To store the start position of each column - Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*)); - m = (MaxBlk) ? MaxBlk : 1; - - // We will need all column sizes and type for Delete - if (b) { - Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); - } // endif b - - for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) { - if (b) { - Clens[i] = cdp->GetClen(); - Isnum[i] = IsTypeNum(cdp->GetType()); - } // endif b - - Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec; - } // endfor cdp - - for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial()) { // Not a pseudo column - cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, - cp->Format.Length, cp->Format.Prec); - cp->AddStatus(BUF_MAPPED); - } // endif IsSpecial - - if (Tdbp->GetMode() == MODE_INSERT) - return InitInsert(g); - - return false; - } // end of AllocateBuffer - -/***********************************************************************/ -/* Do initial action when inserting. */ -/***********************************************************************/ -bool VCMFAM::InitInsert(PGLOBAL g) - { - int rc; - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - // We come here in MODE_INSERT only - if (Last == Nrec) { - CurBlk = Block; - CurNum = 0; - AddBlock = !MaxBlk; - } else { - // The starting point must be at the end of file as for append. - CurBlk = Block - 1; - CurNum = Last; - } // endif Last - - // Prepare error return - if (g->jump_level == MAX_JUMP) { - strcpy(g->Message, MSG(TOO_MANY_JUMPS)); - return true; - } // endif - - if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { - g->jump_level--; - return true; - } // endif - - // Initialize the column block pointer - for (; cp; cp = (PVCTCOL)cp->Next) - cp->ReadBlock(g); - - g->jump_level--; - return false; - } // end of InitInsert - -/***********************************************************************/ -/* Data Base write routine for VMP access method. */ -/***********************************************************************/ -int VCMFAM::WriteBuffer(PGLOBAL g) - { - if (trace) - htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", - Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); - - // Mode Update being done in ReadDB we process here Insert mode only. - if (Tdbp->GetMode() == MODE_INSERT) { - if (CurBlk == MaxBlk) { - strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); - return RC_EF; // Too many lines for vector formatted table - } // endif MaxBlk - - if (Closing || ++CurNum == Nrec) { - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - // Write back the updated last block values - for (; cp; cp = (PVCTCOL)cp->Next) - cp->WriteBlock(g); - - if (!Closing) { - CurBlk++; - CurNum = 0; - - // Re-initialize the column block pointer - for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) - cp->ReadBlock(g); - - } // endif Closing - - } // endif Closing || CurNum - - } // endif Mode - - return RC_OK; - } // end of WriteBuffer - -/***********************************************************************/ -/* Data Base delete line routine for VMP access method. */ -/* Lines between deleted lines are moved in the mapfile view. */ -/***********************************************************************/ -int VCMFAM::DeleteRecords(PGLOBAL g, int irc) - { - int i; - int m, n; - - if (trace) - htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", - irc, To_Buf, Tpos, Spos); - - if (irc != RC_OK) { - /*******************************************************************/ - /* EOF: position Fpos at the top of map position. */ - /*******************************************************************/ - Fpos = (Block - 1) * Nrec + Last; - - if (trace) - htrc("Fpos placed at file top=%p\n", Fpos); - - } else // Fpos is the Deleted line position - Fpos = CurBlk * Nrec + CurNum; - - if (Tpos == Spos) - /*******************************************************************/ - /* First line to delete. Move of eventual preceeding lines is */ - /* not required here, just setting of future Spos and Tpos. */ - /*******************************************************************/ - Tpos = Fpos; // Spos is set below - else if (Fpos > Spos) { - /*******************************************************************/ - /* Non consecutive line to delete. Move intermediate lines. */ - /*******************************************************************/ - if (!MaxBlk) { - // Old VCT format, moving must respect block limits - char *ps, *pt; - int req, soff, toff; - - for (n = Fpos - Spos; n > 0; n -= req) { - soff = Spos % Nrec; - toff = Tpos % Nrec; - req = (size_t)min(n, Nrec - max(soff, toff)); - - for (i = 0; i < Ncol; i++) { - ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i]; - pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i]; - memmove(pt, ps, req * Clens[i]); - } // endfor i - - Tpos += req; - Spos += req; - } // endfor n - - } else { - // True vector format, all is simple... - n = Fpos - Spos; - - for (i = 0; i < Ncol; i++) { - m = Clens[i]; - memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m); - } // endfor i - - Tpos += n; - } // endif MaxBlk - - if (trace) - htrc("move %d bytes\n", n); - - } // endif n - - if (irc == RC_OK) { - Spos = Fpos + 1; // New start position - - if (trace) - htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); - - } else { - /*******************************************************************/ - /* Last call after EOF has been reached. Reset the Block and */ - /* Last values for TDBVCT::MakeBlockValues. */ - /*******************************************************************/ - Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; - Last = (Tpos + Nrec - 1) % Nrec + 1; - - if (!MaxBlk) { - PFBLOCK fp = To_Fb; - - // Clean the unused part of the last block - m = (Block - 1) * Blksize; - n = Nrec - Last; - - for (i = 0; i < Ncol; i++) - memset(Memcol[i] + m + Last * Clens[i], - (Isnum[i]) ? 0 : ' ', n * Clens[i]); - - // We must Unmap the view and use the saved file handle - // to put an EOF at the end of the last block of the file. - CloseMemMap(fp->Memory, (size_t)fp->Length); - fp->Count = 0; // Avoid doing it twice - - // Remove extra blocks - n = Block * Blksize; - -#if defined(WIN32) - DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); - - if (drc == 0xFFFFFFFF) { - sprintf(g->Message, MSG(FUNCTION_ERROR), - "SetFilePointer", GetLastError()); - CloseHandle(fp->Handle); - return RC_FX; - } // endif - - if (trace) - htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); - - if (!SetEndOfFile(fp->Handle)) { - sprintf(g->Message, MSG(FUNCTION_ERROR), - "SetEndOfFile", GetLastError()); - CloseHandle(fp->Handle); - return RC_FX; - } // endif - - CloseHandle(fp->Handle); -#else // UNIX - if (ftruncate(fp->Handle, (off_t)n)) { - sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); - close(fp->Handle); - return RC_FX; - } // endif - - close(fp->Handle); -#endif // UNIX - } else - // True vector table, Table file size does not change. - // Just clean the unused part of the file. - for (n = Fpos - Tpos, i = 0; i < Ncol; i++) - memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]); - - // Reset Last and Block values in the catalog - PlugCloseFile(g, To_Fb); // in case of Header - ResetTableSize(g, Block, Last); - } // endif irc - - return RC_OK; // All is correct - } // end of DeleteRecords - -/***********************************************************************/ -/* Data Base close routine for VMP access method. */ -/***********************************************************************/ -void VCMFAM::CloseTableFile(PGLOBAL g) - { - int wrc = RC_OK; - MODE mode = Tdbp->GetMode(); - - if (mode == MODE_INSERT) { - if (!Closing) { - if (CurNum) { - // Some more inserted lines remain to be written - Last = CurNum; - Block = CurBlk + 1; - Closing = true; - wrc = WriteBuffer(g); - } else { - Last = Nrec; - Block = CurBlk; - wrc = RC_OK; - } // endif CurNum - - } else - wrc = RC_FX; // Last write was in error - - PlugCloseFile(g, To_Fb); - - if (wrc != RC_FX) - /*rc =*/ ResetTableSize(g, Block, Last); - - } else if (mode != MODE_DELETE) - PlugCloseFile(g, To_Fb); - - } // end of CloseTableFile - -/***********************************************************************/ -/* ReadBlock: Read column values from current block. */ -/***********************************************************************/ -bool VCMFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) - { - char *mempos; - int i = colp->Index - 1; - int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl); - - /*********************************************************************/ - /* Calculate the start position of the column block to read. */ - /*********************************************************************/ - mempos = Memcol[i] + n * CurBlk; - - if (trace) - htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n", - mempos, i, Nrec, colp->Clen, CurBlk); - - if (colp->GetStatus(BUF_MAPPED)) - colp->Blk->SetValPointer(mempos); - - if (trace) - num_read++; - - return false; - } // end of ReadBlock - -/***********************************************************************/ -/* WriteBlock: Write back current column values for one block. */ -/* Note: there is nothing to do because we are working directly into */ -/* the mapped file, except when checking for Update but in this case */ -/* we do not want to write back the modifications either. */ -/***********************************************************************/ -bool VCMFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) - { -#if defined(_DEBUG) - char *mempos; - int i = colp->Index - 1; - int n = Nrec * colp->Clen; - - /*********************************************************************/ - /* Calculate the offset and size of the block to write. */ - /*********************************************************************/ - mempos = Memcol[i] + n * CurBlk; - - if (trace) - htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n", - Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk); - -#endif // _DEBUG - - return false; - } // end of WriteBlock - -/* -------------------------- Class VECFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the VECFAM class. */ -/***********************************************************************/ -VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) - { - Streams = NULL; - To_Fbs = NULL; - To_Bufs = NULL; - Split = true; - Block = Last = -1; - InitUpdate = false; - } // end of VECFAM standard constructor - -VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp) - { - Streams = txfp->Streams; - To_Fbs = txfp->To_Fbs; - Clens = txfp->Clens; - To_Bufs = txfp->To_Bufs; - InitUpdate = txfp->InitUpdate; - } // end of VECFAM copy constructor - -/***********************************************************************/ -/* VEC 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 VECFAM::OpenTableFile(PGLOBAL g) - { - char opmode[4]; - int i; - bool b= false; - PCOLDEF cdp; - PVCTCOL cp; - MODE mode = Tdbp->GetMode(); - PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - - /*********************************************************************/ - /* Call Cardinality to set Block and Last values in case it was not */ - /* already called (this happens indeed in test xmode) */ - /*********************************************************************/ - Cardinality(g); - - /*********************************************************************/ - /* Open according to input/output mode required. */ - /*********************************************************************/ - switch (mode) { - case MODE_READ: - strcpy(opmode, "rb"); - break; - case MODE_DELETE: - if (!Tdbp->GetNext()) { - // Store the number of deleted lines - DelRows = Cardinality(g); - - // This will delete the whole file - strcpy(opmode, "wb"); - - // This will stop the process by causing GetProgMax to return 0. - ResetTableSize(g, 0, Nrec); - break; - } // endif filter - - // Selective delete, pass thru - case MODE_UPDATE: - UseTemp = Tdbp->IsUsingTemp(g); - strcpy(opmode, (UseTemp) ? "r": "r+"); - break; - case MODE_INSERT: - strcpy(opmode, "ab"); - break; - default: - sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); - return true; - } // endswitch Mode - - /*********************************************************************/ - /* Initialize the array of file structures. */ - /*********************************************************************/ - if (!Colfn) { - // Prepare the column file name pattern and set Ncol - Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); - Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); - } // endif Colfn - - Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); - To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); - - for (i = 0; i < Ncol; i++) { - Streams[i] = NULL; - To_Fbs[i] = NULL; - } // endif i - - /*********************************************************************/ - /* Open the files corresponding to columns used in the query. */ - /*********************************************************************/ - if (mode == MODE_INSERT || mode == MODE_DELETE) { - // All columns must be written or deleted - for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) - if (OpenColumnFile(g, opmode, i)) - return true; - - // Check for void table or missing columns - for (b = !Streams[0], i = 1; i < Ncol; i++) - if (b != !Streams[i]) - return true; - - } else { - /*******************************************************************/ - /* Open the files corresponding to updated columns of the query. */ - /*******************************************************************/ - for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; - cp = (PVCTCOL)cp->Next) - if (OpenColumnFile(g, opmode, cp->Index - 1)) - return true; - - // Open in read only mode the used columns not already open - for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial() && !Streams[cp->Index - 1]) - if (OpenColumnFile(g, "rb", cp->Index - 1)) - return true; - - // Check for void table or missing columns - for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp; - cp = (PVCTCOL)cp->Next) - if (!i++) - b = !Streams[cp->Index - 1]; - else if (b != !Streams[cp->Index - 1]) - return true; - - } // endif mode - - /*********************************************************************/ - /* Allocate the table and column block buffer. */ - /*********************************************************************/ - return (b) ? false : AllocateBuffer(g); - } // end of OpenTableFile - -/***********************************************************************/ -/* Open the file corresponding to one column. */ -/***********************************************************************/ -bool VECFAM::OpenColumnFile(PGLOBAL g, char *opmode, int i) - { - char filename[_MAX_PATH]; - PDBUSER dup = PlgGetUser(g); - - sprintf(filename, Colfn, i+1); - - if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) { - if (trace) - htrc("%s\n", g->Message); - - return (Tdbp->GetMode() == MODE_READ && errno == ENOENT) - ? PushWarning(g, Tdbp) : true; - } // endif Streams - - if (trace) - htrc("File %s is open in mode %s\n", filename, opmode); - - To_Fbs[i] = dup->Openlist; // Keep track of File blocks - return false; - } // end of OpenColumnFile - -/***********************************************************************/ -/* Allocate the block buffers for columns used in the query. */ -/***********************************************************************/ -bool VECFAM::AllocateBuffer(PGLOBAL g) - { - int i; - PVCTCOL cp; - PCOLDEF cdp; - PTDBVCT tdbp = (PTDBVCT)Tdbp; - MODE mode = tdbp->GetMode(); - PDOSDEF defp = (PDOSDEF)tdbp->GetDef(); - - if (mode != MODE_READ) { - // Allocate what is needed by all modes except Read - T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); - Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - - // Give default values - for (i = 0; i < Ncol; i++) { - T_Streams[i] = Streams[i]; - Clens[i] = 0; - } // endfor i - - } // endif mode - - if (mode == MODE_INSERT) { - bool chk = PlgGetUser(g)->Check & CHK_TYPE; - - To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*)); - cdp = defp->GetCols(); - - for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { - Clens[i] = cdp->GetClen(); - To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]); - - if (cdp->GetType() == TYPE_STRING) - memset(To_Bufs[i], ' ', Nrec * Clens[i]); - else - memset(To_Bufs[i], 0, Nrec * Clens[i]); - - } // endfor cdp - - for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) - cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1], - cp->Buf_Type, Nrec, cp->Format.Length, - cp->Format.Prec, chk); - - return InitInsert(g); - } else { - if (UseTemp || mode == MODE_DELETE) { - // Allocate all that is needed to move lines and make Temp - if (UseTemp) { - Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); - strcpy(Tempat, Colfn); - PlugSetPath(Tempat, Tempat, Tdbp->GetPath()); - strcat(PlugRemoveType(Tempat, Tempat), ".t"); - T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); - } // endif UseTemp - - if (UseTemp) - for (i = 0; i < Ncol; i++) { - T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL; - T_Fbs[i] = NULL; - } // endfor i - - if (mode == MODE_DELETE) { // All columns are moved - cdp = defp->GetCols(); - - for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { - Clens[i] = cdp->GetClen(); - Buflen = max(Buflen, cdp->GetClen()); - } // endfor cdp - - } else { // Mode Update, only some columns are updated - for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) { - i = cp->Index -1; - - if (UseTemp) - T_Streams[i] = NULL; // Mark the streams to open - - Clens[i] = cp->Clen; - Buflen = max(Buflen, cp->Clen); - } // endfor cp - - InitUpdate = true; // To be initialized - } // endif mode - - To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec); - } // endif mode - - // Finally allocate column buffers for all modes - for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial()) // Not a pseudo column - cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, - cp->Format.Length, cp->Format.Prec); - - } // endif mode - - return false; - } // end of AllocateBuffer - -/***********************************************************************/ -/* Do initial action when inserting. */ -/***********************************************************************/ -bool VECFAM::InitInsert(PGLOBAL g) - { - // We come here in MODE_INSERT only - CurBlk = 0; - CurNum = 0; - AddBlock = true; - return false; - } // end of InitInsert - -/***********************************************************************/ -/* Reset buffer access according to indexing and to mode. */ -/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ -/***********************************************************************/ -void VECFAM::ResetBuffer(PGLOBAL g) - { - /*********************************************************************/ - /* If access is random, performances can be much better when the */ - /* reads are done on only one row, except for small tables that can */ - /* be entirely read in one block. If the index is just used as a */ - /* bitmap filter, as for Update or Delete, reading will be */ - /* sequential and we better keep block reading. */ - /*********************************************************************/ - if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) { - Nrec = 1; // Better for random access - Rbuf = 0; - OldBlk = -2; // Has no meaning anymore - Block = Tdbp->Cardinality(g); // Blocks are one line now - Last = 1; // Probably unuseful - } // endif Mode - - } // end of ResetBuffer - -/***********************************************************************/ -/* Data Base write routine for VCT access method. */ -/***********************************************************************/ -int VECFAM::WriteBuffer(PGLOBAL g) - { - if (trace) - htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", - Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); - - if (Tdbp->GetMode() == MODE_INSERT) { - if (Closing || ++CurNum == Nrec) { - // Here we must add a new blocks to the files - int i; - size_t n = (size_t)CurNum; - - for (i = 0; i < Ncol; i++) - if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) { - sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); - return RC_FX; - } // endif - - if (!Closing) { - CurBlk++; - CurNum = 0; - } // endif Closing - - } // endif Closing || CurNum - - } else // Mode Update - // Writing updates being done in ReadDB we do initialization only. - if (InitUpdate) { - if (OpenTempFile(g)) - return RC_FX; - - InitUpdate = false; // Done - } // endif InitUpdate - - return RC_OK; - } // end of WriteBuffer - -/***********************************************************************/ -/* Data Base delete line routine for split vertical access methods. */ -/* Note: lines are moved directly in the files (ooops...) */ -/***********************************************************************/ -int VECFAM::DeleteRecords(PGLOBAL g, int irc) - { - /*********************************************************************/ - /* There is an alternative here: */ - /* 1 - use a temporary file in which are copied all not deleted */ - /* lines, at the end the original file will be deleted and */ - /* the temporary file renamed to the original file name. */ - /* 2 - directly move the not deleted lines inside the original */ - /* file, and at the end erase all trailing records. */ - /* This depends on the Check setting, false by default. */ - /*********************************************************************/ - if (trace) - htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", - irc, UseTemp, Fpos, Tpos, Spos); - - if (irc != RC_OK) { - /*******************************************************************/ - /* EOF: position Fpos at the end-of-file position. */ - /*******************************************************************/ - Fpos = Cardinality(g); - - if (trace) - htrc("Fpos placed at file end=%d\n", Fpos); - - } else // Fpos is the Deleted line position - Fpos = CurBlk * Nrec + CurNum; - - if (Tpos == Spos) - // First line to delete - if (UseTemp) { - /*****************************************************************/ - /* Open the temporary files, Spos is at the beginning of file. */ - /*****************************************************************/ - if (OpenTempFile(g)) - return RC_FX; - - } else - /*****************************************************************/ - /* Move of eventual preceeding lines is not required here. */ - /* Set the future Tpos, and give Spos a value to block copying. */ - /*****************************************************************/ - Spos = Tpos = Fpos; - - /*********************************************************************/ - /* Move any intermediate lines. */ - /*********************************************************************/ - if (MoveIntermediateLines(g)) - return RC_FX; - - if (irc == RC_OK) { -#ifdef _DEBUG - assert(Spos == Fpos); -#endif - Spos++; // New start position is on next line - - if (trace) - htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); - - } else { - /*******************************************************************/ - /* Last call after EOF has been reached. */ - /*******************************************************************/ - if (!UseTemp) { - /*****************************************************************/ - /* Because the chsize functionality is only accessible with a */ - /* system call we must close the file and reopen it with the */ - /* open function (_fopen for MS??) this is still to be checked */ - /* for compatibility with other OS's. */ - /*****************************************************************/ - char filename[_MAX_PATH]; - int h; // File handle, return code - - for (int i = 0; i < Ncol; i++) { - sprintf(filename, Colfn, i + 1); - /*rc =*/ PlugCloseFile(g, To_Fbs[i]); - - if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) - return RC_FX; - - /***************************************************************/ - /* Remove extra records. */ - /***************************************************************/ -#if defined(UNIX) - if (ftruncate(h, (off_t)(Tpos * Clens[i]))) { - sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); - close(h); - return RC_FX; - } // endif -#else - if (chsize(h, Tpos * Clens[i])) { - sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); - close(h); - return RC_FX; - } // endif -#endif - - close(h); - - if (trace) - htrc("done, h=%d irc=%d\n", h, irc); - - } // endfor i - - } else // UseTemp - // Ok, now delete old files and rename new temp files - if (RenameTempFile(g) == RC_FX) - return RC_FX; - - // Reset these values for TDBVCT::MakeBlockValues - Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; - Last = (Tpos + Nrec - 1) % Nrec + 1; - - if (ResetTableSize(g, Block, Last)) - return RC_FX; - - } // endif irc - - return RC_OK; // All is correct - } // end of DeleteRecords - -/***********************************************************************/ -/* Open temporary files used while updating or deleting. */ -/* Note: the files not updated have been given a T_Stream value of 1. */ -/***********************************************************************/ -bool VECFAM::OpenTempFile(PGLOBAL g) - { - char tempname[_MAX_PATH]; - - for (int i = 0; i < Ncol; i++) - if (!T_Streams[i]) { - /*****************************************************************/ - /* Open the temporary file, Spos is at the beginning of file. */ - /*****************************************************************/ - sprintf(tempname, Tempat, i+1); - - if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) { - if (trace) - htrc("%s\n", g->Message); - - return true; - } else - T_Fbs[i] = PlgGetUser(g)->Openlist; - - } else // This is a column that is not updated - T_Streams[i] = NULL; // For RenameTempFile - - return false; - } // end of OpenTempFile - -/***********************************************************************/ -/* Move intermediate updated lines before writing blocks. */ -/***********************************************************************/ -bool VECFAM::MoveLines(PGLOBAL g) - { - if (UseTemp && !InitUpdate) { // Don't do it in check pass - Fpos = OldBlk * Nrec; - - if (MoveIntermediateLines(g)) { - Closing = true; // ??? - return true; - } // endif UseTemp - -// Spos = Fpos + Nrec; - } // endif UseTemp - return false; - - } // end of MoveLines - -/***********************************************************************/ -/* Move intermediate deleted or updated lines. */ -/***********************************************************************/ -bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *bn) - { - int i; - int n; - bool b = false; - size_t req, len; - - for (n = Fpos - Spos; n > 0; n -= Nrec) { - /*******************************************************************/ - /* Non consecutive line to delete. Move intermediate lines. */ - /*******************************************************************/ - req = (size_t)min(n, Nrec); - - for (i = 0; i < Ncol; i++) { - if (!T_Streams[i]) - continue; // Non updated column - - if (!UseTemp || !b) - if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) { - sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); - return true; - } // endif - - len = fread(To_Buf, Clens[i], req, Streams[i]); - - if (trace) - htrc("after read req=%d len=%d\n", req, len); - - if (len != req) { - sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); - return true; - } // endif len - - if (!UseTemp) - if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) { - sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); - return true; - } // endif - - if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) { - sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); - return true; - } // endif - - if (trace) - htrc("after write pos=%d\n", ftell(Streams[i])); - - } // endfor i - - Tpos += (int)req; - Spos += (int)req; - - if (trace) - htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); - - b = true; - } // endfor n - - return false; - } // end of MoveIntermediate Lines - -/***********************************************************************/ -/* Delete the old files and rename the new temporary files. */ -/***********************************************************************/ -int VECFAM::RenameTempFile(PGLOBAL g) - { - char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH]; - int rc = RC_OK; - - // Close all files. - // This loop is necessary because, in case of join, - // the table files can have been open several times. - for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next) - rc = PlugCloseFile(g, fb); - - for (int i = 0; i < Ncol && rc == RC_OK; i++) { - if (!T_Fbs[i]) - continue; - - tempname = (char*)T_Fbs[i]->Fname; - sprintf(filename, Colfn, i+1); - PlugSetPath(filename, filename, Tdbp->GetPath()); - strcat(PlugRemoveType(filetemp, filename), ".ttt"); - remove(filetemp); // May still be there from previous error - - if (rename(filename, filetemp)) { // Save file for security - sprintf(g->Message, MSG(RENAME_ERROR), - filename, filetemp, strerror(errno)); - rc = RC_FX; - } else if (rename(tempname, filename)) { - sprintf(g->Message, MSG(RENAME_ERROR), - tempname, filename, strerror(errno)); - rc = rename(filetemp, filename); // Restore saved file - rc = RC_FX; - } else if (remove(filetemp)) { - sprintf(g->Message, MSG(REMOVE_ERROR), - filetemp, strerror(errno)); - rc = RC_INFO; // Acceptable - } // endif's - - } // endfor i - - return rc; - } // end of RenameTempFile - -/***********************************************************************/ -/* Data Base close routine for VEC access method. */ -/***********************************************************************/ -void VECFAM::CloseTableFile(PGLOBAL g) - { - int rc = 0, wrc = RC_OK; - MODE mode = Tdbp->GetMode(); - - if (mode == MODE_INSERT) { - if (Closing) - wrc = RC_FX; // Last write was in error - else - if (CurNum) { - // Some more inserted lines remain to be written - Last += (CurBlk * Nrec + CurNum -1); - Block += (Last / Nrec); - Last = Last % Nrec + 1; - Closing = true; - wrc = WriteBuffer(g); - } else { - Block += CurBlk; - wrc = RC_OK; - } // endif CurNum - - if (wrc != RC_FX) - rc = ResetTableSize(g, Block, Last); - else - longjmp(g->jumper[g->jump_level], 44); - - } else if (mode == MODE_UPDATE) { - if (UseTemp && !InitUpdate) { - // Write any intermediate lines to temp file - Fpos = OldBlk * Nrec; - wrc = MoveIntermediateLines(g); -// Spos = Fpos + Nrec; - } // endif UseTemp - - // Write back to file any pending modifications - if (wrc == RC_OK) - for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; - colp; colp = (PVCTCOL)colp->Next) - colp->WriteBlock(g); - - if (wrc == RC_OK && UseTemp && !InitUpdate) { - // Write any intermediate lines to temp file - Fpos = (Block - 1) * Nrec + Last; - wrc = MoveIntermediateLines(g); - } // endif UseTemp - - } // endif's mode - - if (UseTemp && !InitUpdate) { - // If they are errors, leave files unchanged - if (wrc == RC_OK) - rc = RenameTempFile(g); - else - longjmp(g->jumper[g->jump_level], 44); - - } else if (Streams) - for (int i = 0; i < Ncol; i++) - if (Streams[i]) { - rc = PlugCloseFile(g, To_Fbs[i]); - Streams[i] = NULL; - To_Fbs[i] = NULL; - } // endif Streams - - if (trace) - htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc); - - } // end of CloseTableFile - -/***********************************************************************/ -/* ReadBlock: Read column values from current block. */ -/***********************************************************************/ -bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) - { - int i, len; - size_t n; - - /*********************************************************************/ - /* Calculate the offset and size of the block to read. */ - /*********************************************************************/ - len = Nrec * colp->Clen * CurBlk; - i = colp->Index - 1; - - if (trace) - htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n", - len, i, Nrec, colp->Deplac, Lrecl, CurBlk); - - if (fseek(Streams[i], len, SEEK_SET)) { - sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); - return true; - } // endif - - n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, - (size_t)Nrec, Streams[i]); - - if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) { - char fn[_MAX_PATH]; - - sprintf(fn, Colfn, colp->Index); -#if defined(WIN32) - if (feof(Streams[i])) -#else // !WIN32 - if (errno == NO_ERROR) -#endif // !WIN32 - sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn); - else - sprintf(g->Message, MSG(READ_ERROR), - fn, strerror(errno)); - - if (trace) - htrc(" Read error: %s\n", g->Message); - - return true; - } // endif - - if (trace) - num_read++; - - return false; - } // end of ReadBlock - -/***********************************************************************/ -/* WriteBlock: Write back current column values for one block. */ -/* Note: the test of Status is meant to prevent physical writing of */ -/* the block during the checking loop in mode Update. It is set to */ -/* BUF_EMPTY when reopening the table between the two loops. */ -/***********************************************************************/ -bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) - { - int i, len; - size_t n; - - /*********************************************************************/ - /* Calculate the offset and size of the block to write. */ - /*********************************************************************/ - len = Nrec * colp->Clen * colp->ColBlk; - i = colp->Index - 1; - - if (trace) - htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", - Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk); - - if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp) - if (fseek(T_Streams[i], len, SEEK_SET)) { - sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); - return true; - } // endif - - // Here Nrec was changed to CurNum in mode Insert, - // this is the true number of records to write, - // this also avoid writing garbage in the file for true vector tables. - n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum - : (colp->ColBlk == Block - 1) ? Last : Nrec; - - if (n != fwrite(colp->Blk->GetValPointer(), - (size_t)colp->Clen, n, T_Streams[i])) { - char fn[_MAX_PATH]; - - sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index); - sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); - - if (trace) - htrc("Write error: %s\n", strerror(errno)); - - return true; - } else - Spos = Fpos + n; - -#if defined(UNIX) - fflush(Streams[i]); //NGC -#endif - return false; - } // end of WriteBlock - -/* -------------------------- Class VMPFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the VMPFAM class. */ -/***********************************************************************/ -VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp) - { - To_Fbs = NULL; - Split = true; - Block = Last = -1; - } // end of VMPFAM standard constructor - -VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp) - { - To_Fbs = txfp->To_Fbs; - } // end of VMPFAM copy constructor - -/***********************************************************************/ -/* VCT 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 VMPFAM::OpenTableFile(PGLOBAL g) - { - int i; - bool b; - MODE mode = Tdbp->GetMode(); - PCOLDEF cdp; - PVCTCOL cp; - PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - - if (mode == MODE_DELETE && !Tdbp->GetNext()) { - DelRows = Cardinality(g); - - // This will stop the process by causing GetProgMax to return 0. - ResetTableSize(g, 0, Nrec); - } else - Cardinality(g); // See comment in VECFAM::OpenTbleFile - - - /*********************************************************************/ - /* Prepare the filename pattern for column files and set Ncol. */ - /*********************************************************************/ - if (!Colfn) { - // Prepare the column file name pattern - Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); - Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); - } // endif Colfn - - /*********************************************************************/ - /* Initialize the array of file structures. */ - /*********************************************************************/ - Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *)); - To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); - - for (i = 0; i < Ncol; i++) { - Memcol[i] = NULL; - To_Fbs[i] = NULL; - } // endif i - - /*********************************************************************/ - /* Open the files corresponding to columns used in the query. */ - /*********************************************************************/ - if (mode == MODE_DELETE) { - // All columns are used in Delete mode - for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) - if (MapColumnFile(g, mode, i)) - return true; - - } else { - /*******************************************************************/ - /* Open the files corresponding updated columns of the query. */ - /*******************************************************************/ - for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; - cp = (PVCTCOL)cp->Next) - if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1)) - return true; - - /*******************************************************************/ - /* Open other non already open used columns (except pseudos) */ - /*******************************************************************/ - for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial() && !Memcol[cp->Index - 1]) - if (MapColumnFile(g, MODE_READ, cp->Index - 1)) - return true; - - } // endif mode - - /*********************************************************************/ - /* Check for void table or missing columns */ - /*********************************************************************/ - for (b = !Memcol[0], i = 1; i < Ncol; i++) - if (b != !Memcol[i]) - return true; - - /*********************************************************************/ - /* Allocate the table and column block buffer. */ - /*********************************************************************/ - return (b) ? false : AllocateBuffer(g); - } // end of OpenTableFile - -/***********************************************************************/ -/* Open the file corresponding to one column. */ -/***********************************************************************/ -bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i) - { - char filename[_MAX_PATH]; - int len; - HANDLE hFile; - MEMMAP mm; - PFBLOCK fp; - PDBUSER dup = PlgGetUser(g); - - sprintf(filename, Colfn, i+1); - - /*********************************************************************/ - /* The whole file will be mapped so we can use it as */ - /* if it were entirely read into virtual memory. */ - /* Firstly we check whether this file have been already mapped. */ - /*********************************************************************/ - if (mode == MODE_READ) { - for (fp = dup->Openlist; fp; fp = fp->Next) - if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) - && fp->Count && fp->Mode == mode) - break; - - if (trace) - htrc("Mapping file, fp=%p\n", fp); - - } else - fp = NULL; - - if (fp) { - /*******************************************************************/ - /* File already mapped. Just increment use count and get pointer. */ - /*******************************************************************/ - fp->Count++; - Memcol[i] = fp->Memory; - len = fp->Length; - } else { - /*******************************************************************/ - /* Create the mapping file object. */ - /*******************************************************************/ - hFile = CreateFileMap(g, filename, &mm, mode, DelRows); - - if (hFile == INVALID_HANDLE_VALUE) { - DWORD rc = GetLastError(); - - if (!(*g->Message)) - sprintf(g->Message, MSG(OPEN_MODE_ERROR), - "map", (int) rc, filename); - if (trace) - htrc("%s\n", g->Message); - - return (mode == MODE_READ && rc == ENOENT) - ? PushWarning(g, Tdbp) : true; - } // endif hFile - - /*****************************************************************/ - /* Get the file size (assuming file is smaller than 4 GB) */ - /*****************************************************************/ - len = mm.lenL; - Memcol[i] = (char *)mm.memory; - - if (!len) { // Empty or deleted file - CloseFileHandle(hFile); - ResetTableSize(g, 0, Nrec); - return false; - } // endif len - - if (!Memcol[i]) { - CloseFileHandle(hFile); - sprintf(g->Message, MSG(MAP_VIEW_ERROR), - filename, GetLastError()); - return true; - } // endif Memory - - if (mode != MODE_DELETE) { - CloseFileHandle(hFile); // Not used anymore - hFile = INVALID_HANDLE_VALUE; // For Fblock - } // endif Mode - - /*******************************************************************/ - /* Link a Fblock. This make possible to reuse already opened maps */ - /* and also to automatically unmap them in case of error g->jump. */ - /* Note: block can already exist for previously closed file. */ - /*******************************************************************/ - fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); - fp->Type = TYPE_FB_MAP; - fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); - strcpy((char*)fp->Fname, filename); - fp->Next = dup->Openlist; - dup->Openlist = fp; - fp->Count = 1; - fp->Length = len; - fp->Memory = Memcol[i]; - fp->Mode = mode; - fp->File = NULL; - fp->Handle = hFile; // Used for Delete - } // endif fp - - To_Fbs[i] = fp; // Useful when closing - - if (trace) - htrc("fp=%p count=%d MapView=%p len=%d\n", - fp, fp->Count, Memcol[i], len); - - return false; - } // end of MapColumnFile - -/***********************************************************************/ -/* Allocate the block buffers for columns used in the query. */ -/* Give a dummy value (1) to prevent allocating the value block. */ -/* It will be set pointing into the memory map of the file. */ -/***********************************************************************/ -bool VMPFAM::AllocateBuffer(PGLOBAL g) - { - PVCTCOL cp; - - if (Tdbp->GetMode() == MODE_DELETE) { - PCOLDEF cdp = Tdbp->GetDef()->GetCols(); - - Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - - for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) - Clens[i] = cdp->GetClen(); - - } // endif mode - - for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial()) { // Not a pseudo column - cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, - cp->Format.Length, cp->Format.Prec); - cp->AddStatus(BUF_MAPPED); - } // endif IsSpecial - - return false; - } // end of AllocateBuffer - -/***********************************************************************/ -/* Data Base delete line routine for VMP access method. */ -/* Lines between deleted lines are moved in the mapfile view. */ -/***********************************************************************/ -int VMPFAM::DeleteRecords(PGLOBAL g, int irc) - { - int i; - int m, n; - - if (trace) - htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", - irc, To_Buf, Tpos, Spos); - - if (irc != RC_OK) { - /*******************************************************************/ - /* EOF: position Fpos at the top of map position. */ - /*******************************************************************/ - Fpos = (Block - 1) * Nrec + Last; - - if (trace) - htrc("Fpos placed at file top=%p\n", Fpos); - - } else // Fpos is the Deleted line position - Fpos = CurBlk * Nrec + CurNum; - - if (Tpos == Spos) - /*******************************************************************/ - /* First line to delete. Move of eventual preceeding lines is */ - /* not required here, just setting of future Spos and Tpos. */ - /*******************************************************************/ - Tpos = Fpos; // Spos is set below - else if ((n = Fpos - Spos) > 0) { - /*******************************************************************/ - /* Non consecutive line to delete. Move intermediate lines. */ - /*******************************************************************/ - for (i = 0; i < Ncol; i++) { - m = Clens[i]; - memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n); - } // endif i - - Tpos += n; - - if (trace) - htrc("move %d bytes\n", n); - - } // endif n - - if (irc == RC_OK) { - Spos = Fpos + 1; // New start position - - if (trace) - htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); - - } else { - /*******************************************************************/ - /* Last call after EOF has been reached. */ - /* We must firstly Unmap the view and use the saved file handle */ - /* to put an EOF at the end of the copied part of the file. */ - /*******************************************************************/ - PFBLOCK fp; - - for (i = 0; i < Ncol; i++) { - fp = To_Fbs[i]; - CloseMemMap(fp->Memory, (size_t)fp->Length); - fp->Count = 0; // Avoid doing it twice - - /*****************************************************************/ - /* Remove extra records. */ - /*****************************************************************/ - n = Tpos * Clens[i]; - -#if defined(WIN32) - DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); - - if (drc == 0xFFFFFFFF) { - sprintf(g->Message, MSG(FUNCTION_ERROR), - "SetFilePointer", GetLastError()); - CloseHandle(fp->Handle); - return RC_FX; - } // endif - - if (trace) - htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); - - if (!SetEndOfFile(fp->Handle)) { - sprintf(g->Message, MSG(FUNCTION_ERROR), - "SetEndOfFile", GetLastError()); - CloseHandle(fp->Handle); - return RC_FX; - } // endif - - CloseHandle(fp->Handle); -#else // UNIX - if (ftruncate(fp->Handle, (off_t)n)) { - sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); - close(fp->Handle); - return RC_FX; - } // endif - - close(fp->Handle); -#endif // UNIX - } // endfor i - - } // endif irc - - return RC_OK; // All is correct - } // end of DeleteRecords - -/***********************************************************************/ -/* Data Base close routine for VMP access method. */ -/***********************************************************************/ -void VMPFAM::CloseTableFile(PGLOBAL g) - { - if (Tdbp->GetMode() == MODE_DELETE) { - // Set Block and Nrec values for TDBVCT::MakeBlockValues - Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; - Last = (Tpos + Nrec - 1) % Nrec + 1; - ResetTableSize(g, Block, Last); - } else if (Tdbp->GetMode() == MODE_INSERT) - assert(false); - - for (int i = 0; i < Ncol; i++) - PlugCloseFile(g, To_Fbs[i]); - - } // end of CloseTableFile - -/* -------------------------- Class BGVFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the BGVFAM class. */ -/***********************************************************************/ -// Constructors -BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp) - { - Hfile = INVALID_HANDLE_VALUE; - Tfile = INVALID_HANDLE_VALUE; - BigDep = NULL; - } // end of BGVFAM constructor - -BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp) - { - Hfile = txfp->Hfile; - Tfile = txfp->Tfile; - BigDep= txfp->BigDep; - } // end of BGVFAM copy constructor - -/***********************************************************************/ -/* Set current position in a big file. */ -/***********************************************************************/ -bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b) - { -#if defined(WIN32) - char buf[256]; - DWORD drc, m = (b) ? FILE_END : FILE_BEGIN; - LARGE_INTEGER of; - - of.QuadPart = pos; - of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m); - - if (of.LowPart == INVALID_SET_FILE_POINTER && - (drc = GetLastError()) != NO_ERROR) { - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, - (LPTSTR)buf, sizeof(buf), NULL); - sprintf(g->Message, MSG(SFP_ERROR), buf); - return true; - } // endif -#else // !WIN32 - if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) { - sprintf(g->Message, MSG(ERROR_IN_LSK), errno); - return true; - } // endif -#endif // !WIN32 - - return false; - } // end of BigSeek - -/***********************************************************************/ -/* Read from a big file. */ -/***********************************************************************/ -bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req) - { - bool rc = false; - -#if defined(WIN32) - DWORD nbr, drc, len = (DWORD)req; - bool brc = ReadFile(h, inbuf, len, &nbr, NULL); - - if (trace) - htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr); - - if (!brc || nbr != len) { - char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile"; - - if (brc) - strcpy(buf, MSG(BAD_BYTE_READ)); - else { - drc = GetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, - (LPTSTR)buf, sizeof(buf), NULL); - } // endelse brc - - sprintf(g->Message, MSG(READ_ERROR), To_File, buf); - - if (trace) - htrc("BIGREAD: %s\n", g->Message); - - rc = true; - } // endif brc || nbr -#else // !WIN32 - size_t len = (size_t)req; - ssize_t nbr = read(h, inbuf, len); - - if (nbr != (ssize_t)len) { - const char *fn = (h == Hfile) ? To_File : "Tempfile"; - - sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno)); - - if (trace) - htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n", - nbr, len, errno, g->Message); - - rc = true; - } // endif nbr -#endif // !WIN32 - - return rc; - } // end of BigRead - -/***********************************************************************/ -/* Write into a big file. */ -/***********************************************************************/ -bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req) - { - bool rc = false; - -#if defined(WIN32) - DWORD nbw, drc, len = (DWORD)req; - bool brc = WriteFile(h, inbuf, len, &nbw, NULL); - - if (trace) - htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw); - - if (!brc || nbw != len) { - char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile"; - - if (brc) - strcpy(buf, MSG(BAD_BYTE_NUM)); - else { - drc = GetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, - (LPTSTR)buf, sizeof(buf), NULL); - } // endelse brc - - sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf); - - if (trace) - htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", - nbw, len, drc, g->Message); - - rc = true; - } // endif brc || nbw -#else // !WIN32 - size_t len = (size_t)req; - ssize_t nbw = write(h, inbuf, len); - - if (nbw != (ssize_t)len) { - const char *fn = (h == Hfile) ? To_File : "Tempfile"; - - sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); - - if (trace) - htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", - nbw, len, errno, g->Message); - - rc = true; - } // endif nbr -#endif // !WIN32 - - return rc; - } // end of BigWrite - -/***********************************************************************/ -/* Get the Headlen, Block and Last info from the file header. */ -/***********************************************************************/ -int BGVFAM::GetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - int n; - VECHEADER vh; - HANDLE h; - - if (Header < 1 || Header > 3 || !MaxBlk) { - sprintf(g->Message, "Invalid header value %d", Header); - return -1; - } else - n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header == 2) - strcat(PlugRemoveType(filename, filename), ".blk"); - -#if defined(WIN32) - LARGE_INTEGER len; - - h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (h != INVALID_HANDLE_VALUE) { - // Get the size of the file (can be greater than 4 GB) - len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart); - } // endif h - - if (h == INVALID_HANDLE_VALUE || !len.QuadPart) { -#else // !WIN32 - h = open64(filename, O_RDONLY, 0); - - if (h == INVALID_HANDLE_VALUE || !_filelength(h)) { -#endif // !WIN32 - // Consider this is a void table - if (trace) - htrc("Void table h=%d\n", h); - - Last = Nrec; - Block = 0; - - if (h != INVALID_HANDLE_VALUE) - CloseFileHandle(h); - - return n; - } else if (Header == 3) - /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true); - - if (BigRead(g, h, &vh, sizeof(vh))) { - sprintf(g->Message, "Error reading header file %s", filename); - n = -1; - } else if (MaxBlk * Nrec != vh.MaxRec) { - sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", - vh.MaxRec, MaxBlk, Nrec); - n = -1; - } else { - Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; - Last = (vh.NumRec + Nrec - 1) % Nrec + 1; - - if (trace) - htrc("Block=%d Last=%d\n", Block, Last); - - } // endif's - - CloseFileHandle(h); - return n; - } // end of GetBlockInfo - -/***********************************************************************/ -/* Set the MaxRec and NumRec info in the file header. */ -/***********************************************************************/ -bool BGVFAM::SetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - bool b = false, rc = false; - VECHEADER vh; - HANDLE h = INVALID_HANDLE_VALUE; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header != 2) { - if (Hfile != INVALID_HANDLE_VALUE) { - h = Hfile; - - if (Header == 1) - /*bk =*/ BigSeek(g, h, (BIGINT)0); - - } else - b = true; - - } else // Header == 2 - strcat(PlugRemoveType(filename, filename), ".blk"); - - if (h == INVALID_HANDLE_VALUE) { -#if defined(WIN32) - DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING; - - h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, - NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); - -#else // !WIN32 - int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC; - - h = open64(filename, oflag, 0); -#endif // !WIN32 - - if (h == INVALID_HANDLE_VALUE) { - sprintf(g->Message, "Error opening header file %s", filename); - return true; - } // endif h - - } // endif h - - if (Header == 3) - /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true); - - vh.MaxRec = MaxBlk * Bsize; - vh.NumRec = (Block - 1) * Nrec + Last; - - if (BigWrite(g, h, &vh, sizeof(vh))) { - sprintf(g->Message, "Error writing header file %s", filename); - rc = true; - } // endif fread - - if (Header == 2 || Hfile == INVALID_HANDLE_VALUE) - CloseFileHandle(h); - - return rc; - } // end of SetBlockInfo - -/***********************************************************************/ -/* VEC Create an empty file for new Vector formatted tables. */ -/***********************************************************************/ -bool BGVFAM::MakeEmptyFile(PGLOBAL g, char *fn) - { - // Vector formatted file this will create an empty file of the - // required length if it does not exists yet. - char filename[_MAX_PATH], c = 0; - int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; - - PlugSetPath(filename, fn, Tdbp->GetPath()); - -#if defined(WIN32) - char *p; - DWORD rc; - bool brc; - LARGE_INTEGER of; - HANDLE h; - - h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL); - - if (h == INVALID_HANDLE_VALUE) { - p = MSG(OPENING); - goto err; - } // endif h - - of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; - - if (trace) - htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n", - of.QuadPart, n, MaxBlk, Blksize); - - of.LowPart = SetFilePointer(h, of.LowPart, - &of.HighPart, FILE_BEGIN); - - if (of.LowPart == INVALID_SET_FILE_POINTER && - GetLastError() != NO_ERROR) { - p = MSG(MAKING); - goto err; - } // endif - - brc = WriteFile(h, &c, 1, &rc, NULL); - - if (!brc || rc != 1) { - p = MSG(WRITING); - goto err; - } // endif - - CloseHandle(h); - return false; - - err: - rc = GetLastError(); - sprintf(g->Message, MSG(EMPTY_FILE), p, filename); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, - (LPTSTR)filename, sizeof(filename), NULL); - strcat(g->Message, filename); - - if (h != INVALID_HANDLE_VALUE) - CloseHandle(h); - - return true; -#else // !WIN32 - int h; - BIGINT pos; - - h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); - - if (h == -1) - return true; - - pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; - - if (trace) - htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n", - pos, n, MaxBlk, Blksize); - - if (lseek64(h, pos, SEEK_SET) < 0) { - sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); - close(h); - return true; - } // endif h - - write(h, &c, 1); // This actually fills the empty file - close(h); - return false; -#endif // !WIN32 - } // end of MakeEmptyFile - -/***********************************************************************/ -/* Vopen function: opens a file using Windows or Unix API's. */ -/***********************************************************************/ -bool BGVFAM::OpenTableFile(PGLOBAL g) - { - char filename[_MAX_PATH]; - bool del = false; - MODE mode = Tdbp->GetMode(); - PDBUSER dbuserp = PlgGetUser(g); - - if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) { - sprintf(g->Message, MSG(FILE_OPEN_YET), To_File); - return true; - } // endif - - /*********************************************************************/ - /* Update block info if necessary. */ - /*********************************************************************/ - if (Block < 0) - if ((Headlen = GetBlockInfo(g)) < 0) - return true; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (trace) - htrc("OpenTableFile: filename=%s mode=%d Last=%d\n", - filename, mode, Last); - -#if defined(WIN32) - DWORD access, creation, share = 0, rc = 0; - - /*********************************************************************/ - /* Create the file object according to access mode */ - /*********************************************************************/ - switch (mode) { - case MODE_READ: - access = GENERIC_READ; - share = FILE_SHARE_READ; - creation = OPEN_EXISTING; - break; - case MODE_INSERT: - if (MaxBlk) { - if (!Block) - if (MakeEmptyFile(g, To_File)) - return true; - - // Required to update empty blocks - access = GENERIC_READ | GENERIC_WRITE; - } else if (Last == Nrec) - access = GENERIC_WRITE; - else - // Required to update the last block - access = GENERIC_READ | GENERIC_WRITE; - - creation = OPEN_ALWAYS; - break; - case MODE_DELETE: - if (!Tdbp->GetNext()) { - // Store the number of deleted lines - DelRows = Cardinality(g); - - // This will stop the process by - // causing GetProgMax to return 0. -// ResetTableSize(g, 0, Nrec); must be done later - del = true; - - // This will delete the whole file - access = GENERIC_READ | GENERIC_WRITE; - creation = TRUNCATE_EXISTING; - break; - } // endif - - // Selective delete, pass thru - case MODE_UPDATE: - if ((UseTemp = Tdbp->IsUsingTemp(g))) - access = GENERIC_READ; - else - access = GENERIC_READ | GENERIC_WRITE; - - creation = OPEN_EXISTING; - break; - default: - sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); - return true; - } // endswitch - - /*********************************************************************/ - /* Use specific Windows API functions. */ - /*********************************************************************/ - Hfile = CreateFile(filename, access, share, NULL, creation, - FILE_ATTRIBUTE_NORMAL, NULL); - - if (Hfile == INVALID_HANDLE_VALUE) { - rc = GetLastError(); - sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, - (LPTSTR)filename, sizeof(filename), NULL); - strcat(g->Message, filename); - } // endif Hfile - - if (trace) - htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n", - rc, access, share, creation, Hfile, filename); - - if (mode == MODE_INSERT) { - /*******************************************************************/ - /* In Insert mode we must position the cursor at end of file. */ - /*******************************************************************/ - LARGE_INTEGER of; - - of.QuadPart = (BIGINT)0; - of.LowPart = SetFilePointer(Hfile, of.LowPart, - &of.HighPart, FILE_END); - - if (of.LowPart == INVALID_SET_FILE_POINTER && - (rc = GetLastError()) != NO_ERROR) { - sprintf(g->Message, MSG(ERROR_IN_SFP), rc); - CloseHandle(Hfile); - Hfile = INVALID_HANDLE_VALUE; - } // endif - - } // endif Mode - -#else // UNIX - /*********************************************************************/ - /* The open() function has a transitional interface for 64-bit */ - /* file offsets. Note that using open64() is equivalent to using */ - /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */ - /*********************************************************************/ - int rc = 0; - int oflag; - mode_t pmd = 0; - - /*********************************************************************/ - /* Create the file object according to access mode */ - /*********************************************************************/ - switch (mode) { - case MODE_READ: - oflag = O_RDONLY; - break; - case MODE_INSERT: - if (MaxBlk) { - if (!Block) - if (MakeEmptyFile(g, To_File)) - return true; - - // Required to update empty blocks - oflag = O_RDWR; - } else if (Last == Nrec) - oflag = O_WRONLY | O_CREAT | O_APPEND; - else - // Required to update the last block - oflag = O_RDWR | O_CREAT | O_APPEND; - - pmd = S_IREAD | S_IWRITE; - break; - case MODE_DELETE: - // This is temporary until a partial delete is implemented - if (!Tdbp->GetNext()) { - // Store the number of deleted lines - DelRows = Cardinality(g); - del = true; - - // This will delete the whole file and provoque ReadDB to - // return immediately. - oflag = O_RDWR | O_TRUNC; - strcpy(g->Message, MSG(NO_VCT_DELETE)); - break; - } // endif - - // Selective delete, pass thru - case MODE_UPDATE: - UseTemp = Tdbp->IsUsingTemp(g); - oflag = (UseTemp) ? O_RDONLY : O_RDWR; - break; - default: - sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); - return true; - } // endswitch - - Hfile = open64(filename, oflag, pmd); // Enable file size > 2G - - if (Hfile == INVALID_HANDLE_VALUE) { - rc = errno; - sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); - strcat(g->Message, strerror(errno)); - } // endif Hfile - - if (trace) - htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n", - rc, oflag, mode, Hfile, filename); -#endif // UNIX - - if (!rc) { - if (!To_Fb) { - To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); - To_Fb->Fname = To_File; - To_Fb->Type = TYPE_FB_HANDLE; - To_Fb->Memory = NULL; - To_Fb->Length = 0; - To_Fb->File = NULL; - To_Fb->Next = dbuserp->Openlist; - dbuserp->Openlist = To_Fb; - } // endif To_Fb - - To_Fb->Count = 1; - To_Fb->Mode = mode; - To_Fb->Handle = Hfile; - - if (trace) - htrc("File %s is open in mode %d\n", filename, mode); - - if (del) - // This will stop the process by - // causing GetProgMax to return 0. - return ResetTableSize(g, 0, Nrec); - - /*********************************************************************/ - /* Allocate the table and column block buffers. */ - /*********************************************************************/ - return AllocateBuffer(g); - } else - return (mode == MODE_READ && rc == ENOENT) - ? PushWarning(g, Tdbp) : true; - - } // end of OpenTableFile - -/***********************************************************************/ -/* Allocate the block buffers for columns used in the query. */ -/***********************************************************************/ -bool BGVFAM::AllocateBuffer(PGLOBAL g) - { - MODE mode = Tdbp->GetMode(); - PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - PCOLDEF cdp; - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - if (mode == MODE_INSERT) { - if (!NewBlock) { - // Not reopening after inserting the last block - bool chk = PlgGetUser(g)->Check & CHK_TYPE; - - NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); - - for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) - memset(NewBlock + Nrec * cdp->GetPoff(), - (IsTypeNum(cdp->GetType()) ? 0 : ' '), - Nrec * cdp->GetClen()); - - for (; cp; cp = (PVCTCOL)cp->Next) - cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, - cp->Buf_Type, Nrec, cp->Format.Length, - cp->Format.Prec, chk); - - InitInsert(g); // Initialize inserting - - // Currently we don't use a temporary file for inserting - Tfile = Hfile; - } // endif NewBlock - - } else { - if (UseTemp || mode == MODE_DELETE) { - // Allocate all that is needed to move lines - int i = 0; - - if (!Ncol) - for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) - Ncol++; - - if (MaxBlk) - BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT)); - else - Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - - Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); - Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); - - for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { - if (MaxBlk) - BigDep[i] = (BIGINT)Headlen - + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk; - else - Deplac[i] = cdp->GetPoff() * Nrec; - - Clens[i] = cdp->GetClen(); - Isnum[i] = IsTypeNum(cdp->GetType()); - Buflen = max(Buflen, cdp->GetClen()); - } // endfor cdp - - if (!UseTemp || MaxBlk) { - Buflen *= Nrec; - To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); - } else - NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); - - } // endif mode - - for (; cp; cp = (PVCTCOL)cp->Next) - if (!cp->IsSpecial()) // Not a pseudo column - cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, - cp->Format.Length, cp->Format.Prec); - - } //endif mode - - return false; - } // end of AllocateBuffer - -/***********************************************************************/ -/* Data Base write routine for huge VCT access method. */ -/***********************************************************************/ -int BGVFAM::WriteBuffer(PGLOBAL g) - { - if (trace) - htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n", - Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); - - if (Tdbp->GetMode() == MODE_UPDATE) { - // Mode Update is done in ReadDB, we just initialize it here - if (Tfile == INVALID_HANDLE_VALUE) { - if (UseTemp) { - if (OpenTempFile(g)) - return RC_FX; - - // Most of the time, not all table columns are updated. - // This why we must completely pre-fill the temporary file. - Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last - : Block * Nrec; // To write last lock - - if (MoveIntermediateLines(g)) - return RC_FX; - - } else - Tfile = Hfile; - - } // endif Tfile - - } else { - // Mode Insert - if (MaxBlk && CurBlk == MaxBlk) { - strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); - return RC_EF; // Too many lines for a Vector formatted table - } // endif MaxBlk - - if (Closing || ++CurNum == Nrec) { - PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); - - if (!AddBlock) { - // Write back the updated last block values - for (; cp; cp = (PVCTCOL)cp->Next) - cp->WriteBlock(g); - - if (!Closing && !MaxBlk) { - // Close the VCT file and reopen it in mode Insert -//#if defined(WIN32) //OB -// CloseHandle(Hfile); -//#else // UNIX -// close(Hfile); -//#endif // UNIX - CloseFileHandle(Hfile); - Hfile = INVALID_HANDLE_VALUE; - To_Fb->Count = 0; - Last = Nrec; // Tested in OpenTableFile - - if (OpenTableFile(g)) { - Closing = true; // Tell CloseDB of error - return RC_FX; - } // endif Vopen - - AddBlock = true; - } // endif Closing - - } else { - // Here we must add a new block to the VCT file - if (Closing) - // Reset the overwritten columns for last block extra records - for (; cp; cp = (PVCTCOL)cp->Next) - memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, - (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', - (Nrec - Last) * cp->Clen); - - if (BigWrite(g, Hfile, NewBlock, Blksize)) - return RC_FX; - - } // endif AddBlock - - if (!Closing) { - CurBlk++; - CurNum = 0; - } // endif Closing - - } // endif - - } // endif Mode - - return RC_OK; - } // end of WriteBuffer - -/***********************************************************************/ -/* Data Base delete line routine for BGVFAM access method. */ -/***********************************************************************/ -int BGVFAM::DeleteRecords(PGLOBAL g, int irc) - { - bool eof = false; - - /*********************************************************************/ - /* There is an alternative here depending on UseTemp: */ - /* 1 - use a temporary file in which are copied all not deleted */ - /* lines, at the end the original file will be deleted and */ - /* the temporary file renamed to the original file name. */ - /* 2 - directly move the not deleted lines inside the original */ - /* file, and at the end erase all trailing records. */ - /*********************************************************************/ - if (trace) - htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", - irc, UseTemp, Fpos, Tpos, Spos); - - if (irc != RC_OK) { - /*******************************************************************/ - /* EOF: position Fpos at the end-of-file position. */ - /*******************************************************************/ - Fpos = (Block - 1) * Nrec + Last; - - if (trace) - htrc("Fpos placed at file end=%d\n", Fpos); - - eof = UseTemp && !MaxBlk; - } else // Fpos is the deleted line position - Fpos = CurBlk * Nrec + CurNum; - - if (Tpos == Spos) { - if (UseTemp) { - /*****************************************************************/ - /* Open the temporary file, Spos is at the beginning of file. */ - /*****************************************************************/ - if (OpenTempFile(g)) - return RC_FX; - - } else { - /*****************************************************************/ - /* Move of eventual preceeding lines is not required here. */ - /* Set the target file as being the source file itself. */ - /* Set the future Tpos, and give Spos a value to block copying. */ - /*****************************************************************/ - Tfile = Hfile; - Spos = Tpos = Fpos; - } // endif UseTemp - - } // endif Tpos == Spos - - /*********************************************************************/ - /* Move any intermediate lines. */ - /*********************************************************************/ - if (MoveIntermediateLines(g, &eof)) - return RC_FX; - - if (irc == RC_OK) { -#ifdef _DEBUG - assert(Spos == Fpos); -#endif - Spos++; // New start position is on next line - - if (trace) - htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); - - } else { - /*******************************************************************/ - /* Last call after EOF has been reached. */ - /*******************************************************************/ - Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; - Last = (Tpos + Nrec - 1) % Nrec + 1; - - if (!UseTemp) { // The UseTemp case is treated in CloseTableFile - if (!MaxBlk) { - if (Last < Nrec) // Clean last block - if (CleanUnusedSpace(g)) - return RC_FX; - - /***************************************************************/ - /* Remove extra records. */ - /***************************************************************/ -#if defined(WIN32) - BIGINT pos = (BIGINT)Block * (BIGINT)Blksize; - - if (BigSeek(g, Hfile, pos)) - return RC_FX; - - if (!SetEndOfFile(Hfile)) { - DWORD drc = GetLastError(); - - sprintf(g->Message, MSG(SETEOF_ERROR), drc); - return RC_FX; - } // endif error -#else // !WIN32 - if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) { - sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); - return RC_FX; - } // endif -#endif // !WIN32 - } else // MaxBlk - // Clean the unused space in the file, this is required when - // inserting again with a partial column list. - if (CleanUnusedSpace(g)) - return RC_FX; - - if (ResetTableSize(g, Block, Last)) - return RC_FX; - - } // endif UseTemp - - } // endif irc - - return RC_OK; // All is correct - } // end of DeleteRecords - -/***********************************************************************/ -/* Open a temporary file used while updating or deleting. */ -/***********************************************************************/ -bool BGVFAM::OpenTempFile(PGLOBAL g) - { - char *tempname; - PDBUSER dup = PlgGetUser(g); - - /*********************************************************************/ - /* Open the temporary file, Spos is at the beginning of file. */ - /*********************************************************************/ - tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); - PlugSetPath(tempname, To_File, Tdbp->GetPath()); - strcat(PlugRemoveType(tempname, tempname), ".t"); - - if (!MaxBlk) - remove(tempname); // Be sure it does not exist yet - else if (MakeEmptyFile(g, tempname)) - return true; - -#if defined(WIN32) - DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW; - - Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL, - access, FILE_ATTRIBUTE_NORMAL, NULL); - - if (Tfile == INVALID_HANDLE_VALUE) { - DWORD rc = GetLastError(); - sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, - (LPTSTR)tempname, _MAX_PATH, NULL); - strcat(g->Message, tempname); - return true; - } // endif Tfile -#else // UNIX - int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC; - - Tfile = open64(tempname, oflag, S_IWRITE); - - if (Tfile == INVALID_HANDLE_VALUE) { - int rc = errno; - sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); - strcat(g->Message, strerror(errno)); - return true; - } //endif Tfile -#endif // UNIX - - To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); - To_Fbt->Fname = tempname; - To_Fbt->Type = TYPE_FB_HANDLE; - To_Fbt->Memory = NULL; - To_Fbt->Length = 0; - To_Fbt->File = NULL; - To_Fbt->Next = dup->Openlist; - To_Fbt->Count = 1; - To_Fbt->Mode = MODE_INSERT; - To_Fbt->Handle = Tfile; - dup->Openlist = To_Fbt; - return false; - } // end of OpenTempFile - -/***********************************************************************/ -/* Move intermediate deleted or updated lines. */ -/***********************************************************************/ -bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b) - { - int i, n, req, dep; - bool eof = (b) ? *b : false; - BIGINT pos; - - for (n = Fpos - Spos; n > 0 || eof; n -= req) { - /*******************************************************************/ - /* Non consecutive line to delete. Move intermediate lines. */ - /*******************************************************************/ - if (!MaxBlk) - req = (DWORD)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); - else - req = (DWORD)min(n, Nrec); - - if (req) for (i = 0; i < Ncol; i++) { - if (!MaxBlk) { - if (UseTemp) - To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; - - pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i]) - + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize; - } else - pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i]; - - if (BigSeek(g, Hfile, pos)) - return true; - - if (BigRead(g, Hfile, To_Buf, req * Clens[i])) - return true; - - if (!UseTemp || MaxBlk) { - if (!MaxBlk) - pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i]) - + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize; - else - pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; - - if (BigSeek(g, Tfile, pos)) - return true; - - if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) - return true; - - } // endif UseTemp - - } // endfor i - - Tpos += (int)req; - Spos += (int)req; - - if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) { - // Write the full or last block to the temporary file - if ((dep = Nrec - (Tpos % Nrec)) < Nrec) - // Clean the last block in case of future insert, must be - // done here because Tfile was open in write only. - for (i = 0; i < Ncol; i++) { - To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; - memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); - } // endfor i - - if (BigWrite(g, Tfile, NewBlock, Blksize)) - return true; - - if (Spos == Fpos) - eof = false; - - } // endif Usetemp... - - if (trace) - htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); - - } // endfor n - - return false; - } // end of MoveIntermediateLines - -/***********************************************************************/ -/* Clean deleted space in a huge VCT or Vec table file. */ -/***********************************************************************/ -bool BGVFAM::CleanUnusedSpace(PGLOBAL g) - { - int i; - int n; - BIGINT pos, dep; - - if (!MaxBlk) { - /*******************************************************************/ - /* Clean last block of the VCT table file. */ - /*******************************************************************/ - assert(!UseTemp); // This case is handled in MoveIntermediateLines - - if (!(n = Nrec - Last)) - return false; - - dep = (BIGINT)((Block - 1) * Blksize); - - for (i = 0; i < Ncol; i++) { - memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); - pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]); - - if (BigSeek(g, Hfile, pos)) - return true; - - if (BigWrite(g, Hfile, To_Buf, n * Clens[i])) - return true; - - } // endfor i - - } else { - int req; - - memset(To_Buf, 0, Buflen); - - for (n = Fpos - Tpos; n > 0; n -= req) { - /*****************************************************************/ - /* Fill VEC file remaining lines with 0's. */ - /* This seems to work even column blocks have been made with */ - /* Blanks = true. Perhaps should it be set to false for VEC. */ - /*****************************************************************/ - req = min(n, Nrec); - - for (i = 0; i < Ncol; i++) { - pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; - - if (BigSeek(g, Tfile, pos)) - return true; - - if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) - return true; - - } // endfor i - - Tpos += req; - } // endfor n - - } // endif MaxBlk - - return false; - } // end of CleanUnusedSpace - -/***********************************************************************/ -/* Data Base close routine for huge VEC access method. */ -/***********************************************************************/ -void BGVFAM::CloseTableFile(PGLOBAL g) - { - int rc = 0, wrc = RC_OK; - MODE mode = Tdbp->GetMode(); - - if (mode == MODE_INSERT) { - if (Closing) - wrc = RC_FX; // Last write was in error - else - if (CurNum) { - // Some more inserted lines remain to be written - Last = CurNum; - Block = CurBlk + 1; - Closing = true; - wrc = WriteBuffer(g); - } else { - Last = Nrec; - Block = CurBlk; - wrc = RC_OK; - } // endif CurNum - - if (wrc != RC_FX) { - rc = ResetTableSize(g, Block, Last); - } else if (AddBlock) { - // Last block was not written - rc = ResetTableSize(g, CurBlk, Nrec); - longjmp(g->jumper[g->jump_level], 44); - } // endif - - } else if (mode == MODE_UPDATE) { - // Write back to file any pending modifications - for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols(); - colp; colp = (PVCTCOL)colp->Next) - colp->WriteBlock(g); - - if (UseTemp && Tfile) { - rc = RenameTempFile(g); - Hfile = Tfile = INVALID_HANDLE_VALUE; - - if (Header) - // Header must be set because it was not set in temp file - rc = SetBlockInfo(g); - - } // endif UseTemp - - } else if (mode == MODE_DELETE && UseTemp && Tfile) { - if (MaxBlk) - rc = CleanUnusedSpace(g); - - if ((rc = RenameTempFile(g)) != RC_FX) { - Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo - rc = ResetTableSize(g, Block, Last); - } // endif rc - - } // endif's mode - - if (Hfile != INVALID_HANDLE_VALUE) - rc = PlugCloseFile(g, To_Fb); - - if (trace) - htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n", - To_File, wrc, rc); - - Hfile = INVALID_HANDLE_VALUE; - } // end of CloseDB - -/***********************************************************************/ -/* Rewind routine for huge VCT access method. */ -/***********************************************************************/ -void BGVFAM::Rewind(void) - { - // In mode update we need to read Set Column blocks - if (Tdbp->GetMode() == MODE_UPDATE) - OldBlk = -1; - - // Initialize so block optimization is called for 1st block - CurBlk = -1; - CurNum = Nrec - 1; - -#if 0 // This is probably unuseful as the file is directly accessed -#if defined(WIN32) //OB - SetFilePointer(Hfile, 0, NULL, FILE_BEGIN); -#else // UNIX - lseek64(Hfile, 0, SEEK_SET); -#endif // UNIX -#endif // 0 - } // end of Rewind - -/***********************************************************************/ -/* ReadBlock: Read column values from current block. */ -/***********************************************************************/ -bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) - { - BIGINT pos; - - /*********************************************************************/ - /* Calculate the offset and size of the block to read. */ - /*********************************************************************/ - if (MaxBlk) // File has Vector format - pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk - + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen; - else // Old VCT format - pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac - + (BIGINT)Lrecl * (BIGINT)CurBlk); - - if (trace) - htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n", - pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); - - if (BigSeek(g, Hfile, pos)) - return true; - - if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec)) - return true; - - if (trace) - num_read++; - - return false; - } // end of ReadBlock - -/***********************************************************************/ -/* WriteBlock: Write back current column values for one block. */ -/* Note: the test of Status is meant to prevent physical writing of */ -/* the block during the checking loop in mode Update. It is set to */ -/* BUF_EMPTY when reopening the table between the two loops. */ -/***********************************************************************/ -bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) - { - int len; - BIGINT pos; - - /*********************************************************************/ - /* Calculate the offset and size of the block to write. */ - /*********************************************************************/ - if (MaxBlk) // File has Vector format - pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk - + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen; - else // Old VCT format - pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac - + (BIGINT)Lrecl * (BIGINT)colp->ColBlk); - - if (trace) - htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n", - pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk); - - if (BigSeek(g, Tfile, pos)) - return true; - -//len = colp->Clen * Nrec; see comment in VCTFAM - len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec); - - if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len)) - return true; - - return false; - } // end of WriteBlock - -/* ----------------------- End of FilAMVct --------------------------- */ +/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMVCT */ +/* ------------- */ +/* Version 2.5 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the VCT file access method classes. */ +/* Added in version 2: F */ +/* - Split Vec format. */ +/* - Partial delete. */ +/* - Use of tempfile for update. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLAND__ +//#include +#include +#else // !WIN32 F +#if defined(UNIX) +#include +#include +#include +#include +#define NO_ERROR 0 +#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. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "osutil.h" // Unuseful for WIN32 +#include "plgdbsem.h" +#include "valblk.h" +#include "filamfix.h" +#include "tabdos.h" +#include "tabvct.h" +#include "maputil.h" +#include "filamvct.h" + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +extern int num_read, num_there; // Statistics +static int num_write; +extern "C" int trace; + +#if defined(UNIX) +// Add dummy strerror (NGC) +char *strerror(int num); +#endif // UNIX + +/***********************************************************************/ +/* Header containing block info for not split VEC tables. */ +/* Block and last values can be calculated from NumRec and Nrec. */ +/* This is better than directly storing Block and Last because it */ +/* make possible to use the same file with tables having a different */ +/* block size value (Element -> Nrec) */ +/* Note: can be in a separate file if header=1 or a true header (2) */ +/***********************************************************************/ +typedef struct _vecheader { +//int Block; /* The number of used blocks */ +//int Last; /* The number of used records in last block */ + int MaxRec; /* Max number of records (True vector format)*/ + int NumRec; /* Number of valid records in the table */ + } VECHEADER; + +/***********************************************************************/ +/* Char VCT column blocks are right filled with blanks (blank = true) */ +/* Conversion of block values allowed conditionally for insert only. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, + bool check = true, bool blank = true, bool un = false); + +/* -------------------------- Class VCTFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCTFAM class. */ +/***********************************************************************/ +VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp) + { + Last = tdp->GetLast(); + MaxBlk = (tdp->GetEstimate() > 0) ? + ((tdp->GetEstimate() - 1) / Nrec + 1) : 0; + NewBlock = NULL; + AddBlock = false; + Split = false; + + if ((Header = (MaxBlk) ? tdp->Header : 0)) + Block = Last = -1; + + Bsize = Nrec; + CurNum = Nrec - 1; + Colfn = NULL; + Tempat = NULL; + Clens = NULL; + Deplac = NULL; + Isnum = NULL; + Ncol = 0; + } // end of VCTFAM standard constructor + +VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp) + { + MaxBlk = txfp->MaxBlk; + NewBlock = NULL; + AddBlock = false; + Split = txfp->Split; + Header = txfp->Header; + Bsize = txfp->Bsize; + Colfn = txfp->Colfn; + Tempat = txfp->Tempat; + Clens = txfp->Clens; + Deplac = txfp->Deplac; + Isnum = txfp->Isnum; + Ncol = txfp->Ncol; + } // end of VCTFAM copy constructor + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void VCTFAM::Reset(void) + { + FIXFAM::Reset(); + NewBlock = NULL; + AddBlock = false; + CurNum = Nrec - 1; + } // end of Reset + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int VCTFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int h, k, n; + VECHEADER vh; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + + if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1 + || !_filelength(h)) { + // Consider this is a void table + Last = Nrec; + Block = 0; + + if (h != -1) + close(h); + + return n; + } else if (Header == 3) + k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END); + + if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + } // endif s + + close(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +bool VCTFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc = false; + size_t n; + VECHEADER vh; + FILE *s; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Stream) { + s = Stream; + + if (Header == 1) + /*k =*/ fseek(s, 0, SEEK_SET); + + } else + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b"); + + } else { // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb"); + } // endif Header + + if (!s) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } else if (Header == 3) + /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || !Stream) + fclose(s); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/***********************************************************************/ +int VCTFAM::MaxBlkSize(PGLOBAL g, int s) + { + int rc = RC_OK, savcur = CurBlk; + int size; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + if ((rc = Tdbp->TestBlock(g)) == RC_OK) + size += (CurBlk == Block - 1) ? Last : Nrec; + else if (rc == RC_EF) + break; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/***********************************************************************/ +/* VCT 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 VCTFAM::Cardinality(PGLOBAL g) + { + if (!g) + return 1; + + if (Block < 0) + if (Split) { + // Separate column files and no pre setting of Block and Last + // This allows to see a table modified externally, but Block + // and Last must be set from the file cardinality. + // Only happens when called by sub classes. + char filename[_MAX_PATH]; + PSZ savfn = To_File; + int len, clen, card = -1; + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + // Use the first column file to calculate the cardinality + clen = cdp->GetClen(); + sprintf(filename, Colfn, 1); + To_File = filename; + len = GetFileLength(g); + To_File = savfn; + + if (len >= 0) { + if (!(len % clen)) + card = len / clen; // Fixed length file + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen); + + if (trace) + htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen); + + } else + card = 0; + + // Set number of blocks for later use + Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; + Last = (card + Nrec - 1) % Nrec + 1; + return card; + } else { + // Vector table having Block and Last info in a Header (file) + if ((Headlen = GetBlockInfo(g)) < 0) + return -1; // Error + + } // endif split + + return (int)((Block - 1) * Nrec + Last); + } // end of Cardinality + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int VCTFAM::GetRowID(void) + { + return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk + : (Block - 1) * Nrec + Last); + } // end of GetRowID + +/***********************************************************************/ +/* VCT Create an empty file for Vector formatted tables. */ +/***********************************************************************/ +bool VCTFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file: this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int h, n; + + PlugSetPath(filename, fn, Tdbp->GetPath()); +#if defined(WIN32) + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE); +#else // !WIN32 + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); +#endif // !WIN32 + + if (h == -1) + return true; + + n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) == -1) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; + } // end of MakeEmptyFile + +/***********************************************************************/ +/* VCT 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 VCTFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "rb" : "r+b"); + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + strcpy(opmode, "r+b"); // Required to update empty blocks + } else if (Last == Nrec) + strcpy(opmode, "ab"); + else + strcpy(opmode, "r+b"); // Required to update the last block + + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Use conventionnal input/output functions. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Stream + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fb = dbuserp->Openlist; // Keep track of File block + + if (!strcmp(opmode, "wb")) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + num_read = num_there = num_write = 0; + + // Allocate the table and column block buffer + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VCTFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); // Initialize inserting + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0, n = (MaxBlk) ? MaxBlk : 1; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec; + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCTFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Last block must be updated by new values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + } // endif Last + + // We are not currently using a temporary file for Insert + T_Stream = Stream; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* ReadBuffer: Read one line for a VCT file. */ +/***********************************************************************/ +int VCTFAM::ReadBuffer(PGLOBAL g) + { + int rc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (Placed) + Placed = false; + else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + next: + if (++CurBlk == Block) + return RC_EF; // End of file + + /*******************************************************************/ + /* Before reading a new block, check whether block optimizing */ + /* can be done, as well as for join as for local filtering. */ + /*******************************************************************/ + switch (Tdbp->TestBlock(g)) { + case RC_EF: + return RC_EF; + case RC_NF: + goto next; + } // endswitch rc + + num_there++; + } // endif CurNum + + if (OldBlk != CurBlk) { + if (mode == MODE_UPDATE) { + /*****************************************************************/ + /* Flush the eventually modified column buffers in old blocks */ + /* and read the blocks to modify attached to Set columns. */ + /*****************************************************************/ + if (MoveLines(g)) // For VECFAM + return RC_FX; + + for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) { + colp->WriteBlock(g); + colp->ReadBlock(g); + } // endfor colp + + } // endif mode + + OldBlk = CurBlk; // Last block actually read + } // endif oldblk + + if (trace) + htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK); + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VCTFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (!T_Stream) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + T_Stream = Stream; + + } // endif T_Stream + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // For VCT tables, future blocks must be added + char filename[_MAX_PATH]; + + // Close the file and reopen it in mode Insert + fclose(Stream); + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Stream + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if ((size_t)Nrec != + fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VCT access method. */ +/* Note: lines are moved directly in the files (ooops...) */ +/* Using temp file depends on the Check setting, false by default. */ +/***********************************************************************/ +int VCTFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + if (trace) + htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just the setting of future Spos and Tpos. */ + /*****************************************************************/ + T_Stream = Stream; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { + /*******************************************************************/ + /* Reposition the file pointer and set Spos. */ + /*******************************************************************/ +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* Update the Block and Last values. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + /***************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS ??) this is still to be */ + /* checked for compatibility with Text files and other OS's. */ + /***************************************************************/ + char filename[_MAX_PATH]; + int h; + + /*rc =*/ CleanUnusedSpace(g); // Clean last block + /*rc =*/ PlugCloseFile(g, To_Fb); + Stream = NULL; // For SetBlockInfo + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra blocks. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Headlen + Block * Blksize)) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } else + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool VCTFAM::OpenTempFile(PGLOBAL g) + { + char *opmode, tempname[_MAX_PATH]; + bool rc = false; + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (MaxBlk) { + if (MakeEmptyFile(g, tempname)) + return true; + + opmode = "r+b"; + } else + opmode = "wb"; + + if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + rc = true; + } else + To_Fbt = PlgGetUser(g)->Openlist; + + return rc; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, dep, off; + int n; + bool eof = (b) ? *b : false; + size_t req, len; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (size_t)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (size_t)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (MaxBlk) { + dep = Deplac[i]; + off = Spos * Clens[i]; + } else { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + dep = Deplac[i] + (Spos / Nrec) * Blksize; + off = (Spos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Stream); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp || MaxBlk) { + if (MaxBlk) { + dep = Deplac[i]; + off = Tpos * Clens[i]; + } else { + dep = Deplac[i] + (Tpos / Nrec) * Blksize; + off = (Tpos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(T_Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endif UseTemp + + if (trace) + htrc("after write pos=%d\n", ftell(Stream)); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, + // must be done here because T_Stream was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + // Write a new block in the temporary file + len = (size_t)Blksize; + + if (fwrite(NewBlock, 1, len, T_Stream) != len) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (Spos == Fpos) + eof = false; + + } // endif UseTemp + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a VCT or Vec table file. */ +/***********************************************************************/ +bool VCTFAM::CleanUnusedSpace(PGLOBAL g) + { + int i, dep; + int n; + size_t req, len; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); + + if (!(n = Nrec - Last)) + return false; + + dep = (Block - 1) * Blksize; + req = (size_t)n; + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + } else for (n = Fpos - Tpos; n > 0; n -= req) { + /*******************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* Note: this seems to work even column blocks have been made */ + /* with Blanks = true. Perhaps should it be set to false for VEC. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + memset(To_Buf, 0, Buflen); + + for (i = 0; i < Ncol; i++) { + if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + Tpos += (int)req; + } // endfor n + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && T_Stream) { + rc = RenameTempFile(g); + + if (Header) { + // Header must be set because it was not set in temp file + Stream = T_Stream = NULL; // For SetBlockInfo + rc = SetBlockInfo(g); + } // endif Header + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && T_Stream) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Stream = T_Stream = NULL; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (!(UseTemp && T_Stream)) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Stream = NULL; + } // end of CloseTableFile + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last) + { + bool rc = false; + + // Set Block and Last values for TDBVCT::MakeBlockValues + Block = block; + Last = last; + + if (!Split) { + if (!Header) { + // Update catalog values for Block and Last + PVCTDEF defp = (PVCTDEF)Tdbp->GetDef(); + LPCSTR name = Tdbp->GetName(); + + defp->SetBlock(Block); + defp->SetLast(Last); + + if (!defp->SetIntCatInfo("Blocks", Block) || + !defp->SetIntCatInfo("Last", Last)) { + sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); + rc = true; + } // endif + + } else + rc = SetBlockInfo(g); + + } // endif Split + + Tdbp->ResetSize(); + return rc; + } // end of ResetTableSize + +/***********************************************************************/ +/* Rewind routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; +//rewind(Stream); will be placed by fseek + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // True vector format + len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk); + else // Blocked vector format + len = Nrec * (colp->Deplac + Lrecl * CurBlk); + + if (trace) + htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n", + len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (fseek(Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Stream); + + if (n != (size_t)Nrec) { + if (errno == NO_ERROR) + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File); + else + sprintf(g->Message, MSG(READ_ERROR), + To_File, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + len = Headlen + + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk); + else // Old VCT format + len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk); + + if (trace) + htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (fseek(T_Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), + (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } // endif + +#if defined(UNIX) + fflush(T_Stream); //NGC +#endif + +#ifdef _DEBUG + num_write++; +#endif + + return false; + } // end of WriteBlock + +/* -------------------------- Class VCMFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCMFAM class. */ +/***********************************************************************/ +VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Memory = NULL; + Memcol = NULL; + } // end of VCMFAM standard constructor + +VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp) + { + Memory = txfp->Memory; + Memcol = txfp->Memcol; + } // end of VCMFAM copy constructor + +/***********************************************************************/ +/* Mapped VCT 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 VCMFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + int len; + MODE mode = Tdbp->GetMode(); + PFBLOCK fp = NULL; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* We used the file name relative to recorded datapath. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as if it were */ + /* entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dbuserp->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memory = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* If required, delete the whole file if no filtering is implied. */ + /*******************************************************************/ + bool del; + HANDLE hFile; + MEMMAP mm; + MODE mapmode = mode; + + if (mode == MODE_INSERT) { + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Inserting will be like updating the file + mapmode = MODE_UPDATE; + } else { + strcpy(g->Message, "MAP Insert is for VEC Estimate tables only"); + return true; + } // endif MaxBlk + + } // endif mode + + del = mode == MODE_DELETE && !Tdbp->GetNext(); + + if (del) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + } // endif del + + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mapmode, del); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*******************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************/ + len = mm.lenL; + Memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + bool rc = ResetTableSize(g, 0, Nrec); + return (mapmode == MODE_UPDATE) ? true : rc; + } // endif len + + if (!Memory) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memory; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fb = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n", + fp, fp->Count, Memory, len); + + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/* Note: Memcol must be set for all columns because it can be used */ +/* for set columns in Update. Clens values are used only in Delete. */ +/***********************************************************************/ +bool VCMFAM::AllocateBuffer(PGLOBAL g) + { + int m, i = 0; + bool b = Tdbp->GetMode() == MODE_DELETE; + PVCTCOL cp; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + // Calculate the number of columns + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + // To store the start position of each column + Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*)); + m = (MaxBlk) ? MaxBlk : 1; + + // We will need all column sizes and type for Delete + if (b) { + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + } // endif b + + for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) { + if (b) { + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + } // endif b + + Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec; + } // endfor cdp + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + if (Tdbp->GetMode() == MODE_INSERT) + return InitInsert(g); + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCMFAM::InitInsert(PGLOBAL g) + { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + } // endif Last + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Initialize the column block pointer + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Data Base write routine for VMP access method. */ +/***********************************************************************/ +int VCMFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + // Mode Update being done in ReadDB we process here Insert mode only. + if (Tdbp->GetMode() == MODE_INSERT) { + if (CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing) { + CurBlk++; + CurNum = 0; + + // Re-initialize the column block pointer + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VCMFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if (Fpos > Spos) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) { + // Old VCT format, moving must respect block limits + char *ps, *pt; + int req, soff, toff; + + for (n = Fpos - Spos; n > 0; n -= req) { + soff = Spos % Nrec; + toff = Tpos % Nrec; + req = (size_t)min(n, Nrec - max(soff, toff)); + + for (i = 0; i < Ncol; i++) { + ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i]; + pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i]; + memmove(pt, ps, req * Clens[i]); + } // endfor i + + Tpos += req; + Spos += req; + } // endfor n + + } else { + // True vector format, all is simple... + n = Fpos - Spos; + + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m); + } // endfor i + + Tpos += n; + } // endif MaxBlk + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. Reset the Block and */ + /* Last values for TDBVCT::MakeBlockValues. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!MaxBlk) { + PFBLOCK fp = To_Fb; + + // Clean the unused part of the last block + m = (Block - 1) * Blksize; + n = Nrec - Last; + + for (i = 0; i < Ncol; i++) + memset(Memcol[i] + m + Last * Clens[i], + (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + // We must Unmap the view and use the saved file handle + // to put an EOF at the end of the last block of the file. + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + // Remove extra blocks + n = Block * Blksize; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } else + // True vector table, Table file size does not change. + // Just clean the unused part of the file. + for (n = Fpos - Tpos, i = 0; i < Ncol; i++) + memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]); + + // Reset Last and Block values in the catalog + PlugCloseFile(g, To_Fb); // in case of Header + ResetTableSize(g, Block, Last); + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VCMFAM::CloseTableFile(PGLOBAL g) + { + int wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (!Closing) { + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + } else + wrc = RC_FX; // Last write was in error + + PlugCloseFile(g, To_Fb); + + if (wrc != RC_FX) + /*rc =*/ ResetTableSize(g, Block, Last); + + } else if (mode != MODE_DELETE) + PlugCloseFile(g, To_Fb); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCMFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + char *mempos; + int i = colp->Index - 1; + int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl); + + /*********************************************************************/ + /* Calculate the start position of the column block to read. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n", + mempos, i, Nrec, colp->Clen, CurBlk); + + if (colp->GetStatus(BUF_MAPPED)) + colp->Blk->SetValPointer(mempos); + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: there is nothing to do because we are working directly into */ +/* the mapped file, except when checking for Update but in this case */ +/* we do not want to write back the modifications either. */ +/***********************************************************************/ +bool VCMFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { +#if defined(_DEBUG) + char *mempos; + int i = colp->Index - 1; + int n = Nrec * colp->Clen; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n", + Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk); + +#endif // _DEBUG + + return false; + } // end of WriteBlock + +/* -------------------------- Class VECFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VECFAM class. */ +/***********************************************************************/ +VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Streams = NULL; + To_Fbs = NULL; + To_Bufs = NULL; + Split = true; + Block = Last = -1; + InitUpdate = false; + } // end of VECFAM standard constructor + +VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp) + { + Streams = txfp->Streams; + To_Fbs = txfp->To_Fbs; + Clens = txfp->Clens; + To_Bufs = txfp->To_Bufs; + InitUpdate = txfp->InitUpdate; + } // end of VECFAM copy constructor + +/***********************************************************************/ +/* VEC 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 VECFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4]; + int i; + bool b= false; + PCOLDEF cdp; + PVCTCOL cp; + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + /*********************************************************************/ + /* Call Cardinality to set Block and Last values in case it was not */ + /* already called (this happens indeed in test xmode) */ + /*********************************************************************/ + Cardinality(g); + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + break; + } // endif filter + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "r": "r+"); + break; + case MODE_INSERT: + strcpy(opmode, "ab"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern and set Ncol + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_INSERT || mode == MODE_DELETE) { + // All columns must be written or deleted + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (OpenColumnFile(g, opmode, i)) + return true; + + // Check for void table or missing columns + for (b = !Streams[0], i = 1; i < Ncol; i++) + if (b != !Streams[i]) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding to updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (OpenColumnFile(g, opmode, cp->Index - 1)) + return true; + + // Open in read only mode the used columns not already open + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Streams[cp->Index - 1]) + if (OpenColumnFile(g, "rb", cp->Index - 1)) + return true; + + // Check for void table or missing columns + for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp; + cp = (PVCTCOL)cp->Next) + if (!i++) + b = !Streams[cp->Index - 1]; + else if (b != !Streams[cp->Index - 1]) + return true; + + } // endif mode + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VECFAM::OpenColumnFile(PGLOBAL g, char *opmode, int i) + { + char filename[_MAX_PATH]; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (Tdbp->GetMode() == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Streams + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fbs[i] = dup->Openlist; // Keep track of File blocks + return false; + } // end of OpenColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VECFAM::AllocateBuffer(PGLOBAL g) + { + int i; + PVCTCOL cp; + PCOLDEF cdp; + PTDBVCT tdbp = (PTDBVCT)Tdbp; + MODE mode = tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)tdbp->GetDef(); + + if (mode != MODE_READ) { + // Allocate what is needed by all modes except Read + T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + // Give default values + for (i = 0; i < Ncol; i++) { + T_Streams[i] = Streams[i]; + Clens[i] = 0; + } // endfor i + + } // endif mode + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*)); + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]); + + if (cdp->GetType() == TYPE_STRING) + memset(To_Bufs[i], ' ', Nrec * Clens[i]); + else + memset(To_Bufs[i], 0, Nrec * Clens[i]); + + } // endfor cdp + + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1], + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines and make Temp + if (UseTemp) { + Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + strcpy(Tempat, Colfn); + PlugSetPath(Tempat, Tempat, Tdbp->GetPath()); + strcat(PlugRemoveType(Tempat, Tempat), ".t"); + T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + } // endif UseTemp + + if (UseTemp) + for (i = 0; i < Ncol; i++) { + T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL; + T_Fbs[i] = NULL; + } // endfor i + + if (mode == MODE_DELETE) { // All columns are moved + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + } else { // Mode Update, only some columns are updated + for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) { + i = cp->Index -1; + + if (UseTemp) + T_Streams[i] = NULL; // Mark the streams to open + + Clens[i] = cp->Clen; + Buflen = max(Buflen, cp->Clen); + } // endfor cp + + InitUpdate = true; // To be initialized + } // endif mode + + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec); + } // endif mode + + // Finally allocate column buffers for all modes + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } // endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VECFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + CurBlk = 0; + CurNum = 0; + AddBlock = true; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Reset buffer access according to indexing and to mode. */ +/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ +/***********************************************************************/ +void VECFAM::ResetBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* If access is random, performances can be much better when the */ + /* reads are done on only one row, except for small tables that can */ + /* be entirely read in one block. If the index is just used as a */ + /* bitmap filter, as for Update or Delete, reading will be */ + /* sequential and we better keep block reading. */ + /*********************************************************************/ + if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) { + Nrec = 1; // Better for random access + Rbuf = 0; + OldBlk = -2; // Has no meaning anymore + Block = Tdbp->Cardinality(g); // Blocks are one line now + Last = 1; // Probably unuseful + } // endif Mode + + } // end of ResetBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VECFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_INSERT) { + if (Closing || ++CurNum == Nrec) { + // Here we must add a new blocks to the files + int i; + size_t n = (size_t)CurNum; + + for (i = 0; i < Ncol; i++) + if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } else // Mode Update + // Writing updates being done in ReadDB we do initialization only. + if (InitUpdate) { + if (OpenTempFile(g)) + return RC_FX; + + InitUpdate = false; // Done + } // endif InitUpdate + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for split vertical access methods. */ +/* Note: lines are moved directly in the files (ooops...) */ +/***********************************************************************/ +int VECFAM::DeleteRecords(PGLOBAL g, int irc) + { + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This depends on the Check setting, false by default. */ + /*********************************************************************/ + if (trace) + htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = Cardinality(g); + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + // First line to delete + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary files, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Spos = Tpos = Fpos; + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + if (!UseTemp) { + /*****************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS??) this is still to be checked */ + /* for compatibility with other OS's. */ + /*****************************************************************/ + char filename[_MAX_PATH]; + int h; // File handle, return code + + for (int i = 0; i < Ncol; i++) { + sprintf(filename, Colfn, i + 1); + /*rc =*/ PlugCloseFile(g, To_Fbs[i]); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Tpos * Clens[i]))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Tpos * Clens[i])) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } // endfor i + + } else // UseTemp + // Ok, now delete old files and rename new temp files + if (RenameTempFile(g) == RC_FX) + return RC_FX; + + // Reset these values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open temporary files used while updating or deleting. */ +/* Note: the files not updated have been given a T_Stream value of 1. */ +/***********************************************************************/ +bool VECFAM::OpenTempFile(PGLOBAL g) + { + char tempname[_MAX_PATH]; + + for (int i = 0; i < Ncol; i++) + if (!T_Streams[i]) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + sprintf(tempname, Tempat, i+1); + + if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) { + if (trace) + htrc("%s\n", g->Message); + + return true; + } else + T_Fbs[i] = PlgGetUser(g)->Openlist; + + } else // This is a column that is not updated + T_Streams[i] = NULL; // For RenameTempFile + + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate updated lines before writing blocks. */ +/***********************************************************************/ +bool VECFAM::MoveLines(PGLOBAL g) + { + if (UseTemp && !InitUpdate) { // Don't do it in check pass + Fpos = OldBlk * Nrec; + + if (MoveIntermediateLines(g)) { + Closing = true; // ??? + return true; + } // endif UseTemp + +// Spos = Fpos + Nrec; + } // endif UseTemp + return false; + + } // end of MoveLines + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *bn) + { + int i; + int n; + bool b = false; + size_t req, len; + + for (n = Fpos - Spos; n > 0; n -= Nrec) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + if (!T_Streams[i]) + continue; // Non updated column + + if (!UseTemp || !b) + if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Streams[i]); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp) + if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (trace) + htrc("after write pos=%d\n", ftell(Streams[i])); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + b = true; + } // endfor n + + return false; + } // end of MoveIntermediate Lines + +/***********************************************************************/ +/* Delete the old files and rename the new temporary files. */ +/***********************************************************************/ +int VECFAM::RenameTempFile(PGLOBAL g) + { + char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH]; + int rc = RC_OK; + + // Close all files. + // This loop is necessary because, in case of join, + // the table files can have been open several times. + for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next) + rc = PlugCloseFile(g, fb); + + for (int i = 0; i < Ncol && rc == RC_OK; i++) { + if (!T_Fbs[i]) + continue; + + tempname = (char*)T_Fbs[i]->Fname; + sprintf(filename, Colfn, i+1); + PlugSetPath(filename, filename, Tdbp->GetPath()); + strcat(PlugRemoveType(filetemp, filename), ".ttt"); + remove(filetemp); // May still be there from previous error + + if (rename(filename, filetemp)) { // Save file for security + sprintf(g->Message, MSG(RENAME_ERROR), + filename, filetemp, strerror(errno)); + rc = RC_FX; + } else if (rename(tempname, filename)) { + sprintf(g->Message, MSG(RENAME_ERROR), + tempname, filename, strerror(errno)); + rc = rename(filetemp, filename); // Restore saved file + rc = RC_FX; + } else if (remove(filetemp)) { + sprintf(g->Message, MSG(REMOVE_ERROR), + filetemp, strerror(errno)); + rc = RC_INFO; // Acceptable + } // endif's + + } // endfor i + + return rc; + } // end of RenameTempFile + +/***********************************************************************/ +/* Data Base close routine for VEC access method. */ +/***********************************************************************/ +void VECFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last += (CurBlk * Nrec + CurNum -1); + Block += (Last / Nrec); + Last = Last % Nrec + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Block += CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) + rc = ResetTableSize(g, Block, Last); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (mode == MODE_UPDATE) { + if (UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = OldBlk * Nrec; + wrc = MoveIntermediateLines(g); +// Spos = Fpos + Nrec; + } // endif UseTemp + + // Write back to file any pending modifications + if (wrc == RC_OK) + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (wrc == RC_OK && UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = (Block - 1) * Nrec + Last; + wrc = MoveIntermediateLines(g); + } // endif UseTemp + + } // endif's mode + + if (UseTemp && !InitUpdate) { + // If they are errors, leave files unchanged + if (wrc == RC_OK) + rc = RenameTempFile(g); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (Streams) + for (int i = 0; i < Ncol; i++) + if (Streams[i]) { + rc = PlugCloseFile(g, To_Fbs[i]); + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif Streams + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + len = Nrec * colp->Clen * CurBlk; + i = colp->Index - 1; + + if (trace) + htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n", + len, i, Nrec, colp->Deplac, Lrecl, CurBlk); + + if (fseek(Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Streams[i]); + + if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) { + char fn[_MAX_PATH]; + + sprintf(fn, Colfn, colp->Index); +#if defined(WIN32) + if (feof(Streams[i])) +#else // !WIN32 + if (errno == NO_ERROR) +#endif // !WIN32 + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn); + else + sprintf(g->Message, MSG(READ_ERROR), + fn, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + len = Nrec * colp->Clen * colp->ColBlk; + i = colp->Index - 1; + + if (trace) + htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp) + if (fseek(T_Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum + : (colp->ColBlk == Block - 1) ? Last : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Streams[i])) { + char fn[_MAX_PATH]; + + sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index); + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } else + Spos = Fpos + n; + +#if defined(UNIX) + fflush(Streams[i]); //NGC +#endif + return false; + } // end of WriteBlock + +/* -------------------------- Class VMPFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VMPFAM class. */ +/***********************************************************************/ +VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp) + { + To_Fbs = NULL; + Split = true; + Block = Last = -1; + } // end of VMPFAM standard constructor + +VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp) + { + To_Fbs = txfp->To_Fbs; + } // end of VMPFAM copy constructor + +/***********************************************************************/ +/* VCT 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 VMPFAM::OpenTableFile(PGLOBAL g) + { + int i; + bool b; + MODE mode = Tdbp->GetMode(); + PCOLDEF cdp; + PVCTCOL cp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + if (mode == MODE_DELETE && !Tdbp->GetNext()) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + } else + Cardinality(g); // See comment in VECFAM::OpenTbleFile + + + /*********************************************************************/ + /* Prepare the filename pattern for column files and set Ncol. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Memcol[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_DELETE) { + // All columns are used in Delete mode + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (MapColumnFile(g, mode, i)) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1)) + return true; + + /*******************************************************************/ + /* Open other non already open used columns (except pseudos) */ + /*******************************************************************/ + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Memcol[cp->Index - 1]) + if (MapColumnFile(g, MODE_READ, cp->Index - 1)) + return true; + + } // endif mode + + /*********************************************************************/ + /* Check for void table or missing columns */ + /*********************************************************************/ + for (b = !Memcol[0], i = 1; i < Ncol; i++) + if (b != !Memcol[i]) + return true; + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i) + { + char filename[_MAX_PATH]; + int len; + HANDLE hFile; + MEMMAP mm; + PFBLOCK fp; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as */ + /* if it were entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dup->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping file, fp=%p\n", fp); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memcol[i] = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mode, DelRows); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*****************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*****************************************************************/ + len = mm.lenL; + Memcol[i] = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + ResetTableSize(g, 0, Nrec); + return false; + } // endif len + + if (!Memcol[i]) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dup->Openlist; + dup->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memcol[i]; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fbs[i] = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d\n", + fp, fp->Count, Memcol[i], len); + + return false; + } // end of MapColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/***********************************************************************/ +bool VMPFAM::AllocateBuffer(PGLOBAL g) + { + PVCTCOL cp; + + if (Tdbp->GetMode() == MODE_DELETE) { + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) + Clens[i] = cdp->GetClen(); + + } // endif mode + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VMPFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if ((n = Fpos - Spos) > 0) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n); + } // endif i + + Tpos += n; + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* We must firstly Unmap the view and use the saved file handle */ + /* to put an EOF at the end of the copied part of the file. */ + /*******************************************************************/ + PFBLOCK fp; + + for (i = 0; i < Ncol; i++) { + fp = To_Fbs[i]; + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ + n = Tpos * Clens[i]; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } // endfor i + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VMPFAM::CloseTableFile(PGLOBAL g) + { + if (Tdbp->GetMode() == MODE_DELETE) { + // Set Block and Nrec values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + ResetTableSize(g, Block, Last); + } else if (Tdbp->GetMode() == MODE_INSERT) + assert(false); + + for (int i = 0; i < Ncol; i++) + PlugCloseFile(g, To_Fbs[i]); + + } // end of CloseTableFile + +/* -------------------------- Class BGVFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the BGVFAM class. */ +/***********************************************************************/ +// Constructors +BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp) + { + Hfile = INVALID_HANDLE_VALUE; + Tfile = INVALID_HANDLE_VALUE; + BigDep = NULL; + } // end of BGVFAM constructor + +BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp) + { + Hfile = txfp->Hfile; + Tfile = txfp->Tfile; + BigDep= txfp->BigDep; + } // end of BGVFAM copy constructor + +/***********************************************************************/ +/* Set current position in a big file. */ +/***********************************************************************/ +bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b) + { +#if defined(WIN32) + char buf[256]; + DWORD drc, m = (b) ? FILE_END : FILE_BEGIN; + LARGE_INTEGER of; + + of.QuadPart = pos; + of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (drc = GetLastError()) != NO_ERROR) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(SFP_ERROR), buf); + return true; + } // endif +#else // !WIN32 + if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) { + sprintf(g->Message, MSG(ERROR_IN_LSK), errno); + return true; + } // endif +#endif // !WIN32 + + return false; + } // end of BigSeek + +/***********************************************************************/ +/* Read from a big file. */ +/***********************************************************************/ +bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbr, drc, len = (DWORD)req; + bool brc = ReadFile(h, inbuf, len, &nbr, NULL); + + if (trace) + htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr); + + if (!brc || nbr != len) { + char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_READ)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(READ_ERROR), To_File, buf); + + if (trace) + htrc("BIGREAD: %s\n", g->Message); + + rc = true; + } // endif brc || nbr +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbr = read(h, inbuf, len); + + if (nbr != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n", + nbr, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigRead + +/***********************************************************************/ +/* Write into a big file. */ +/***********************************************************************/ +bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbw, drc, len = (DWORD)req; + bool brc = WriteFile(h, inbuf, len, &nbw, NULL); + + if (trace) + htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw); + + if (!brc || nbw != len) { + char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_NUM)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, drc, g->Message); + + rc = true; + } // endif brc || nbw +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbw = write(h, inbuf, len); + + if (nbw != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigWrite + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int BGVFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int n; + VECHEADER vh; + HANDLE h; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + +#if defined(WIN32) + LARGE_INTEGER len; + + h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h != INVALID_HANDLE_VALUE) { + // Get the size of the file (can be greater than 4 GB) + len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart); + } // endif h + + if (h == INVALID_HANDLE_VALUE || !len.QuadPart) { +#else // !WIN32 + h = open64(filename, O_RDONLY, 0); + + if (h == INVALID_HANDLE_VALUE || !_filelength(h)) { +#endif // !WIN32 + // Consider this is a void table + if (trace) + htrc("Void table h=%d\n", h); + + Last = Nrec; + Block = 0; + + if (h != INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return n; + } else if (Header == 3) + /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + if (BigRead(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + + if (trace) + htrc("Block=%d Last=%d\n", Block, Last); + + } // endif's + + CloseFileHandle(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Set the MaxRec and NumRec info in the file header. */ +/***********************************************************************/ +bool BGVFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool b = false, rc = false; + VECHEADER vh; + HANDLE h = INVALID_HANDLE_VALUE; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Hfile != INVALID_HANDLE_VALUE) { + h = Hfile; + + if (Header == 1) + /*bk =*/ BigSeek(g, h, (BIGINT)0); + + } else + b = true; + + } else // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + + if (h == INVALID_HANDLE_VALUE) { +#if defined(WIN32) + DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING; + + h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + +#else // !WIN32 + int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC; + + h = open64(filename, oflag, 0); +#endif // !WIN32 + + if (h == INVALID_HANDLE_VALUE) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } // endif h + + } // endif h + + if (Header == 3) + /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if (BigWrite(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || Hfile == INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* VEC Create an empty file for new Vector formatted tables. */ +/***********************************************************************/ +bool BGVFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + PlugSetPath(filename, fn, Tdbp->GetPath()); + +#if defined(WIN32) + char *p; + DWORD rc; + bool brc; + LARGE_INTEGER of; + HANDLE h; + + h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) { + p = MSG(OPENING); + goto err; + } // endif h + + of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n", + of.QuadPart, n, MaxBlk, Blksize); + + of.LowPart = SetFilePointer(h, of.LowPart, + &of.HighPart, FILE_BEGIN); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) { + p = MSG(MAKING); + goto err; + } // endif + + brc = WriteFile(h, &c, 1, &rc, NULL); + + if (!brc || rc != 1) { + p = MSG(WRITING); + goto err; + } // endif + + CloseHandle(h); + return false; + + err: + rc = GetLastError(); + sprintf(g->Message, MSG(EMPTY_FILE), p, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + + if (h != INVALID_HANDLE_VALUE) + CloseHandle(h); + + return true; +#else // !WIN32 + int h; + BIGINT pos; + + h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); + + if (h == -1) + return true; + + pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n", + pos, n, MaxBlk, Blksize); + + if (lseek64(h, pos, SEEK_SET) < 0) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; +#endif // !WIN32 + } // end of MakeEmptyFile + +/***********************************************************************/ +/* Vopen function: opens a file using Windows or Unix API's. */ +/***********************************************************************/ +bool BGVFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool del = false; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) { + sprintf(g->Message, MSG(FILE_OPEN_YET), To_File); + return true; + } // endif + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (trace) + htrc("OpenTableFile: filename=%s mode=%d Last=%d\n", + filename, mode, Last); + +#if defined(WIN32) + DWORD access, creation, share = 0, rc = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + creation = OPEN_EXISTING; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + access = GENERIC_READ | GENERIC_WRITE; + } else if (Last == Nrec) + access = GENERIC_WRITE; + else + // Required to update the last block + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_ALWAYS; + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will stop the process by + // causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + del = true; + + // This will delete the whole file + access = GENERIC_READ | GENERIC_WRITE; + creation = TRUNCATE_EXISTING; + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + if ((UseTemp = Tdbp->IsUsingTemp(g))) + access = GENERIC_READ; + else + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_EXISTING; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + /*********************************************************************/ + /* Use specific Windows API functions. */ + /*********************************************************************/ + Hfile = CreateFile(filename, access, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + } // endif Hfile + + if (trace) + htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n", + rc, access, share, creation, Hfile, filename); + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode we must position the cursor at end of file. */ + /*******************************************************************/ + LARGE_INTEGER of; + + of.QuadPart = (BIGINT)0; + of.LowPart = SetFilePointer(Hfile, of.LowPart, + &of.HighPart, FILE_END); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (rc = GetLastError()) != NO_ERROR) { + sprintf(g->Message, MSG(ERROR_IN_SFP), rc); + CloseHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + } // endif + + } // endif Mode + +#else // UNIX + /*********************************************************************/ + /* The open() function has a transitional interface for 64-bit */ + /* file offsets. Note that using open64() is equivalent to using */ + /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */ + /*********************************************************************/ + int rc = 0; + int oflag; + mode_t pmd = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + oflag = O_RDONLY; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + oflag = O_RDWR; + } else if (Last == Nrec) + oflag = O_WRONLY | O_CREAT | O_APPEND; + else + // Required to update the last block + oflag = O_RDWR | O_CREAT | O_APPEND; + + pmd = S_IREAD | S_IWRITE; + break; + case MODE_DELETE: + // This is temporary until a partial delete is implemented + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + del = true; + + // This will delete the whole file and provoque ReadDB to + // return immediately. + oflag = O_RDWR | O_TRUNC; + strcpy(g->Message, MSG(NO_VCT_DELETE)); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + oflag = (UseTemp) ? O_RDONLY : O_RDWR; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + Hfile = open64(filename, oflag, pmd); // Enable file size > 2G + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + strcat(g->Message, strerror(errno)); + } // endif Hfile + + if (trace) + htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n", + rc, oflag, mode, Hfile, filename); +#endif // UNIX + + if (!rc) { + if (!To_Fb) { + To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fb->Fname = To_File; + To_Fb->Type = TYPE_FB_HANDLE; + To_Fb->Memory = NULL; + To_Fb->Length = 0; + To_Fb->File = NULL; + To_Fb->Next = dbuserp->Openlist; + dbuserp->Openlist = To_Fb; + } // endif To_Fb + + To_Fb->Count = 1; + To_Fb->Mode = mode; + To_Fb->Handle = Hfile; + + if (trace) + htrc("File %s is open in mode %d\n", filename, mode); + + if (del) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + /*********************************************************************/ + /* Allocate the table and column block buffers. */ + /*********************************************************************/ + return AllocateBuffer(g); + } else + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool BGVFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + if (!NewBlock) { + // Not reopening after inserting the last block + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + InitInsert(g); // Initialize inserting + + // Currently we don't use a temporary file for inserting + Tfile = Hfile; + } // endif NewBlock + + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + if (MaxBlk) + BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT)); + else + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + if (MaxBlk) + BigDep[i] = (BIGINT)Headlen + + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk; + else + Deplac[i] = cdp->GetPoff() * Nrec; + + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base write routine for huge VCT access method. */ +/***********************************************************************/ +int BGVFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (Tfile == INVALID_HANDLE_VALUE) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + Tfile = Hfile; + + } // endif Tfile + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for a Vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // Close the VCT file and reopen it in mode Insert +//#if defined(WIN32) //OB +// CloseHandle(Hfile); +//#else // UNIX +// close(Hfile); +//#endif // UNIX + CloseFileHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + To_Fb->Count = 0; + Last = Nrec; // Tested in OpenTableFile + + if (OpenTableFile(g)) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Vopen + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the VCT file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if (BigWrite(g, Hfile, NewBlock, Blksize)) + return RC_FX; + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for BGVFAM access method. */ +/***********************************************************************/ +int BGVFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + /*********************************************************************/ + /* There is an alternative here depending on UseTemp: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /*********************************************************************/ + if (trace) + htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Tfile = Hfile; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + if (Last < Nrec) // Clean last block + if (CleanUnusedSpace(g)) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(WIN32) + BIGINT pos = (BIGINT)Block * (BIGINT)Blksize; + + if (BigSeek(g, Hfile, pos)) + return RC_FX; + + if (!SetEndOfFile(Hfile)) { + DWORD drc = GetLastError(); + + sprintf(g->Message, MSG(SETEOF_ERROR), drc); + return RC_FX; + } // endif error +#else // !WIN32 + if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + return RC_FX; + } // endif +#endif // !WIN32 + } else // MaxBlk + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool BGVFAM::OpenTempFile(PGLOBAL g) + { + char *tempname; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (!MaxBlk) + remove(tempname); // Be sure it does not exist yet + else if (MakeEmptyFile(g, tempname)) + return true; + +#if defined(WIN32) + DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW; + + Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL, + access, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Tfile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)tempname, _MAX_PATH, NULL); + strcat(g->Message, tempname); + return true; + } // endif Tfile +#else // UNIX + int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC; + + Tfile = open64(tempname, oflag, S_IWRITE); + + if (Tfile == INVALID_HANDLE_VALUE) { + int rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); + strcat(g->Message, strerror(errno)); + return true; + } //endif Tfile +#endif // UNIX + + To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fbt->Fname = tempname; + To_Fbt->Type = TYPE_FB_HANDLE; + To_Fbt->Memory = NULL; + To_Fbt->Length = 0; + To_Fbt->File = NULL; + To_Fbt->Next = dup->Openlist; + To_Fbt->Count = 1; + To_Fbt->Mode = MODE_INSERT; + To_Fbt->Handle = Tfile; + dup->Openlist = To_Fbt; + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, n, req, dep; + bool eof = (b) ? *b : false; + BIGINT pos; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (DWORD)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (DWORD)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (!MaxBlk) { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i]) + + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize; + } else + pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i]; + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, To_Buf, req * Clens[i])) + return true; + + if (!UseTemp || MaxBlk) { + if (!MaxBlk) + pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i]) + + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize; + else + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endif UseTemp + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, must be + // done here because Tfile was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + if (BigWrite(g, Tfile, NewBlock, Blksize)) + return true; + + if (Spos == Fpos) + eof = false; + + } // endif Usetemp... + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a huge VCT or Vec table file. */ +/***********************************************************************/ +bool BGVFAM::CleanUnusedSpace(PGLOBAL g) + { + int i; + int n; + BIGINT pos, dep; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); // This case is handled in MoveIntermediateLines + + if (!(n = Nrec - Last)) + return false; + + dep = (BIGINT)((Block - 1) * Blksize); + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigWrite(g, Hfile, To_Buf, n * Clens[i])) + return true; + + } // endfor i + + } else { + int req; + + memset(To_Buf, 0, Buflen); + + for (n = Fpos - Tpos; n > 0; n -= req) { + /*****************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* This seems to work even column blocks have been made with */ + /* Blanks = true. Perhaps should it be set to false for VEC. */ + /*****************************************************************/ + req = min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endfor i + + Tpos += req; + } // endfor n + + } // endif MaxBlk + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for huge VEC access method. */ +/***********************************************************************/ +void BGVFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && Tfile) { + rc = RenameTempFile(g); + Hfile = Tfile = INVALID_HANDLE_VALUE; + + if (Header) + // Header must be set because it was not set in temp file + rc = SetBlockInfo(g); + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && Tfile) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (Hfile != INVALID_HANDLE_VALUE) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Hfile = INVALID_HANDLE_VALUE; + } // end of CloseDB + +/***********************************************************************/ +/* Rewind routine for huge VCT access method. */ +/***********************************************************************/ +void BGVFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; + +#if 0 // This is probably unuseful as the file is directly accessed +#if defined(WIN32) //OB + SetFilePointer(Hfile, 0, NULL, FILE_BEGIN); +#else // UNIX + lseek64(Hfile, 0, SEEK_SET); +#endif // UNIX +#endif // 0 + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)CurBlk); + + if (trace) + htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec)) + return true; + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)colp->ColBlk); + + if (trace) + htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (BigSeek(g, Tfile, pos)) + return true; + +//len = colp->Clen * Nrec; see comment in VCTFAM + len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec); + + if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len)) + return true; + + return false; + } // end of WriteBlock + +/* ----------------------- End of FilAMVct --------------------------- */ diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp index 1b687ab9ddc..22ddffe26a1 100644 --- a/storage/connect/filamzip.cpp +++ b/storage/connect/filamzip.cpp @@ -652,13 +652,12 @@ int ZBKFAM::DeleteRecords(PGLOBAL g, int irc) if (irc == RC_EF) { LPCSTR name = Tdbp->GetName(); PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); - PCATLG cat = PlgGetCatalog(g); defp->SetBlock(0); defp->SetLast(Nrec); - if (!cat->SetIntCatInfo("Blocks", 0) || - !cat->SetIntCatInfo("Last", 0)) { + if (!defp->SetIntCatInfo("Blocks", 0) || + !defp->SetIntCatInfo("Last", 0)) { sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); return RC_FX; } else @@ -677,7 +676,6 @@ void ZBKFAM::CloseTableFile(PGLOBAL g) int rc = RC_OK; if (Tdbp->GetMode() == MODE_INSERT) { - PCATLG cat = PlgGetCatalog(g); LPCSTR name = Tdbp->GetName(); PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); @@ -696,8 +694,8 @@ void ZBKFAM::CloseTableFile(PGLOBAL g) if (rc != RC_FX) { defp->SetBlock(Block); defp->SetLast(Last); - cat->SetIntCatInfo("Blocks", Block); - cat->SetIntCatInfo("Last", Last); + defp->SetIntCatInfo("Blocks", Block); + defp->SetIntCatInfo("Last", Last); } // endif gzclose(Zfile); @@ -1335,7 +1333,6 @@ void ZLBFAM::CloseTableFile(PGLOBAL g) int rc = RC_OK; if (Tdbp->GetMode() == MODE_INSERT) { - PCATLG cat = PlgGetCatalog(g); LPCSTR name = Tdbp->GetName(); PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); @@ -1355,8 +1352,8 @@ void ZLBFAM::CloseTableFile(PGLOBAL g) if (rc != RC_FX) { defp->SetBlock(Block); defp->SetLast(Last); - cat->SetIntCatInfo("Blocks", Block); - cat->SetIntCatInfo("Last", Last); + defp->SetIntCatInfo("Blocks", Block); + defp->SetIntCatInfo("Last", Last); } // endif fclose(Stream); diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 24a9ff36b98..8290d1d765c 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -637,10 +637,16 @@ static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp) /****************************************************************************/ TABTYPE ha_connect::GetRealType(PTOS pos) { - TABTYPE type= GetTypeID(pos->type); + TABTYPE type; + + if (pos || (pos= GetTableOptionStruct(table))) { + type= GetTypeID(pos->type); - if (type == TAB_UNDEF) - type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS; + if (type == TAB_UNDEF) + type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS; + + } else + type= TAB_UNDEF; return type; } // end of GetRealType @@ -653,8 +659,8 @@ ulonglong ha_connect::table_flags() const { ulonglong flags= HA_CAN_VIRTUAL_COLUMNS | HA_REC_NOT_IN_SEQ | HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS | - HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | - HA_PARTIAL_COLUMN_READ | +// HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | + HA_PARTIAL_COLUMN_READ | HA_FILE_BASED | // HA_NULL_IN_KEY | not implemented yet // HA_FAST_KEY_READ | causes error when sorting (???) HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER | @@ -1433,8 +1439,9 @@ int ha_connect::MakeRecord(char *buf) } // endif colp value= colp->GetValue(); + p= NULL; - // All this could be better optimized + // All this was better optimized if (!value->IsNull()) { switch (value->GetType()) { case TYPE_DATE: @@ -1459,39 +1466,37 @@ int ha_connect::MakeRecord(char *buf) // Get date in the format required by MySQL fields value->FormatValue(sdvalout, fmt); p= sdvalout->GetCharValue(); - break; - case TYPE_DOUBLE: - p= NULL; + rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN); break; case TYPE_STRING: - // Passthru - default: + case TYPE_DECIM: p= value->GetCharString(val); + charset= tdbp->data_charset(); + rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN); + break; + case TYPE_DOUBLE: + rc= fp->store(value->GetFloatValue()); + break; + default: + rc= fp->store(value->GetBigintValue(), value->IsUnsigned()); break; } // endswitch Type - if (p) { - if (fp->store(p, strlen(p), charset, CHECK_FIELD_WARN)) { - // Avoid "error" on null fields - if (value->GetIntValue()) - rc= HA_ERR_WRONG_IN_RECORD; + // Store functions returns 1 on overflow and -1 on fatal error + if (rc > 0) { + char buf[128]; + THD *thd= ha_thd(); - DBUG_PRINT("MakeRecord", ("%s", p)); - } // endif store + sprintf(buf, "Out of range value %s for column '%s' at row %ld", + value->GetCharString(val), + fp->field_name, + thd->get_stmt_da()->current_row_for_warning()); - } else - if (fp->store(value->GetFloatValue())) { -// rc= HA_ERR_WRONG_IN_RECORD; a Warning was ignored - char buf[128]; - THD *thd= ha_thd(); - - sprintf(buf, "Out of range value for column '%s' at row %ld", - fp->field_name, - thd->get_stmt_da()->current_row_for_warning()); - - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, buf); - DBUG_PRINT("MakeRecord", ("%s", value->GetCharString(val))); - } // endif store + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, buf); + DBUG_PRINT("MakeRecord", ("%s", buf)); + rc= 0; + } else if (rc < 0) + rc= HA_ERR_WRONG_IN_RECORD; fp->set_notnull(); } else @@ -2670,7 +2675,6 @@ int ha_connect::index_next(uchar *buf) } // end of index_next -#ifdef NOT_USED /** @brief Used to read backwards through the index. @@ -2678,9 +2682,15 @@ int ha_connect::index_next(uchar *buf) int ha_connect::index_prev(uchar *buf) { DBUG_ENTER("ha_connect::index_prev"); - DBUG_RETURN(HA_ERR_WRONG_COMMAND); -} -#endif // NOT_USED + int rc; + + if (indexing > 0) { + rc= ReadIndexed(buf, OP_PREV); + } else + rc= HA_ERR_WRONG_COMMAND; + + DBUG_RETURN(rc); +} // end of index_prev /** @@ -3017,12 +3027,21 @@ int ha_connect::info(uint flag) if (!valid_info) { // tdbp must be available to get updated info if (xp->CheckQuery(valid_query_id) || !tdbp) { + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; + if (xmod == MODE_ANY || xmod == MODE_ALTER) { // Pure info, not a query pure= true; xp->CheckCleanup(); } // endif xmod + // This is necessary for getting file length + if (cat && table) + cat->SetDataPath(g, table->s->db.str); + else + return HA_ERR_INTERNAL_ERROR; // Should never happen + tdbp= GetTDB(g); } // endif tdbp @@ -4330,8 +4349,8 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, char v, spc= ',', qch= 0; const char *fncn= "?"; const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src; - const char *col, *ocl, *rnk, *pic, *fcl; - char *tab, *dsn, *shm; + const char *col, *ocl, *rnk, *pic, *fcl, *skc; + char *tab, *dsn, *shm; #if defined(WIN32) char *nsp= NULL, *cls= NULL; #endif // WIN32 @@ -4360,7 +4379,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, if (!g) return HA_ERR_INTERNAL_ERROR; - user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL; + user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= NULL; // Get the useful create options ttp= GetTypeID(topt->type); @@ -4386,6 +4405,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, ocl= GetListOption(g, "occurcol", topt->oplist, NULL); pic= GetListOption(g, "pivotcol", topt->oplist, NULL); fcl= GetListOption(g, "fnccol", topt->oplist, NULL); + skc= GetListOption(g, "skipcol", topt->oplist, NULL); rnk= GetListOption(g, "rankcol", topt->oplist, NULL); pwd= GetListOption(g, "password", topt->oplist); #if defined(WIN32) @@ -4652,7 +4672,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, break; case TAB_PIVOT: - qrp= PivotColumns(g, tab, src, pic, fcl, host, db, user, pwd, port); + qrp= PivotColumns(g, tab, src, pic, fcl, skc, host, db, user, pwd, port); break; case TAB_OEM: qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL); @@ -5236,7 +5256,7 @@ int ha_connect::create(const char *name, TABLE *table_arg, // Get the index definitions if (xdp= GetIndexInfo()) { - if (IsTypeIndexable(type)) { + if (GetIndexType(type) == 1) { PDBUSER dup= PlgGetUser(g); PCATLG cat= (dup) ? dup->Catalog : NULL; @@ -5251,8 +5271,8 @@ int ha_connect::create(const char *name, TABLE *table_arg, CloseTable(g); } // endif cat - - } else { + + } else if (!GetIndexType(type)) { sprintf(g->Message, "Table type %s is not indexable", options->type); my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); rc= HA_ERR_UNSUPPORTED; @@ -5546,35 +5566,36 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table, if (ha_alter_info->handler_flags & index_operations || !SameString(altered_table, "optname") || !SameBool(altered_table, "sepindex")) { - if (!IsTypeIndexable(type)) { + if (GetIndexType(type) == 1) { + g->Xchk= new(g) XCHK; + PCHK xcp= (PCHK)g->Xchk; + + xcp->oldpix= GetIndexInfo(table->s); + xcp->newpix= GetIndexInfo(altered_table->s); + xcp->oldsep= GetBooleanOption("sepindex", false); + xcp->oldsep= xcp->SetName(g, GetStringOption("optname")); + tshp= altered_table->s; + xcp->newsep= GetBooleanOption("sepindex", false); + xcp->newsep= xcp->SetName(g, GetStringOption("optname")); + tshp= NULL; + + if (xtrace && g->Xchk) + htrc( + "oldsep=%d newsep=%d oldopn=%s newopn=%s oldpix=%p newpix=%p\n", + xcp->oldsep, xcp->newsep, + SVP(xcp->oldopn), SVP(xcp->newopn), + xcp->oldpix, xcp->newpix); + + if (sqlcom == SQLCOM_ALTER_TABLE) + idx= true; + else + DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK); + + } else if (!GetIndexType(type)) { sprintf(g->Message, "Table type %s is not indexable", oldopt->type); my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); DBUG_RETURN(HA_ALTER_ERROR); - } // endif Indexable - - g->Xchk= new(g) XCHK; - PCHK xcp= (PCHK)g->Xchk; - - xcp->oldpix= GetIndexInfo(table->s); - xcp->newpix= GetIndexInfo(altered_table->s); - xcp->oldsep= GetBooleanOption("sepindex", false); - xcp->oldsep= xcp->SetName(g, GetStringOption("optname")); - tshp= altered_table->s; - xcp->newsep= GetBooleanOption("sepindex", false); - xcp->newsep= xcp->SetName(g, GetStringOption("optname")); - tshp= NULL; - - if (xtrace && g->Xchk) - htrc( - "oldsep=%d newsep=%d oldopn=%s newopn=%s oldpix=%p newpix=%p\n", - xcp->oldsep, xcp->newsep, - SVP(xcp->oldopn), SVP(xcp->newopn), - xcp->oldpix, xcp->newpix); - - if (sqlcom == SQLCOM_ALTER_TABLE) - idx= true; - else - DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK); + } // endif index type } // endif index operation diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index 46763394945..fac7560ddd6 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -180,7 +180,7 @@ public: // CONNECT Implementation static bool connect_init(void); static bool connect_end(void); - TABTYPE GetRealType(PTOS pos); + TABTYPE GetRealType(PTOS pos= NULL); char *GetStringOption(char *opname, char *sdef= NULL); PTOS GetTableOptionStruct(TABLE *table_arg); bool GetBooleanOption(char *opname, bool bdef); @@ -256,8 +256,8 @@ public: */ ulong index_flags(uint inx, uint part, bool all_parts) const { - return HA_READ_NEXT | HA_READ_RANGE | HA_READ_ORDER - | HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR; + return HA_READ_NEXT | HA_READ_RANGE | HA_READ_ORDER | + HA_READ_PREV | HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR; } // end of index_flags /** @brief @@ -421,7 +421,7 @@ PFIL CondFilter(PGLOBAL g, Item *cond); We implement this in ha_connect.cc. It's not an obligatory method; skip it and and MySQL will treat it as not implemented. */ -//int index_prev(uchar *buf); +int index_prev(uchar *buf); /** @brief We implement this in ha_connect.cc. It's not an obligatory method; diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index 909bccd8864..7a6f317526a 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -211,7 +211,7 @@ bool IsTypeNullable(TABTYPE type) } // end of IsTypeNullable /***********************************************************************/ -/* Return true for table types with fix length records. */ +/* Return true for indexable table by XINDEX. */ /***********************************************************************/ bool IsTypeFixed(TABTYPE type) { @@ -233,7 +233,7 @@ bool IsTypeFixed(TABTYPE type) } // end of IsTypeFixed /***********************************************************************/ -/* Return true for table types with fix length records. */ +/* Return true for table indexable by XINDEX. */ /***********************************************************************/ bool IsTypeIndexable(TABTYPE type) { @@ -257,6 +257,35 @@ bool IsTypeIndexable(TABTYPE type) return idx; } // end of IsTypeIndexable +/***********************************************************************/ +/* Return index type: 0 NO, 1 XINDEX, 2 REMOTE. */ +/***********************************************************************/ +int GetIndexType(TABTYPE type) + { + int xtyp; + + switch (type) { + case TAB_DOS: + case TAB_CSV: + case TAB_FMT: + case TAB_FIX: + case TAB_BIN: + case TAB_VEC: + case TAB_DBF: + xtyp= 1; + break; + case TAB_MYSQL: + case TAB_ODBC: +// xtyp= 2; Remote indexes not implemented yet +// break; + default: + xtyp= 0; + break; + } // endswitch type + + return xtyp; + } // end of GetIndexType + /***********************************************************************/ /* Get a unique enum catalog function ID. */ /***********************************************************************/ @@ -434,281 +463,6 @@ void MYCAT::SetPath(PGLOBAL g, LPCSTR *datapath, const char *path) } // end of SetDataPath -/***********************************************************************/ -/* This function sets an integer MYCAT information. */ -/***********************************************************************/ -bool MYCAT::SetIntCatInfo(PSZ what, int n) - { - return Hc->SetIntegerOption(what, n); - } // end of SetIntCatInfo - -/***********************************************************************/ -/* This function returns integer MYCAT information. */ -/***********************************************************************/ -int MYCAT::GetIntCatInfo(PSZ what, int idef) - { - int n= Hc->GetIntegerOption(what); - - return (n == NO_IVAL) ? idef : n; - } // end of GetIntCatInfo - -/***********************************************************************/ -/* This function returns Boolean MYCAT information. */ -/***********************************************************************/ -bool MYCAT::GetBoolCatInfo(PSZ what, bool bdef) - { - bool b= Hc->GetBooleanOption(what, bdef); - - return b; - } // end of GetBoolCatInfo - -/***********************************************************************/ -/* This function returns size catalog information. */ -/***********************************************************************/ -int MYCAT::GetSizeCatInfo(PSZ what, PSZ sdef) - { - char * s, c; - int i, n= 0; - - if (!(s= Hc->GetStringOption(what))) - s= sdef; - - if ((i= sscanf(s, " %d %c ", &n, &c)) == 2) - switch (toupper(c)) { - case 'M': - n *= 1024; - case 'K': - n *= 1024; - } // endswitch c - - return n; -} // end of GetSizeCatInfo - -/***********************************************************************/ -/* This function sets char MYCAT information in buf. */ -/***********************************************************************/ -int MYCAT::GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size) - { - char *s= Hc->GetStringOption(what); - - strncpy(buf, ((s) ? s : sdef), size); - return size; - } // end of GetCharCatInfo - -/***********************************************************************/ -/* This function returns string MYCAT information. */ -/* Default parameter is "*" to get the handler default. */ -/***********************************************************************/ -char *MYCAT::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef) - { - char *sval= NULL, *s= Hc->GetStringOption(what, sdef); - - if (s) { - sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1); - strcpy(sval, s); - } else if (!stricmp(what, "filename")) { - // Return default file name - char *ftype= Hc->GetStringOption("Type", "*"); - int i, n; - - if (IsFileType(GetTypeID(ftype))) { - sval= (char*)PlugSubAlloc(g, NULL, strlen(Hc->GetTableName()) + 12); - strcat(strcpy(sval, Hc->GetTableName()), "."); - n= strlen(sval); - - // Fold ftype to lower case - for (i= 0; i < 12; i++) - if (!ftype[i]) { - sval[n+i]= 0; - break; - } else - sval[n+i]= tolower(ftype[i]); - - } // endif FileType - - } // endif s - - return sval; - } // end of GetStringCatInfo - -/***********************************************************************/ -/* This function returns column MYCAT information. */ -/***********************************************************************/ -int MYCAT::GetColCatInfo(PGLOBAL g, PTABDEF defp) - { - char *type= GetStringCatInfo(g, "Type", "*"); - int i, loff, poff, nof, nlg; - void *field= NULL; - TABTYPE tc; - PCOLDEF cdp, lcdp= NULL, tocols= NULL; - PCOLINFO pcf= (PCOLINFO)PlugSubAlloc(g, NULL, sizeof(COLINFO)); - - memset(pcf, 0, sizeof(COLINFO)); - - // Get a unique char identifier for type - tc= (defp->Catfunc == FNC_NO) ? GetTypeID(type) : TAB_PRX; - - // Take care of the column definitions - i= poff= nof= nlg= 0; - - // Offsets of HTML and DIR tables start from 0, DBF at 1 - loff= (tc == TAB_DBF) ? 1 : (tc == TAB_XML || tc == TAB_DIR) ? -1 : 0; - - while (true) { - // Default Offset depends on table type - switch (tc) { - case TAB_DOS: - case TAB_FIX: - case TAB_BIN: - case TAB_VEC: - case TAB_DBF: - poff= loff + nof; // Default next offset - nlg= max(nlg, poff); // Default lrecl - break; - case TAB_CSV: - case TAB_FMT: - nlg+= nof; - case TAB_DIR: - case TAB_XML: - poff= loff + 1; - break; - case TAB_INI: - case TAB_MAC: - case TAB_TBL: - case TAB_XCL: - case TAB_OCCUR: - case TAB_PRX: - case TAB_OEM: - poff = 0; // Offset represents an independant flag - break; - default: // VCT PLG ODBC MYSQL WMI... - poff = 0; // NA - break; - } // endswitch tc - -// do { - field= Hc->GetColumnOption(g, field, pcf); -// } while (field && (*pcf->Name =='*' /*|| pcf->Flags & U_VIRTUAL*/)); - - if (tc == TAB_DBF && pcf->Type == TYPE_DATE && !pcf->Datefmt) { - // DBF date format defaults to 'YYYMMDD' - pcf->Datefmt= "YYYYMMDD"; - pcf->Length= 8; - } // endif tc - - if (!field) - break; - - // Allocate the column description block - cdp= new(g) COLDEF; - - if ((nof= cdp->Define(g, NULL, pcf, poff)) < 0) - return -1; // Error, probably unhandled type - else if (nof) - loff= cdp->GetOffset(); - - switch (tc) { - case TAB_VEC: - cdp->SetOffset(0); // Not to have shift - case TAB_BIN: - // BIN/VEC are packed by default - if (nof) - // Field width is the internal representation width - // that can also depend on the column format - switch (cdp->Fmt ? *cdp->Fmt : 'X') { - case 'C': break; - case 'R': - case 'F': - case 'L': - case 'I': nof= 4; break; - case 'D': nof= 8; break; - case 'S': nof= 2; break; - case 'T': nof= 1; break; - default: nof= cdp->Clen; - } // endswitch Fmt - - default: - break; - } // endswitch tc - - if (lcdp) - lcdp->SetNext(cdp); - else - tocols= cdp; - - lcdp= cdp; - i++; - } // endwhile - - // Degree is the the number of defined columns (informational) - if (i != defp->GetDegree()) - defp->SetDegree(i); - - if (defp->GetDefType() == TYPE_AM_DOS) { - int ending, recln= 0; - PDOSDEF ddp= (PDOSDEF)defp; - - // Was commented because sometimes ending is 0 even when - // not specified (for instance if quoted is specified) -// if ((ending= Hc->GetIntegerOption("Ending")) < 0) { - if ((ending= Hc->GetIntegerOption("Ending")) <= 0) { -#if defined(WIN32) - ending= 2; -#else - ending= 1; -#endif - Hc->SetIntegerOption("Ending", ending); - } // endif ending - - // Calculate the default record size - switch (tc) { - case TAB_FIX: - recln= nlg + ending; // + length of line ending - break; - case TAB_BIN: - case TAB_VEC: - recln= nlg; - -// if ((k= (pak < 0) ? 8 : pak) > 1) - // See above for detailed comment - // Round up lrecl to multiple of 8 or pak -// recln= ((recln + k - 1) / k) * k; - - break; - case TAB_DOS: - case TAB_DBF: - recln= nlg; - break; - case TAB_CSV: - case TAB_FMT: - // The number of separators (assuming an extra one can exist) -// recln= poff * ((qotd) ? 3 : 1); to be investigated - recln= nlg + poff * 3; // To be safe - default: - break; - } // endswitch tc - - // lrecl must be at least recln to avoid buffer overflow - recln= max(recln, Hc->GetIntegerOption("Lrecl")); - Hc->SetIntegerOption("Lrecl", recln); - ddp->SetLrecl(recln); - } // endif Lrecl - - // Attach the column definition to the tabdef - defp->SetCols(tocols); - return poff; - } // end of GetColCatInfo - -/***********************************************************************/ -/* GetIndexInfo: retrieve index description from the table structure. */ -/***********************************************************************/ -bool MYCAT::GetIndexInfo(PGLOBAL g, PTABDEF defp) - { - // Attach new index(es) - defp->SetIndx(Hc->GetIndexInfo()); - return false; - } // end of GetIndexInfo - /***********************************************************************/ /* GetTableDesc: retrieve a table descriptor. */ /* Look for a table descriptor matching the name and type. */ diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h index 4c1d548d6a5..b45d3a08725 100644 --- a/storage/connect/mycat.h +++ b/storage/connect/mycat.h @@ -40,6 +40,7 @@ bool IsExactType(TABTYPE type); bool IsTypeNullable(TABTYPE type); bool IsTypeFixed(TABTYPE type); bool IsTypeIndexable(TABTYPE type); +int GetIndexType(TABTYPE type); uint GetFuncID(const char *func); /***********************************************************************/ @@ -57,14 +58,6 @@ class MYCAT : public CATALOG { void Reset(void); void SetDataPath(PGLOBAL g, const char *path) {SetPath(g, &DataPath, path);} - bool GetBoolCatInfo(PSZ what, bool bdef); - bool SetIntCatInfo(PSZ what, int ival); - int GetIntCatInfo(PSZ what, int idef); - int GetSizeCatInfo(PSZ what, PSZ sdef); - int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size); - char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef); - int GetColCatInfo(PGLOBAL g, PTABDEF defp); - bool GetIndexInfo(PGLOBAL g, PTABDEF defp); bool StoreIndex(PGLOBAL g, PTABDEF defp) {return false;} // Temporary PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, LPCSTR type, PRELDEF *prp = NULL); diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp index 18c80f2b24e..02309e78d55 100644 --- a/storage/connect/myconn.cpp +++ b/storage/connect/myconn.cpp @@ -365,7 +365,7 @@ int MYSQLC::Open(PGLOBAL g, const char *host, const char *db, int pt) { const char *pipe = NULL; - uint cto = 60, nrt = 120; + uint cto = 6000, nrt = 12000; m_DB = mysql_init(NULL); diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result index 4768573dc7c..5018eec47fc 100644 --- a/storage/connect/mysql-test/connect/r/xml.result +++ b/storage/connect/mysql-test/connect/r/xml.result @@ -326,6 +326,9 @@ Warnings: Level Warning Code 1366 Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column 'c' at row 1 +Level Warning +Code 1105 +Message Out of range value ÁÂÃÄÅÆÇ for column 'c' at row 1 DROP TABLE t1; # # Testing Cyrillic diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp index 6b47aa7433e..04e4a3b5704 100644 --- a/storage/connect/plgdbutl.cpp +++ b/storage/connect/plgdbutl.cpp @@ -374,7 +374,7 @@ PCATLG PlgGetCatalog(PGLOBAL g, bool jump) } // end of PlgGetCatalog /***********************************************************************/ -/* PlgGetCatalog: returns CATALOG class pointer. */ +/* PlgGetDataPath: returns the default data path. */ /***********************************************************************/ char *PlgGetDataPath(PGLOBAL g) { diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index 63cb2034e53..ae528db470f 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -24,6 +24,7 @@ #include "osutil.h" //#include "sqlext.h" #endif +#include "handler.h" /***********************************************************************/ /* Include application header files */ @@ -46,6 +47,7 @@ #include "tabdos.h" #include "valblk.h" #include "tabmul.h" +#include "ha_connect.h" /* --------------------------- Class RELDEF -------------------------- */ @@ -60,8 +62,106 @@ RELDEF::RELDEF(void) Name = NULL; Database = NULL; Cat = NULL; + Hc = NULL; } // end of RELDEF constructor +/***********************************************************************/ +/* This function sets an integer table information. */ +/***********************************************************************/ +bool RELDEF::SetIntCatInfo(PSZ what, int n) + { + return Hc->SetIntegerOption(what, n); + } // end of SetIntCatInfo + +/***********************************************************************/ +/* This function returns integer table information. */ +/***********************************************************************/ +int RELDEF::GetIntCatInfo(PSZ what, int idef) + { + int n= Hc->GetIntegerOption(what); + + return (n == NO_IVAL) ? idef : n; + } // end of GetIntCatInfo + +/***********************************************************************/ +/* This function returns Boolean table information. */ +/***********************************************************************/ +bool RELDEF::GetBoolCatInfo(PSZ what, bool bdef) + { + bool b= Hc->GetBooleanOption(what, bdef); + + return b; + } // end of GetBoolCatInfo + +/***********************************************************************/ +/* This function returns size catalog information. */ +/***********************************************************************/ +int RELDEF::GetSizeCatInfo(PSZ what, PSZ sdef) + { + char * s, c; + int i, n= 0; + + if (!(s= Hc->GetStringOption(what))) + s= sdef; + + if ((i= sscanf(s, " %d %c ", &n, &c)) == 2) + switch (toupper(c)) { + case 'M': + n *= 1024; + case 'K': + n *= 1024; + } // endswitch c + + return n; +} // end of GetSizeCatInfo + +/***********************************************************************/ +/* This function sets char table information in buf. */ +/***********************************************************************/ +int RELDEF::GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size) + { + char *s= Hc->GetStringOption(what); + + strncpy(buf, ((s) ? s : sdef), size); + return size; + } // end of GetCharCatInfo + +/***********************************************************************/ +/* This function returns string table information. */ +/* Default parameter is "*" to get the handler default. */ +/***********************************************************************/ +char *RELDEF::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef) + { + char *sval= NULL, *s= Hc->GetStringOption(what, sdef); + + if (s) { + sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1); + strcpy(sval, s); + } else if (!stricmp(what, "filename")) { + // Return default file name + char *ftype= Hc->GetStringOption("Type", "*"); + int i, n; + + if (IsFileType(GetTypeID(ftype))) { + sval= (char*)PlugSubAlloc(g, NULL, strlen(Hc->GetTableName()) + 12); + strcat(strcpy(sval, Hc->GetTableName()), "."); + n= strlen(sval); + + // Fold ftype to lower case + for (i= 0; i < 12; i++) + if (!ftype[i]) { + sval[n+i]= 0; + break; + } else + sval[n+i]= tolower(ftype[i]); + + } // endif FileType + + } // endif s + + return sval; + } // end of GetStringCatInfo + /* --------------------------- Class TABDEF -------------------------- */ /***********************************************************************/ @@ -91,24 +191,201 @@ bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) Name = (PSZ)PlugSubAlloc(g, NULL, strlen(name) + 1); strcpy(Name, name); Cat = cat; - Catfunc = GetFuncID(Cat->GetStringCatInfo(g, "Catfunc", NULL)); - Elemt = cat->GetIntCatInfo("Elements", 0); - Multiple = cat->GetIntCatInfo("Multiple", 0); - Degree = cat->GetIntCatInfo("Degree", 0); - Read_Only = cat->GetBoolCatInfo("ReadOnly", false); - const char *data_charset_name= cat->GetStringCatInfo(g, "Data_charset", NULL); + Hc = ((MYCAT*)cat)->GetHandler(); + Catfunc = GetFuncID(GetStringCatInfo(g, "Catfunc", NULL)); + Elemt = GetIntCatInfo("Elements", 0); + Multiple = GetIntCatInfo("Multiple", 0); + Degree = GetIntCatInfo("Degree", 0); + Read_Only = GetBoolCatInfo("ReadOnly", false); + const char *data_charset_name= GetStringCatInfo(g, "Data_charset", NULL); m_data_charset= data_charset_name ? get_charset_by_csname(data_charset_name, MY_CS_PRIMARY, 0): NULL; // Get The column definitions - if ((poff = cat->GetColCatInfo(g, this)) < 0) + if ((poff = GetColCatInfo(g)) < 0) return true; // Do the definition of AM specific fields return DefineAM(g, am, poff); } // end of Define +/***********************************************************************/ +/* This function returns column table information. */ +/***********************************************************************/ +int TABDEF::GetColCatInfo(PGLOBAL g) + { + char *type= GetStringCatInfo(g, "Type", "*"); + int i, loff, poff, nof, nlg; + void *field= NULL; + TABTYPE tc; + PCOLDEF cdp, lcdp= NULL, tocols= NULL; + PCOLINFO pcf= (PCOLINFO)PlugSubAlloc(g, NULL, sizeof(COLINFO)); + + memset(pcf, 0, sizeof(COLINFO)); + + // Get a unique char identifier for type + tc= (Catfunc == FNC_NO) ? GetTypeID(type) : TAB_PRX; + + // Take care of the column definitions + i= poff= nof= nlg= 0; + + // Offsets of HTML and DIR tables start from 0, DBF at 1 + loff= (tc == TAB_DBF) ? 1 : (tc == TAB_XML || tc == TAB_DIR) ? -1 : 0; + + while (true) { + // Default Offset depends on table type + switch (tc) { + case TAB_DOS: + case TAB_FIX: + case TAB_BIN: + case TAB_VEC: + case TAB_DBF: + poff= loff + nof; // Default next offset + nlg= max(nlg, poff); // Default lrecl + break; + case TAB_CSV: + case TAB_FMT: + nlg+= nof; + case TAB_DIR: + case TAB_XML: + poff= loff + 1; + break; + case TAB_INI: + case TAB_MAC: + case TAB_TBL: + case TAB_XCL: + case TAB_OCCUR: + case TAB_PRX: + case TAB_OEM: + poff = 0; // Offset represents an independant flag + break; + default: // VCT PLG ODBC MYSQL WMI... + poff = 0; // NA + break; + } // endswitch tc + +// do { + field= Hc->GetColumnOption(g, field, pcf); +// } while (field && (*pcf->Name =='*' /*|| pcf->Flags & U_VIRTUAL*/)); + + if (tc == TAB_DBF && pcf->Type == TYPE_DATE && !pcf->Datefmt) { + // DBF date format defaults to 'YYYMMDD' + pcf->Datefmt= "YYYYMMDD"; + pcf->Length= 8; + } // endif tc + + if (!field) + break; + + // Allocate the column description block + cdp= new(g) COLDEF; + + if ((nof= cdp->Define(g, NULL, pcf, poff)) < 0) + return -1; // Error, probably unhandled type + else if (nof) + loff= cdp->GetOffset(); + + switch (tc) { + case TAB_VEC: + cdp->SetOffset(0); // Not to have shift + case TAB_BIN: + // BIN/VEC are packed by default + if (nof) + // Field width is the internal representation width + // that can also depend on the column format + switch (cdp->Fmt ? *cdp->Fmt : 'X') { + case 'C': break; + case 'R': + case 'F': + case 'L': + case 'I': nof= 4; break; + case 'D': nof= 8; break; + case 'S': nof= 2; break; + case 'T': nof= 1; break; + default: nof= cdp->Clen; + } // endswitch Fmt + + default: + break; + } // endswitch tc + + if (lcdp) + lcdp->SetNext(cdp); + else + tocols= cdp; + + lcdp= cdp; + i++; + } // endwhile + + // Degree is the the number of defined columns (informational) + if (i != GetDegree()) + SetDegree(i); + + if (GetDefType() == TYPE_AM_DOS) { + int ending, recln= 0; + + // Was commented because sometimes ending is 0 even when + // not specified (for instance if quoted is specified) +// if ((ending= Hc->GetIntegerOption("Ending")) < 0) { + if ((ending= Hc->GetIntegerOption("Ending")) <= 0) { +#if defined(WIN32) + ending= 2; +#else + ending= 1; +#endif + Hc->SetIntegerOption("Ending", ending); + } // endif ending + + // Calculate the default record size + switch (tc) { + case TAB_FIX: + recln= nlg + ending; // + length of line ending + break; + case TAB_BIN: + case TAB_VEC: + recln= nlg; + +// if ((k= (pak < 0) ? 8 : pak) > 1) + // See above for detailed comment + // Round up lrecl to multiple of 8 or pak +// recln= ((recln + k - 1) / k) * k; + + break; + case TAB_DOS: + case TAB_DBF: + recln= nlg; + break; + case TAB_CSV: + case TAB_FMT: + // The number of separators (assuming an extra one can exist) +// recln= poff * ((qotd) ? 3 : 1); to be investigated + recln= nlg + poff * 3; // To be safe + default: + break; + } // endswitch tc + + // lrecl must be at least recln to avoid buffer overflow + recln= max(recln, Hc->GetIntegerOption("Lrecl")); + Hc->SetIntegerOption("Lrecl", recln); + ((PDOSDEF)this)->SetLrecl(recln); + } // endif Lrecl + + // Attach the column definition to the tabdef + SetCols(tocols); + return poff; + } // end of GetColCatInfo + +/***********************************************************************/ +/* SetIndexInfo: retrieve index description from the table structure. */ +/***********************************************************************/ +void TABDEF::SetIndexInfo(void) + { + // Attach new index(es) + SetIndx(Hc->GetIndexInfo()); + } // end of SetIndexInfo + /* --------------------------- Class OEMDEF -------------------------- */ /***********************************************************************/ @@ -188,7 +465,7 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g) // Have the external class do its complete definition if (!cat->Cbuf) { // Suballocate a temporary buffer for the entire column section - cat->Cblen = cat->GetSizeCatInfo("Colsize", "8K"); + cat->Cblen = GetSizeCatInfo("Colsize", "8K"); cat->Cbuf = (char*)PlugSubAlloc(g, NULL, cat->Cblen); } // endif Cbuf @@ -218,8 +495,8 @@ bool OEMDEF::DeleteTableFile(PGLOBAL g) /***********************************************************************/ bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - Module = Cat->GetStringCatInfo(g, "Module", ""); - Subtype = Cat->GetStringCatInfo(g, "Subtype", Module); + Module = GetStringCatInfo(g, "Module", ""); + Subtype = GetStringCatInfo(g, "Subtype", Module); if (!*Module) Module = Subtype; diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h index b64c21b3a43..f9f49787d75 100644 --- a/storage/connect/reldef.h +++ b/storage/connect/reldef.h @@ -1,225 +1,233 @@ -/*************** RelDef H Declares Source Code File (.H) ***************/ -/* Name: RELDEF.H Version 1.4 */ -/* */ -/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */ -/* */ -/* This file contains the DEF classes definitions. */ -/***********************************************************************/ - -#ifndef __RELDEF_H -#define __RELDEF_H - -#include "block.h" -#include "catalog.h" -#include "my_sys.h" - -typedef class INDEXDEF *PIXDEF; - -/***********************************************************************/ -/* Table or View (relation) definition block. */ -/***********************************************************************/ -class DllExport RELDEF : public BLOCK { // Relation definition block - friend class CATALOG; - friend class PLUGCAT; - friend class MYCAT; - public: - RELDEF(void); // Constructor - - // Implementation - PRELDEF GetNext(void) {return Next;} - PSZ GetName(void) {return Name;} - PSZ GetDB(void) {return (PSZ)Database;} - PCOLDEF GetCols(void) {return To_Cols;} - void SetCols(PCOLDEF pcd) {To_Cols = pcd;} - PCATLG GetCat(void) {return Cat;} - virtual const char *GetType(void) = 0; - virtual AMT GetDefType(void) = 0; - void SetName(const char *str) { Name=(char*)str; } - void SetCat(PCATLG cat) { Cat=cat; } - - // Methods - virtual bool Indexable(void) {return false;} - virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) = 0; - virtual PTDB GetTable(PGLOBAL g, MODE mode) = 0; - - protected: - PRELDEF Next; /* To next definition block */ - PSZ Name; /* Name of the view */ - LPCSTR Database; /* Table database */ - PCOLDEF To_Cols; /* To a list of column desc */ - PCATLG Cat; /* To DB catalog info */ - }; // end of RELDEF - -/***********************************************************************/ -/* These classes correspond to the data base description contained in */ -/* a .XDB file the A.M. DOS, FIX, CSV, MAP, BIN, VCT, PLG, ODBC, DOM. */ -/***********************************************************************/ -class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ - friend class CATALOG; - friend class PLUGCAT; - friend class MYCAT; - public: - // Constructor - TABDEF(void); // Constructor - - // Implementation - int GetDegree(void) {return Degree;} - void SetDegree(int d) {Degree = d;} - int GetElemt(void) {return Elemt;} - void SetNext(PTABDEF tdfp) {Next = tdfp;} - int GetMultiple(void) {return Multiple;} - int GetPseudo(void) {return Pseudo;} - PSZ GetPath(void) - {return (Database) ? (PSZ)Database : Cat->GetDataPath();} - bool SepIndex(void) {return Cat->GetBoolCatInfo("SepIndex", false);} - bool IsReadOnly(void) {return Read_Only;} - virtual AMT GetDefType(void) {return TYPE_AM_TAB;} - virtual PIXDEF GetIndx(void) {return NULL;} - virtual void SetIndx(PIXDEF xp) {} - virtual bool IsHuge(void) {return false;} - const CHARSET_INFO *data_charset() {return m_data_charset;} - - // Methods - bool DropTable(PGLOBAL g, PSZ name); - virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am); - virtual bool DefineAM(PGLOBAL, LPCSTR, int) = 0; - - protected: - // Members - PSZ Schema; /* Table schema (for ODBC) */ - PSZ Desc; /* Table description */ - uint Catfunc; /* Catalog function ID */ - int Card; /* (max) number of rows in table */ - int Elemt; /* Number of rows in blocks or rowset */ - int Sort; /* Table already sorted ??? */ - int Multiple; /* 0: No 1: DIR 2: Section 3: filelist */ - int Degree; /* Number of columns in the table */ - int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */ - bool Read_Only; /* true for read only tables */ - const CHARSET_INFO *m_data_charset; - }; // end of TABDEF - -/***********************************************************************/ -/* Externally defined OEM tables. */ -/***********************************************************************/ -class DllExport OEMDEF : public TABDEF { /* OEM table */ - friend class CATALOG; - friend class PLUGCAT; - friend class MYCAT; - public: - // Constructor - OEMDEF(void) {Hdll = NULL; Pxdef = NULL; Module = Subtype = NULL;} - - // Implementation - virtual const char *GetType(void) {return "OEM";} - virtual AMT GetDefType(void) {return TYPE_AM_OEM;} - - // Methods - virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); - virtual PTDB GetTable(PGLOBAL g, MODE mode); - - protected: - PTABDEF GetXdef(PGLOBAL g); - - // Members -#if defined(WIN32) - HANDLE Hdll; /* Handle to the external DLL */ -#else // !WIN32 - void *Hdll; /* Handle for the loaded shared library */ -#endif // !WIN32 - PTABDEF Pxdef; /* Pointer to the external TABDEF class */ - char *Module; /* Path/Name of the DLL implenting it */ - char *Subtype; /* The name of the OEM table sub type */ - }; // end of OEMDEF - -/***********************************************************************/ -/* Column definition block used during creation. */ -/***********************************************************************/ -class DllExport COLCRT : public BLOCK { /* Column description block */ - friend class TABDEF; - public: - COLCRT(PSZ name); // Constructor - COLCRT(void); // Constructor (for views) - - // Implementation - PSZ GetName(void) {return Name;} - PSZ GetDecode(void) {return Decode;} - PSZ GetFmt(void) {return Fmt;} - int GetOpt(void) {return Opt;} - int GetFreq(void) {return Freq;} - int GetLong(void) {return Long;} - int GetPrecision(void) {return Precision;} - int GetOffset(void) {return Offset;} - void SetOffset(int offset) {Offset = offset;} - - protected: - PCOLCRT Next; /* To next block */ - PSZ Name; /* Column name */ - PSZ Desc; /* Column description */ - PSZ Decode; /* Date format */ - PSZ Fmt; /* Input format for formatted files */ - int Offset; /* Offset of field within record */ - int Long; /* Length of field in file record (!BIN) */ - int Key; /* Key (greater than 1 if multiple) */ - int Precision; /* Logical column length */ - int Scale; /* Decimals for float/decimal values */ - int Opt; /* 0:Not 1:clustered 2:sorted-asc 3:desc */ - int Freq; /* Estimated number of different values */ - char DataType; /* Internal data type (C, N, F, T) */ - }; // end of COLCRT - -/***********************************************************************/ -/* Column definition block. */ -/***********************************************************************/ -class DllExport COLDEF : public COLCRT { /* Column description block */ - friend class CATALOG; - friend class PLUGCAT; - friend class MYCAT; - friend class COLBLK; - friend class DBFFAM; - friend class TDBASE; - public: - COLDEF(void); // Constructor - - // Implementation - PCOLDEF GetNext(void) {return (PCOLDEF)Next;} - void SetNext(PCOLDEF pcdf) {Next = pcdf;} - int GetLength(void) {return (int)F.Length;} - int GetClen(void) {return Clen;} - int GetType(void) {return Buf_Type;} - int GetPoff(void) {return Poff;} - void *GetMin(void) {return To_Min;} - void SetMin(void *minp) {To_Min = minp;} - void *GetMax(void) {return To_Max;} - void SetMax(void *maxp) {To_Max = maxp;} - bool GetXdb2(void) {return Xdb2;} - void SetXdb2(bool b) {Xdb2 = b;} - void *GetBmap(void) {return To_Bmap;} - void SetBmap(void *bmp) {To_Bmap = bmp;} - void *GetDval(void) {return To_Dval;} - void SetDval(void *dvp) {To_Dval = dvp;} - int GetNdv(void) {return Ndv;} - void SetNdv(int ndv) {Ndv = ndv;} - int GetNbm(void) {return Nbm;} - void SetNbm(int nbm) {Nbm = nbm;} - int Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff); - void Define(PGLOBAL g, PCOL colp); - - protected: - void *To_Min; /* Point to array of block min values */ - void *To_Max; /* Point to array of block max values */ - int *To_Pos; /* Point to array of block positions */ - bool Xdb2; /* TRUE if to be optimized by XDB2 */ - void *To_Bmap; /* To array of block bitmap values */ - void *To_Dval; /* To array of column distinct values */ - int Ndv; /* Number of distinct values */ - int Nbm; /* Number of ULONG in bitmap (XDB2) */ - int Buf_Type; /* Internal data type */ - int Clen; /* Internal data size in chars (bytes) */ - int Poff; /* Calculated offset for Packed tables */ - FORMAT F; /* Output format (should be in COLCRT) */ - ushort Flags; /* Used by MariaDB CONNECT handler */ - }; // end of COLDEF - -#endif // __RELDEF_H - +/*************** RelDef H Declares Source Code File (.H) ***************/ +/* Name: RELDEF.H Version 1.4 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */ +/* */ +/* This file contains the DEF classes definitions. */ +/***********************************************************************/ + +#ifndef __RELDEF_H +#define __RELDEF_H + +#include "block.h" +#include "catalog.h" +#include "my_sys.h" + +typedef class INDEXDEF *PIXDEF; +typedef class ha_connect *PHC; + +/***********************************************************************/ +/* Table or View (relation) definition block. */ +/***********************************************************************/ +class DllExport RELDEF : public BLOCK { // Relation definition block + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + RELDEF(void); // Constructor + + // Implementation + PRELDEF GetNext(void) {return Next;} + PSZ GetName(void) {return Name;} + PSZ GetDB(void) {return (PSZ)Database;} + PCOLDEF GetCols(void) {return To_Cols;} + void SetCols(PCOLDEF pcd) {To_Cols = pcd;} + PCATLG GetCat(void) {return Cat;} + virtual const char *GetType(void) = 0; + virtual AMT GetDefType(void) = 0; + void SetName(const char *str) { Name=(char*)str; } + void SetCat(PCATLG cat) { Cat=cat; } + + // Methods + bool GetBoolCatInfo(PSZ what, bool bdef); + bool SetIntCatInfo(PSZ what, int ival); + int GetIntCatInfo(PSZ what, int idef); + int GetSizeCatInfo(PSZ what, PSZ sdef); + int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size); + char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef); + virtual bool Indexable(void) {return false;} + virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) = 0; + virtual PTDB GetTable(PGLOBAL g, MODE mode) = 0; + + protected: + PRELDEF Next; /* To next definition block */ + PSZ Name; /* Name of the view */ + LPCSTR Database; /* Table database */ + PCOLDEF To_Cols; /* To a list of column desc */ + PCATLG Cat; /* To DB catalog info */ + PHC Hc; /* The Connect handler */ + }; // end of RELDEF + +/***********************************************************************/ +/* These classes correspond to the data base description contained in */ +/* a .XDB file the A.M. DOS, FIX, CSV, MAP, BIN, VCT, PLG, ODBC, DOM. */ +/***********************************************************************/ +class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + // Constructor + TABDEF(void); // Constructor + + // Implementation + int GetDegree(void) {return Degree;} + void SetDegree(int d) {Degree = d;} + int GetElemt(void) {return Elemt;} + void SetNext(PTABDEF tdfp) {Next = tdfp;} + int GetMultiple(void) {return Multiple;} + int GetPseudo(void) {return Pseudo;} + PSZ GetPath(void) + {return (Database) ? (PSZ)Database : Cat->GetDataPath();} + bool SepIndex(void) {return GetBoolCatInfo("SepIndex", false);} + bool IsReadOnly(void) {return Read_Only;} + virtual AMT GetDefType(void) {return TYPE_AM_TAB;} + virtual PIXDEF GetIndx(void) {return NULL;} + virtual void SetIndx(PIXDEF xp) {} + virtual bool IsHuge(void) {return false;} + const CHARSET_INFO *data_charset() {return m_data_charset;} + + // Methods + int GetColCatInfo(PGLOBAL g); + void SetIndexInfo(void); + bool DropTable(PGLOBAL g, PSZ name); + virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am); + virtual bool DefineAM(PGLOBAL, LPCSTR, int) = 0; + + protected: + // Members + PSZ Schema; /* Table schema (for ODBC) */ + PSZ Desc; /* Table description */ + uint Catfunc; /* Catalog function ID */ + int Card; /* (max) number of rows in table */ + int Elemt; /* Number of rows in blocks or rowset */ + int Sort; /* Table already sorted ??? */ + int Multiple; /* 0: No 1: DIR 2: Section 3: filelist */ + int Degree; /* Number of columns in the table */ + int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */ + bool Read_Only; /* true for read only tables */ + const CHARSET_INFO *m_data_charset; + }; // end of TABDEF + +/***********************************************************************/ +/* Externally defined OEM tables. */ +/***********************************************************************/ +class DllExport OEMDEF : public TABDEF { /* OEM table */ + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + // Constructor + OEMDEF(void) {Hdll = NULL; Pxdef = NULL; Module = Subtype = NULL;} + + // Implementation + virtual const char *GetType(void) {return "OEM";} + virtual AMT GetDefType(void) {return TYPE_AM_OEM;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE mode); + + protected: + PTABDEF GetXdef(PGLOBAL g); + + // Members +#if defined(WIN32) + HANDLE Hdll; /* Handle to the external DLL */ +#else // !WIN32 + void *Hdll; /* Handle for the loaded shared library */ +#endif // !WIN32 + PTABDEF Pxdef; /* Pointer to the external TABDEF class */ + char *Module; /* Path/Name of the DLL implenting it */ + char *Subtype; /* The name of the OEM table sub type */ + }; // end of OEMDEF + +/***********************************************************************/ +/* Column definition block used during creation. */ +/***********************************************************************/ +class DllExport COLCRT : public BLOCK { /* Column description block */ + friend class TABDEF; + public: + COLCRT(PSZ name); // Constructor + COLCRT(void); // Constructor (for views) + + // Implementation + PSZ GetName(void) {return Name;} + PSZ GetDecode(void) {return Decode;} + PSZ GetFmt(void) {return Fmt;} + int GetOpt(void) {return Opt;} + int GetFreq(void) {return Freq;} + int GetLong(void) {return Long;} + int GetPrecision(void) {return Precision;} + int GetOffset(void) {return Offset;} + void SetOffset(int offset) {Offset = offset;} + + protected: + PCOLCRT Next; /* To next block */ + PSZ Name; /* Column name */ + PSZ Desc; /* Column description */ + PSZ Decode; /* Date format */ + PSZ Fmt; /* Input format for formatted files */ + int Offset; /* Offset of field within record */ + int Long; /* Length of field in file record (!BIN) */ + int Key; /* Key (greater than 1 if multiple) */ + int Precision; /* Logical column length */ + int Scale; /* Decimals for float/decimal values */ + int Opt; /* 0:Not 1:clustered 2:sorted-asc 3:desc */ + int Freq; /* Estimated number of different values */ + char DataType; /* Internal data type (C, N, F, T) */ + }; // end of COLCRT + +/***********************************************************************/ +/* Column definition block. */ +/***********************************************************************/ +class DllExport COLDEF : public COLCRT { /* Column description block */ + friend class TABDEF; + friend class COLBLK; + friend class DBFFAM; + friend class TDBASE; + public: + COLDEF(void); // Constructor + + // Implementation + PCOLDEF GetNext(void) {return (PCOLDEF)Next;} + void SetNext(PCOLDEF pcdf) {Next = pcdf;} + int GetLength(void) {return (int)F.Length;} + int GetClen(void) {return Clen;} + int GetType(void) {return Buf_Type;} + int GetPoff(void) {return Poff;} + void *GetMin(void) {return To_Min;} + void SetMin(void *minp) {To_Min = minp;} + void *GetMax(void) {return To_Max;} + void SetMax(void *maxp) {To_Max = maxp;} + bool GetXdb2(void) {return Xdb2;} + void SetXdb2(bool b) {Xdb2 = b;} + void *GetBmap(void) {return To_Bmap;} + void SetBmap(void *bmp) {To_Bmap = bmp;} + void *GetDval(void) {return To_Dval;} + void SetDval(void *dvp) {To_Dval = dvp;} + int GetNdv(void) {return Ndv;} + void SetNdv(int ndv) {Ndv = ndv;} + int GetNbm(void) {return Nbm;} + void SetNbm(int nbm) {Nbm = nbm;} + int Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff); + void Define(PGLOBAL g, PCOL colp); + + protected: + void *To_Min; /* Point to array of block min values */ + void *To_Max; /* Point to array of block max values */ + int *To_Pos; /* Point to array of block positions */ + bool Xdb2; /* TRUE if to be optimized by XDB2 */ + void *To_Bmap; /* To array of block bitmap values */ + void *To_Dval; /* To array of column distinct values */ + int Ndv; /* Number of distinct values */ + int Nbm; /* Number of ULONG in bitmap (XDB2) */ + int Buf_Type; /* Internal data type */ + int Clen; /* Internal data size in chars (bytes) */ + int Poff; /* Calculated offset for Packed tables */ + FORMAT F; /* Output format (should be in COLCRT) */ + ushort Flags; /* Used by MariaDB CONNECT handler */ + }; // end of COLDEF + +#endif // __RELDEF_H + diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 37ff34710ec..155e626d2e9 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -1,2572 +1,2570 @@ -/************* TabDos C++ Program Source Code File (.CPP) **************/ -/* PROGRAM NAME: TABDOS */ -/* ------------- */ -/* Version 4.9 */ -/* */ -/* COPYRIGHT: */ -/* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */ -/* */ -/* 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" -#include "array.h" -#include "blkfil.h" - -/***********************************************************************/ -/* DB static variables. */ -/***********************************************************************/ -int num_read, num_there, num_eq[2]; // Statistics -extern "C" int trace; - -/***********************************************************************/ -/* Size of optimize file header. */ -/***********************************************************************/ -#define NZ 4 - -/***********************************************************************/ -/* 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, bool un = 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; - Optimized = 0; - AllocBlks = 0; - Compressed = 0; - Lrecl = 0; - AvgLen = 0; - Block = 0; - Last = 0; - Blksize = 0; - Maxerr = 0; - ReadMode = 0; - Ending = 0; - } // end of DOSDEF constructor - -/***********************************************************************/ -/* 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 - -/***********************************************************************/ -/* Get the full path/name of the optization file. */ -/***********************************************************************/ -bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename) - { - char *ftype; - - switch (Recfm) { - case RECFM_VAR: ftype = ".dop"; break; - case RECFM_FIX: ftype = ".fop"; break; - case RECFM_BIN: ftype = ".bop"; break; - case RECFM_VCT: ftype = ".vop"; break; - case RECFM_DBF: ftype = ".dbp"; break; - default: - sprintf(g->Message, MSG(INVALID_FTYPE), Recfm); - return TRUE; - } // endswitch Ftype - - PlugSetPath(filename, Ofn, GetPath()); - strcat(PlugRemoveType(filename, filename), ftype); - return FALSE; - } // end of GetOptFileName - -/***********************************************************************/ -/* After an optimize error occured, remove all set optimize values. */ -/***********************************************************************/ -void DOSDEF::RemoveOptValues(PGLOBAL g) - { - char filename[_MAX_PATH]; - PCOLDEF cdp; - - // Delete settings of optimized columns - for (cdp = To_Cols; cdp; cdp = cdp->GetNext()) - if (cdp->GetOpt()) { - cdp->SetMin(NULL); - cdp->SetMax(NULL); - cdp->SetNdv(0); - cdp->SetNbm(0); - cdp->SetDval(NULL); - cdp->SetBmap(NULL); - } // endif Opt - - // Delete block position setting for not fixed tables - To_Pos = NULL; - AllocBlks = 0; - - // Delete any eventually ill formed non matching optimization file - if (!GetOptFileName(g, filename)) -#if defined(WIN32) - DeleteFile(filename); -#else // UNIX - remove(filename); -#endif // WIN32 - - Optimized = 0; - } // end of RemoveOptValues - -/***********************************************************************/ -/* 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 - -/***********************************************************************/ -/* 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); - else if (Compressed) { -#if defined(ZIP_SUPPORT) - txfp = new(g) ZIXFAM(this); -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); - return NULL; -#endif // !ZIP_SUPPORT - } else - txfp = new(g) FIXFAM(this); - - tdbp = new(g) TDBFIX(this, txfp); - } else { - if (Compressed) { -#if defined(ZIP_SUPPORT) - if (Compressed == 1) - txfp = new(g) ZIPFAM(this); - else - txfp = new(g) ZLBFAM(this); - -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); - return NULL; -#endif // !ZIP_SUPPORT - } else 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); - else - /*******************************************************************/ - /* For block tables, get eventually saved optimization values. */ - /*******************************************************************/ - if (tdbp->GetBlockValues(g)) { - PushWarning(g, tdbp); -// return NULL; // causes a crash when deleting index - } else if (Recfm == RECFM_VAR || Compressed > 1) { - if (IsOptimized()) { - if (map) { - txfp = new(g) MBKFAM(this); - } else if (Compressed) { -#if defined(ZIP_SUPPORT) - if (Compressed == 1) - txfp = new(g) ZBKFAM(this); - else { - txfp->SetBlkPos(To_Pos); - ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL); - } // endelse -#else - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); - return NULL; -#endif - } else - txfp = new(g) BLKFAM(this); - - ((PTDBDOS)tdbp)->SetTxfp(txfp); - } // endif Optimized - - } // endif Recfm - - 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; -//To_BlkIdx = NULL; - To_BlkFil = NULL; - SavFil = NULL; -//Xeval = 0; - Beval = 0; - } // 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; -//To_BlkIdx = tdbp->To_BlkIdx; - To_BlkFil = tdbp->To_BlkFil; - SavFil = tdbp->SavFil; -//Xeval = tdbp->Xeval; - Beval = tdbp->Beval; - } // 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 dop, bool dox) - { - int prc = RC_OK, rc = RC_OK; - - MaxSize = -1; // Size must be recalculated - Cardinal = -1; // as well as Cardinality - - PTXF xp = Txfp; - - To_Filter = NULL; // Disable filtering -//To_BlkIdx = NULL; // and index filtering - To_BlkFil = NULL; // and block filtering - - if (dop) { - Columns = NULL; // Not used anymore - - if (Txfp->Blocked) { - // MakeBlockValues must be executed in non blocked mode - // except for ZLIB access method. - if (Txfp->GetAmType() == TYPE_AM_MAP) { - Txfp = new(g) MAPFAM((PDOSDEF)To_Def); -#if defined(ZIP_SUPPORT) - } else if (Txfp->GetAmType() == TYPE_AM_ZIP) { - Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); - } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) { - Txfp->Reset(); - ((PZLBFAM)Txfp)->SetOptimized(FALSE); -#endif // ZIP_SUPPORT - } else // (Txfp->GetAmType() == TYPE_AM_BLK) - Txfp = new(g) DOSFAM((PDOSDEF)To_Def); - - Txfp->SetTdbp(this); - } else - Txfp->Reset(); - - Use = USE_READY; // So the table can be reopened - Mode = MODE_ANY; // Just to be clean - rc = MakeBlockValues(g); // Redo optimization - } // endif dop - - if (dox && (rc == RC_OK || rc == RC_INFO)) { - // Remake eventual indexes - if (Mode != MODE_UPDATE) - To_SetCols = NULL; // 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 - prc = rc; - - 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); - - rc = (rc == RC_INFO) ? prc : rc; - } // endif dox - - return rc; - } // end of ResetTableOpt - -/***********************************************************************/ -/* Calculate the block sizes so block I/O can be used and also the */ -/* Min/Max values for clustered/sorted table columns. */ -/***********************************************************************/ -int TDBDOS::MakeBlockValues(PGLOBAL g) - { - int i, lg, nrec, rc; - int curnum, curblk, block, last, savndv, savnbm; - void *savmin, *savmax; - bool blocked, xdb2 = FALSE; -//POOLHEADER save; - PCOLDEF cdp; - PDOSDEF defp = (PDOSDEF)To_Def; - PDOSCOL colp = NULL; - PDBUSER dup = PlgGetUser(g); - PCATLG cat = defp->GetCat(); -//void *memp = cat->GetDescp(); - - if ((nrec = defp->GetElemt()) < 2) { - strcpy(g->Message, MSG(TABLE_NOT_OPT)); - return RC_INFO; // Not to be optimized - } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) { - // Suppress the opt file firstly if the table is void, - // secondly when it was modified with OPTIMIZATION unchecked - // because it is no more valid. - defp->RemoveOptValues(g); // Erase opt file - return RC_OK; // void table - } else if (MaxSize < 0) - return RC_FX; - - defp->SetOptimized(0); - - // Estimate the number of needed blocks - block = (int)((MaxSize + (int)nrec - 1) / (int)nrec); - - // We have to use local variables because Txfp->CurBlk is set - // to Rows+1 by unblocked variable length table access methods. - curblk = -1; - curnum = nrec - 1; - last = 0; - Txfp->Block = block; // This is useful mainly for - Txfp->CurBlk = curblk; // blocked tables (ZLBFAM), for - Txfp->CurNum = curnum; // others it is just to be clean. - - /*********************************************************************/ - /* Allocate the array of block starting positions. */ - /*********************************************************************/ -//if (memp) -// save = *(PPOOLHEADER)memp; - - Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int)); - - /*********************************************************************/ - /* Allocate the blocks for clustered columns. */ - /*********************************************************************/ - blocked = Txfp->Blocked; // Save - Txfp->Blocked = TRUE; // So column block can be allocated - - for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) - if (cdp->GetOpt()) { - lg = cdp->GetClen(); - - if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) { - cdp->SetXdb2(TRUE); - savndv = cdp->GetNdv(); - cdp->SetNdv(0); // Reset Dval number of values - xdb2 = TRUE; - savmax = cdp->GetDval(); - cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg)); - savnbm = cdp->GetNbm(); - cdp->SetNbm(0); // Prevent Bmap allocation -// savmin = cdp->GetBmap(); -// cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int))); - - if (trace) - htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n", - cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg); - - // colp will be initialized with proper Dval VALBLK - colp = (PDOSCOL)MakeCol(g, cdp, colp, i); - colp->InitValue(g); // Allocate column value buffer - cdp->SetNbm(savnbm); -// cdp->SetBmap(savmin); // Can be reused if the new size - cdp->SetDval(savmax); // is not greater than this one. - cdp->SetNdv(savndv); - } else { - cdp->SetXdb2(FALSE); // Maxbmp may have been reset - savmin = cdp->GetMin(); - savmax = cdp->GetMax(); - cdp->SetMin(PlugSubAlloc(g, NULL, block * lg)); - cdp->SetMax(PlugSubAlloc(g, NULL, block * lg)); - - if (trace) - htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n", - cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg); - - // colp will be initialized with proper opt VALBLK's - colp = (PDOSCOL)MakeCol(g, cdp, colp, i); - colp->InitValue(g); // Allocate column value buffer - cdp->SetMin(savmin); // Can be reused if the number - cdp->SetMax(savmax); // of blocks does not change. - } // endif Freq - - } // endif Clustered - -//if (!colp) -// return RC_INFO; - - Txfp->Blocked = blocked; - - /*********************************************************************/ - /* Now do calculate the optimization values. */ - /*********************************************************************/ - Mode = MODE_READ; - - if (OpenDB(g)) - return RC_FX; - - if (xdb2) { - /*********************************************************************/ - /* Retrieve the distinct values of XDB2 columns. */ - /*********************************************************************/ - if (GetDistinctColumnValues(g, nrec)) - return RC_FX; - - OpenDB(g); // Rewind the table file - } // endif xdb2 - -#if defined(PROG_INFO) - /*********************************************************************/ - /* Initialize progress information */ - /*********************************************************************/ - char *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name)); - - dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name); - dup->ProgMax = GetProgMax(g); - dup->ProgCur = 0; -#endif // SOCKET_MODE || THREAD - - /*********************************************************************/ - /* Make block starting pos and min/max values of cluster columns. */ - /*********************************************************************/ - while ((rc = ReadDB(g)) == RC_OK) { - if (blocked) { - // A blocked FAM class handles CurNum and CurBlk (ZLBFAM) - if (!Txfp->CurNum) - Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos(); - - } else { - if (++curnum >= nrec) { - if (++curblk >= block) { - strcpy(g->Message, MSG(BAD_BLK_ESTIM)); - goto err; - } else - curnum = 0; - - // Get block starting position - Txfp->BlkPos[curblk] = Txfp->GetPos(); - } // endif CurNum - - last = curnum + 1; // curnum is zero based - Txfp->CurBlk = curblk; // Used in COLDOS::SetMinMax - Txfp->CurNum = curnum; // Used in COLDOS::SetMinMax - } // endif blocked - - /*******************************************************************/ - /* Now calculate the min and max values for the cluster columns. */ - /*******************************************************************/ - for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext()) - if (colp->Clustered == 2) { - if (colp->SetBitMap(g)) - goto err; - - } else - if (colp->SetMinMax(g)) - goto err; // Currently: column is not sorted - -#if defined(SOCKET_MODE) || defined(THREAD) -#if defined(SOCKET_MODE) - if (SendProgress(dup)) { - strcpy(g->Message, MSG(OPT_CANCELLED)); - goto err; - } else -#elif defined(THREAD) - if (!dup->Step) { - strcpy(g->Message, MSG(OPT_CANCELLED)); - goto err; - } else -#endif // THREAD - dup->ProgCur = GetProgCur(); -#endif // SOCKET_MODE || THREAD - - } // endwhile - - if (rc == RC_EF) { - Txfp->Nrec = nrec; - - if (blocked) { - Txfp->Block = Txfp->CurBlk + 1; - Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec; - } else { - Txfp->Block = curblk + 1; - Txfp->Last = last; - } // endif blocked - - // This is needed to be able to calculate the last block size - Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos(); - } else - goto err; - - /*********************************************************************/ - /* Save the optimization values for this table. */ - /*********************************************************************/ - if (!SaveBlockValues(g)) { - PCATLG cat = PlgGetCatalog(g); - - defp->Block = Txfp->Block; - defp->Last = Txfp->Last; - CloseDB(g); - cat->SetIntCatInfo("Blocks", Txfp->Block); - cat->SetIntCatInfo("Last", Txfp->Last); - return RC_OK; - } // endif SaveBlockValues - - err: - // Restore Desc memory suballocation -//if (memp) -// *(PPOOLHEADER)memp = save; - - defp->RemoveOptValues(g); - CloseDB(g); - return RC_FX; - } // end of MakeBlockValues - -/***********************************************************************/ -/* Save the block and Min/Max values for this table. */ -/* The problem here is to avoid name duplication, because more than */ -/* one data file can have the same name (but different types) and/or */ -/* the same data file can be used with different block sizes. This is */ -/* why we use Ofn that defaults to the file name but can be set to a */ -/* different name if necessary. */ -/***********************************************************************/ -bool TDBDOS::SaveBlockValues(PGLOBAL g) - { - char filename[_MAX_PATH]; - int lg, n[NZ + 2]; - size_t nbk, ndv, nbm, block = Txfp->Block; - bool rc = FALSE; - FILE *opfile; - PDOSCOL colp; - PDOSDEF defp = (PDOSDEF)To_Def; - - if (defp->GetOptFileName(g, filename)) - return TRUE; - - if (!(opfile = fopen(filename, "wb"))) { - sprintf(g->Message, MSG(OPEN_MODE_ERROR), - "wb", (int)errno, filename); - strcat(strcat(g->Message, ": "), strerror(errno)); - - if (trace) - htrc("%s\n", g->Message); - - return TRUE; - } // endif opfile - - if (Ftype == RECFM_VAR || defp->Compressed == 2) { - /*******************************************************************/ - /* Write block starting positions into the opt file. */ - /*******************************************************************/ - block++; - lg = sizeof(int); - n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block; - - if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { - sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) { - sprintf(g->Message, MSG(OPTBLK_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - block--; // = Txfp->Block; - } // endif Ftype - - /*********************************************************************/ - /* Write the Min/Max values into the opt file. */ - /*********************************************************************/ - for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) { - lg = colp->Value->GetClen(); - - // Now start the writing process - if (colp->Clustered == 2) { - // New XDB2 block optimization. Will be recognized when reading - // because the column index is negated. - ndv = colp->Ndv; nbm = colp->Nbm; - nbk = nbm * block; - n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; - n[4] = ndv; n[5] = nbm; - - if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) { - sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) { - sprintf(g->Message, MSG(OPT_DVAL_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) { - sprintf(g->Message, MSG(OPT_BMAP_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - } else { - n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; - - if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { - sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) { - sprintf(g->Message, MSG(OPT_MIN_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) { - sprintf(g->Message, MSG(OPT_MAX_WR_ERR), strerror(errno)); - rc = TRUE; - } // endif size - - } // endif Clustered - - } // endfor colp - - fclose(opfile); - return rc; - } // end of SaveBlockValues - -/***********************************************************************/ -/* Read the Min/Max values for this table. */ -/* The problem here is to avoid name duplication, because more than */ -/* one data file can have the same name (but different types) and/or */ -/* the same data file can be used with different block sizes. This is */ -/* why we use Ofn that defaults to the file name but can be set to a */ -/* different name if necessary. */ -/***********************************************************************/ -bool TDBDOS::GetBlockValues(PGLOBAL g) - { - char filename[_MAX_PATH]; - int i, lg, n[NZ]; - int nrec, block, last = 0, allocblk = 0; - int len; - bool newblk = FALSE; - size_t ndv, nbm, nbk, blk; - FILE *opfile; - PCOLDEF cdp; - PDOSDEF defp = (PDOSDEF)To_Def; - PCATLG cat = defp->GetCat(); - - if (defp->Optimized) - return FALSE; // Already done or to be redone - - if (Ftype == RECFM_VAR || defp->Compressed == 2) { - /*******************************************************************/ - /* Variable length file that can be read by block. */ - /*******************************************************************/ - block = defp->GetBlock(); - nrec = (defp->GetElemt()) ? defp->GetElemt() : 1; - last = defp->GetLast(); - - if (nrec > 1 && block) - len = (int)((block - 1) * nrec + last); - else - len = 0; - - if (!len) { - if (nrec > 1) { - // The table can be declared optimized if it is void. - // This is useful to handle Insert in optimized mode. - char filename[_MAX_PATH]; - int h; - int flen = -1; - - PlugSetPath(filename, defp->Fn, GetPath()); - h = open(filename, _O_RDONLY); - flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h); - defp->SetOptimized((flen) ? 0 : 1); - - if (h != -1) - close(h); - - } // endif nrec - - return FALSE; // Opt file does not exist yet - } else if (len < 0) - return TRUE; // Table error - - cdp = defp->GetCols(); - i = 1; - } else { - /*******************************************************************/ - /* Fixed length file. Opt file exists only for clustered columns. */ - /*******************************************************************/ - // Check for existence of clustered columns - for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) - if (cdp->GetOpt()) - break; - - if (!cdp) - return FALSE; // No optimization needed - - if ((len = Cardinality(g)) < 0) - return TRUE; // Table error - else if (!len) - return FALSE; // File does not exist yet - - block = Txfp->Block; // Was set in Cardinality - nrec = Txfp->Nrec; - } // endif Ftype - - if (defp->GetOptFileName(g, filename)) - return TRUE; - - if (!(opfile = fopen(filename, "rb"))) - return FALSE; // No saved values - -//if (memp) { -// save = *(PPOOLHEADER)memp; -// allocblk = defp->GetAllocBlks(); -// } // endif memp - - if (block > allocblk) - newblk = TRUE; // Current allocation is too small - - if (Ftype == RECFM_VAR || defp->Compressed == 2) { - /*******************************************************************/ - /* Read block starting positions from the opt file. */ - /*******************************************************************/ - blk = block + 1; - lg = sizeof(int); - - if (fread(n, sizeof(int), NZ, opfile) != NZ) { - sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); - goto err; - } // endif size - - if (n[0] != last || n[1] != lg || n[2] != nrec || n[3] != block) { - sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); - goto err; - } // endif - - if (newblk) - defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg); - - if (fread(defp->To_Pos, lg, blk, opfile) != blk) { - sprintf(g->Message, MSG(OPTBLK_RD_ERR), strerror(errno)); - goto err; - } // endif size - - } // endif Ftype - - /*********************************************************************/ - /* Read the Min/Max values from the opt file. */ - /*********************************************************************/ - for (; cdp; cdp = cdp->GetNext(), i++) - if (cdp->GetOpt()) { - lg = cdp->GetClen(); - blk = block; - - // Now start the reading process. - if (fread(n, sizeof(int), NZ, opfile) != NZ) { - sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); - goto err; - } // endif size - - if (n[0] == -i) { - // Read the XDB2 opt values from the opt file - if (n[1] != lg || n[2] != nrec || n[3] != block) { - sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); - goto err; - } // endif - - if (fread(n, sizeof(int), 2, opfile) != 2) { - sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); - goto err; - } // endif fread - - ndv = n[0]; nbm = n[1]; nbk = nbm * blk; - - if (cdp->GetNdv() < (int)ndv || !cdp->GetDval()) - cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg)); - - cdp->SetNdv((int)ndv); - - if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) { - sprintf(g->Message, MSG(OPT_DVAL_RD_ERR), strerror(errno)); - goto err; - } // endif size - - if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap()) - cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int))); - - cdp->SetNbm((int)nbm); - - if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) { - sprintf(g->Message, MSG(OPT_BMAP_RD_ERR), strerror(errno)); - goto err; - } // endif size - - cdp->SetXdb2(TRUE); - } else { - // Read the Min/Max values from the opt file - if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) { - sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); - goto err; - } // endif - - if (newblk || !cdp->GetMin()) - cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg)); - - if (fread(cdp->GetMin(), lg, blk, opfile) != blk) { - sprintf(g->Message, MSG(OPT_MIN_RD_ERR), strerror(errno)); - goto err; - } // endif size - - if (newblk || !cdp->GetMax()) - cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg)); - - if (fread(cdp->GetMax(), lg, blk, opfile) != blk) { - sprintf(g->Message, MSG(OPT_MAX_RD_ERR), strerror(errno)); - goto err; - } // endif size - - cdp->SetXdb2(FALSE); - } // endif n[0] (XDB2) - - } // endif Clustered - - defp->SetBlock(block); - - if (newblk) - defp->SetAllocBlks(block); - - defp->SetOptimized(1); - fclose(opfile); - MaxSize = -1; // Can be refined later - return FALSE; - - err: - defp->RemoveOptValues(g); - fclose(opfile); -//return TRUE; - // Ignore error if not in mode CHK_OPT - return (PlgGetUser(g)->Check & CHK_OPT) != 0; - } // end of GetBlockValues - -/***********************************************************************/ -/* This fonction is used while making XDB2 block optimization. */ -/* It constructs for each elligible columns, the sorted list of the */ -/* distinct values existing in the column. This function uses an */ -/* algorithm that permit to get several sets of distinct values by */ -/* reading the table only once, which cannot be done using a standard */ -/* SQL query. */ -/***********************************************************************/ -bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec) - { - char *p; - int rc, blk, n = 0; - PDOSCOL colp; - PDBUSER dup = PlgGetUser(g); - - /*********************************************************************/ - /* Initialize progress information */ - /*********************************************************************/ - p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name)); - dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name); - dup->ProgMax = GetProgMax(g); - dup->ProgCur = 0; - - while ((rc = ReadDB(g)) == RC_OK) { - for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) - if (colp->Clustered == 2) - if (colp->AddDistinctValue(g)) - return TRUE; // Too many distinct values - -#if defined(SOCKET_MODE) - if (SendProgress(dup)) { - strcpy(g->Message, MSG(OPT_CANCELLED)); - return TRUE; - } else -#elif defined(THREAD) - if (!dup->Step) { - strcpy(g->Message, MSG(OPT_CANCELLED)); - return TRUE; - } else -#endif // THREAD - dup->ProgCur = GetProgCur(); - - n++; - } // endwhile - - if (rc != RC_EF) - return TRUE; - - // Reset the number of table blocks -//nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value) - blk = (n + nrec - 1) / nrec; - Txfp->Block = blk; // Useful mainly for ZLBFAM ??? - - // Set Nbm, Bmap for XDB2 columns - for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) - if (colp->Clustered == 2) { -// colp->Cdp->SetNdv(colp->Ndv); - colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP; - colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk); - } // endif Clustered - - return FALSE; - } // end of GetDistinctColumnValues - -/***********************************************************************/ -/* Analyze the filter and construct the Block Evaluation Filter. */ -/* This is possible when a filter contains predicates implying a */ -/* column marked as "clustered" or "sorted" matched to a constant */ -/* argument. It is then possible by comparison against the smallest */ -/* and largest column values in each block to determine whether the */ -/* filter condition will be always true or always false for the block.*/ -/***********************************************************************/ -PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp) - { - bool blk = Txfp->Blocked; - - if (To_BlkFil) - return To_BlkFil; // Already done - else if (!filp) - return NULL; - else if (blk) { - if (Txfp->GetAmType() == TYPE_AM_DBF) - /*****************************************************************/ - /* If RowID is used in this query, block optimization cannot be */ - /* used because currently the file must be read sequentially. */ - /*****************************************************************/ - for (PCOL cp = Columns; cp; cp = cp->GetNext()) - if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm()) - return NULL; - - } // endif blk - - int i, op = filp->GetOpc(), opm = filp->GetOpm(), n = 0; - bool cnv[2]; - PCOL colp; - PXOB arg[2] = {NULL,NULL}; - PBF *fp = NULL, bfp = NULL; - - switch (op) { - case OP_EQ: - case OP_NE: - case OP_GT: - case OP_GE: - case OP_LT: - case OP_LE: - if (! opm) { - for (i = 0; i < 2; i++) { - arg[i] = filp->Arg(i); - cnv[i] = filp->Conv(i); - } // endfor i - - bfp = CheckBlockFilari(g, arg, op, cnv); - break; - } // endif !opm - - // if opm, pass thru - case OP_IN: - if (filp->GetArgType(0) == TYPE_COLBLK && - filp->GetArgType(1) == TYPE_ARRAY) { - arg[0] = filp->Arg(0); - arg[1] = filp->Arg(1); - colp = (PCOL)arg[0]; - - if (colp->GetTo_Tdb() == this) { - // Block evaluation is possible for... - if (colp->GetAmType() == TYPE_AM_ROWID) { - // Special column ROWID and constant array, but - // currently we don't know how to retrieve a RowID - // from a DBF table that is not sequentially read. -// if (Txfp->GetAmType() != TYPE_AM_DBF || -// ((RIDBLK*)arg[0])->GetRnm()) - bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec); - - } else if (blk && Txfp->Nrec > 1 && colp->IsClustered()) - // Clustered column and constant array - if (colp->GetClustered() == 2) - bfp = new(g) BLKFILIN2(g, this, op, opm, arg); - else - bfp = new(g) BLKFILIN(g, this, op, opm, arg); - - } // endif this - -#if 0 - } else if (filp->GetArgType(0) == TYPE_SCALF && - filp->GetArgType(1) == TYPE_ARRAY) { - arg[0] = filp->Arg(0); - arg[1] = filp->Arg(1); - - if (((PSCALF)arg[0])->GetOp() == OP_ROW && - arg[1]->GetResultType() == TYPE_LIST) { - PARRAY par = (PARRAY)arg[1]; - LSTVAL *vlp = (LSTVAL*)par->GetValue(); - - ((SFROW*)arg[0])->GetParms(n); - - if (n != vlp->GetN()) - return NULL; - else - n = par->GetNval(); - - arg[1] = new(g) CONSTANT(vlp); - fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF)); - cnv[0] = cnv[1] = FALSE; - - if (op == OP_IN) - op = OP_EQ; - - for (i = 0; i < n; i++) { - par->GetNthValue(vlp, i); - - if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv))) - return NULL; - - } // endfor i - - bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n); - } // endif ROW -#endif // 0 - - } // endif Type - - break; - case OP_AND: - case OP_OR: - fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF)); - fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0))); - fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1))); - - if (fp[0] || fp[1]) - bfp = new(g) BLKFILLOG(this, op, fp, 2); - - break; - case OP_NOT: - fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF)); - - if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0))))) - bfp = new(g) BLKFILLOG(this, op, fp, 1); - - break; - case OP_LIKE: - default: - break; - } // endswitch op - - return bfp; - } // end of InitBlockFilter - -/***********************************************************************/ -/* Analyze the passed arguments and construct the Block Filter. */ -/***********************************************************************/ -PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv) - { -//int i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; -//bool conv = FALSE, xdb2 = FALSE, ok = FALSE, b[2]; -//PXOB *xarg1, *xarg2 = NULL, xp[2]; - int i, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; - bool conv = FALSE, xdb2 = FALSE, ok = FALSE; - PXOB *xarg2 = NULL, xp[2]; - PCOL colp; -//LSTVAL *vlp = NULL; -//SFROW *sfr[2]; - PBF *fp = NULL, bfp = NULL; - - for (i = 0; i < 2; i++) { - switch (arg[i]->GetType()) { - case TYPE_CONST: - type[i] = 1; - ctype = arg[i]->GetResultType(); - break; - case TYPE_COLBLK: - conv = cnv[i]; - colp = (PCOL)arg[i]; - - if (colp->GetTo_Tdb() == this) { - if (colp->GetAmType() == TYPE_AM_ROWID) { - // Currently we don't know how to retrieve a RowID - // from a DBF table that is not sequentially read. -// if (Txfp->GetAmType() != TYPE_AM_DBF || -// ((RIDBLK*)arg[i])->GetRnm()) - type[i] = 5; - - } else if (Txfp->Blocked && Txfp->Nrec > 1 && - colp->IsClustered()) { - type[i] = 2; - xdb2 = colp->GetClustered() == 2; - } // endif Clustered - - } else if (colp->GetColUse(U_CORREL)) { - // This is a column pointing to the outer query of a - // correlated subquery, it has a constant value during - // each execution of the subquery. - type[i] = 1; - ctype = arg[i]->GetResultType(); - } // endif this - - break; -// case TYPE_SCALF: -// if (((PSCALF)arg[i])->GetOp() == OP_ROW) { -// sfr[i] = (SFROW*)arg[i]; -// type[i] = 7; -// } // endif Op - -// break; - default: - break; - } // endswitch ArgType - - if (!type[i]) - break; - - n += type[i]; - } // endfor i - - if (n == 3 || n == 6) { - if (conv) { - // The constant has not the good type and will not match - // the block min/max values. What we can do here is either - // abort with an error message or simply not do the block - // optimization (as column values can be converted when - // evaluating the filter.) Currently we prefer aborting - // because the user may count on the performance enhancing - // and silently not doing it is probably worse than just - // telling him to fix his query. - sprintf(g->Message, "Block opt: %s", MSG(VALTYPE_NOMATCH)); - longjmp(g->jumper[g->jump_level], 99); - } // endif Conv - - if (type[0] == 1) { - // Make it always as Column-op-Value - *xp = arg[0]; - arg[0] = arg[1]; - arg[1] = *xp; - - switch (op) { - case OP_GT: op = OP_LT; break; - case OP_GE: op = OP_LE; break; - case OP_LT: op = OP_GT; break; - case OP_LE: op = OP_GE; break; - } // endswitch op - - } // endif - -#if defined(_DEBUG) -// assert(arg[0]->GetResultType() == ctype); -#endif - - if (n == 3) { - if (xdb2) { - if (((PDOSCOL)arg[0])->GetNbm() == 1) - bfp = new(g) BLKFILAR2(g, this, op, arg); - else // Multiple bitmap made of several ULONG's - bfp = new(g) BLKFILMR2(g, this, op, arg); - } else - bfp = new(g) BLKFILARI(g, this, op, arg); - - } else // n = 6 - bfp = new(g) BLKSPCARI(this, op, arg, Txfp->Nrec); - -#if 0 - } else if (n == 8 || n == 14) { - if (n == 8 && ctype != TYPE_LIST) { - // Should never happen - strcpy(g->Message, "Block opt: bad constant"); - longjmp(g->jumper[g->jump_level], 99); - } // endif Conv - - if (type[0] == 1) { - // Make it always as Column-op-Value - sfr[0] = sfr[1]; - arg[1] = arg[0]; - - switch (op) { - case OP_GT: op = OP_LT; break; - case OP_GE: op = OP_LE; break; - case OP_LT: op = OP_GT; break; - case OP_LE: op = OP_GE; break; - } // endswitch op - - } // endif - - xarg1 = sfr[0]->GetParms(n1); - - if (n == 8) { - vlp = (LSTVAL*)arg[1]->GetValue(); - n2 = vlp->GetN(); - xp[1] = new(g) CONSTANT((PVAL)NULL); - } else - xarg2 = sfr[1]->GetParms(n2); - - if (n1 != n2) - return NULL; // Should we flag an error ? - - fp = (PBF*)PlugSubAlloc(g, NULL, n1 * sizeof(PBF)); - - for (i = 0; i < n1; i++) { - xp[0] = xarg1[i]; - - if (n == 8) - ((CONSTANT*)xp[1])->SetValue(vlp->GetSubVal(i)); - else - xp[1] = xarg2[i]; - - b[0] = b[1] = (xp[0]->GetResultType() != xp[1]->GetResultType()); - ok |= ((fp[i] = CheckBlockFilari(g, xp, op, b)) != NULL); - } // endfor i - - if (ok) - bfp = new(g) BLKFILLOG(this, OP_AND, fp, n1); -#endif // 0 - - } // endif n - - return bfp; - } // end of CheckBlockFilari - -/***********************************************************************/ -/* ResetBlkFil: reset the block filter and restore filtering. */ -/***********************************************************************/ -void TDBDOS::ResetBlockFilter(PGLOBAL g) - { - if (!To_BlkFil) - return; - - To_BlkFil->Reset(g); - - if (SavFil && !To_Filter) { - // Restore filter if it was disabled by optimization - To_Filter = SavFil; - SavFil = NULL; - } // endif - - Beval = 0; - } // end of ResetBlockFilter - -/***********************************************************************/ -/* Block optimization: evaluate the block index filter against */ -/* the min and max values of this block and return: */ -/* RC_OK: if some records in the block can meet filter criteria. */ -/* RC_NF: if no record in the block can meet filter criteria. */ -/* RC_EF: if no record in the remaining file can meet filter criteria.*/ -/* In addition, temporarily supress filtering if all the records in */ -/* the block meet filter criteria. */ -/***********************************************************************/ -int TDBDOS::TestBlock(PGLOBAL g) - { - int rc = RC_OK; - - if (To_BlkFil && Beval != 2) { - // Check for block filtering evaluation - if (Beval == 1) { - // Filter was removed for last block, restore it - To_Filter = SavFil; - SavFil = NULL; - } // endif Beval - - // Check for valid records in new block - switch (Beval = To_BlkFil->BlockEval(g)) { - case -2: // No more valid values in file - rc = RC_EF; - break; - case -1: // No valid values in block - rc = RC_NF; - break; - case 1: // All block values are valid - case 2: // All subsequent file values are Ok - // Before suppressing the filter for the block(s) it is - // necessary to reset the filtered columns to NOT_READ - // so their new values are retrieved by the SELECT list. - if (To_Filter) // Can be NULL when externally called (XDB) - To_Filter->Reset(); - - SavFil = To_Filter; - To_Filter = NULL; // So remove filter - } // endswitch Beval - - if (trace) - htrc("BF Eval Beval=%d\n", Beval); - - } // endif To_BlkFil - - return rc; - } // end of TestBlock - -/***********************************************************************/ -/* 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; - } else if (colp->GetResultType() == TYPE_DECIM) { - sprintf(g->Message, "Decimal columns are not indexable yet"); - goto err; - } // endif Type - - 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 - return RC_INFO; // Error or Physical table does not exist - - 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. */ - /*******************************************************************/ - if (!To_Kindex) { - Txfp->Rewind(); // see comment in Work.log - - if (SkipHeader(g)) - return TRUE; - - } else - /*****************************************************************/ - /* Table is to be accessed through a sorted index table. */ - /*****************************************************************/ - To_Kindex->Reset(); - - ResetBlockFilter(g); - 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 block filter tree if evaluation is possible. */ - /*********************************************************************/ - To_BlkFil = InitBlockFilter(g, To_Filter); - - /*********************************************************************/ - /* 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; - Clustered = 0; - Sorted = 0; - Ndv = 0; // Currently used only for XDB2 - Nbm = 0; // Currently used only for XDB2 - Min = NULL; - Max = NULL; - Bmap = NULL; - Dval = NULL; - Buf = NULL; - - if (txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) { - int nblk = txfp->GetBlock(); - - Clustered = (cdp->GetXdb2()) ? 2 : 1; - Sorted = (cdp->GetOpt() > 1) ? 1 : 0; // Currently ascending only - - if (Clustered == 1) { - Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec); - Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec); - } else { // Clustered == 2 - // Ndv is the number of distinct values in Dval. Ndv and Nbm - // may be 0 when optimizing because Ndval is not filled yet, - // but the size of the passed Dval memory block is Ok. - Ndv = cdp->GetNdv(); - Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec); - - // Bmap cannot be allocated when optimizing, we must know Nbm first - if ((Nbm = cdp->GetNbm())) - Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk); - - } // endif Clustered - - } // endif Opt - - OldVal = NULL; // Currently used only in MinMax - 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) : GetScale(); - } // 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; - Clustered = col1->Clustered; - Sorted = col1->Sorted; - Min = col1->Min; - Max = col1->Max; - Bmap = col1->Bmap; - Dval = col1->Dval; - Ndv = col1->Ndv; - Nbm = col1->Nbm; - } // end of DOSCOL copy constructor - -/***********************************************************************/ -/* VarSize: This function tells UpdateDB whether or not the block */ -/* optimization file must be redone if this column is updated, even */ -/* it is not sorted or clustered. This applies to the last column of */ -/* a variable length table that is blocked, because if it is updated */ -/* using a temporary file, the block size may be modified. */ -/***********************************************************************/ -bool DOSCOL::VarSize(void) - { - PTDBDOS tdbp = (PTDBDOS)To_Tdb; - PTXF txfp = tdbp->Txfp; - - if (Cdp && !Cdp->GetNext() // Must be the last column - && tdbp->Ftype == RECFM_VAR // of a DOS variable length - && txfp->Blocked // blocked table - && txfp->GetUseTemp()) // using a temporary file. - 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_DOUBLE) - // Float values must be written with the correct (column) precision - // Note: maybe this should be forced by ShowValue instead of this ? - value->SetPrec(GetScale()); - - Value = value; // Directly access the external value - } else { - // Values are not of the (good) column type - if (check) { - 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 = NULL; - 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: - if (Value->SetValue_char(p, field - Dcm)) { - sprintf(g->Message, "Out of range value for column %s at row %d", - Name, tdbp->RowNumber(g)); - PushWarning(g, tdbp); - } // endif SetValue_char - - break; - case TYPE_DOUBLE: - 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 - if (Value->SetValue_char(p, field)) { - sprintf(g->Message, "Out of range value for column %s at row %d", - Name, tdbp->RowNumber(g)); - PushWarning(g, tdbp); - } // endif SetValue_char - - break; - default: - sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype); - longjmp(g->jumper[g->jump_level], 34); - } // endswitch Ftype - - // Set null when applicable - if (Nullable) - Value->SetNull(Value->IsZero()); - - } // end of ReadColumn - -/***********************************************************************/ -/* WriteColumn: what this routine does is to access the last line */ -/* read from the corresponding table, and rewrite the field */ -/* corresponding to this column from the column buffer and type. */ -/***********************************************************************/ -void DOSCOL::WriteColumn(PGLOBAL g) - { - char *p, *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_DOUBLE: - 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 - -/***********************************************************************/ -/* SetMinMax: Calculate minimum and maximum values for one block. */ -/* Note: TYPE_STRING is stored and processed with zero ended strings */ -/* to be matching the way the FILTER Eval function processes them. */ -/***********************************************************************/ -bool DOSCOL::SetMinMax(PGLOBAL g) - { - PTDBDOS tp = (PTDBDOS)To_Tdb; - - ReadColumn(g); // Extract column value from current line - - if (CheckSorted(g)) - return TRUE; - - if (!tp->Txfp->CurNum) { - Min->SetValue(Value, tp->Txfp->CurBlk); - Max->SetValue(Value, tp->Txfp->CurBlk); - } else { - Min->SetMin(Value, tp->Txfp->CurBlk); - Max->SetMax(Value, tp->Txfp->CurBlk); - } // endif CurNum - - return FALSE; - } // end of SetMinMax - -/***********************************************************************/ -/* SetBitMap: Calculate the bit map of existing values in one block. */ -/* Note: TYPE_STRING is processed with zero ended strings */ -/* to be matching the way the FILTER Eval function processes them. */ -/***********************************************************************/ -bool DOSCOL::SetBitMap(PGLOBAL g) - { - int i, m, n; - PULONG bmp; - PTDBDOS tp = (PTDBDOS)To_Tdb; - PDBUSER dup = PlgGetUser(g); - - n = tp->Txfp->CurNum; - bmp = (PULONG)Bmap->GetValPtr(Nbm * tp->Txfp->CurBlk); - - // Extract column value from current line - ReadColumn(g); - - if (CheckSorted(g)) - return TRUE; - - if (!n) // New block - for (m = 0; m < Nbm; m++) - bmp[m] = 0; // Reset the new bit map - - if ((i = Dval->Find(Value)) < 0) { - char buf[32]; - - sprintf(g->Message, MSG(DVAL_NOTIN_LIST), - Value->GetCharString(buf), Name); - return TRUE; - } else if (i >= dup->Maxbmp) { - sprintf(g->Message, MSG(OPT_LOGIC_ERR), i); - return TRUE; - } else { - m = i / MAXBMP; -#if defined(_DEBUG) - assert (m < Nbm); -#endif // _DEBUG - bmp[m] |= (1 << (i % MAXBMP)); - } // endif's i - - return FALSE; - } // end of SetBitMap - -/***********************************************************************/ -/* Checks whether a column declared as sorted is sorted indeed. */ -/***********************************************************************/ -bool DOSCOL::CheckSorted(PGLOBAL g) - { - if (Sorted) - if (OldVal) { - // Verify whether this column is sorted all right - if (OldVal->CompareValue(Value) > 0) { - // Column is no more in ascending order - sprintf(g->Message, MSG(COL_NOT_SORTED), Name, To_Tdb->GetName()); - Sorted = FALSE; - return TRUE; - } else - OldVal->SetValue_pval(Value); - - } else - OldVal = AllocateValue(g, Value); - - return FALSE; - } // end of CheckSorted - -/***********************************************************************/ -/* AddDistinctValue: Check whether this value already exist in the */ -/* list and if not add it to the distinct values list. */ -/***********************************************************************/ -bool DOSCOL::AddDistinctValue(PGLOBAL g) - { - bool found = FALSE; - int i, m, n; - - ReadColumn(g); // Extract column value from current line - - // Perhaps a better algorithm can be used when Ndv gets bigger - // Here we cannot use Find because we must get the index of where - // to insert a new value if it is not found in the array. - for (n = 0; n < Ndv; n++) { - m = Dval->CompVal(Value, n); - - if (m > 0) - continue; - else if (!m) - found = TRUE; // Already there - - break; - } // endfor n - - if (!found) { - // Check whether we have room for an additional value - if (Ndv == Freq) { - // Too many values because of wrong Freq setting - sprintf(g->Message, MSG(BAD_FREQ_SET), Name); - return TRUE; - } // endif Ndv - - // New value, add it to the list before the nth value - Dval->SetNval(Ndv + 1); - - for (i = Ndv; i > n; i--) - Dval->Move(i - 1, i); - - Dval->SetValue(Value, n); - Ndv++; - } // endif found - - return FALSE; - } // end of AddDistinctValue - -/***********************************************************************/ -/* 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 - -/* ------------------------------------------------------------------- */ - +/************* TabDos C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABDOS */ +/* ------------- */ +/* Version 4.9 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */ +/* */ +/* 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" +#include "array.h" +#include "blkfil.h" + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +int num_read, num_there, num_eq[2]; // Statistics +extern "C" int trace; + +/***********************************************************************/ +/* Size of optimize file header. */ +/***********************************************************************/ +#define NZ 4 + +/***********************************************************************/ +/* 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, bool un = 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; + Optimized = 0; + AllocBlks = 0; + Compressed = 0; + Lrecl = 0; + AvgLen = 0; + Block = 0; + Last = 0; + Blksize = 0; + Maxerr = 0; + ReadMode = 0; + Ending = 0; + } // end of DOSDEF constructor + +/***********************************************************************/ +/* 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 = GetStringCatInfo(g, "Filename", NULL); + Ofn = GetStringCatInfo(g, "Optname", Fn); + 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 = GetIntCatInfo("Lrecl", 0); + + if (Recfm != RECFM_DBF) + Compressed = GetIntCatInfo("Compressed", 0); + + Mapped = GetBoolCatInfo("Mapped", map); + Block = GetIntCatInfo("Blocks", 0); + Last = GetIntCatInfo("Last", 0); + Ending = GetIntCatInfo("Ending", CRLF); + + if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) { + Huge = GetBoolCatInfo("Huge", Cat->GetDefHuge()); + Padded = GetBoolCatInfo("Padded", false); + Blksize = GetIntCatInfo("Blksize", 0); + Eof = (GetIntCatInfo("EOF", 0) != 0); + } else if (Recfm == RECFM_DBF) { + Maxerr = GetIntCatInfo("Maxerr", 0); + Accept = (GetIntCatInfo("Accept", 0) != 0); + ReadMode = GetIntCatInfo("Readmode", 0); + } else // (Recfm == RECFM_VAR) + AvgLen = GetIntCatInfo("Avglen", 0); + + // Ignore wrong Index definitions for catalog commands + SetIndexInfo(); + return false; + } // end of DefineAM + +/***********************************************************************/ +/* Get the full path/name of the optization file. */ +/***********************************************************************/ +bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename) + { + char *ftype; + + switch (Recfm) { + case RECFM_VAR: ftype = ".dop"; break; + case RECFM_FIX: ftype = ".fop"; break; + case RECFM_BIN: ftype = ".bop"; break; + case RECFM_VCT: ftype = ".vop"; break; + case RECFM_DBF: ftype = ".dbp"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Recfm); + return TRUE; + } // endswitch Ftype + + PlugSetPath(filename, Ofn, GetPath()); + strcat(PlugRemoveType(filename, filename), ftype); + return FALSE; + } // end of GetOptFileName + +/***********************************************************************/ +/* After an optimize error occured, remove all set optimize values. */ +/***********************************************************************/ +void DOSDEF::RemoveOptValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + PCOLDEF cdp; + + // Delete settings of optimized columns + for (cdp = To_Cols; cdp; cdp = cdp->GetNext()) + if (cdp->GetOpt()) { + cdp->SetMin(NULL); + cdp->SetMax(NULL); + cdp->SetNdv(0); + cdp->SetNbm(0); + cdp->SetDval(NULL); + cdp->SetBmap(NULL); + } // endif Opt + + // Delete block position setting for not fixed tables + To_Pos = NULL; + AllocBlks = 0; + + // Delete any eventually ill formed non matching optimization file + if (!GetOptFileName(g, filename)) +#if defined(WIN32) + DeleteFile(filename); +#else // UNIX + remove(filename); +#endif // WIN32 + + Optimized = 0; + } // end of RemoveOptValues + +/***********************************************************************/ +/* 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 = 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 + +/***********************************************************************/ +/* 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); + else if (Compressed) { +#if defined(ZIP_SUPPORT) + txfp = new(g) ZIXFAM(this); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else + txfp = new(g) FIXFAM(this); + + tdbp = new(g) TDBFIX(this, txfp); + } else { + if (Compressed) { +#if defined(ZIP_SUPPORT) + if (Compressed == 1) + txfp = new(g) ZIPFAM(this); + else + txfp = new(g) ZLBFAM(this); + +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else 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); + else + /*******************************************************************/ + /* For block tables, get eventually saved optimization values. */ + /*******************************************************************/ + if (tdbp->GetBlockValues(g)) { + PushWarning(g, tdbp); +// return NULL; // causes a crash when deleting index + } else if (Recfm == RECFM_VAR || Compressed > 1) { + if (IsOptimized()) { + if (map) { + txfp = new(g) MBKFAM(this); + } else if (Compressed) { +#if defined(ZIP_SUPPORT) + if (Compressed == 1) + txfp = new(g) ZBKFAM(this); + else { + txfp->SetBlkPos(To_Pos); + ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL); + } // endelse +#else + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif + } else + txfp = new(g) BLKFAM(this); + + ((PTDBDOS)tdbp)->SetTxfp(txfp); + } // endif Optimized + + } // endif Recfm + + 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; +//To_BlkIdx = NULL; + To_BlkFil = NULL; + SavFil = NULL; +//Xeval = 0; + Beval = 0; + } // 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; +//To_BlkIdx = tdbp->To_BlkIdx; + To_BlkFil = tdbp->To_BlkFil; + SavFil = tdbp->SavFil; +//Xeval = tdbp->Xeval; + Beval = tdbp->Beval; + } // 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 dop, bool dox) + { + int prc = RC_OK, rc = RC_OK; + + MaxSize = -1; // Size must be recalculated + Cardinal = -1; // as well as Cardinality + + PTXF xp = Txfp; + + To_Filter = NULL; // Disable filtering +//To_BlkIdx = NULL; // and index filtering + To_BlkFil = NULL; // and block filtering + + if (dop) { + Columns = NULL; // Not used anymore + + if (Txfp->Blocked) { + // MakeBlockValues must be executed in non blocked mode + // except for ZLIB access method. + if (Txfp->GetAmType() == TYPE_AM_MAP) { + Txfp = new(g) MAPFAM((PDOSDEF)To_Def); +#if defined(ZIP_SUPPORT) + } else if (Txfp->GetAmType() == TYPE_AM_ZIP) { + Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); + } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) { + Txfp->Reset(); + ((PZLBFAM)Txfp)->SetOptimized(FALSE); +#endif // ZIP_SUPPORT + } else // (Txfp->GetAmType() == TYPE_AM_BLK) + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + + Txfp->SetTdbp(this); + } else + Txfp->Reset(); + + Use = USE_READY; // So the table can be reopened + Mode = MODE_ANY; // Just to be clean + rc = MakeBlockValues(g); // Redo optimization + } // endif dop + + if (dox && (rc == RC_OK || rc == RC_INFO)) { + // Remake eventual indexes + if (Mode != MODE_UPDATE) + To_SetCols = NULL; // 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 + prc = rc; + + 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); + + rc = (rc == RC_INFO) ? prc : rc; + } // endif dox + + return rc; + } // end of ResetTableOpt + +/***********************************************************************/ +/* Calculate the block sizes so block I/O can be used and also the */ +/* Min/Max values for clustered/sorted table columns. */ +/***********************************************************************/ +int TDBDOS::MakeBlockValues(PGLOBAL g) + { + int i, lg, nrec, rc; + int curnum, curblk, block, last, savndv, savnbm; + void *savmin, *savmax; + bool blocked, xdb2 = FALSE; +//POOLHEADER save; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)To_Def; + PDOSCOL colp = NULL; + PDBUSER dup = PlgGetUser(g); + PCATLG cat = defp->GetCat(); +//void *memp = cat->GetDescp(); + + if ((nrec = defp->GetElemt()) < 2) { + strcpy(g->Message, MSG(TABLE_NOT_OPT)); + return RC_INFO; // Not to be optimized + } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) { + // Suppress the opt file firstly if the table is void, + // secondly when it was modified with OPTIMIZATION unchecked + // because it is no more valid. + defp->RemoveOptValues(g); // Erase opt file + return RC_OK; // void table + } else if (MaxSize < 0) + return RC_FX; + + defp->SetOptimized(0); + + // Estimate the number of needed blocks + block = (int)((MaxSize + (int)nrec - 1) / (int)nrec); + + // We have to use local variables because Txfp->CurBlk is set + // to Rows+1 by unblocked variable length table access methods. + curblk = -1; + curnum = nrec - 1; + last = 0; + Txfp->Block = block; // This is useful mainly for + Txfp->CurBlk = curblk; // blocked tables (ZLBFAM), for + Txfp->CurNum = curnum; // others it is just to be clean. + + /*********************************************************************/ + /* Allocate the array of block starting positions. */ + /*********************************************************************/ +//if (memp) +// save = *(PPOOLHEADER)memp; + + Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int)); + + /*********************************************************************/ + /* Allocate the blocks for clustered columns. */ + /*********************************************************************/ + blocked = Txfp->Blocked; // Save + Txfp->Blocked = TRUE; // So column block can be allocated + + for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) { + lg = cdp->GetClen(); + + if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) { + cdp->SetXdb2(TRUE); + savndv = cdp->GetNdv(); + cdp->SetNdv(0); // Reset Dval number of values + xdb2 = TRUE; + savmax = cdp->GetDval(); + cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg)); + savnbm = cdp->GetNbm(); + cdp->SetNbm(0); // Prevent Bmap allocation +// savmin = cdp->GetBmap(); +// cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int))); + + if (trace) + htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n", + cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg); + + // colp will be initialized with proper Dval VALBLK + colp = (PDOSCOL)MakeCol(g, cdp, colp, i); + colp->InitValue(g); // Allocate column value buffer + cdp->SetNbm(savnbm); +// cdp->SetBmap(savmin); // Can be reused if the new size + cdp->SetDval(savmax); // is not greater than this one. + cdp->SetNdv(savndv); + } else { + cdp->SetXdb2(FALSE); // Maxbmp may have been reset + savmin = cdp->GetMin(); + savmax = cdp->GetMax(); + cdp->SetMin(PlugSubAlloc(g, NULL, block * lg)); + cdp->SetMax(PlugSubAlloc(g, NULL, block * lg)); + + if (trace) + htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n", + cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg); + + // colp will be initialized with proper opt VALBLK's + colp = (PDOSCOL)MakeCol(g, cdp, colp, i); + colp->InitValue(g); // Allocate column value buffer + cdp->SetMin(savmin); // Can be reused if the number + cdp->SetMax(savmax); // of blocks does not change. + } // endif Freq + + } // endif Clustered + +//if (!colp) +// return RC_INFO; + + Txfp->Blocked = blocked; + + /*********************************************************************/ + /* Now do calculate the optimization values. */ + /*********************************************************************/ + Mode = MODE_READ; + + if (OpenDB(g)) + return RC_FX; + + if (xdb2) { + /*********************************************************************/ + /* Retrieve the distinct values of XDB2 columns. */ + /*********************************************************************/ + if (GetDistinctColumnValues(g, nrec)) + return RC_FX; + + OpenDB(g); // Rewind the table file + } // endif xdb2 + +#if defined(PROG_INFO) + /*********************************************************************/ + /* Initialize progress information */ + /*********************************************************************/ + char *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name)); + + dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name); + dup->ProgMax = GetProgMax(g); + dup->ProgCur = 0; +#endif // SOCKET_MODE || THREAD + + /*********************************************************************/ + /* Make block starting pos and min/max values of cluster columns. */ + /*********************************************************************/ + while ((rc = ReadDB(g)) == RC_OK) { + if (blocked) { + // A blocked FAM class handles CurNum and CurBlk (ZLBFAM) + if (!Txfp->CurNum) + Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos(); + + } else { + if (++curnum >= nrec) { + if (++curblk >= block) { + strcpy(g->Message, MSG(BAD_BLK_ESTIM)); + goto err; + } else + curnum = 0; + + // Get block starting position + Txfp->BlkPos[curblk] = Txfp->GetPos(); + } // endif CurNum + + last = curnum + 1; // curnum is zero based + Txfp->CurBlk = curblk; // Used in COLDOS::SetMinMax + Txfp->CurNum = curnum; // Used in COLDOS::SetMinMax + } // endif blocked + + /*******************************************************************/ + /* Now calculate the min and max values for the cluster columns. */ + /*******************************************************************/ + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext()) + if (colp->Clustered == 2) { + if (colp->SetBitMap(g)) + goto err; + + } else + if (colp->SetMinMax(g)) + goto err; // Currently: column is not sorted + +#if defined(SOCKET_MODE) || defined(THREAD) +#if defined(SOCKET_MODE) + if (SendProgress(dup)) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + goto err; + } else +#elif defined(THREAD) + if (!dup->Step) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + goto err; + } else +#endif // THREAD + dup->ProgCur = GetProgCur(); +#endif // SOCKET_MODE || THREAD + + } // endwhile + + if (rc == RC_EF) { + Txfp->Nrec = nrec; + + if (blocked) { + Txfp->Block = Txfp->CurBlk + 1; + Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec; + } else { + Txfp->Block = curblk + 1; + Txfp->Last = last; + } // endif blocked + + // This is needed to be able to calculate the last block size + Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos(); + } else + goto err; + + /*********************************************************************/ + /* Save the optimization values for this table. */ + /*********************************************************************/ + if (!SaveBlockValues(g)) { + defp->Block = Txfp->Block; + defp->Last = Txfp->Last; + CloseDB(g); + defp->SetIntCatInfo("Blocks", Txfp->Block); + defp->SetIntCatInfo("Last", Txfp->Last); + return RC_OK; + } // endif SaveBlockValues + + err: + // Restore Desc memory suballocation +//if (memp) +// *(PPOOLHEADER)memp = save; + + defp->RemoveOptValues(g); + CloseDB(g); + return RC_FX; + } // end of MakeBlockValues + +/***********************************************************************/ +/* Save the block and Min/Max values for this table. */ +/* The problem here is to avoid name duplication, because more than */ +/* one data file can have the same name (but different types) and/or */ +/* the same data file can be used with different block sizes. This is */ +/* why we use Ofn that defaults to the file name but can be set to a */ +/* different name if necessary. */ +/***********************************************************************/ +bool TDBDOS::SaveBlockValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + int lg, n[NZ + 2]; + size_t nbk, ndv, nbm, block = Txfp->Block; + bool rc = FALSE; + FILE *opfile; + PDOSCOL colp; + PDOSDEF defp = (PDOSDEF)To_Def; + + if (defp->GetOptFileName(g, filename)) + return TRUE; + + if (!(opfile = fopen(filename, "wb"))) { + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "wb", (int)errno, filename); + strcat(strcat(g->Message, ": "), strerror(errno)); + + if (trace) + htrc("%s\n", g->Message); + + return TRUE; + } // endif opfile + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Write block starting positions into the opt file. */ + /*******************************************************************/ + block++; + lg = sizeof(int); + n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block; + + if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPTBLK_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + block--; // = Txfp->Block; + } // endif Ftype + + /*********************************************************************/ + /* Write the Min/Max values into the opt file. */ + /*********************************************************************/ + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) { + lg = colp->Value->GetClen(); + + // Now start the writing process + if (colp->Clustered == 2) { + // New XDB2 block optimization. Will be recognized when reading + // because the column index is negated. + ndv = colp->Ndv; nbm = colp->Nbm; + nbk = nbm * block; + n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; + n[4] = ndv; n[5] = nbm; + + if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) { + sprintf(g->Message, MSG(OPT_DVAL_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) { + sprintf(g->Message, MSG(OPT_BMAP_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + } else { + n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; + + if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPT_MIN_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPT_MAX_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + } // endif Clustered + + } // endfor colp + + fclose(opfile); + return rc; + } // end of SaveBlockValues + +/***********************************************************************/ +/* Read the Min/Max values for this table. */ +/* The problem here is to avoid name duplication, because more than */ +/* one data file can have the same name (but different types) and/or */ +/* the same data file can be used with different block sizes. This is */ +/* why we use Ofn that defaults to the file name but can be set to a */ +/* different name if necessary. */ +/***********************************************************************/ +bool TDBDOS::GetBlockValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + int i, lg, n[NZ]; + int nrec, block, last = 0, allocblk = 0; + int len; + bool newblk = FALSE; + size_t ndv, nbm, nbk, blk; + FILE *opfile; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)To_Def; + PCATLG cat = defp->GetCat(); + + if (defp->Optimized) + return FALSE; // Already done or to be redone + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Variable length file that can be read by block. */ + /*******************************************************************/ + block = defp->GetBlock(); + nrec = (defp->GetElemt()) ? defp->GetElemt() : 1; + last = defp->GetLast(); + + if (nrec > 1 && block) + len = (int)((block - 1) * nrec + last); + else + len = 0; + + if (!len) { + if (nrec > 1) { + // The table can be declared optimized if it is void. + // This is useful to handle Insert in optimized mode. + char filename[_MAX_PATH]; + int h; + int flen = -1; + + PlugSetPath(filename, defp->Fn, GetPath()); + h = open(filename, _O_RDONLY); + flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h); + defp->SetOptimized((flen) ? 0 : 1); + + if (h != -1) + close(h); + + } // endif nrec + + return FALSE; // Opt file does not exist yet + } else if (len < 0) + return TRUE; // Table error + + cdp = defp->GetCols(); + i = 1; + } else { + /*******************************************************************/ + /* Fixed length file. Opt file exists only for clustered columns. */ + /*******************************************************************/ + // Check for existence of clustered columns + for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) + break; + + if (!cdp) + return FALSE; // No optimization needed + + if ((len = Cardinality(g)) < 0) + return TRUE; // Table error + else if (!len) + return FALSE; // File does not exist yet + + block = Txfp->Block; // Was set in Cardinality + nrec = Txfp->Nrec; + } // endif Ftype + + if (defp->GetOptFileName(g, filename)) + return TRUE; + + if (!(opfile = fopen(filename, "rb"))) + return FALSE; // No saved values + +//if (memp) { +// save = *(PPOOLHEADER)memp; +// allocblk = defp->GetAllocBlks(); +// } // endif memp + + if (block > allocblk) + newblk = TRUE; // Current allocation is too small + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Read block starting positions from the opt file. */ + /*******************************************************************/ + blk = block + 1; + lg = sizeof(int); + + if (fread(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (n[0] != last || n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (newblk) + defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg); + + if (fread(defp->To_Pos, lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPTBLK_RD_ERR), strerror(errno)); + goto err; + } // endif size + + } // endif Ftype + + /*********************************************************************/ + /* Read the Min/Max values from the opt file. */ + /*********************************************************************/ + for (; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) { + lg = cdp->GetClen(); + blk = block; + + // Now start the reading process. + if (fread(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (n[0] == -i) { + // Read the XDB2 opt values from the opt file + if (n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (fread(n, sizeof(int), 2, opfile) != 2) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif fread + + ndv = n[0]; nbm = n[1]; nbk = nbm * blk; + + if (cdp->GetNdv() < (int)ndv || !cdp->GetDval()) + cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg)); + + cdp->SetNdv((int)ndv); + + if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) { + sprintf(g->Message, MSG(OPT_DVAL_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap()) + cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int))); + + cdp->SetNbm((int)nbm); + + if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) { + sprintf(g->Message, MSG(OPT_BMAP_RD_ERR), strerror(errno)); + goto err; + } // endif size + + cdp->SetXdb2(TRUE); + } else { + // Read the Min/Max values from the opt file + if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (newblk || !cdp->GetMin()) + cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg)); + + if (fread(cdp->GetMin(), lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPT_MIN_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (newblk || !cdp->GetMax()) + cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg)); + + if (fread(cdp->GetMax(), lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPT_MAX_RD_ERR), strerror(errno)); + goto err; + } // endif size + + cdp->SetXdb2(FALSE); + } // endif n[0] (XDB2) + + } // endif Clustered + + defp->SetBlock(block); + + if (newblk) + defp->SetAllocBlks(block); + + defp->SetOptimized(1); + fclose(opfile); + MaxSize = -1; // Can be refined later + return FALSE; + + err: + defp->RemoveOptValues(g); + fclose(opfile); +//return TRUE; + // Ignore error if not in mode CHK_OPT + return (PlgGetUser(g)->Check & CHK_OPT) != 0; + } // end of GetBlockValues + +/***********************************************************************/ +/* This fonction is used while making XDB2 block optimization. */ +/* It constructs for each elligible columns, the sorted list of the */ +/* distinct values existing in the column. This function uses an */ +/* algorithm that permit to get several sets of distinct values by */ +/* reading the table only once, which cannot be done using a standard */ +/* SQL query. */ +/***********************************************************************/ +bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec) + { + char *p; + int rc, blk, n = 0; + PDOSCOL colp; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Initialize progress information */ + /*********************************************************************/ + p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name)); + dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name); + dup->ProgMax = GetProgMax(g); + dup->ProgCur = 0; + + while ((rc = ReadDB(g)) == RC_OK) { + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) + if (colp->Clustered == 2) + if (colp->AddDistinctValue(g)) + return TRUE; // Too many distinct values + +#if defined(SOCKET_MODE) + if (SendProgress(dup)) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + return TRUE; + } else +#elif defined(THREAD) + if (!dup->Step) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + return TRUE; + } else +#endif // THREAD + dup->ProgCur = GetProgCur(); + + n++; + } // endwhile + + if (rc != RC_EF) + return TRUE; + + // Reset the number of table blocks +//nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value) + blk = (n + nrec - 1) / nrec; + Txfp->Block = blk; // Useful mainly for ZLBFAM ??? + + // Set Nbm, Bmap for XDB2 columns + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) + if (colp->Clustered == 2) { +// colp->Cdp->SetNdv(colp->Ndv); + colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP; + colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk); + } // endif Clustered + + return FALSE; + } // end of GetDistinctColumnValues + +/***********************************************************************/ +/* Analyze the filter and construct the Block Evaluation Filter. */ +/* This is possible when a filter contains predicates implying a */ +/* column marked as "clustered" or "sorted" matched to a constant */ +/* argument. It is then possible by comparison against the smallest */ +/* and largest column values in each block to determine whether the */ +/* filter condition will be always true or always false for the block.*/ +/***********************************************************************/ +PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp) + { + bool blk = Txfp->Blocked; + + if (To_BlkFil) + return To_BlkFil; // Already done + else if (!filp) + return NULL; + else if (blk) { + if (Txfp->GetAmType() == TYPE_AM_DBF) + /*****************************************************************/ + /* If RowID is used in this query, block optimization cannot be */ + /* used because currently the file must be read sequentially. */ + /*****************************************************************/ + for (PCOL cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm()) + return NULL; + + } // endif blk + + int i, op = filp->GetOpc(), opm = filp->GetOpm(), n = 0; + bool cnv[2]; + PCOL colp; + PXOB arg[2] = {NULL,NULL}; + PBF *fp = NULL, bfp = NULL; + + switch (op) { + case OP_EQ: + case OP_NE: + case OP_GT: + case OP_GE: + case OP_LT: + case OP_LE: + if (! opm) { + for (i = 0; i < 2; i++) { + arg[i] = filp->Arg(i); + cnv[i] = filp->Conv(i); + } // endfor i + + bfp = CheckBlockFilari(g, arg, op, cnv); + break; + } // endif !opm + + // if opm, pass thru + case OP_IN: + if (filp->GetArgType(0) == TYPE_COLBLK && + filp->GetArgType(1) == TYPE_ARRAY) { + arg[0] = filp->Arg(0); + arg[1] = filp->Arg(1); + colp = (PCOL)arg[0]; + + if (colp->GetTo_Tdb() == this) { + // Block evaluation is possible for... + if (colp->GetAmType() == TYPE_AM_ROWID) { + // Special column ROWID and constant array, but + // currently we don't know how to retrieve a RowID + // from a DBF table that is not sequentially read. +// if (Txfp->GetAmType() != TYPE_AM_DBF || +// ((RIDBLK*)arg[0])->GetRnm()) + bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec); + + } else if (blk && Txfp->Nrec > 1 && colp->IsClustered()) + // Clustered column and constant array + if (colp->GetClustered() == 2) + bfp = new(g) BLKFILIN2(g, this, op, opm, arg); + else + bfp = new(g) BLKFILIN(g, this, op, opm, arg); + + } // endif this + +#if 0 + } else if (filp->GetArgType(0) == TYPE_SCALF && + filp->GetArgType(1) == TYPE_ARRAY) { + arg[0] = filp->Arg(0); + arg[1] = filp->Arg(1); + + if (((PSCALF)arg[0])->GetOp() == OP_ROW && + arg[1]->GetResultType() == TYPE_LIST) { + PARRAY par = (PARRAY)arg[1]; + LSTVAL *vlp = (LSTVAL*)par->GetValue(); + + ((SFROW*)arg[0])->GetParms(n); + + if (n != vlp->GetN()) + return NULL; + else + n = par->GetNval(); + + arg[1] = new(g) CONSTANT(vlp); + fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF)); + cnv[0] = cnv[1] = FALSE; + + if (op == OP_IN) + op = OP_EQ; + + for (i = 0; i < n; i++) { + par->GetNthValue(vlp, i); + + if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv))) + return NULL; + + } // endfor i + + bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n); + } // endif ROW +#endif // 0 + + } // endif Type + + break; + case OP_AND: + case OP_OR: + fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF)); + fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0))); + fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1))); + + if (fp[0] || fp[1]) + bfp = new(g) BLKFILLOG(this, op, fp, 2); + + break; + case OP_NOT: + fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF)); + + if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0))))) + bfp = new(g) BLKFILLOG(this, op, fp, 1); + + break; + case OP_LIKE: + default: + break; + } // endswitch op + + return bfp; + } // end of InitBlockFilter + +/***********************************************************************/ +/* Analyze the passed arguments and construct the Block Filter. */ +/***********************************************************************/ +PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv) + { +//int i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; +//bool conv = FALSE, xdb2 = FALSE, ok = FALSE, b[2]; +//PXOB *xarg1, *xarg2 = NULL, xp[2]; + int i, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; + bool conv = FALSE, xdb2 = FALSE, ok = FALSE; + PXOB *xarg2 = NULL, xp[2]; + PCOL colp; +//LSTVAL *vlp = NULL; +//SFROW *sfr[2]; + PBF *fp = NULL, bfp = NULL; + + for (i = 0; i < 2; i++) { + switch (arg[i]->GetType()) { + case TYPE_CONST: + type[i] = 1; + ctype = arg[i]->GetResultType(); + break; + case TYPE_COLBLK: + conv = cnv[i]; + colp = (PCOL)arg[i]; + + if (colp->GetTo_Tdb() == this) { + if (colp->GetAmType() == TYPE_AM_ROWID) { + // Currently we don't know how to retrieve a RowID + // from a DBF table that is not sequentially read. +// if (Txfp->GetAmType() != TYPE_AM_DBF || +// ((RIDBLK*)arg[i])->GetRnm()) + type[i] = 5; + + } else if (Txfp->Blocked && Txfp->Nrec > 1 && + colp->IsClustered()) { + type[i] = 2; + xdb2 = colp->GetClustered() == 2; + } // endif Clustered + + } else if (colp->GetColUse(U_CORREL)) { + // This is a column pointing to the outer query of a + // correlated subquery, it has a constant value during + // each execution of the subquery. + type[i] = 1; + ctype = arg[i]->GetResultType(); + } // endif this + + break; +// case TYPE_SCALF: +// if (((PSCALF)arg[i])->GetOp() == OP_ROW) { +// sfr[i] = (SFROW*)arg[i]; +// type[i] = 7; +// } // endif Op + +// break; + default: + break; + } // endswitch ArgType + + if (!type[i]) + break; + + n += type[i]; + } // endfor i + + if (n == 3 || n == 6) { + if (conv) { + // The constant has not the good type and will not match + // the block min/max values. What we can do here is either + // abort with an error message or simply not do the block + // optimization (as column values can be converted when + // evaluating the filter.) Currently we prefer aborting + // because the user may count on the performance enhancing + // and silently not doing it is probably worse than just + // telling him to fix his query. + sprintf(g->Message, "Block opt: %s", MSG(VALTYPE_NOMATCH)); + longjmp(g->jumper[g->jump_level], 99); + } // endif Conv + + if (type[0] == 1) { + // Make it always as Column-op-Value + *xp = arg[0]; + arg[0] = arg[1]; + arg[1] = *xp; + + switch (op) { + case OP_GT: op = OP_LT; break; + case OP_GE: op = OP_LE; break; + case OP_LT: op = OP_GT; break; + case OP_LE: op = OP_GE; break; + } // endswitch op + + } // endif + +#if defined(_DEBUG) +// assert(arg[0]->GetResultType() == ctype); +#endif + + if (n == 3) { + if (xdb2) { + if (((PDOSCOL)arg[0])->GetNbm() == 1) + bfp = new(g) BLKFILAR2(g, this, op, arg); + else // Multiple bitmap made of several ULONG's + bfp = new(g) BLKFILMR2(g, this, op, arg); + } else + bfp = new(g) BLKFILARI(g, this, op, arg); + + } else // n = 6 + bfp = new(g) BLKSPCARI(this, op, arg, Txfp->Nrec); + +#if 0 + } else if (n == 8 || n == 14) { + if (n == 8 && ctype != TYPE_LIST) { + // Should never happen + strcpy(g->Message, "Block opt: bad constant"); + longjmp(g->jumper[g->jump_level], 99); + } // endif Conv + + if (type[0] == 1) { + // Make it always as Column-op-Value + sfr[0] = sfr[1]; + arg[1] = arg[0]; + + switch (op) { + case OP_GT: op = OP_LT; break; + case OP_GE: op = OP_LE; break; + case OP_LT: op = OP_GT; break; + case OP_LE: op = OP_GE; break; + } // endswitch op + + } // endif + + xarg1 = sfr[0]->GetParms(n1); + + if (n == 8) { + vlp = (LSTVAL*)arg[1]->GetValue(); + n2 = vlp->GetN(); + xp[1] = new(g) CONSTANT((PVAL)NULL); + } else + xarg2 = sfr[1]->GetParms(n2); + + if (n1 != n2) + return NULL; // Should we flag an error ? + + fp = (PBF*)PlugSubAlloc(g, NULL, n1 * sizeof(PBF)); + + for (i = 0; i < n1; i++) { + xp[0] = xarg1[i]; + + if (n == 8) + ((CONSTANT*)xp[1])->SetValue(vlp->GetSubVal(i)); + else + xp[1] = xarg2[i]; + + b[0] = b[1] = (xp[0]->GetResultType() != xp[1]->GetResultType()); + ok |= ((fp[i] = CheckBlockFilari(g, xp, op, b)) != NULL); + } // endfor i + + if (ok) + bfp = new(g) BLKFILLOG(this, OP_AND, fp, n1); +#endif // 0 + + } // endif n + + return bfp; + } // end of CheckBlockFilari + +/***********************************************************************/ +/* ResetBlkFil: reset the block filter and restore filtering. */ +/***********************************************************************/ +void TDBDOS::ResetBlockFilter(PGLOBAL g) + { + if (!To_BlkFil) + return; + + To_BlkFil->Reset(g); + + if (SavFil && !To_Filter) { + // Restore filter if it was disabled by optimization + To_Filter = SavFil; + SavFil = NULL; + } // endif + + Beval = 0; + } // end of ResetBlockFilter + +/***********************************************************************/ +/* Block optimization: evaluate the block index filter against */ +/* the min and max values of this block and return: */ +/* RC_OK: if some records in the block can meet filter criteria. */ +/* RC_NF: if no record in the block can meet filter criteria. */ +/* RC_EF: if no record in the remaining file can meet filter criteria.*/ +/* In addition, temporarily supress filtering if all the records in */ +/* the block meet filter criteria. */ +/***********************************************************************/ +int TDBDOS::TestBlock(PGLOBAL g) + { + int rc = RC_OK; + + if (To_BlkFil && Beval != 2) { + // Check for block filtering evaluation + if (Beval == 1) { + // Filter was removed for last block, restore it + To_Filter = SavFil; + SavFil = NULL; + } // endif Beval + + // Check for valid records in new block + switch (Beval = To_BlkFil->BlockEval(g)) { + case -2: // No more valid values in file + rc = RC_EF; + break; + case -1: // No valid values in block + rc = RC_NF; + break; + case 1: // All block values are valid + case 2: // All subsequent file values are Ok + // Before suppressing the filter for the block(s) it is + // necessary to reset the filtered columns to NOT_READ + // so their new values are retrieved by the SELECT list. + if (To_Filter) // Can be NULL when externally called (XDB) + To_Filter->Reset(); + + SavFil = To_Filter; + To_Filter = NULL; // So remove filter + } // endswitch Beval + + if (trace) + htrc("BF Eval Beval=%d\n", Beval); + + } // endif To_BlkFil + + return rc; + } // end of TestBlock + +/***********************************************************************/ +/* 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; + + 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; + } else if (colp->GetResultType() == TYPE_DECIM) { + sprintf(g->Message, "Decimal columns are not indexable yet"); + goto err; + } // endif Type + + colp->InitValue(g); + n = max(n, xdp->GetNparts()); + } // endfor kdp + + keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL)); + sep = dfp->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 + return RC_INFO; // Error or Physical table does not exist + + 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. */ + /*******************************************************************/ + if (!To_Kindex) { + Txfp->Rewind(); // see comment in Work.log + + if (SkipHeader(g)) + return TRUE; + + } else + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + ResetBlockFilter(g); + 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 block filter tree if evaluation is possible. */ + /*********************************************************************/ + To_BlkFil = InitBlockFilter(g, To_Filter); + + /*********************************************************************/ + /* 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; + Clustered = 0; + Sorted = 0; + Ndv = 0; // Currently used only for XDB2 + Nbm = 0; // Currently used only for XDB2 + Min = NULL; + Max = NULL; + Bmap = NULL; + Dval = NULL; + Buf = NULL; + + if (txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) { + int nblk = txfp->GetBlock(); + + Clustered = (cdp->GetXdb2()) ? 2 : 1; + Sorted = (cdp->GetOpt() > 1) ? 1 : 0; // Currently ascending only + + if (Clustered == 1) { + Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec); + Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec); + } else { // Clustered == 2 + // Ndv is the number of distinct values in Dval. Ndv and Nbm + // may be 0 when optimizing because Ndval is not filled yet, + // but the size of the passed Dval memory block is Ok. + Ndv = cdp->GetNdv(); + Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec); + + // Bmap cannot be allocated when optimizing, we must know Nbm first + if ((Nbm = cdp->GetNbm())) + Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk); + + } // endif Clustered + + } // endif Opt + + OldVal = NULL; // Currently used only in MinMax + 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) : GetScale(); + } // 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; + Clustered = col1->Clustered; + Sorted = col1->Sorted; + Min = col1->Min; + Max = col1->Max; + Bmap = col1->Bmap; + Dval = col1->Dval; + Ndv = col1->Ndv; + Nbm = col1->Nbm; + } // end of DOSCOL copy constructor + +/***********************************************************************/ +/* VarSize: This function tells UpdateDB whether or not the block */ +/* optimization file must be redone if this column is updated, even */ +/* it is not sorted or clustered. This applies to the last column of */ +/* a variable length table that is blocked, because if it is updated */ +/* using a temporary file, the block size may be modified. */ +/***********************************************************************/ +bool DOSCOL::VarSize(void) + { + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + PTXF txfp = tdbp->Txfp; + + if (Cdp && !Cdp->GetNext() // Must be the last column + && tdbp->Ftype == RECFM_VAR // of a DOS variable length + && txfp->Blocked // blocked table + && txfp->GetUseTemp()) // using a temporary file. + 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_DOUBLE) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetScale()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + 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 = NULL; + 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: + if (Value->SetValue_char(p, field - Dcm)) { + sprintf(g->Message, "Out of range value for column %s at row %d", + Name, tdbp->RowNumber(g)); + PushWarning(g, tdbp); + } // endif SetValue_char + + break; + case TYPE_DOUBLE: + 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 + if (Value->SetValue_char(p, field)) { + sprintf(g->Message, "Out of range value for column %s at row %d", + Name, tdbp->RowNumber(g)); + PushWarning(g, tdbp); + } // endif SetValue_char + + break; + default: + sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype); + longjmp(g->jumper[g->jump_level], 34); + } // endswitch Ftype + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void DOSCOL::WriteColumn(PGLOBAL g) + { + char *p, *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_DOUBLE: + 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 + +/***********************************************************************/ +/* SetMinMax: Calculate minimum and maximum values for one block. */ +/* Note: TYPE_STRING is stored and processed with zero ended strings */ +/* to be matching the way the FILTER Eval function processes them. */ +/***********************************************************************/ +bool DOSCOL::SetMinMax(PGLOBAL g) + { + PTDBDOS tp = (PTDBDOS)To_Tdb; + + ReadColumn(g); // Extract column value from current line + + if (CheckSorted(g)) + return TRUE; + + if (!tp->Txfp->CurNum) { + Min->SetValue(Value, tp->Txfp->CurBlk); + Max->SetValue(Value, tp->Txfp->CurBlk); + } else { + Min->SetMin(Value, tp->Txfp->CurBlk); + Max->SetMax(Value, tp->Txfp->CurBlk); + } // endif CurNum + + return FALSE; + } // end of SetMinMax + +/***********************************************************************/ +/* SetBitMap: Calculate the bit map of existing values in one block. */ +/* Note: TYPE_STRING is processed with zero ended strings */ +/* to be matching the way the FILTER Eval function processes them. */ +/***********************************************************************/ +bool DOSCOL::SetBitMap(PGLOBAL g) + { + int i, m, n; + PULONG bmp; + PTDBDOS tp = (PTDBDOS)To_Tdb; + PDBUSER dup = PlgGetUser(g); + + n = tp->Txfp->CurNum; + bmp = (PULONG)Bmap->GetValPtr(Nbm * tp->Txfp->CurBlk); + + // Extract column value from current line + ReadColumn(g); + + if (CheckSorted(g)) + return TRUE; + + if (!n) // New block + for (m = 0; m < Nbm; m++) + bmp[m] = 0; // Reset the new bit map + + if ((i = Dval->Find(Value)) < 0) { + char buf[32]; + + sprintf(g->Message, MSG(DVAL_NOTIN_LIST), + Value->GetCharString(buf), Name); + return TRUE; + } else if (i >= dup->Maxbmp) { + sprintf(g->Message, MSG(OPT_LOGIC_ERR), i); + return TRUE; + } else { + m = i / MAXBMP; +#if defined(_DEBUG) + assert (m < Nbm); +#endif // _DEBUG + bmp[m] |= (1 << (i % MAXBMP)); + } // endif's i + + return FALSE; + } // end of SetBitMap + +/***********************************************************************/ +/* Checks whether a column declared as sorted is sorted indeed. */ +/***********************************************************************/ +bool DOSCOL::CheckSorted(PGLOBAL g) + { + if (Sorted) + if (OldVal) { + // Verify whether this column is sorted all right + if (OldVal->CompareValue(Value) > 0) { + // Column is no more in ascending order + sprintf(g->Message, MSG(COL_NOT_SORTED), Name, To_Tdb->GetName()); + Sorted = FALSE; + return TRUE; + } else + OldVal->SetValue_pval(Value); + + } else + OldVal = AllocateValue(g, Value); + + return FALSE; + } // end of CheckSorted + +/***********************************************************************/ +/* AddDistinctValue: Check whether this value already exist in the */ +/* list and if not add it to the distinct values list. */ +/***********************************************************************/ +bool DOSCOL::AddDistinctValue(PGLOBAL g) + { + bool found = FALSE; + int i, m, n; + + ReadColumn(g); // Extract column value from current line + + // Perhaps a better algorithm can be used when Ndv gets bigger + // Here we cannot use Find because we must get the index of where + // to insert a new value if it is not found in the array. + for (n = 0; n < Ndv; n++) { + m = Dval->CompVal(Value, n); + + if (m > 0) + continue; + else if (!m) + found = TRUE; // Already there + + break; + } // endfor n + + if (!found) { + // Check whether we have room for an additional value + if (Ndv == Freq) { + // Too many values because of wrong Freq setting + sprintf(g->Message, MSG(BAD_FREQ_SET), Name); + return TRUE; + } // endif Ndv + + // New value, add it to the list before the nth value + Dval->SetNval(Ndv + 1); + + for (i = Ndv; i > n; i--) + Dval->Move(i - 1, i); + + Dval->SetValue(Value, n); + Ndv++; + } // endif found + + return FALSE; + } // end of AddDistinctValue + +/***********************************************************************/ +/* 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 + +/* ------------------------------------------------------------------- */ + diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index 0fbba03dde8..3d0770bc374 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -415,10 +415,10 @@ bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) if (DOSDEF::DefineAM(g, "CSV", poff)) return true; - Cat->GetCharCatInfo("Separator", ",", buf, sizeof(buf)); + GetCharCatInfo("Separator", ",", buf, sizeof(buf)); Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf; - Quoted = Cat->GetIntCatInfo("Quoted", -1); - Cat->GetCharCatInfo("Qchar", "", buf, sizeof(buf)); + Quoted = GetIntCatInfo("Quoted", -1); + GetCharCatInfo("Qchar", "", buf, sizeof(buf)); Qot = *buf; if (Qot && Quoted < 0) @@ -427,9 +427,9 @@ bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) Qot = '"'; Fmtd = (!Sep || (am && (*am == 'F' || *am == 'f'))); - Header = (Cat->GetIntCatInfo("Header", 0) != 0); - Maxerr = Cat->GetIntCatInfo("Maxerr", 0); - Accept = (Cat->GetIntCatInfo("Accept", 0) != 0); + Header = (GetIntCatInfo("Header", 0) != 0); + Maxerr = GetIntCatInfo("Maxerr", 0); + Accept = (GetIntCatInfo("Accept", 0) != 0); return false; } // end of DefineAM diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp index 6f68cd5381a..6e4a63d0f6d 100755 --- a/storage/connect/tabmul.cpp +++ b/storage/connect/tabmul.cpp @@ -591,9 +591,9 @@ void TDBMUL::CloseDB(PGLOBAL g) /***********************************************************************/ bool DIRDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - Desc = Fn = Cat->GetStringCatInfo(g, "Filename", NULL); - Incl = (Cat->GetIntCatInfo("Subdir", 0) != 0); - Huge = (Cat->GetIntCatInfo("Huge", 0) != 0); + Desc = Fn = GetStringCatInfo(g, "Filename", NULL); + Incl = (GetIntCatInfo("Subdir", 0) != 0); + Huge = (GetIntCatInfo("Huge", 0) != 0); return false; } // end of DefineAM diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index 5029b7539f9..56df9edc980 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -267,23 +267,23 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) // For unspecified values, get the values of old style options // but only if called from MYSQLDEF, else set them to NULL - Portnumber = (sport && sport[0]) ? atoi(sport) - : (b) ? Cat->GetIntCatInfo("Port", GetDefaultPort()) : 0; + Portnumber = (sport && sport[0]) ? atoi(sport) + : (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0; if (Username[0] == 0) - Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL; + Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL; if (Hostname[0] == 0) - Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL; + Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL; if (!Database || !*Database) - Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL; + Database = (b) ? GetStringCatInfo(g, "Database", "*") : NULL; if (!Tabname || !*Tabname) - Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL; + Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL; if (!Password) - Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL; + Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL; } // endif URL #if 0 @@ -308,37 +308,37 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) if (stricmp(am, "MYPRX")) { // Normal case of specific MYSQL table - url = Cat->GetStringCatInfo(g, "Connect", NULL); + url = GetStringCatInfo(g, "Connect", NULL); if (!url || !*url) { // Not using the connection URL - Hostname = Cat->GetStringCatInfo(g, "Host", "localhost"); - Database = Cat->GetStringCatInfo(g, "Database", "*"); - Tabname = Cat->GetStringCatInfo(g, "Name", Name); // Deprecated - Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); - Username = Cat->GetStringCatInfo(g, "User", "*"); - Password = Cat->GetStringCatInfo(g, "Password", NULL); - Portnumber = Cat->GetIntCatInfo("Port", GetDefaultPort()); + Hostname = GetStringCatInfo(g, "Host", "localhost"); + Database = GetStringCatInfo(g, "Database", "*"); + Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated + Tabname = GetStringCatInfo(g, "Tabname", Tabname); + Username = GetStringCatInfo(g, "User", "*"); + Password = GetStringCatInfo(g, "Password", NULL); + Portnumber = GetIntCatInfo("Port", GetDefaultPort()); Server = Hostname; } else if (ParseURL(g, url)) return true; - Bind = !!Cat->GetIntCatInfo("Bind", 0); - Delayed = !!Cat->GetIntCatInfo("Delayed", 0); + Bind = !!GetIntCatInfo("Bind", 0); + Delayed = !!GetIntCatInfo("Delayed", 0); } else { - // MYSQL access from a PROXY table - Database = Cat->GetStringCatInfo(g, "Database", "*"); - Isview = Cat->GetBoolCatInfo("View", FALSE); + // MYSQL access from a PROXY table + Database = GetStringCatInfo(g, "Database", "*"); + Isview = GetBoolCatInfo("View", FALSE); // We must get other connection parms from the calling table Remove_tshp(Cat); - url = Cat->GetStringCatInfo(g, "Connect", NULL); + url = GetStringCatInfo(g, "Connect", NULL); - if (!url || !*url) { - Hostname = Cat->GetStringCatInfo(g, "Host", "localhost"); - Username = Cat->GetStringCatInfo(g, "User", "*"); - Password = Cat->GetStringCatInfo(g, "Password", NULL); - Portnumber = Cat->GetIntCatInfo("Port", GetDefaultPort()); + if (!url || !*url) { + Hostname = GetStringCatInfo(g, "Host", "localhost"); + Username = GetStringCatInfo(g, "User", "*"); + Password = GetStringCatInfo(g, "Password", NULL); + Portnumber = GetIntCatInfo("Port", GetDefaultPort()); Server = Hostname; } else { char *locdb = Database; @@ -352,16 +352,16 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) Tabname = Name; } // endif am - if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL))) + if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) Isview = true; // Used for Update and Delete - Qrystr = Cat->GetStringCatInfo(g, "Query_String", "?"); - Quoted = Cat->GetIntCatInfo("Quoted", 0); + Qrystr = GetStringCatInfo(g, "Query_String", "?"); + Quoted = GetIntCatInfo("Quoted", 0); // Specific for command executing tables - Xsrc = Cat->GetBoolCatInfo("Execsrc", false); - Mxr = Cat->GetIntCatInfo("Maxerr", 0); + Xsrc = GetBoolCatInfo("Execsrc", false); + Mxr = GetIntCatInfo("Maxerr", 0); return FALSE; } // end of DefineAM diff --git a/storage/connect/taboccur.cpp b/storage/connect/taboccur.cpp index 5edbe932d50..96901424a7d 100644 --- a/storage/connect/taboccur.cpp +++ b/storage/connect/taboccur.cpp @@ -264,9 +264,9 @@ bool OcrSrcCols(PGLOBAL g, PQRYRES qrp, const char *col, /***********************************************************************/ bool OCCURDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - Rcol = Cat->GetStringCatInfo(g, "RankCol", ""); - Colist = Cat->GetStringCatInfo(g, "Colist", ""); - Xcol = Cat->GetStringCatInfo(g, "OccurCol", Colist); + Rcol = GetStringCatInfo(g, "RankCol", ""); + Colist = GetStringCatInfo(g, "Colist", ""); + Xcol = GetStringCatInfo(g, "OccurCol", Colist); return PRXDEF::DefineAM(g, am, poff); } // end of DefineAM diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index cbce9adc7ac..10ac1fda211 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -100,22 +100,22 @@ ODBCDEF::ODBCDEF(void) /***********************************************************************/ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - Desc = Connect = Cat->GetStringCatInfo(g, "Connect", ""); - Tabname = Cat->GetStringCatInfo(g, "Name", + Desc = Connect = GetStringCatInfo(g, "Connect", ""); + Tabname = GetStringCatInfo(g, "Name", (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); - Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); - Tabschema = Cat->GetStringCatInfo(g, "Dbname", NULL); - Tabschema = Cat->GetStringCatInfo(g, "Schema", Tabschema); - Tabcat = Cat->GetStringCatInfo(g, "Qualifier", NULL); - Tabcat = Cat->GetStringCatInfo(g, "Catalog", Tabcat); - Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL); - Qrystr = Cat->GetStringCatInfo(g, "Query_String", "?"); - Sep = Cat->GetStringCatInfo(g, "Separator", NULL); - Catver = Cat->GetIntCatInfo("Catver", 2); - Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE); - Maxerr = Cat->GetIntCatInfo("Maxerr", 0); - Maxres = Cat->GetIntCatInfo("Maxres", 0); - Quoted = Cat->GetIntCatInfo("Quoted", 0); + Tabname = GetStringCatInfo(g, "Tabname", Tabname); + Tabschema = GetStringCatInfo(g, "Dbname", NULL); + Tabschema = GetStringCatInfo(g, "Schema", Tabschema); + Tabcat = GetStringCatInfo(g, "Qualifier", NULL); + Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); + Srcdef = GetStringCatInfo(g, "Srcdef", NULL); + Qrystr = GetStringCatInfo(g, "Query_String", "?"); + Sep = GetStringCatInfo(g, "Separator", NULL); + Catver = GetIntCatInfo("Catver", 2); + Xsrc = GetBoolCatInfo("Execsrc", FALSE); + Maxerr = GetIntCatInfo("Maxerr", 0); + Maxres = GetIntCatInfo("Maxres", 0); + Quoted = GetIntCatInfo("Quoted", 0); Options = ODBConn::noOdbcDialog; Pseudo = 2; // FILID is Ok but not ROWID return false; diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp index b236d3c62cd..7e54b62caaa 100644 --- a/storage/connect/tabpivot.cpp +++ b/storage/connect/tabpivot.cpp @@ -58,11 +58,11 @@ extern "C" int trace; /***********************************************************************/ PQRYRES PivotColumns(PGLOBAL g, const char *tab, const char *src, const char *picol, const char *fncol, - const char *host, const char *db, - const char *user, const char *pwd, - int port) + const char *skcol, const char *host, + const char *db, const char *user, + const char *pwd, int port) { - PIVAID pvd(tab, src, picol, fncol, host, db, user, pwd, port); + PIVAID pvd(tab, src, picol, fncol, skcol, host, db, user, pwd, port); return pvd.MakePivotColumns(g); } // end of PivotColumns @@ -72,10 +72,10 @@ PQRYRES PivotColumns(PGLOBAL g, const char *tab, const char *src, /***********************************************************************/ /* PIVAID constructor. */ /***********************************************************************/ -PIVAID::PIVAID(const char *tab, const char *src, const char *picol, - const char *fncol, const char *host, const char *db, - const char *user, const char *pwd, int port) - : CSORT(false) +PIVAID::PIVAID(const char *tab, const char *src, const char *picol, + const char *fncol, const char *skcol, const char *host, + const char *db, const char *user, const char *pwd, + int port) : CSORT(false) { Host = (char*)host; User = (char*)user; @@ -86,19 +86,34 @@ PIVAID::PIVAID(const char *tab, const char *src, const char *picol, Tabsrc = (char*)src; Picol = (char*)picol; Fncol = (char*)fncol; + Skcol = (char*)skcol; Rblkp = NULL; Port = (port) ? port : GetDefaultPort(); } // end of PIVAID constructor +/***********************************************************************/ +/* Skip columns that are in the skipped column list. */ +/***********************************************************************/ +bool PIVAID::SkipColumn(PCOLRES crp, char *skc) + { + if (skc) + for (char *p = skc; *p; p += (strlen(p) + 1)) + if (!stricmp(crp->Name, p)) + return true; + + return false; + } // end of SkipColumn + /***********************************************************************/ /* Make the Pivot table column list. */ /***********************************************************************/ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) { - char *query, *colname, buf[64]; + char *p, *query, *colname, *skc, buf[64]; int rc, ndif, nblin, w = 0; bool b = false; - PVAL valp; + PVAL valp; + PQRYRES qrp; PCOLRES *pcrp, crp, fncrp = NULL; // Save stack and allocation environment and prepare error return @@ -111,10 +126,25 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) goto err; } // endif rc + // Are there columns to skip? + if (Skcol) { + uint n = strlen(Skcol); + + skc = (char*)PlugSubAlloc(g, NULL, n + 2); + strcpy(skc, Skcol); + skc[n + 1] = 0; + + // Replace ; by nulls in skc + for (p = strchr(skc, ';'); p; p = strchr(p, ';')) + *p++ = 0; + + } else + skc = NULL; + if (!Tabsrc && Tabname) { // Locate the query - query = (char*)PlugSubAlloc(g, NULL, strlen(Tabname) + 16); - sprintf(query, "SELECT * FROM %s", Tabname); + query = (char*)PlugSubAlloc(g, NULL, strlen(Tabname) + 26); + sprintf(query, "SELECT * FROM `%s` LIMIT 1", Tabname); } else if (!Tabsrc) { strcpy(g->Message, MSG(SRC_TABLE_UNDEF)); return NULL; @@ -132,18 +162,17 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) goto err; // We must have a storage query to get pivot column values - Qryp = Myc.GetResult(g, true); - Myc.Close(); - b = false; + if (!(Qryp = Myc.GetResult(g, true))) + goto err; if (!Fncol) { for (crp = Qryp->Colresp; crp; crp = crp->Next) - if (!Picol || stricmp(Picol, crp->Name)) + if ((!Picol || stricmp(Picol, crp->Name)) && !SkipColumn(crp, skc)) Fncol = crp->Name; if (!Fncol) { strcpy(g->Message, MSG(NO_DEF_FNCCOL)); - return NULL; + goto err; } // endif Fncol } // endif Fncol @@ -151,22 +180,25 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) if (!Picol) { // Find default Picol as the last one not equal to Fncol for (crp = Qryp->Colresp; crp; crp = crp->Next) - if (stricmp(Fncol, crp->Name)) + if (stricmp(Fncol, crp->Name) && !SkipColumn(crp, skc)) Picol = crp->Name; if (!Picol) { strcpy(g->Message, MSG(NO_DEF_PIVOTCOL)); - return NULL; + goto err; } // endif Picol } // endif picol // Prepare the column list for (pcrp = &Qryp->Colresp; crp = *pcrp; ) - if (!stricmp(Picol, crp->Name)) { + if (SkipColumn(crp, skc)) { + // Ignore this column + *pcrp = crp->Next; + } else if (!stricmp(Picol, crp->Name)) { if (crp->Nulls) { sprintf(g->Message, "Pivot column %s cannot be nullable", Picol); - return NULL; + goto err; } // endif Nulls Rblkp = crp->Kdata; @@ -179,31 +211,59 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) if (!Rblkp) { strcpy(g->Message, MSG(NO_DEF_PIVOTCOL)); - return NULL; + goto err; } else if (!fncrp) { strcpy(g->Message, MSG(NO_DEF_FNCCOL)); - return NULL; + goto err; } // endif - // Before calling sort, initialize all - nblin = Qryp->Nblin; + if (Tabsrc) { + Myc.Close(); + b = false; - Index.Size = nblin * sizeof(int); - Index.Sub = TRUE; // Should be small enough + // Before calling sort, initialize all + nblin = Qryp->Nblin; - if (!PlgDBalloc(g, NULL, Index)) - return NULL; + Index.Size = nblin * sizeof(int); + Index.Sub = TRUE; // Should be small enough - Offset.Size = (nblin + 1) * sizeof(int); - Offset.Sub = TRUE; // Should be small enough + if (!PlgDBalloc(g, NULL, Index)) + return NULL; - if (!PlgDBalloc(g, NULL, Offset)) - return NULL; + Offset.Size = (nblin + 1) * sizeof(int); + Offset.Sub = TRUE; // Should be small enough - ndif = Qsort(g, nblin); + if (!PlgDBalloc(g, NULL, Offset)) + return NULL; - if (ndif < 0) // error - return NULL; + ndif = Qsort(g, nblin); + + if (ndif < 0) // error + return NULL; + + } else { + // The query was limited, we must get pivot column values + query = (char*)PlugSubAlloc(g, NULL, 0); + sprintf(query, "SELECT DISTINCT `%s` FROM `%s`", Picol, Tabname); + PlugSubAlloc(g, NULL, strlen(query) + 1); + Myc.FreeResult(); + + // Send the source command to MySQL + if (Myc.ExecSQL(g, query, &w) == RC_FX) + goto err; + + // We must have a storage query to get pivot column values + if (!(qrp = Myc.GetResult(g, true))) + goto err; + + Myc.Close(); + b = false; + + // Get the column list + crp = qrp->Colresp; + Rblkp = crp->Kdata; + ndif = qrp->Nblin; + } // endif Tabsrc // Allocate the Value used to retieve column names if (!(valp = AllocateValue(g, Rblkp->GetType(), @@ -220,7 +280,11 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) crp = fncrp; // Get the value that will be the generated column name - valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]); + if (Tabsrc) + valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]); + else + valp->SetValue_pvblk(Rblkp, i); + colname = valp->GetCharString(buf); crp->Name = (char*)PlugSubAlloc(g, NULL, strlen(colname) + 1); strcpy(crp->Name, colname); @@ -280,11 +344,11 @@ bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) DB = (char*)Tablep->GetQualifier(); Tabsrc = (char*)Tablep->GetSrc(); - Host = Cat->GetStringCatInfo(g, "Host", "localhost"); - User = Cat->GetStringCatInfo(g, "User", "*"); - Pwd = Cat->GetStringCatInfo(g, "Password", NULL); - Picol = Cat->GetStringCatInfo(g, "PivotCol", NULL); - Fncol = Cat->GetStringCatInfo(g, "FncCol", NULL); + Host = GetStringCatInfo(g, "Host", "localhost"); + User = GetStringCatInfo(g, "User", "*"); + Pwd = GetStringCatInfo(g, "Password", NULL); + Picol = GetStringCatInfo(g, "PivotCol", NULL); + Fncol = GetStringCatInfo(g, "FncCol", NULL); // If fncol is like avg(colname), separate Fncol and Function if (Fncol && (p1 = strchr(Fncol, '(')) && (p2 = strchr(p1, ')')) && @@ -293,11 +357,11 @@ bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) Function = Fncol; Fncol = p1; } else - Function = Cat->GetStringCatInfo(g, "Function", "SUM"); + Function = GetStringCatInfo(g, "Function", "SUM"); - GBdone = Cat->GetBoolCatInfo("Groupby", false); - Accept = Cat->GetBoolCatInfo("Accept", false); - Port = Cat->GetIntCatInfo("Port", 3306); + GBdone = GetBoolCatInfo("Groupby", false); + Accept = GetBoolCatInfo("Accept", false); + Port = GetIntCatInfo("Port", 3306); Desc = (Tabsrc) ? Tabsrc : Tabname; return FALSE; } // end of DefineAM diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h index 0b5bf50d13b..c7248ee2e1d 100644 --- a/storage/connect/tabpivot.h +++ b/storage/connect/tabpivot.h @@ -18,12 +18,13 @@ class PIVAID : public CSORT { friend class SRCCOL; public: // Constructor - PIVAID(const char *tab, const char *src, const char *picol, - const char *fncol, const char *host, const char *db, - const char *user, const char *pwd, int port); + PIVAID(const char *tab, const char *src, const char *picol, + const char *fncol, const char *skcol, const char *host, + const char *db, const char *user, const char *pwd, int port); // Methods PQRYRES MakePivotColumns(PGLOBAL g); + bool SkipColumn(PCOLRES crp, char *skc); // The sorting function virtual int Qcompare(int *, int *); @@ -40,6 +41,7 @@ class PIVAID : public CSORT { char *Tabsrc; // SQL of source table char *Picol; // Pivot column name char *Fncol; // Function column name + char *Skcol; // Skipped columns PVBLK Rblkp; // The value block of the pivot column int Port; // MySQL port number }; // end of class PIVAID @@ -191,6 +193,6 @@ class SRCCOL : public PRXCOL { PQRYRES PivotColumns(PGLOBAL g, const char *tab, const char *src, const char *picol, const char *fncol, - const char *host, const char *db, - const char *user, const char *pwd, - int port); + const char *skcol, const char *host, + const char *db, const char *user, + const char *pwd, int port); diff --git a/storage/connect/tabsys.cpp b/storage/connect/tabsys.cpp index 409352fdee6..e8ea7f4e43a 100644 --- a/storage/connect/tabsys.cpp +++ b/storage/connect/tabsys.cpp @@ -76,8 +76,8 @@ bool INIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { char buf[8]; - Fn = Cat->GetStringCatInfo(g, "Filename", NULL); - Cat->GetCharCatInfo("Layout", "C", buf, sizeof(buf)); + Fn = GetStringCatInfo(g, "Filename", NULL); + GetCharCatInfo("Layout", "C", buf, sizeof(buf)); Layout = toupper(*buf); if (Fn) { @@ -90,7 +90,7 @@ bool INIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) return true; } // endif Fn - Ln = Cat->GetSizeCatInfo("Secsize", "8K"); + Ln = GetSizeCatInfo("Secsize", "8K"); Desc = Fn; return false; } // end of DefineAM diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp index c78c62af9cc..0aeeb0b9d8d 100644 --- a/storage/connect/tabtbl.cpp +++ b/storage/connect/tabtbl.cpp @@ -111,9 +111,9 @@ bool TBLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) char *tablist, *dbname, *def = NULL; Desc = "Table list table"; - tablist = Cat->GetStringCatInfo(g, "Tablist", ""); - dbname = Cat->GetStringCatInfo(g, "Dbname", "*"); - def = Cat->GetStringCatInfo(g, "Srcdef", NULL); + tablist = GetStringCatInfo(g, "Tablist", ""); + dbname = GetStringCatInfo(g, "Dbname", "*"); + def = GetStringCatInfo(g, "Srcdef", NULL); Ntables = 0; if (*tablist) { @@ -155,9 +155,9 @@ bool TBLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) } // endfor pdb - Maxerr = Cat->GetIntCatInfo("Maxerr", 0); - Accept = Cat->GetBoolCatInfo("Accept", false); - Thread = Cat->GetBoolCatInfo("Thread", false); + Maxerr = GetIntCatInfo("Maxerr", 0); + Accept = GetBoolCatInfo("Accept", false); + Thread = GetBoolCatInfo("Thread", false); } // endif tablist return FALSE; diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp index e77af35a8a4..4b9046e08d1 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -307,10 +307,10 @@ bool PRXDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { char *pn, *db, *tab, *def = NULL; - db = Cat->GetStringCatInfo(g, "Dbname", "*"); - def = Cat->GetStringCatInfo(g, "Srcdef", NULL); + db = GetStringCatInfo(g, "Dbname", "*"); + def = GetStringCatInfo(g, "Srcdef", NULL); - if (!(tab = Cat->GetStringCatInfo(g, "Tabname", NULL))) { + if (!(tab = GetStringCatInfo(g, "Tabname", NULL))) { if (!def) { strcpy(g->Message, "Missing object table definition"); return TRUE; diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp index 251af4d02f8..ed258f3a80f 100644 --- a/storage/connect/tabvct.cpp +++ b/storage/connect/tabvct.cpp @@ -95,13 +95,13 @@ bool VCTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { DOSDEF::DefineAM(g, "BIN", poff); - Estimate = Cat->GetIntCatInfo("Estimate", 0); - Split = Cat->GetIntCatInfo("Split", (Estimate) ? 0 : 1); - Header = Cat->GetIntCatInfo("Header", 0); + Estimate = GetIntCatInfo("Estimate", 0); + Split = GetIntCatInfo("Split", (Estimate) ? 0 : 1); + Header = GetIntCatInfo("Header", 0); // CONNECT must have Block/Last info for VEC tables if (Estimate && !Split && !Header) { - char *fn = Cat->GetStringCatInfo(g, "Filename", "?"); + char *fn = GetStringCatInfo(g, "Filename", "?"); // No separate header file fo urbi tables Header = (*fn == '?') ? 3 : 2; @@ -112,7 +112,7 @@ bool VCTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) // For packed files the logical record length is calculated in poff if (poff != Lrecl) { Lrecl = poff; - Cat->SetIntCatInfo("Lrecl", poff); + SetIntCatInfo("Lrecl", poff); } // endif poff Padded = false; diff --git a/storage/connect/tabwmi.cpp b/storage/connect/tabwmi.cpp index 5052268b9e2..e47df028dc2 100644 --- a/storage/connect/tabwmi.cpp +++ b/storage/connect/tabwmi.cpp @@ -335,8 +335,8 @@ PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info) /***********************************************************************/ bool WMIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - Nspace = Cat->GetStringCatInfo(g, "Namespace", "Root\\CimV2"); - Wclass = Cat->GetStringCatInfo(g, "Class", + Nspace = GetStringCatInfo(g, "Namespace", "Root\\CimV2"); + Wclass = GetStringCatInfo(g, "Class", (!stricmp(Nspace, "root\\cimv2") ? "ComputerSystemProduct" : !stricmp(Nspace, "root\\cli") ? "Msft_CliAlias" : "")); @@ -349,7 +349,7 @@ bool WMIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) } // endif Wclass if (Catfunc == FNC_NO) - Ems = Cat->GetIntCatInfo("Estimate", 100); + Ems = GetIntCatInfo("Estimate", 100); return false; } // end of DefineAM diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp index 41a4283fd22..bd3d57257ff 100644 --- a/storage/connect/tabxcl.cpp +++ b/storage/connect/tabxcl.cpp @@ -78,10 +78,10 @@ bool XCLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { char buf[8]; - Xcol = Cat->GetStringCatInfo(g, "Colname", ""); - Cat->GetCharCatInfo("Separator", ",", buf, sizeof(buf)); + Xcol = GetStringCatInfo(g, "Colname", ""); + GetCharCatInfo("Separator", ",", buf, sizeof(buf)); Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf; - Mult = Cat->GetIntCatInfo("Mult", 10); + Mult = GetIntCatInfo("Mult", 10); return PRXDEF::DefineAM(g, am, poff); } // end of DefineAM diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 77dc7617e8a..c7c61f0dcbb 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -95,21 +95,21 @@ bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) //void *memp = Cat->GetDescp(); //PSZ dbfile = Cat->GetDescFile(); - Fn = Cat->GetStringCatInfo(g, "Filename", NULL); - Encoding = Cat->GetStringCatInfo(g, "Encoding", "UTF-8"); + Fn = GetStringCatInfo(g, "Filename", NULL); + Encoding = GetStringCatInfo(g, "Encoding", "UTF-8"); if (*Fn == '?') { strcpy(g->Message, MSG(MISSING_FNAME)); return true; } // endif fn - if ((signed)Cat->GetIntCatInfo("Flag", -1) != -1) { + if ((signed)GetIntCatInfo("Flag", -1) != -1) { strcpy(g->Message, MSG(DEPREC_FLAG)); return true; } // endif flag defrow = defcol = ""; - Cat->GetCharCatInfo("Coltype", "", buf, sizeof(buf)); + GetCharCatInfo("Coltype", "", buf, sizeof(buf)); switch (toupper(*buf)) { case 'A': // Attribute @@ -136,25 +136,25 @@ bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) return true; } // endswitch typname - Tabname = Cat->GetStringCatInfo(g, "Name", Name); // Deprecated - Tabname = Cat->GetStringCatInfo(g, "Table_name", Tabname); // Deprecated - Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); - Rowname = Cat->GetStringCatInfo(g, "Rownode", defrow); - Colname = Cat->GetStringCatInfo(g, "Colnode", defcol); - Mulnode = Cat->GetStringCatInfo(g, "Mulnode", ""); - XmlDB = Cat->GetStringCatInfo(g, "XmlDB", ""); - Nslist = Cat->GetStringCatInfo(g, "Nslist", ""); - DefNs = Cat->GetStringCatInfo(g, "DefNs", ""); - Limit = Cat->GetIntCatInfo("Limit", 2); - Xpand = (Cat->GetIntCatInfo("Expand", 0) != 0); - Header = Cat->GetIntCatInfo("Header", 0); - Cat->GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf)); + Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated + Tabname = GetStringCatInfo(g, "Table_name", Tabname); // Deprecated + Tabname = GetStringCatInfo(g, "Tabname", Tabname); + Rowname = GetStringCatInfo(g, "Rownode", defrow); + Colname = GetStringCatInfo(g, "Colnode", defcol); + Mulnode = GetStringCatInfo(g, "Mulnode", ""); + XmlDB = GetStringCatInfo(g, "XmlDB", ""); + Nslist = GetStringCatInfo(g, "Nslist", ""); + DefNs = GetStringCatInfo(g, "DefNs", ""); + Limit = GetIntCatInfo("Limit", 2); + Xpand = (GetIntCatInfo("Expand", 0) != 0); + Header = GetIntCatInfo("Header", 0); + GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf)); //if (*buf == '*') // Try the old (deprecated) option -// Cat->GetCharCatInfo("Method", "*", buf, sizeof(buf)); +// GetCharCatInfo("Method", "*", buf, sizeof(buf)); //if (*buf == '*') // Is there a default for the database? -// Cat->GetCharCatInfo("Defxml", XMLSUP, buf, sizeof(buf)); +// GetCharCatInfo("Defxml", XMLSUP, buf, sizeof(buf)); // Note that if no support is specified, the default is MS-DOM // on Windows and libxml2 otherwise @@ -168,8 +168,8 @@ bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D'); // Get eventual table node attribute - Attrib = Cat->GetStringCatInfo(g, "Attribute", ""); - Hdattr = Cat->GetStringCatInfo(g, "HeadAttr", ""); + Attrib = GetStringCatInfo(g, "Attribute", ""); + Hdattr = GetStringCatInfo(g, "HeadAttr", ""); return false; } // end of DefineAM diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp index e36d958f28a..a7576eab9cd 100755 --- a/storage/connect/xindex.cpp +++ b/storage/connect/xindex.cpp @@ -723,7 +723,7 @@ bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp) return true; } // endswitch Ftype - if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) { + if ((sep = defp->GetBoolCatInfo("SepIndex", false))) { // Index is saved in a separate file #if !defined(UNIX) char drive[_MAX_DRIVE]; @@ -1627,7 +1627,7 @@ int XINDEX::Fetch(PGLOBAL g) switch (Op) { case OP_NEXT: // Read next if (NextVal(false)) - return -1; // End of indexed file + return -1; // End of indexed file break; case OP_FIRST: // Read first @@ -1643,7 +1643,7 @@ int XINDEX::Fetch(PGLOBAL g) if (NextVal(true)) { Op = OP_EQ; - return -2; // no more equal values + return -2; // no more equal values } // endif NextVal break; @@ -1651,9 +1651,9 @@ int XINDEX::Fetch(PGLOBAL g) // while (!NextVal(true)) ; // if (Cur_K >= Num_K) -// return -1; // End of indexed file +// return -1; // End of indexed file if (NextValDif()) - return -1; // End of indexed file + return -1; // End of indexed file break; case OP_FSTDIF: // Read first diff @@ -1662,12 +1662,17 @@ int XINDEX::Fetch(PGLOBAL g) Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT; break; - case OP_LAST: // Read last key + case OP_LAST: // Read last key for (Cur_K = Num_K - 1, kp = To_KeyCol; kp; kp = kp->Next) kp->Val_K = kp->Kblp->GetNval() - 1; Op = OP_NEXT; break; + case OP_PREV: // Read previous + if (PrevVal()) + return -1; // End of indexed file + + break; default: // Should be OP_EQ // if (Tbxp->Key_Rank < 0) { /***************************************************************/