diff options
author | Olivier Bertrand <bertrandop@gmail.com> | 2014-04-19 16:41:25 +0200 |
---|---|---|
committer | Olivier Bertrand <bertrandop@gmail.com> | 2014-04-19 16:41:25 +0200 |
commit | 50953a0e9de6ae36a62fc195f3fc97d6d319493e (patch) | |
tree | 33689c716390d4e5f3abf302937322baaae3cf04 | |
parent | 812520315318e358d2d9daf9ca709578af1efe40 (diff) | |
parent | 213ecbbb4f3d252ba2d653eacf756e123821c41a (diff) | |
download | mariadb-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
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) { /***************************************************************/ |