summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2014-04-19 16:41:25 +0200
committerOlivier Bertrand <bertrandop@gmail.com>2014-04-19 16:41:25 +0200
commit50953a0e9de6ae36a62fc195f3fc97d6d319493e (patch)
tree33689c716390d4e5f3abf302937322baaae3cf04
parent812520315318e358d2d9daf9ca709578af1efe40 (diff)
parent213ecbbb4f3d252ba2d653eacf756e123821c41a (diff)
downloadmariadb-git-50953a0e9de6ae36a62fc195f3fc97d6d319493e.tar.gz
- Commit merge files
modified: storage/connect/catalog.h storage/connect/filamvct.cpp storage/connect/filamzip.cpp storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/mycat.cc storage/connect/mycat.h storage/connect/myconn.cpp storage/connect/mysql-test/connect/r/xml.result storage/connect/plgdbutl.cpp storage/connect/reldef.cpp storage/connect/reldef.h storage/connect/tabdos.cpp storage/connect/tabfmt.cpp storage/connect/tabmul.cpp storage/connect/tabmysql.cpp storage/connect/taboccur.cpp storage/connect/tabodbc.cpp storage/connect/tabpivot.cpp storage/connect/tabpivot.h storage/connect/tabsys.cpp storage/connect/tabtbl.cpp storage/connect/tabutil.cpp storage/connect/tabvct.cpp storage/connect/tabwmi.cpp storage/connect/tabxcl.cpp storage/connect/tabxml.cpp storage/connect/xindex.cpp
-rw-r--r--storage/connect/catalog.h234
-rwxr-xr-xstorage/connect/filamvct.cpp8467
-rw-r--r--storage/connect/filamzip.cpp15
-rw-r--r--storage/connect/ha_connect.cc155
-rw-r--r--storage/connect/ha_connect.h8
-rw-r--r--storage/connect/mycat.cc308
-rw-r--r--storage/connect/mycat.h9
-rw-r--r--storage/connect/myconn.cpp2
-rw-r--r--storage/connect/mysql-test/connect/r/xml.result3
-rw-r--r--storage/connect/plgdbutl.cpp2
-rw-r--r--storage/connect/reldef.cpp297
-rw-r--r--storage/connect/reldef.h458
-rw-r--r--storage/connect/tabdos.cpp5142
-rw-r--r--storage/connect/tabfmt.cpp12
-rwxr-xr-xstorage/connect/tabmul.cpp6
-rw-r--r--storage/connect/tabmysql.cpp62
-rw-r--r--storage/connect/taboccur.cpp6
-rw-r--r--storage/connect/tabodbc.cpp30
-rw-r--r--storage/connect/tabpivot.cpp156
-rw-r--r--storage/connect/tabpivot.h14
-rw-r--r--storage/connect/tabsys.cpp6
-rw-r--r--storage/connect/tabtbl.cpp12
-rw-r--r--storage/connect/tabutil.cpp6
-rw-r--r--storage/connect/tabvct.cpp10
-rw-r--r--storage/connect/tabwmi.cpp6
-rw-r--r--storage/connect/tabxcl.cpp6
-rw-r--r--storage/connect/tabxml.cpp42
-rwxr-xr-xstorage/connect/xindex.cpp17
28 files changed, 7801 insertions, 7690 deletions
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 <io.h>
-#include <fcntl.h>
-#if defined(__BORLANDC__)
-#define __MFC_COMPAT__ // To define min/max as macro
-#endif // __BORLAND__
-//#include <windows.h>
-#include <sys/stat.h>
-#else // !WIN32 F
-#if defined(UNIX)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#define NO_ERROR 0
-#else // !UNIX
-#include <io.h>
-#endif // !UNIX
-#include <fcntl.h>
-#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 <io.h>
+#include <fcntl.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif // __BORLAND__
+//#include <windows.h>
+#include <sys/stat.h>
+#else // !WIN32 F
+#if defined(UNIX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#define NO_ERROR 0
+#else // !UNIX
+#include <io.h>
+#endif // !UNIX
+#include <fcntl.h>
+#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();
+ rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
+ break;
+ case TYPE_STRING:
+ 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:
- p= NULL;
+ rc= fp->store(value->GetFloatValue());
break;
- case TYPE_STRING:
- // Passthru
default:
- p= value->GetCharString(val);
+ 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)
{
@@ -258,6 +258,35 @@ bool IsTypeIndexable(TABTYPE type)
} // 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. */
/***********************************************************************/
uint GetFuncID(const char *func)
@@ -435,281 +464,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 <io.h>
-#include <sys\timeb.h> // For testing only
-#include <fcntl.h>
-#include <errno.h>
-#if defined(__BORLANDC__)
-#define __MFC_COMPAT__ // To define min/max as macro
-#endif // __BORLANDC__
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX)
-#include <errno.h>
-#include <unistd.h>
-#else // !UNIX
-#include <io.h>
-#endif // !UNIX
-#include <fcntl.h>
-#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 <io.h>
+#include <sys\timeb.h> // For testing only
+#include <fcntl.h>
+#include <errno.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif // __BORLANDC__
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX)
+#include <errno.h>
+#include <unistd.h>
+#else // !UNIX
+#include <io.h>
+#endif // !UNIX
+#include <fcntl.h>
+#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
+
+ if (!PlgDBalloc(g, NULL, Offset))
+ return NULL;
- ndif = Qsort(g, nblin);
+ ndif = Qsort(g, nblin);
- if (ndif < 0) // error
- return NULL;
+ 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) {
/***************************************************************/