summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
committerOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
commit7b400a088d049661b9a4dded385ac78923bb0017 (patch)
treebb3ee7bac4454f66ec6f38cf6683f24bc6c065d4
parentd67ad26b33ea16a3b59215ef967bdd9b89345e04 (diff)
parente5729127b8a50a0e553fd8b87b2683e4a684dfcc (diff)
downloadmariadb-git-7b400a088d049661b9a4dded385ac78923bb0017.tar.gz
- MRR + Block Indexing
modified: storage/connect/array.h storage/connect/catalog.h storage/connect/colblk.cpp storage/connect/colblk.h storage/connect/connect.cc storage/connect/connect.h storage/connect/domdoc.h storage/connect/filamap.cpp storage/connect/filamap.h storage/connect/filamdbf.h storage/connect/filamfix.cpp storage/connect/filamfix.h storage/connect/filamtxt.cpp storage/connect/filamtxt.h storage/connect/filamvct.cpp storage/connect/filamvct.h storage/connect/filamzip.cpp storage/connect/filamzip.h storage/connect/filter.cpp storage/connect/filter.h storage/connect/global.h storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/myconn.h storage/connect/plgcnx.h storage/connect/plgdbsem.h storage/connect/plgdbutl.cpp storage/connect/plugutil.c storage/connect/preparse.h storage/connect/reldef.cpp storage/connect/reldef.h storage/connect/tabcol.h storage/connect/tabdos.cpp storage/connect/tabdos.h storage/connect/tabfix.cpp storage/connect/tabfmt.cpp storage/connect/tabfmt.h storage/connect/table.cpp storage/connect/tabmysql.cpp storage/connect/tabmysql.h storage/connect/taboccur.h storage/connect/tabodbc.h storage/connect/tabsys.h storage/connect/tabtbl.h storage/connect/tabutil.h storage/connect/tabvct.cpp storage/connect/tabvct.h storage/connect/tabwmi.h storage/connect/tabxml.h storage/connect/user_connect.cc storage/connect/user_connect.h storage/connect/valblk.cpp storage/connect/valblk.h storage/connect/value.cpp storage/connect/value.h storage/connect/xindex.cpp storage/connect/xindex.h storage/connect/xobject.cpp storage/connect/xobject.h storage/connect/xtable.h
-rw-r--r--storage/connect/array.h2
-rw-r--r--storage/connect/catalog.h244
-rw-r--r--storage/connect/colblk.cpp781
-rw-r--r--storage/connect/colblk.h426
-rw-r--r--storage/connect/connect.cc1798
-rw-r--r--storage/connect/connect.h6
-rw-r--r--storage/connect/domdoc.h4
-rw-r--r--storage/connect/filamap.cpp1479
-rw-r--r--storage/connect/filamap.h227
-rw-r--r--storage/connect/filamdbf.h11
-rw-r--r--storage/connect/filamfix.cpp9
-rw-r--r--storage/connect/filamfix.h178
-rw-r--r--storage/connect/filamtxt.cpp2904
-rw-r--r--storage/connect/filamtxt.h392
-rwxr-xr-xstorage/connect/filamvct.cpp8476
-rw-r--r--storage/connect/filamvct.h498
-rw-r--r--storage/connect/filamzip.cpp2829
-rw-r--r--storage/connect/filamzip.h340
-rw-r--r--storage/connect/filter.cpp6
-rw-r--r--storage/connect/filter.h22
-rw-r--r--storage/connect/global.h518
-rw-r--r--storage/connect/ha_connect.cc11421
-rw-r--r--storage/connect/ha_connect.h1053
-rw-r--r--storage/connect/myconn.h6
-rw-r--r--storage/connect/plgcnx.h140
-rw-r--r--storage/connect/plgdbsem.h1240
-rw-r--r--storage/connect/plgdbutl.cpp11
-rw-r--r--storage/connect/plugutil.c1102
-rw-r--r--storage/connect/preparse.h27
-rw-r--r--storage/connect/reldef.cpp880
-rw-r--r--storage/connect/reldef.h456
-rw-r--r--storage/connect/tabcol.h2
-rw-r--r--storage/connect/tabdos.cpp5289
-rw-r--r--storage/connect/tabdos.h539
-rw-r--r--storage/connect/tabfix.cpp15
-rw-r--r--storage/connect/tabfmt.cpp2865
-rw-r--r--storage/connect/tabfmt.h380
-rw-r--r--storage/connect/table.cpp1182
-rw-r--r--storage/connect/tabmysql.cpp3240
-rw-r--r--storage/connect/tabmysql.h23
-rw-r--r--storage/connect/taboccur.h1
-rw-r--r--storage/connect/tabodbc.h1
-rw-r--r--storage/connect/tabsys.h363
-rw-r--r--storage/connect/tabtbl.h328
-rw-r--r--storage/connect/tabutil.h3
-rw-r--r--storage/connect/tabvct.cpp4
-rw-r--r--storage/connect/tabvct.h244
-rw-r--r--storage/connect/tabwmi.h301
-rw-r--r--storage/connect/tabxml.h486
-rw-r--r--storage/connect/user_connect.cc324
-rw-r--r--storage/connect/user_connect.h3
-rw-r--r--storage/connect/valblk.cpp8
-rw-r--r--storage/connect/valblk.h682
-rw-r--r--storage/connect/value.cpp4424
-rw-r--r--storage/connect/value.h687
-rwxr-xr-xstorage/connect/xindex.cpp6056
-rw-r--r--storage/connect/xindex.h15
-rw-r--r--storage/connect/xobject.cpp374
-rw-r--r--storage/connect/xobject.h256
-rw-r--r--storage/connect/xtable.h526
60 files changed, 32618 insertions, 33489 deletions
diff --git a/storage/connect/array.h b/storage/connect/array.h
index 489a26ac0fd..c05757d7abc 100644
--- a/storage/connect/array.h
+++ b/storage/connect/array.h
@@ -51,7 +51,7 @@ class DllExport ARRAY : public XOBJECT, public CSORT { // Array descblock
virtual int Qcompare(int *, int *);
virtual bool Compare(PXOB) {assert(FALSE); return FALSE;}
virtual bool SetFormat(PGLOBAL, FORMAT&) {assert(FALSE); return FALSE;}
- virtual int CheckSpcCol(PTDB, int) {return 0;}
+//virtual int CheckSpcCol(PTDB, int) {return 0;}
virtual void Print(PGLOBAL g, FILE *f, UINT n);
virtual void Print(PGLOBAL g, char *ps, UINT z);
void Empty(void);
diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h
index 86d973e0036..61f151ba794 100644
--- a/storage/connect/catalog.h
+++ b/storage/connect/catalog.h
@@ -1,122 +1,122 @@
-/*************** 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 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
diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp
index 364628bfca6..e890ad1ce44 100644
--- a/storage/connect/colblk.cpp
+++ b/storage/connect/colblk.cpp
@@ -1,399 +1,382 @@
-/************* Colblk C++ Functions Source Code File (.CPP) ************/
-/* Name: COLBLK.CPP Version 2.0 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
-/* */
-/* This file contains the COLBLK class functions. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant MariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "tabcol.h"
-#include "colblk.h"
-#include "xindex.h"
-#include "xtable.h"
-
-/***********************************************************************/
-/* COLBLK protected constructor. */
-/***********************************************************************/
-COLBLK::COLBLK(PCOLDEF cdp, PTDB tdbp, int i)
- {
- Next = NULL;
- Index = i;
-//Number = 0;
- ColUse = 0;
-
- if ((Cdp = cdp)) {
- Name = cdp->Name;
- Format = cdp->F;
- Opt = cdp->Opt;
- Long = cdp->Long;
- Precision = cdp->Precision;
- Freq = cdp->Freq;
- Buf_Type = cdp->Buf_Type;
- ColUse |= cdp->Flags; // Used by CONNECT
- Nullable = !!(cdp->Flags & U_NULLS);
- Unsigned = !!(cdp->Flags & U_UNSIGNED);
- } else {
- Name = NULL;
- memset(&Format, 0, sizeof(FORMAT));
- Opt = 0;
- Long = 0;
- Precision = 0;
- Freq = 0;
- Buf_Type = TYPE_ERROR;
- Nullable = false;
- Unsigned = false;
- } // endif cdp
-
- To_Tdb = tdbp;
- Status = BUF_NO;
-//Value = NULL; done in XOBJECT constructor
- To_Kcol = NULL;
- } // end of COLBLK constructor
-
-/***********************************************************************/
-/* COLBLK constructor used for copying columns. */
-/* tdbp is the pointer to the new table descriptor. */
-/***********************************************************************/
-COLBLK::COLBLK(PCOL col1, PTDB tdbp)
- {
- PCOL colp;
-
- // Copy the old column block to the new one
- *this = *col1;
- Next = NULL;
-//To_Orig = col1;
- To_Tdb = tdbp;
-
-#ifdef DEBTRACE
- htrc(" copying COLBLK %s from %p to %p\n", Name, col1, this);
-#endif
-
- if (tdbp)
- // Attach the new column to the table block
- if (!tdbp->GetColumns())
- tdbp->SetColumns(this);
- else {
- for (colp = tdbp->GetColumns(); colp->Next; colp = colp->Next) ;
-
- colp->Next = this;
- } // endelse
-
- } // end of COLBLK copy constructor
-
-/***********************************************************************/
-/* Reset the column descriptor to non evaluated yet. */
-/***********************************************************************/
-void COLBLK::Reset(void)
- {
- Status &= ~BUF_READ;
- } // end of Reset
-
-/***********************************************************************/
-/* Compare: compares itself to an (expression) object and returns */
-/* true if it is equivalent. */
-/***********************************************************************/
-bool COLBLK::Compare(PXOB xp)
- {
- return (this == xp);
- } // end of Compare
-
-/***********************************************************************/
-/* SetFormat: function used to set SELECT output format. */
-/***********************************************************************/
-bool COLBLK::SetFormat(PGLOBAL g, FORMAT& fmt)
- {
- fmt = Format;
-
-#ifdef DEBTRACE
- htrc("COLBLK: %p format=%c(%d,%d)\n",
- this, *fmt.Type, fmt.Length, fmt.Prec);
-#endif
-
- return false;
- } // end of SetFormat
-
-/***********************************************************************/
-/* CheckColumn: a column descriptor is found, say it by returning 1. */
-/***********************************************************************/
-int COLBLK::CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &p, int &ag)
- {
- return 1;
- } // end of CheckColumn
-
-/***********************************************************************/
-/* Eval: get the column value from the last read record or from a */
-/* matching Index column if there is one. */
-/***********************************************************************/
-bool COLBLK::Eval(PGLOBAL g)
- {
-#ifdef DEBTRACE
- htrc("Col Eval: %s status=%.4X\n", Name, Status);
-#endif
-
- if (!GetStatus(BUF_READ)) {
-// if (To_Tdb->IsNull())
-// Value->Reset();
- if (To_Kcol)
- To_Kcol->FillValue(Value);
- else
- ReadColumn(g);
-
- AddStatus(BUF_READ);
- } // endif
-
- return false;
- } // end of Eval
-
-/***********************************************************************/
-/* CheckSort: */
-/* Used to check that a table is involved in the sort list items. */
-/***********************************************************************/
-bool COLBLK::CheckSort(PTDB tdbp)
- {
- return (tdbp == To_Tdb);
- } // end of CheckSort
-
-/***********************************************************************/
-/* InitValue: prepare a column block for read operation. */
-/* Now we use Format.Length for the len parameter to avoid strings */
-/* to be truncated when converting from string to coded string. */
-/* Added in version 1.5 is the arguments GetScale() and Domain */
-/* in calling AllocateValue. Domain is used for TYPE_DATE only. */
-/***********************************************************************/
-bool COLBLK::InitValue(PGLOBAL g)
- {
- if (Value)
- return false; // Already done
-
- // Allocate a Value object
- if (!(Value = AllocateValue(g, Buf_Type, Precision,
- GetScale(), Unsigned, GetDomain())))
- return true;
-
- AddStatus(BUF_READY);
- Value->SetNullable(Nullable);
-
-#ifdef DEBTRACE
- htrc(" colp=%p type=%d value=%p coluse=%.4X status=%.4X\n",
- this, Buf_Type, Value, ColUse, Status);
-#endif
-
- return false;
- } // end of InitValue
-
-/***********************************************************************/
-/* SetBuffer: prepare a column block for write operation. */
-/***********************************************************************/
-bool COLBLK::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
- {
- sprintf(g->Message, MSG(UNDEFINED_AM), "SetBuffer");
- return true;
- } // end of SetBuffer
-
-/***********************************************************************/
-/* GetLength: returns an evaluation of the column string length. */
-/***********************************************************************/
-int COLBLK::GetLengthEx(void)
- {
- return Long;
- } // end of GetLengthEx
-
-/***********************************************************************/
-/* 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 COLBLK::ReadColumn(PGLOBAL g)
- {
- sprintf(g->Message, MSG(UNDEFINED_AM), "ReadColumn");
- longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
- } // 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 COLBLK::WriteColumn(PGLOBAL g)
- {
- sprintf(g->Message, MSG(UNDEFINED_AM), "WriteColumn");
- longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
- } // end of WriteColumn
-
-/***********************************************************************/
-/* Make file output of a column descriptor block. */
-/***********************************************************************/
-void COLBLK::Print(PGLOBAL g, FILE *f, uint n)
- {
- char m[64];
- int i;
- PCOL colp;
-
- memset(m, ' ', n); // Make margin string
- m[n] = '\0';
-
- for (colp = To_Tdb->GetColumns(), i = 1; colp; colp = colp->Next, i++)
- if (colp == this)
- break;
-
- fprintf(f, "%sR%dC%d type=%d F=%.2s(%d,%d)", m, To_Tdb->GetTdb_No(),
- i, GetAmType(), Format.Type, Format.Length, Format.Prec);
- fprintf(f,
- " coluse=%04X status=%04X buftyp=%d value=%p name=%s\n",
- ColUse, Status, Buf_Type, Value, Name);
- } // end of Print
-
-/***********************************************************************/
-/* Make string output of a column descriptor block. */
-/***********************************************************************/
-void COLBLK::Print(PGLOBAL g, char *ps, uint z)
- {
- sprintf(ps, "R%d.%s", To_Tdb->GetTdb_No(), Name);
- } // end of Print
-
-
-/***********************************************************************/
-/* SPCBLK constructor. */
-/***********************************************************************/
-SPCBLK::SPCBLK(PCOLUMN cp)
- : COLBLK((PCOLDEF)NULL, cp->GetTo_Table()->GetTo_Tdb(), 0)
- {
- Name = (char*)cp->GetName();
- Precision = Long = 0;
- Buf_Type = TYPE_ERROR;
- } // end of SPCBLK constructor
-
-/***********************************************************************/
-/* 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 SPCBLK::WriteColumn(PGLOBAL g)
- {
- sprintf(g->Message, MSG(SPCOL_READONLY), Name);
- longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
- } // end of WriteColumn
-
-/***********************************************************************/
-/* RIDBLK constructor for the ROWID special column. */
-/***********************************************************************/
-RIDBLK::RIDBLK(PCOLUMN cp, bool rnm) : SPCBLK(cp)
- {
- Precision = Long = 10;
- Buf_Type = TYPE_INT;
- Rnm = rnm;
- *Format.Type = 'N';
- Format.Length = 10;
- } // end of RIDBLK constructor
-
-/***********************************************************************/
-/* ReadColumn: what this routine does is to return the ordinal */
-/* number of the current row in the table (if Rnm is true) or in the */
-/* current file (if Rnm is false) the same except for multiple tables.*/
-/***********************************************************************/
-void RIDBLK::ReadColumn(PGLOBAL g)
- {
- Value->SetValue(To_Tdb->RowNumber(g, Rnm));
- } // end of ReadColumn
-
-/***********************************************************************/
-/* FIDBLK constructor for the FILEID special column. */
-/***********************************************************************/
-FIDBLK::FIDBLK(PCOLUMN cp) : SPCBLK(cp)
- {
-//Is_Key = 2; for when the MUL table indexed reading will be implemented.
- Precision = Long = _MAX_PATH;
- Buf_Type = TYPE_STRING;
- *Format.Type = 'C';
- Format.Length = Long;
-#if defined(WIN32)
- Format.Prec = 1; // Case insensitive
-#endif // WIN32
- Constant = (!((PTDBASE)To_Tdb)->GetDef()->GetMultiple() &&
- To_Tdb->GetAmType() != TYPE_AM_PLG &&
- To_Tdb->GetAmType() != TYPE_AM_PLM);
- Fn = NULL;
- } // end of FIDBLK constructor
-
-/***********************************************************************/
-/* ReadColumn: what this routine does is to return the current */
-/* file ID of the table (can change for Multiple tables). */
-/***********************************************************************/
-void FIDBLK::ReadColumn(PGLOBAL g)
- {
- if (Fn != ((PTDBASE)To_Tdb)->GetFile(g)) {
- char filename[_MAX_PATH];
-
- Fn = ((PTDBASE)To_Tdb)->GetFile(g);
- PlugSetPath(filename, Fn, ((PTDBASE)To_Tdb)->GetPath());
- Value->SetValue_psz(filename);
- } // endif Fn
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* TIDBLK constructor for the TABID special column. */
-/***********************************************************************/
-TIDBLK::TIDBLK(PCOLUMN cp) : SPCBLK(cp)
- {
-//Is_Key = 2; for when the MUL table indexed reading will be implemented.
- Precision = Long = 64;
- Buf_Type = TYPE_STRING;
- *Format.Type = 'C';
- Format.Length = Long;
- Format.Prec = 1; // Case insensitive
- Constant = (To_Tdb->GetAmType() != TYPE_AM_TBL);
- Tname = NULL;
- } // end of TIDBLK constructor
-
-/***********************************************************************/
-/* ReadColumn: what this routine does is to return the table ID. */
-/***********************************************************************/
-void TIDBLK::ReadColumn(PGLOBAL g)
- {
- if (Tname == NULL) {
- Tname = (char*)To_Tdb->GetName();
- Value->SetValue_psz(Tname);
- } // endif Tname
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* SIDBLK constructor for the SERVID special column. */
-/***********************************************************************/
-SIDBLK::SIDBLK(PCOLUMN cp) : SPCBLK(cp)
- {
-//Is_Key = 2; for when the MUL table indexed reading will be implemented.
- Precision = Long = 64;
- Buf_Type = TYPE_STRING;
- *Format.Type = 'C';
- Format.Length = Long;
- Format.Prec = 1; // Case insensitive
- Constant = (To_Tdb->GetAmType() != TYPE_AM_TBL);
- Sname = NULL;
- } // end of TIDBLK constructor
-
-/***********************************************************************/
-/* ReadColumn: what this routine does is to return the server ID. */
-/***********************************************************************/
-void SIDBLK::ReadColumn(PGLOBAL g)
- {
-//if (Sname == NULL) {
- Sname = (char*)To_Tdb->GetServer();
- Value->SetValue_psz(Sname);
-// } // endif Sname
-
- } // end of ReadColumn
-
+/************* Colblk C++ Functions Source Code File (.CPP) ************/
+/* Name: COLBLK.CPP Version 2.1 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* */
+/* This file contains the COLBLK class functions. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "tabcol.h"
+#include "colblk.h"
+#include "xindex.h"
+#include "xtable.h"
+
+/***********************************************************************/
+/* COLBLK protected constructor. */
+/***********************************************************************/
+COLBLK::COLBLK(PCOLDEF cdp, PTDB tdbp, int i)
+ {
+ Next = NULL;
+ Index = i;
+//Number = 0;
+ ColUse = 0;
+
+ if ((Cdp = cdp)) {
+ Name = cdp->Name;
+ Format = cdp->F;
+ Opt = cdp->Opt;
+ Long = cdp->Long;
+ Precision = cdp->Precision;
+ Freq = cdp->Freq;
+ Buf_Type = cdp->Buf_Type;
+ ColUse |= cdp->Flags; // Used by CONNECT
+ Nullable = !!(cdp->Flags & U_NULLS);
+ Unsigned = !!(cdp->Flags & U_UNSIGNED);
+ } else {
+ Name = NULL;
+ memset(&Format, 0, sizeof(FORMAT));
+ Opt = 0;
+ Long = 0;
+ Precision = 0;
+ Freq = 0;
+ Buf_Type = TYPE_ERROR;
+ Nullable = false;
+ Unsigned = false;
+ } // endif cdp
+
+ To_Tdb = tdbp;
+ Status = BUF_NO;
+//Value = NULL; done in XOBJECT constructor
+ To_Kcol = NULL;
+ } // end of COLBLK constructor
+
+/***********************************************************************/
+/* COLBLK constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+COLBLK::COLBLK(PCOL col1, PTDB tdbp)
+ {
+ PCOL colp;
+
+ // Copy the old column block to the new one
+ *this = *col1;
+ Next = NULL;
+//To_Orig = col1;
+ To_Tdb = tdbp;
+
+#ifdef DEBTRACE
+ htrc(" copying COLBLK %s from %p to %p\n", Name, col1, this);
+#endif
+
+ if (tdbp)
+ // Attach the new column to the table block
+ if (!tdbp->GetColumns())
+ tdbp->SetColumns(this);
+ else {
+ for (colp = tdbp->GetColumns(); colp->Next; colp = colp->Next) ;
+
+ colp->Next = this;
+ } // endelse
+
+ } // end of COLBLK copy constructor
+
+/***********************************************************************/
+/* Reset the column descriptor to non evaluated yet. */
+/***********************************************************************/
+void COLBLK::Reset(void)
+ {
+ Status &= ~BUF_READ;
+ } // end of Reset
+
+/***********************************************************************/
+/* Compare: compares itself to an (expression) object and returns */
+/* true if it is equivalent. */
+/***********************************************************************/
+bool COLBLK::Compare(PXOB xp)
+ {
+ return (this == xp);
+ } // end of Compare
+
+/***********************************************************************/
+/* SetFormat: function used to set SELECT output format. */
+/***********************************************************************/
+bool COLBLK::SetFormat(PGLOBAL g, FORMAT& fmt)
+ {
+ fmt = Format;
+
+#ifdef DEBTRACE
+ htrc("COLBLK: %p format=%c(%d,%d)\n",
+ this, *fmt.Type, fmt.Length, fmt.Prec);
+#endif
+
+ return false;
+ } // end of SetFormat
+
+/***********************************************************************/
+/* Eval: get the column value from the last read record or from a */
+/* matching Index column if there is one. */
+/***********************************************************************/
+bool COLBLK::Eval(PGLOBAL g)
+ {
+#ifdef DEBTRACE
+ htrc("Col Eval: %s status=%.4X\n", Name, Status);
+#endif
+
+ if (!GetStatus(BUF_READ)) {
+// if (To_Tdb->IsNull())
+// Value->Reset();
+ if (To_Kcol)
+ To_Kcol->FillValue(Value);
+ else
+ ReadColumn(g);
+
+ AddStatus(BUF_READ);
+ } // endif
+
+ return false;
+ } // end of Eval
+
+/***********************************************************************/
+/* InitValue: prepare a column block for read operation. */
+/* Now we use Format.Length for the len parameter to avoid strings */
+/* to be truncated when converting from string to coded string. */
+/* Added in version 1.5 is the arguments GetScale() and Domain */
+/* in calling AllocateValue. Domain is used for TYPE_DATE only. */
+/***********************************************************************/
+bool COLBLK::InitValue(PGLOBAL g)
+ {
+ if (Value)
+ return false; // Already done
+
+ // Allocate a Value object
+ if (!(Value = AllocateValue(g, Buf_Type, Precision,
+ GetScale(), Unsigned, GetDomain())))
+ return true;
+
+ AddStatus(BUF_READY);
+ Value->SetNullable(Nullable);
+
+#ifdef DEBTRACE
+ htrc(" colp=%p type=%d value=%p coluse=%.4X status=%.4X\n",
+ this, Buf_Type, Value, ColUse, Status);
+#endif
+
+ return false;
+ } // end of InitValue
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool COLBLK::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+ {
+ sprintf(g->Message, MSG(UNDEFINED_AM), "SetBuffer");
+ return true;
+ } // end of SetBuffer
+
+/***********************************************************************/
+/* GetLength: returns an evaluation of the column string length. */
+/***********************************************************************/
+int COLBLK::GetLengthEx(void)
+ {
+ return Long;
+ } // end of GetLengthEx
+
+/***********************************************************************/
+/* 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 COLBLK::ReadColumn(PGLOBAL g)
+ {
+ sprintf(g->Message, MSG(UNDEFINED_AM), "ReadColumn");
+ longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
+ } // 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 COLBLK::WriteColumn(PGLOBAL g)
+ {
+ sprintf(g->Message, MSG(UNDEFINED_AM), "WriteColumn");
+ longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
+ } // end of WriteColumn
+
+/***********************************************************************/
+/* Make file output of a column descriptor block. */
+/***********************************************************************/
+void COLBLK::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+ int i;
+ PCOL colp;
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+
+ for (colp = To_Tdb->GetColumns(), i = 1; colp; colp = colp->Next, i++)
+ if (colp == this)
+ break;
+
+ fprintf(f, "%sR%dC%d type=%d F=%.2s(%d,%d)", m, To_Tdb->GetTdb_No(),
+ i, GetAmType(), Format.Type, Format.Length, Format.Prec);
+ fprintf(f,
+ " coluse=%04X status=%04X buftyp=%d value=%p name=%s\n",
+ ColUse, Status, Buf_Type, Value, Name);
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of a column descriptor block. */
+/***********************************************************************/
+void COLBLK::Print(PGLOBAL g, char *ps, uint z)
+ {
+ sprintf(ps, "R%d.%s", To_Tdb->GetTdb_No(), Name);
+ } // end of Print
+
+
+/***********************************************************************/
+/* SPCBLK constructor. */
+/***********************************************************************/
+SPCBLK::SPCBLK(PCOLUMN cp)
+ : COLBLK((PCOLDEF)NULL, cp->GetTo_Table()->GetTo_Tdb(), 0)
+ {
+ Name = (char*)cp->GetName();
+ Precision = Long = 0;
+ Buf_Type = TYPE_ERROR;
+ } // end of SPCBLK constructor
+
+/***********************************************************************/
+/* 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 SPCBLK::WriteColumn(PGLOBAL g)
+ {
+ sprintf(g->Message, MSG(SPCOL_READONLY), Name);
+ longjmp(g->jumper[g->jump_level], TYPE_COLBLK);
+ } // end of WriteColumn
+
+/***********************************************************************/
+/* RIDBLK constructor for the ROWID special column. */
+/***********************************************************************/
+RIDBLK::RIDBLK(PCOLUMN cp, bool rnm) : SPCBLK(cp)
+ {
+ Precision = Long = 10;
+ Buf_Type = TYPE_INT;
+ Rnm = rnm;
+ *Format.Type = 'N';
+ Format.Length = 10;
+ } // end of RIDBLK constructor
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to return the ordinal */
+/* number of the current row in the table (if Rnm is true) or in the */
+/* current file (if Rnm is false) the same except for multiple tables.*/
+/***********************************************************************/
+void RIDBLK::ReadColumn(PGLOBAL g)
+ {
+ Value->SetValue(To_Tdb->RowNumber(g, Rnm));
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* FIDBLK constructor for the FILEID special column. */
+/***********************************************************************/
+FIDBLK::FIDBLK(PCOLUMN cp) : SPCBLK(cp)
+ {
+//Is_Key = 2; for when the MUL table indexed reading will be implemented.
+ Precision = Long = _MAX_PATH;
+ Buf_Type = TYPE_STRING;
+ *Format.Type = 'C';
+ Format.Length = Long;
+#if defined(WIN32)
+ Format.Prec = 1; // Case insensitive
+#endif // WIN32
+ Constant = (!((PTDBASE)To_Tdb)->GetDef()->GetMultiple() &&
+ To_Tdb->GetAmType() != TYPE_AM_PLG &&
+ To_Tdb->GetAmType() != TYPE_AM_PLM);
+ Fn = NULL;
+ } // end of FIDBLK constructor
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to return the current */
+/* file ID of the table (can change for Multiple tables). */
+/***********************************************************************/
+void FIDBLK::ReadColumn(PGLOBAL g)
+ {
+ if (Fn != ((PTDBASE)To_Tdb)->GetFile(g)) {
+ char filename[_MAX_PATH];
+
+ Fn = ((PTDBASE)To_Tdb)->GetFile(g);
+ PlugSetPath(filename, Fn, ((PTDBASE)To_Tdb)->GetPath());
+ Value->SetValue_psz(filename);
+ } // endif Fn
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* TIDBLK constructor for the TABID special column. */
+/***********************************************************************/
+TIDBLK::TIDBLK(PCOLUMN cp) : SPCBLK(cp)
+ {
+//Is_Key = 2; for when the MUL table indexed reading will be implemented.
+ Precision = Long = 64;
+ Buf_Type = TYPE_STRING;
+ *Format.Type = 'C';
+ Format.Length = Long;
+ Format.Prec = 1; // Case insensitive
+ Constant = (To_Tdb->GetAmType() != TYPE_AM_TBL);
+ Tname = NULL;
+ } // end of TIDBLK constructor
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to return the table ID. */
+/***********************************************************************/
+void TIDBLK::ReadColumn(PGLOBAL g)
+ {
+ if (Tname == NULL) {
+ Tname = (char*)To_Tdb->GetName();
+ Value->SetValue_psz(Tname);
+ } // endif Tname
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* SIDBLK constructor for the SERVID special column. */
+/***********************************************************************/
+SIDBLK::SIDBLK(PCOLUMN cp) : SPCBLK(cp)
+ {
+//Is_Key = 2; for when the MUL table indexed reading will be implemented.
+ Precision = Long = 64;
+ Buf_Type = TYPE_STRING;
+ *Format.Type = 'C';
+ Format.Length = Long;
+ Format.Prec = 1; // Case insensitive
+ Constant = (To_Tdb->GetAmType() != TYPE_AM_TBL);
+ Sname = NULL;
+ } // end of TIDBLK constructor
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to return the server ID. */
+/***********************************************************************/
+void SIDBLK::ReadColumn(PGLOBAL g)
+ {
+//if (Sname == NULL) {
+ Sname = (char*)To_Tdb->GetServer();
+ Value->SetValue_psz(Sname);
+// } // endif Sname
+
+ } // end of ReadColumn
+
diff --git a/storage/connect/colblk.h b/storage/connect/colblk.h
index 1204c4623f7..93d39524362 100644
--- a/storage/connect/colblk.h
+++ b/storage/connect/colblk.h
@@ -1,221 +1,205 @@
-/*************** Colblk H Declares Source Code File (.H) ***************/
-/* Name: COLBLK.H Version 1.7 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* This file contains the COLBLK and derived classes declares. */
-/***********************************************************************/
-#ifndef __COLBLK__H
-#define __COLBLK__H
-
-/***********************************************************************/
-/* Include required application header files */
-/***********************************************************************/
-#include "xobject.h"
-#include "reldef.h"
-
-/***********************************************************************/
-/* Class COLBLK: Base class for table column descriptors. */
-/***********************************************************************/
-class DllExport COLBLK : public XOBJECT {
- friend class TDBPIVOT;
- protected:
- // Default constructors used by derived classes
- COLBLK(PCOLDEF cdp = NULL, PTDB tdbp = NULL, int i = 0);
- COLBLK(PCOL colp, PTDB tdbp = NULL); // Used in copy process
- COLBLK(int n) {} // Used when changing a column class in TDBXML
-
- public:
- // Implementation
- virtual int GetType(void) {return TYPE_COLBLK;}
- virtual int GetResultType(void) {return Buf_Type;}
- virtual int GetScale(void) {return Format.Prec;}
- virtual int GetPrecision(void) {return Precision;}
- virtual int GetLength(void) {return Long;}
- virtual int GetLengthEx(void);
- virtual int GetAmType() {return TYPE_AM_ERROR;}
- virtual void SetOk(void) {Status |= BUF_EMPTY;}
- virtual PTDB GetTo_Tdb(void) {return To_Tdb;}
-#if defined(BLK_INDX)
- virtual int GetClustered(void) {return 0;}
- virtual int IsClustered(void) {return FALSE;}
-#endif // BLK_INDX
- PCOL GetNext(void) {return Next;}
- PSZ GetName(void) {return Name;}
- int GetIndex(void) {return Index;}
- int GetOpt(void) {return Opt;}
- ushort GetColUse(void) {return ColUse;}
- ushort GetColUse(ushort u) {return (ColUse & u);}
- ushort GetStatus(void) {return Status;}
- ushort GetStatus(ushort u) {return (Status & u);}
- void SetColUse(ushort u) {ColUse = u;}
- void SetStatus(ushort u) {Status = u;}
- void AddColUse(ushort u) {ColUse |= u;}
- void AddStatus(ushort u) {Status |= u;}
- void SetNext(PCOL cp) {Next = cp;}
-#if defined(MRRBKA_SUPPORT)
- PXCOL GetKcol(void) {return To_Kcol;}
-#endif // MRRBKA_SUPPORT
- void SetKcol(PXCOL kcp) {To_Kcol = kcp;}
- PCOLDEF GetCdp(void) {return Cdp;}
- PSZ GetDomain(void) {return (Cdp) ? Cdp->Decode : NULL;}
- PSZ GetDesc(void) {return (Cdp) ? Cdp->Desc : NULL;}
- PSZ GetFmt(void) {return (Cdp) ? Cdp->Fmt : NULL;}
- bool IsUnsigned(void) {return Unsigned;}
- bool IsNullable(void) {return Nullable;}
- void SetNullable(bool b) {Nullable = b;}
-
- // Methods
- virtual void Reset(void);
- virtual bool Compare(PXOB xp);
- virtual bool SetFormat(PGLOBAL, FORMAT&);
- virtual int CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &xp, int &ag);
- virtual bool IsSpecial(void) {return false;}
- virtual int CheckSpcCol(PTDB tdbp, int n) {return 2;}
- virtual bool CheckSort(PTDB tdbp);
- virtual bool Eval(PGLOBAL g);
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void SetTo_Val(PVAL valp) {}
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- virtual void Print(PGLOBAL g, FILE *, uint);
- virtual void Print(PGLOBAL g, char *, uint);
-#if defined(BLK_INDX)
- virtual bool VarSize(void) {return false;}
-#endif // BLK_INDX
- virtual bool IsColInside(PCOL colp) {return this == colp;}
- bool InitValue(PGLOBAL g);
-
- protected:
- // Members
- PCOL Next; // Next column in table
- PSZ Name; // Column name
- PCOLDEF Cdp; // To column definition block
- PTDB To_Tdb; // Points to Table Descriptor Block
- PXCOL To_Kcol; // Points to Xindex matching column
- bool Nullable; // True if nullable
- bool Unsigned; // True if unsigned
- int Index; // Column number in table
- int Opt; // Cluster/sort information
- int Buf_Type; // Data type
- int Long; // Internal length in table
- int Precision; // Column length (as for ODBC)
- int Freq; // Evaluated ceiling of distinct values
- FORMAT Format; // Output format
- ushort ColUse; // Column usage
- ushort Status; // Column read status
- }; // end of class COLBLK
-
-/***********************************************************************/
-/* Class SPCBLK: Base class for special column descriptors. */
-/***********************************************************************/
-class DllExport SPCBLK : public COLBLK {
- public:
- // Constructor
- SPCBLK(PCOLUMN cp);
-
- // Implementation
- virtual int GetAmType(void) = 0;
- virtual bool GetRnm(void) {return false;}
-
- // Methods
- virtual bool IsSpecial(void) {return true;}
- virtual void ReadColumn(PGLOBAL g) = 0;
- virtual void WriteColumn(PGLOBAL g);
-
- protected:
- // Default constructor not to be used
- SPCBLK(void) : COLBLK(1) {}
- }; // end of class SPCBLK
-
-/***********************************************************************/
-/* Class RIDBLK: ROWID special column descriptor. */
-/***********************************************************************/
-class DllExport RIDBLK : public SPCBLK {
- public:
- // Constructor
- RIDBLK(PCOLUMN cp, bool rnm);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_ROWID;}
- virtual bool GetRnm(void) {return Rnm;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- bool Rnm; // False for RowID, True for RowNum
- }; // end of class RIDBLK
-
-/***********************************************************************/
-/* Class FIDBLK: FILEID special column descriptor. */
-/***********************************************************************/
-class DllExport FIDBLK : public SPCBLK {
- public:
- // Constructor
- FIDBLK(PCOLUMN cp);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_FILID;}
-
- // Methods
- virtual void Reset(void) {} // This is a pseudo constant column
- virtual int CheckSpcCol(PTDB tdbp, int n)
- {return (n == 2 && tdbp == To_Tdb) ? 1 : 2;}
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- PSZ Fn; // The current To_File of the table
- }; // end of class FIDBLK
-
-/***********************************************************************/
-/* Class TIDBLK: TABID special column descriptor. */
-/***********************************************************************/
-class DllExport TIDBLK : public SPCBLK {
- public:
- // Constructor
- TIDBLK(PCOLUMN cp);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_TABID;}
-
- // Methods
- virtual void Reset(void) {} // This is a pseudo constant column
- virtual int CheckSpcCol(PTDB tdbp, int n)
- {return (n == 3 && tdbp == To_Tdb) ? 1 : 2;}
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- // Default constructor not to be used
- TIDBLK(void) {}
-
- // Members
- PSZ Tname; // The current table name
- }; // end of class TIDBLK
-
-/***********************************************************************/
-/* Class SIDBLK: SERVID special column descriptor. */
-/***********************************************************************/
-class DllExport SIDBLK : public SPCBLK {
- public:
- // Constructor
- SIDBLK(PCOLUMN cp);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_SRVID;}
-
- // Methods
- virtual void Reset(void) {} // This is a pseudo constant column
- virtual int CheckSpcCol(PTDB tdbp, int n)
- {return (n == 3 && tdbp == To_Tdb) ? 1 : 2;}
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- // Default constructor not to be used
- SIDBLK(void) {}
-
- // Members
- PSZ Sname; // The current server name
- }; // end of class SIDBLK
-
-#endif // __COLBLK__H
+/*************** Colblk H Declares Source Code File (.H) ***************/
+/* Name: COLBLK.H Version 1.7 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* This file contains the COLBLK and derived classes declares. */
+/***********************************************************************/
+#ifndef __COLBLK__H
+#define __COLBLK__H
+
+/***********************************************************************/
+/* Include required application header files */
+/***********************************************************************/
+#include "xobject.h"
+#include "reldef.h"
+
+/***********************************************************************/
+/* Class COLBLK: Base class for table column descriptors. */
+/***********************************************************************/
+class DllExport COLBLK : public XOBJECT {
+ friend class TDBPIVOT;
+ protected:
+ // Default constructors used by derived classes
+ COLBLK(PCOLDEF cdp = NULL, PTDB tdbp = NULL, int i = 0);
+ COLBLK(PCOL colp, PTDB tdbp = NULL); // Used in copy process
+ COLBLK(int n) {} // Used when changing a column class in TDBXML
+
+ public:
+ // Implementation
+ virtual int GetType(void) {return TYPE_COLBLK;}
+ virtual int GetResultType(void) {return Buf_Type;}
+ virtual int GetScale(void) {return Format.Prec;}
+ virtual int GetPrecision(void) {return Precision;}
+ virtual int GetLength(void) {return Long;}
+ virtual int GetLengthEx(void);
+ virtual int GetAmType() {return TYPE_AM_ERROR;}
+ virtual void SetOk(void) {Status |= BUF_EMPTY;}
+ virtual PTDB GetTo_Tdb(void) {return To_Tdb;}
+ virtual int GetClustered(void) {return 0;}
+ virtual int IsClustered(void) {return FALSE;}
+ PCOL GetNext(void) {return Next;}
+ PSZ GetName(void) {return Name;}
+ int GetIndex(void) {return Index;}
+ ushort GetColUse(void) {return ColUse;}
+ int GetOpt(void) {return Opt;}
+ ushort GetColUse(ushort u) {return (ColUse & u);}
+ ushort GetStatus(void) {return Status;}
+ ushort GetStatus(ushort u) {return (Status & u);}
+ void SetColUse(ushort u) {ColUse = u;}
+ void SetStatus(ushort u) {Status = u;}
+ void AddColUse(ushort u) {ColUse |= u;}
+ void AddStatus(ushort u) {Status |= u;}
+ void SetNext(PCOL cp) {Next = cp;}
+ PXCOL GetKcol(void) {return To_Kcol;}
+ void SetKcol(PXCOL kcp) {To_Kcol = kcp;}
+ PCOLDEF GetCdp(void) {return Cdp;}
+ PSZ GetDomain(void) {return (Cdp) ? Cdp->Decode : NULL;}
+ PSZ GetDesc(void) {return (Cdp) ? Cdp->Desc : NULL;}
+ PSZ GetFmt(void) {return (Cdp) ? Cdp->Fmt : NULL;}
+ bool IsUnsigned(void) {return Unsigned;}
+ bool IsNullable(void) {return Nullable;}
+ void SetNullable(bool b) {Nullable = b;}
+
+ // Methods
+ virtual void Reset(void);
+ virtual bool Compare(PXOB xp);
+ virtual bool SetFormat(PGLOBAL, FORMAT&);
+ virtual bool IsSpecial(void) {return false;}
+ virtual bool Eval(PGLOBAL g);
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void SetTo_Val(PVAL valp) {}
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ virtual void Print(PGLOBAL g, FILE *, uint);
+ virtual void Print(PGLOBAL g, char *, uint);
+ virtual bool VarSize(void) {return false;}
+ bool InitValue(PGLOBAL g);
+
+ protected:
+ // Members
+ PCOL Next; // Next column in table
+ PSZ Name; // Column name
+ PCOLDEF Cdp; // To column definition block
+ PTDB To_Tdb; // Points to Table Descriptor Block
+ PXCOL To_Kcol; // Points to Xindex matching column
+ bool Nullable; // True if nullable
+ bool Unsigned; // True if unsigned
+ int Index; // Column number in table
+ int Opt; // Cluster/sort information
+ int Buf_Type; // Data type
+ int Long; // Internal length in table
+ int Precision; // Column length (as for ODBC)
+ int Freq; // Evaluated ceiling of distinct values
+ FORMAT Format; // Output format
+ ushort ColUse; // Column usage
+ ushort Status; // Column read status
+ }; // end of class COLBLK
+
+/***********************************************************************/
+/* Class SPCBLK: Base class for special column descriptors. */
+/***********************************************************************/
+class DllExport SPCBLK : public COLBLK {
+ public:
+ // Constructor
+ SPCBLK(PCOLUMN cp);
+
+ // Implementation
+ virtual int GetAmType(void) = 0;
+ virtual bool GetRnm(void) {return false;}
+
+ // Methods
+ virtual bool IsSpecial(void) {return true;}
+ virtual void ReadColumn(PGLOBAL g) = 0;
+ virtual void WriteColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ SPCBLK(void) : COLBLK(1) {}
+ }; // end of class SPCBLK
+
+/***********************************************************************/
+/* Class RIDBLK: ROWID special column descriptor. */
+/***********************************************************************/
+class DllExport RIDBLK : public SPCBLK {
+ public:
+ // Constructor
+ RIDBLK(PCOLUMN cp, bool rnm);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_ROWID;}
+ virtual bool GetRnm(void) {return Rnm;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ bool Rnm; // False for RowID, True for RowNum
+ }; // end of class RIDBLK
+
+/***********************************************************************/
+/* Class FIDBLK: FILEID special column descriptor. */
+/***********************************************************************/
+class DllExport FIDBLK : public SPCBLK {
+ public:
+ // Constructor
+ FIDBLK(PCOLUMN cp);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_FILID;}
+
+ // Methods
+ virtual void Reset(void) {} // This is a pseudo constant column
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ PSZ Fn; // The current To_File of the table
+ }; // end of class FIDBLK
+
+/***********************************************************************/
+/* Class TIDBLK: TABID special column descriptor. */
+/***********************************************************************/
+class DllExport TIDBLK : public SPCBLK {
+ public:
+ // Constructor
+ TIDBLK(PCOLUMN cp);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_TABID;}
+
+ // Methods
+ virtual void Reset(void) {} // This is a pseudo constant column
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ TIDBLK(void) {}
+
+ // Members
+ PSZ Tname; // The current table name
+ }; // end of class TIDBLK
+
+/***********************************************************************/
+/* Class SIDBLK: SERVID special column descriptor. */
+/***********************************************************************/
+class DllExport SIDBLK : public SPCBLK {
+ public:
+ // Constructor
+ SIDBLK(PCOLUMN cp);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_SRVID;}
+
+ // Methods
+ virtual void Reset(void) {} // This is a pseudo constant column
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ SIDBLK(void) {}
+
+ // Members
+ PSZ Sname; // The current server name
+ }; // end of class SIDBLK
+
+#endif // __COLBLK__H
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index f6d25b47ce7..f4bc4119f36 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -1,901 +1,897 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2012
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/***********************************************************************/
-/* Author Olivier BERTRAND bertrandop@gmail.com 2004-2012 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the CONNECT general purpose semantic routines. */
-/***********************************************************************/
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-/***********************************************************************/
-/* Include application header files */
-/* */
-/* global.h is header containing all global declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/***********************************************************************/
-#define DONT_DEFINE_VOID
-#include "handler.h"
-#undef OFFSET
-
-#include "global.h"
-#include "plgdbsem.h"
-#include "xobject.h"
-#include "connect.h"
-#include "tabcol.h"
-#include "catalog.h"
-#include "ha_connect.h"
-#include "mycat.h"
-
-#define my_strupr(p) my_caseup_str(default_charset_info, (p));
-#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
-#define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b))
-
-/***********************************************************************/
-/* DB static variables. */
-/***********************************************************************/
-extern int xtrace;
-
-/***********************************************************************/
-/* Routines called internally by semantic routines. */
-/***********************************************************************/
-void CntEndDB(PGLOBAL);
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr= false);
-
-/***********************************************************************/
-/* MySQL routines called externally by semantic routines. */
-/***********************************************************************/
-int rename_file_ext(const char *from, const char *to,const char *ext);
-
-/***********************************************************************/
-/* CntExit: CONNECT termination routine. */
-/***********************************************************************/
-PGLOBAL CntExit(PGLOBAL g)
- {
- if (g) {
- CntEndDB(g);
-
- if (g->Activityp)
- delete g->Activityp;
-
- PlugExit(g);
- g= NULL;
- } // endif g
-
- return g;
- } // end of CntExit
-
-/***********************************************************************/
-/* CntEndDB: DB termination semantic routine. */
-/***********************************************************************/
-void CntEndDB(PGLOBAL g)
- {
- PDBUSER dbuserp= PlgGetUser(g);
-
- if (dbuserp) {
- if (dbuserp->Catalog)
- delete dbuserp->Catalog;
-
- free(dbuserp);
- } // endif dbuserp
-
- } // end of CntEndDB
-
-/***********************************************************************/
-/* CntCheckDB: Initialize a DB application session. */
-/* Note: because MySQL does not call a storage handler when a user */
-/* executes a use db command, a check must be done before an SQL */
-/* command is executed to check whether we are still working on the */
-/* current database, and if not to load the newly used database. */
-/***********************************************************************/
-bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
- {
- bool rc= false;
- PDBUSER dbuserp= PlgGetUser(g);
-
- if (xtrace) {
- printf("CntCheckDB: dbuserp=%p\n", dbuserp);
- } // endif xtrace
-
- if (!dbuserp || !handler)
- return true;
-
- if (xtrace)
- printf("cat=%p oldhandler=%p newhandler=%p\n", dbuserp->Catalog,
- (dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL,
- handler);
-
- if (dbuserp->Catalog) {
-// ((MYCAT *)dbuserp->Catalog)->SetHandler(handler); done later
- ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
- return false; // Nothing else to do
- } // endif Catalog
-
- // Copy new database name in dbuser block
- strncpy(dbuserp->Name, "???", sizeof(dbuserp->Name) - 1);
-
- dbuserp->Vtdbno= 0; // Init of TDB numbers
-
- /*********************************************************************/
- /* Now allocate and initialize the Database Catalog. */
- /*********************************************************************/
- dbuserp->Step= MSG(READY);
-
- if (!(dbuserp->Catalog= new MYCAT(handler)))
- return true;
-
- ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
- dbuserp->UseTemp= TMP_YES; // Must use temporary file
-
- /*********************************************************************/
- /* All is correct. */
- /*********************************************************************/
- sprintf(g->Message, MSG(DATABASE_LOADED), "???");
-
- if (xtrace)
- printf("msg=%s\n", g->Message);
-
- return rc;
- } // end of CntCheckDB
-
-/***********************************************************************/
-/* CntInfo: Get table info. */
-/* Returns valid: true if this is a table info. */
-/***********************************************************************/
-bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
- {
- bool b;
- PTDBDOS tdbp= (PTDBDOS)tp;
-
- if (tdbp) {
- b= tdbp->GetFtype() != RECFM_NAF;
- info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0;
- info->records= (unsigned)tdbp->GetMaxSize(g);
-// info->mean_rec_length= tdbp->GetLrecl();
- info->mean_rec_length= 0;
- info->data_file_name= (b) ? tdbp->GetFile(g) : NULL;
- return true;
- } else {
- info->data_file_length= 0;
- info->records= 0;
- info->mean_rec_length= 0;
- info->data_file_name= NULL;
- return false;
- } // endif tdbp
-
- } // end of CntInfo
-
-/***********************************************************************/
-/* GetTDB: Get the table description block of a CONNECT table. */
-/***********************************************************************/
-PTDB CntGetTDB(PGLOBAL g, LPCSTR name, MODE mode, PHC h)
- {
- int rc;
- PTDB tdbp;
- PTABLE tabp;
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL; // Safe over longjmp
-
- if (xtrace)
- printf("CntGetTDB: name=%s mode=%d cat=%p\n", name, mode, cat);
-
- if (!cat)
- return NULL;
-
- // Save stack and allocation environment and prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return NULL;
- } // endif jump_level
-
- if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
- tdbp= NULL;
- goto err;
- } // endif rc
-
- // Get table object from the catalog
- tabp= new(g) XTAB(name);
-
- if (xtrace)
- printf("CntGetTDB: tabp=%p\n", tabp);
-
- // Perhaps this should be made thread safe
- ((MYCAT*)cat)->SetHandler(h);
-
- if (!(tdbp= cat->GetTable(g, tabp, mode)))
- printf("CntGetTDB: %s\n", g->Message);
-
- err:
- if (xtrace)
- printf("Returning tdbp=%p mode=%d\n", tdbp, mode);
-
- g->jump_level--;
- return tdbp;
- } // end of CntGetTDB
-
-/***********************************************************************/
-/* OPENTAB: Open a Table. */
-/***********************************************************************/
-bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
- bool del, PHC h)
- {
- char *p;
- int i, n, rc;
- bool rcop= true;
- PCOL colp;
-//PCOLUMN cp;
- PDBUSER dup= PlgGetUser(g);
-
- if (xtrace)
- printf("CntOpenTable: tdbp=%p mode=%d\n", tdbp, mode);
-
- if (!tdbp) {
- strcpy(g->Message, "Null tdbp");
- printf("CntOpenTable: %s\n", g->Message);
- return true;
- } // endif tdbp
-
- // Save stack and allocation environment and prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return true;
- } // endif jump_level
-
- if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
- goto err;
- } // endif rc
-
- if (!c1) {
- if (mode == MODE_INSERT)
- // Allocate all column blocks for that table
- tdbp->ColDB(g, NULL, 0);
-
- } else for (p= c1; *p; p+= n) {
- // Allocate only used column blocks
- if (xtrace)
- printf("Allocating column %s\n", p);
-
-// if (*p == '*') {
-// // This is a special column
-// cp= new(g) COLUMN(p + 1);
-// cp->SetTo_Table(tdbp->GetTable());
-// colp= ((PTDBASE)tdbp)->InsertSpcBlk(g, cp);
-// } else
- colp= tdbp->ColDB(g, p, 0);
-
- if (!colp) {
- sprintf(g->Message, "Column %s not found in %s", p, tdbp->GetName());
- goto err;
- } // endif colp
-
- n= strlen(p) + 1;
- } // endfor p
-
- for (i= 0, colp= tdbp->GetColumns(); colp; i++, colp= colp->GetNext()) {
- if (colp->InitValue(g))
- goto err;
-
- if (mode == MODE_INSERT)
- // Allow type conversion
- if (colp->SetBuffer(g, colp->GetValue(), true, false))
- goto err;
-
- colp->AddColUse(U_P); // For PLG tables
- } // endfor colp
-
- /*********************************************************************/
- /* In Update mode, the updated column blocks must be distinct from */
- /* the read column blocks. So make a copy of the TDB and allocate */
- /* its column blocks in mode write (required by XML tables). */
- /*********************************************************************/
- if (mode == MODE_UPDATE) {
- PTDBASE utp;
-
- if (!(utp= (PTDBASE)tdbp->Duplicate(g))) {
- sprintf(g->Message, MSG(INV_UPDT_TABLE), tdbp->GetName());
- goto err;
- } // endif tp
-
- if (!c2)
- // Allocate all column blocks for that table
- utp->ColDB(g, NULL, 0);
- else for (p= c2; *p; p+= n) {
- // Allocate only used column blocks
- colp= utp->ColDB(g, p, 0);
- n= strlen(p) + 1;
- } // endfor p
-
- for (i= 0, colp= utp->GetColumns(); colp; i++, colp= colp->GetNext()) {
- if (colp->InitValue(g))
- goto err;
-
- if (colp->SetBuffer(g, colp->GetValue(), true, false))
- goto err;
-
- } // endfor colp
-
- // Attach the updated columns list to the main table
- ((PTDBASE)tdbp)->SetSetCols(utp->GetColumns());
- } else if (tdbp && mode == MODE_INSERT)
- ((PTDBASE)tdbp)->SetSetCols(tdbp->GetColumns());
-
- // Now do open the physical table
- if (xtrace)
- printf("Opening table %s in mode %d tdbp=%p\n",
- tdbp->GetName(), mode, tdbp);
-
-//tdbp->SetMode(mode);
-
- if (del && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) {
- // To avoid erasing the table when doing a partial delete
- // make a fake Next
- PDOSDEF ddp= new(g) DOSDEF;
- PTDB tp= new(g) TDBDOS(ddp, NULL);
- tdbp->SetNext(tp);
- dup->Check &= ~CHK_DELETE;
- } // endif del
-
-
- if (xtrace)
- printf("About to open the table: tdbp=%p\n", tdbp);
-
- if (mode != MODE_ANY && mode != MODE_ALTER) {
- if (tdbp->OpenDB(g)) {
- printf("%s\n", g->Message);
- goto err;
- } else
- tdbp->SetNext(NULL);
-
- } // endif mode
-
- rcop= false;
-
- err:
- g->jump_level--;
- return rcop;
- } // end of CntOpenTable
-
-/***********************************************************************/
-/* Rewind a table by reopening it. */
-/***********************************************************************/
-bool CntRewindTable(PGLOBAL g, PTDB tdbp)
-{
- if (!tdbp)
- return true;
-
- tdbp->OpenDB(g);
- return false;
-} // end of CntRewindTable
-
-/***********************************************************************/
-/* Evaluate all columns after a record is read. */
-/***********************************************************************/
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr)
- {
- RCODE rc= RC_OK;
- PCOL colp;
-
- // Save stack and allocation environment and prepare error return
- if (g->jump_level == MAX_JUMP) {
- if (xtrace) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- printf("EvalColumns: %s\n", g->Message);
- } // endif
-
- return RC_FX;
- } // endif jump_level
-
- if (setjmp(g->jumper[++g->jump_level]) != 0) {
- if (xtrace)
- printf("Error reading columns: %s\n", g->Message);
-
- rc= RC_FX;
- goto err;
- } // endif rc
-
- for (colp= tdbp->GetColumns(); rc == RC_OK && colp;
- colp= colp->GetNext()) {
- colp->Reset();
-
- // Virtual columns are computed by MariaDB
-#if defined(MRRBKA_SUPPORT)
- if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
-#else // !MRRBKA_SUPPORT
- if (!colp->GetColUse(U_VIRTUAL))
-#endif // !MRRBKA_SUPPORT
- if (colp->Eval(g))
- rc= RC_FX;
-
- } // endfor colp
-
- err:
- g->jump_level--;
- return rc;
- } // end of EvalColumns
-
-/***********************************************************************/
-/* ReadNext: Read next record sequentially. */
-/***********************************************************************/
-RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
- {
- RCODE rc;
-
- if (!tdbp)
- return RC_FX;
- else if (((PTDBASE)tdbp)->GetKindex()) {
- // Reading sequencially an indexed table. This happens after the
- // handler function records_in_range was called and MySQL decides
- // to quit using the index (!!!) Drop the index.
- for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
- colp->SetKcol(NULL);
-
- ((PTDBASE)tdbp)->SetKindex(NULL);
- } // endif index
-
- // Save stack and allocation environment and prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return RC_FX;
- } // endif jump_level
-
- if ((setjmp(g->jumper[++g->jump_level])) != 0) {
- rc= RC_FX;
- goto err;
- } // endif rc
-
- while ((rc= (RCODE)tdbp->ReadDB(g)) == RC_NF) ;
-
- err:
- g->jump_level--;
- return (rc != RC_OK) ? rc : EvalColumns(g, tdbp);
- } // end of CntReadNext
-
-/***********************************************************************/
-/* WriteRow: Insert a new row into a table. */
-/***********************************************************************/
-RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
- {
- RCODE rc;
- PCOL colp;
- PTDBASE tp= (PTDBASE)tdbp;
-
- if (!tdbp)
- return RC_FX;
-
- // Save stack and allocation environment and prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return RC_FX;
- } // endif jump_level
-
- if (setjmp(g->jumper[++g->jump_level]) != 0) {
- printf("%s\n", g->Message);
- rc= RC_FX;
- goto err;
- } // endif rc
-
- // Store column values in table write buffer(s)
- for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
- if (!colp->GetColUse(U_VIRTUAL))
- colp->WriteColumn(g);
-
-// if (tdbp->GetMode() == MODE_INSERT)
-// tbxp->SetModified(true);
-
- // Return result code from write operation
- rc= (RCODE)tdbp->WriteDB(g);
-
- err:
- g->jump_level--;
- return rc;
- } // end of CntWriteRow
-
-/***********************************************************************/
-/* UpdateRow: Update a row into a table. */
-/***********************************************************************/
-RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
- {
- if (!tdbp || tdbp->GetMode() != MODE_UPDATE)
- return RC_FX;
-
- // Return result code from write operation
- return CntWriteRow(g, tdbp);
- } // end of CntUpdateRow
-
-/***********************************************************************/
-/* DeleteRow: Delete a row from a table. */
-/***********************************************************************/
-RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
- {
- RCODE rc;
-
- if (!tdbp || tdbp->GetMode() != MODE_DELETE)
- return RC_FX;
- else if (tdbp->IsReadOnly())
- return RC_NF;
-
- if (((PTDBASE)tdbp)->GetDef()->Indexable() && all)
- ((PTDBDOS)tdbp)->Cardinal= 0;
-
- // Return result code from delete operation
- // Note: if all, this call will be done when closing the table
- rc= (RCODE)tdbp->DeleteDB(g, (all) ? RC_FX : RC_OK);
- return rc;
- } // end of CntDeleteRow
-
-/***********************************************************************/
-/* CLOSETAB: Close a table. */
-/***********************************************************************/
-int CntCloseTable(PGLOBAL g, PTDB tdbp)
- {
- int rc= RC_OK;
- TDBDOX *tbxp= NULL;
-
- if (!tdbp || tdbp->GetUse() != USE_OPEN)
- return rc; // Nothing to do
-
- if (xtrace)
- printf("CntCloseTable: tdbp=%p mode=%d\n", tdbp, tdbp->GetMode());
-
- if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN)
- rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
-
- // Prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- rc= RC_FX;
- goto err;
- } // endif
-
- if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
- g->jump_level--;
- goto err;
- } // endif
-
- // This will close the table file(s) and also finalize write
- // operations such as Insert, Update, or Delete.
- tdbp->CloseDB(g);
-
- g->jump_level--;
-
- if (xtrace > 1)
- printf("Table %s closed\n", tdbp->GetName());
-
-//if (!((PTDBDOX)tdbp)->GetModified())
-// return 0;
-
- if (tdbp->GetMode() == MODE_READ || tdbp->GetMode() == MODE_ANY)
- return 0;
-
- if (xtrace > 1)
- printf("About to reset opt\n");
-
- // Make all the eventual indexes
- tbxp= (TDBDOX*)tdbp;
- tbxp->SetKindex(NULL);
- tbxp->To_Key_Col= NULL;
- rc= tbxp->ResetTableOpt(g, false, ((PTDBASE)tdbp)->GetDef()->Indexable());
-
- err:
- if (xtrace > 1)
- printf("Done rc=%d\n", rc);
-
- return (rc == RC_OK || rc == RC_INFO) ? 0 : rc;
- } // end of CntCloseTable
-
-/***********************************************************************/
-/* Load and initialize the use of an index. */
-/* This is the condition(s) for doing indexing. */
-/* Note: FIX table are not reset here to Nrec= 1. */
-/***********************************************************************/
-int CntIndexInit(PGLOBAL g, PTDB ptdb, int id)
- {
- int k;
- PCOL colp;
- PVAL valp;
- PKXBASE xp;
- PXLOAD pxp;
- PIXDEF xdp;
- XKPDEF *kdp;
- PTDBDOX tdbp;
- PCOLDEF cdp;
- DOXDEF *dfp;
-
- if (!ptdb)
- return -1;
- else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
- sprintf(g->Message, "CntIndexInit: Table %s is not indexable", ptdb->GetName());
- return 0;
- } else
- tdbp= (PTDBDOX)ptdb;
-
- dfp= (DOXDEF*)tdbp->To_Def;
-
-//if (!(k= colp->GetKey()))
-// if (colp->GetOpt() >= 2) {
-// strcpy(g->Message, "Not a valid indexed column");
-// return -1;
-// } else
- // This is a pseudo indexed sorted block optimized column
-// return 0;
-
- if (tdbp->To_Kindex)
- if (((XXBASE*)tdbp->To_Kindex)->GetID() == id) {
- tdbp->To_Kindex->Reset(); // Same index
- return (tdbp->To_Kindex->IsMul()) ? 2 : 1;
- } else {
- tdbp->To_Kindex->Close();
- tdbp->To_Kindex= NULL;
- } // endif colp
-
- for (xdp= dfp->To_Indx; xdp; xdp= xdp->GetNext())
- if (xdp->GetID() == id)
- break;
-
- if (!xdp) {
- sprintf(g->Message, "Wrong index ID %d", id);
- return 0;
- } // endif xdp
-
- // Allocate the key columns definition block
- tdbp->Knum= xdp->GetNparts();
- tdbp->To_Key_Col= (PCOL*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PCOL));
-
- // Get the key column description list
- for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts(); kdp; kdp= (XKPDEF*)kdp->Next)
- if (!(colp= tdbp->ColDB(g, kdp->Name, 0)) || colp->InitValue(g)) {
- sprintf(g->Message, "Wrong column %s", kdp->Name);
- return 0;
- } else
- tdbp->To_Key_Col[k++]= colp;
-
-#if defined(_DEBUG)
- if (k != tdbp->Knum) {
- sprintf(g->Message, "Key part number mismatch for %s",
- xdp->GetName());
- return 0;
- } // endif k
-#endif // _DEBUG
-
- // Allocate the pseudo constants that will contain the key values
- tdbp->To_Link= (PXOB*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PXOB));
-
- for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts();
- kdp; k++, kdp= (XKPDEF*)kdp->Next) {
- cdp= tdbp->Key(k)->GetCdp();
- valp= AllocateValue(g, cdp->GetType(), cdp->GetLength());
- tdbp->To_Link[k]= new(g) CONSTANT(valp);
- } // endfor k
-
- // Make the index on xdp
- if (!xdp->IsAuto()) {
- if (dfp->Huge)
- pxp= new(g) XHUGE;
- else
- pxp= new(g) XFILE;
-
- if (tdbp->Knum == 1) // Single index
- xp= new(g) XINDXS(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link);
- else // Multi-Column index
- xp= new(g) XINDEX(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link);
-
- } else // Column contains same values as ROWID
- xp= new(g) XXROW(tdbp);
-
- if (xp->Init(g))
- return 0;
-
- tdbp->To_Kindex= xp;
- return (xp->IsMul()) ? 2 : 1;
- } // end of CntIndexInit
-
-/***********************************************************************/
-/* IndexRead: fetch a record having the index value. */
-/***********************************************************************/
-RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
- const void *key, int len, bool mrr)
- {
- char *kp= (char*)key;
- int n;
- short lg;
- bool rcb;
- RCODE rc;
- PVAL valp;
- PCOL colp;
- XXBASE *xbp;
- PTDBDOX tdbp;
-
- if (!ptdb)
- return RC_FX;
- if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
- sprintf(g->Message, "CntIndexRead: Table %s is not indexable", ptdb->GetName());
- return RC_FX;
- } else
- tdbp= (PTDBDOX)ptdb;
-
- // Set reference values and index operator
- if (!tdbp->To_Link || !tdbp->To_Kindex) {
- sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
- return RC_FX;
- } else
- xbp= (XXBASE*)tdbp->To_Kindex;
-
- if (key) {
- for (n= 0; n < tdbp->Knum; n++) {
- colp= (PCOL)tdbp->To_Key_Col[n];
-
- if (colp->GetColUse(U_NULLS))
- kp++; // Skip null byte
-
- valp= tdbp->To_Link[n]->GetValue();
-
- if (!valp->IsTypeNum()) {
- if (colp->GetColUse(U_VAR)) {
- lg= *(short*)kp;
- kp+= sizeof(short);
- rcb= valp->SetValue_char(kp, (int)lg);
- } else
- rcb= valp->SetValue_char(kp, valp->GetClen());
-
- if (rcb) {
- if (tdbp->RowNumber(g))
- sprintf(g->Message, "Out of range value for column %s at row %d",
- colp->GetName(), tdbp->RowNumber(g));
- else
- sprintf(g->Message, "Out of range value for column %s",
- colp->GetName());
-
- PushWarning(g, tdbp);
- } // endif b
-
- } else
- valp->SetBinValue((void*)kp);
-
- kp+= valp->GetClen();
-
- if (len == kp - (char*)key) {
- n++;
- break;
- } else if (len < kp - (char*)key) {
- strcpy(g->Message, "Key buffer is too small");
- return RC_FX;
- } // endif len
-
- } // endfor n
-
- xbp->SetNval(n);
- } // endif key
-
- xbp->SetOp(op);
- xbp->SetNth(0);
-
- if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK)
- rc= EvalColumns(g, tdbp, mrr);
-
- return rc;
- } // end of CntIndexRead
-
-/***********************************************************************/
-/* Return the number of rows matching given values. */
-/***********************************************************************/
-int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
- bool *incl, key_part_map *kmap)
- {
- const uchar *p, *kp;
- int i, n, k[2];
- short lg;
- bool b, rcb;
- PVAL valp;
- PCOL colp;
- PTDBDOX tdbp;
- XXBASE *xbp;
-
- if (!ptdb)
- return -1;
- else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
- sprintf(g->Message, "CntIndexRange: Table %s is not indexable", ptdb->GetName());
- DBUG_PRINT("Range", ("%s", g->Message));
- return -1;
- } else
- tdbp= (PTDBDOX)ptdb;
-
- if (!tdbp->To_Link || !tdbp->To_Kindex) {
- sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
- DBUG_PRINT("Range", ("%s", g->Message));
- return -1;
- } else
- xbp= (XXBASE*)tdbp->To_Kindex;
-
- for (b= false, i= 0; i < 2; i++) {
- p= kp= key[i];
-
- if (kp) {
- for (n= 0; n < tdbp->Knum; n++) {
- if (kmap[i] & (key_part_map)(1 << n)) {
- if (b == true)
- // Cannot do indexing with missing intermediate key
- return -1;
-
- colp= (PCOL)tdbp->To_Key_Col[n];
-
- if (colp->GetColUse(U_NULLS))
- p++; // Skip null byte ???
-
- valp= tdbp->To_Link[n]->GetValue();
-
- if (!valp->IsTypeNum()) {
- if (colp->GetColUse(U_VAR)) {
- lg= *(short*)p;
- p+= sizeof(short);
- rcb= valp->SetValue_char((char*)p, (int)lg);
- } else
- rcb= valp->SetValue_char((char*)p, valp->GetClen());
-
- if (rcb) {
- if (tdbp->RowNumber(g))
- sprintf(g->Message,
- "Out of range value for column %s at row %d",
- colp->GetName(), tdbp->RowNumber(g));
- else
- sprintf(g->Message, "Out of range value for column %s",
- colp->GetName());
-
- PushWarning(g, tdbp);
- } // endif b
-
- } else
- valp->SetBinValue((void*)p);
-
- if (xtrace) {
- char bf[32];
- printf("i=%d n=%d key=%s\n", i, n, valp->GetCharString(bf));
- } // endif xtrace
-
- p+= valp->GetClen();
-
- if (len[i] == (unsigned)(p - kp)) {
- n++;
- break;
- } else if (len[i] < (unsigned)(p - kp)) {
- strcpy(g->Message, "Key buffer is too small");
- return -1;
- } // endif len
-
- } else
- b= true;
-
- } // endfor n
-
- xbp->SetNval(n);
-
- if (xtrace)
- printf("xbp=%p Nval=%d i=%d incl=%d\n", xbp, n, i, incl[i]);
-
- k[i]= xbp->Range(g, i + 1, incl[i]);
- } else
- k[i]= (i) ? xbp->GetNum_K() : 0;
-
- } // endfor i
-
- if (xtrace)
- printf("k1=%d k0=%d\n", k[1], k[0]);
-
- return k[1] - k[0];
- } // end of CntIndexRange
+/* Copyright (C) Olivier Bertrand 2004 - 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/***********************************************************************/
+/* Author Olivier BERTRAND bertrandop@gmail.com 2004-2012 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the CONNECT general purpose semantic routines. */
+/***********************************************************************/
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+/***********************************************************************/
+/* Include application header files */
+/* */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/***********************************************************************/
+#define DONT_DEFINE_VOID
+#include "handler.h"
+#undef OFFSET
+
+#include "global.h"
+#include "plgdbsem.h"
+#include "xobject.h"
+#include "connect.h"
+#include "tabcol.h"
+#include "catalog.h"
+#include "ha_connect.h"
+#include "mycat.h"
+
+#define my_strupr(p) my_caseup_str(default_charset_info, (p));
+#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
+#define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b))
+
+/***********************************************************************/
+/* DB static variables. */
+/***********************************************************************/
+extern int xtrace;
+
+/***********************************************************************/
+/* Routines called internally by semantic routines. */
+/***********************************************************************/
+void CntEndDB(PGLOBAL);
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr= false);
+
+/***********************************************************************/
+/* MySQL routines called externally by semantic routines. */
+/***********************************************************************/
+int rename_file_ext(const char *from, const char *to,const char *ext);
+
+/***********************************************************************/
+/* CntExit: CONNECT termination routine. */
+/***********************************************************************/
+PGLOBAL CntExit(PGLOBAL g)
+ {
+ if (g) {
+ CntEndDB(g);
+
+ if (g->Activityp)
+ delete g->Activityp;
+
+ PlugExit(g);
+ g= NULL;
+ } // endif g
+
+ return g;
+ } // end of CntExit
+
+/***********************************************************************/
+/* CntEndDB: DB termination semantic routine. */
+/***********************************************************************/
+void CntEndDB(PGLOBAL g)
+ {
+ PDBUSER dbuserp= PlgGetUser(g);
+
+ if (dbuserp) {
+ if (dbuserp->Catalog)
+ delete dbuserp->Catalog;
+
+ free(dbuserp);
+ } // endif dbuserp
+
+ } // end of CntEndDB
+
+/***********************************************************************/
+/* CntCheckDB: Initialize a DB application session. */
+/* Note: because MySQL does not call a storage handler when a user */
+/* executes a use db command, a check must be done before an SQL */
+/* command is executed to check whether we are still working on the */
+/* current database, and if not to load the newly used database. */
+/***********************************************************************/
+bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
+ {
+ bool rc= false;
+ PDBUSER dbuserp= PlgGetUser(g);
+
+ if (xtrace) {
+ printf("CntCheckDB: dbuserp=%p\n", dbuserp);
+ } // endif xtrace
+
+ if (!dbuserp || !handler)
+ return true;
+
+ if (xtrace)
+ printf("cat=%p oldhandler=%p newhandler=%p\n", dbuserp->Catalog,
+ (dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL,
+ handler);
+
+ if (dbuserp->Catalog) {
+// ((MYCAT *)dbuserp->Catalog)->SetHandler(handler); done later
+ ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
+ return false; // Nothing else to do
+ } // endif Catalog
+
+ // Copy new database name in dbuser block
+ strncpy(dbuserp->Name, "???", sizeof(dbuserp->Name) - 1);
+
+ dbuserp->Vtdbno= 0; // Init of TDB numbers
+
+ /*********************************************************************/
+ /* Now allocate and initialize the Database Catalog. */
+ /*********************************************************************/
+ dbuserp->Step= MSG(READY);
+
+ if (!(dbuserp->Catalog= new MYCAT(handler)))
+ return true;
+
+ ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
+ dbuserp->UseTemp= TMP_YES; // Must use temporary file
+
+ /*********************************************************************/
+ /* All is correct. */
+ /*********************************************************************/
+ sprintf(g->Message, MSG(DATABASE_LOADED), "???");
+
+ if (xtrace)
+ printf("msg=%s\n", g->Message);
+
+ return rc;
+ } // end of CntCheckDB
+
+/***********************************************************************/
+/* CntInfo: Get table info. */
+/* Returns valid: true if this is a table info. */
+/***********************************************************************/
+bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
+ {
+ bool b;
+ PTDBDOS tdbp= (PTDBDOS)tp;
+
+ if (tdbp) {
+ b= tdbp->GetFtype() != RECFM_NAF;
+ info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0;
+ info->records= (unsigned)tdbp->GetMaxSize(g);
+// info->mean_rec_length= tdbp->GetLrecl();
+ info->mean_rec_length= 0;
+ info->data_file_name= (b) ? tdbp->GetFile(g) : NULL;
+ return true;
+ } else {
+ info->data_file_length= 0;
+ info->records= 0;
+ info->mean_rec_length= 0;
+ info->data_file_name= NULL;
+ return false;
+ } // endif tdbp
+
+ } // end of CntInfo
+
+/***********************************************************************/
+/* GetTDB: Get the table description block of a CONNECT table. */
+/***********************************************************************/
+PTDB CntGetTDB(PGLOBAL g, LPCSTR name, MODE mode, PHC h)
+ {
+ int rc;
+ PTDB tdbp;
+ PTABLE tabp;
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL; // Safe over longjmp
+
+ if (xtrace)
+ printf("CntGetTDB: name=%s mode=%d cat=%p\n", name, mode, cat);
+
+ if (!cat)
+ return NULL;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return NULL;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ tdbp= NULL;
+ goto err;
+ } // endif rc
+
+ // Get table object from the catalog
+ tabp= new(g) XTAB(name);
+
+ if (xtrace)
+ printf("CntGetTDB: tabp=%p\n", tabp);
+
+ // Perhaps this should be made thread safe
+ ((MYCAT*)cat)->SetHandler(h);
+
+ if (!(tdbp= cat->GetTable(g, tabp, mode)))
+ printf("CntGetTDB: %s\n", g->Message);
+
+ err:
+ if (xtrace)
+ printf("Returning tdbp=%p mode=%d\n", tdbp, mode);
+
+ g->jump_level--;
+ return tdbp;
+ } // end of CntGetTDB
+
+/***********************************************************************/
+/* OPENTAB: Open a Table. */
+/***********************************************************************/
+bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
+ bool del, PHC h)
+ {
+ char *p;
+ int i, n, rc;
+ bool rcop= true;
+ PCOL colp;
+//PCOLUMN cp;
+ PDBUSER dup= PlgGetUser(g);
+
+ if (xtrace)
+ printf("CntOpenTable: tdbp=%p mode=%d\n", tdbp, mode);
+
+ if (!tdbp) {
+ strcpy(g->Message, "Null tdbp");
+ printf("CntOpenTable: %s\n", g->Message);
+ return true;
+ } // endif tdbp
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return true;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ goto err;
+ } // endif rc
+
+ if (!c1) {
+ if (mode == MODE_INSERT)
+ // Allocate all column blocks for that table
+ tdbp->ColDB(g, NULL, 0);
+
+ } else for (p= c1; *p; p+= n) {
+ // Allocate only used column blocks
+ if (xtrace)
+ printf("Allocating column %s\n", p);
+
+// if (*p == '*') {
+// // This is a special column
+// cp= new(g) COLUMN(p + 1);
+// cp->SetTo_Table(tdbp->GetTable());
+// colp= ((PTDBASE)tdbp)->InsertSpcBlk(g, cp);
+// } else
+ colp= tdbp->ColDB(g, p, 0);
+
+ if (!colp) {
+ sprintf(g->Message, "Column %s not found in %s", p, tdbp->GetName());
+ goto err;
+ } // endif colp
+
+ n= strlen(p) + 1;
+ } // endfor p
+
+ for (i= 0, colp= tdbp->GetColumns(); colp; i++, colp= colp->GetNext()) {
+ if (colp->InitValue(g))
+ goto err;
+
+ if (mode == MODE_INSERT)
+ // Allow type conversion
+ if (colp->SetBuffer(g, colp->GetValue(), true, false))
+ goto err;
+
+ colp->AddColUse(U_P); // For PLG tables
+ } // endfor colp
+
+ /*********************************************************************/
+ /* In Update mode, the updated column blocks must be distinct from */
+ /* the read column blocks. So make a copy of the TDB and allocate */
+ /* its column blocks in mode write (required by XML tables). */
+ /*********************************************************************/
+ if (mode == MODE_UPDATE) {
+ PTDBASE utp;
+
+ if (!(utp= (PTDBASE)tdbp->Duplicate(g))) {
+ sprintf(g->Message, MSG(INV_UPDT_TABLE), tdbp->GetName());
+ goto err;
+ } // endif tp
+
+ if (!c2)
+ // Allocate all column blocks for that table
+ utp->ColDB(g, NULL, 0);
+ else for (p= c2; *p; p+= n) {
+ // Allocate only used column blocks
+ colp= utp->ColDB(g, p, 0);
+ n= strlen(p) + 1;
+ } // endfor p
+
+ for (i= 0, colp= utp->GetColumns(); colp; i++, colp= colp->GetNext()) {
+ if (colp->InitValue(g))
+ goto err;
+
+ if (colp->SetBuffer(g, colp->GetValue(), true, false))
+ goto err;
+
+ } // endfor colp
+
+ // Attach the updated columns list to the main table
+ ((PTDBASE)tdbp)->SetSetCols(utp->GetColumns());
+ } else if (tdbp && mode == MODE_INSERT)
+ ((PTDBASE)tdbp)->SetSetCols(tdbp->GetColumns());
+
+ // Now do open the physical table
+ if (xtrace)
+ printf("Opening table %s in mode %d tdbp=%p\n",
+ tdbp->GetName(), mode, tdbp);
+
+//tdbp->SetMode(mode);
+
+ if (del && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) {
+ // To avoid erasing the table when doing a partial delete
+ // make a fake Next
+ PDOSDEF ddp= new(g) DOSDEF;
+ PTDB tp= new(g) TDBDOS(ddp, NULL);
+ tdbp->SetNext(tp);
+ dup->Check &= ~CHK_DELETE;
+ } // endif del
+
+
+ if (xtrace)
+ printf("About to open the table: tdbp=%p\n", tdbp);
+
+ if (mode != MODE_ANY && mode != MODE_ALTER) {
+ if (tdbp->OpenDB(g)) {
+ printf("%s\n", g->Message);
+ goto err;
+ } else
+ tdbp->SetNext(NULL);
+
+ } // endif mode
+
+ rcop= false;
+
+ err:
+ g->jump_level--;
+ return rcop;
+ } // end of CntOpenTable
+
+/***********************************************************************/
+/* Rewind a table by reopening it. */
+/***********************************************************************/
+bool CntRewindTable(PGLOBAL g, PTDB tdbp)
+{
+ if (!tdbp)
+ return true;
+
+ tdbp->OpenDB(g);
+ return false;
+} // end of CntRewindTable
+
+/***********************************************************************/
+/* Evaluate all columns after a record is read. */
+/***********************************************************************/
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr)
+ {
+ RCODE rc= RC_OK;
+ PCOL colp;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ if (xtrace) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ printf("EvalColumns: %s\n", g->Message);
+ } // endif
+
+ return RC_FX;
+ } // endif jump_level
+
+ if (setjmp(g->jumper[++g->jump_level]) != 0) {
+ if (xtrace)
+ printf("Error reading columns: %s\n", g->Message);
+
+ rc= RC_FX;
+ goto err;
+ } // endif rc
+
+ for (colp= tdbp->GetColumns(); rc == RC_OK && colp;
+ colp= colp->GetNext()) {
+ colp->Reset();
+
+ // Virtual columns are computed by MariaDB
+ if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
+ if (colp->Eval(g))
+ rc= RC_FX;
+
+ } // endfor colp
+
+ err:
+ g->jump_level--;
+ return rc;
+ } // end of EvalColumns
+
+/***********************************************************************/
+/* ReadNext: Read next record sequentially. */
+/***********************************************************************/
+RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
+ {
+ RCODE rc;
+
+ if (!tdbp)
+ return RC_FX;
+ else if (((PTDBASE)tdbp)->GetKindex()) {
+ // Reading sequencially an indexed table. This happens after the
+ // handler function records_in_range was called and MySQL decides
+ // to quit using the index (!!!) Drop the index.
+ for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
+ colp->SetKcol(NULL);
+
+ ((PTDBASE)tdbp)->SetKindex(NULL);
+ } // endif index
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return RC_FX;
+ } // endif jump_level
+
+ if ((setjmp(g->jumper[++g->jump_level])) != 0) {
+ rc= RC_FX;
+ goto err;
+ } // endif rc
+
+ while ((rc= (RCODE)tdbp->ReadDB(g)) == RC_NF) ;
+
+ err:
+ g->jump_level--;
+ return (rc != RC_OK) ? rc : EvalColumns(g, tdbp);
+ } // end of CntReadNext
+
+/***********************************************************************/
+/* WriteRow: Insert a new row into a table. */
+/***********************************************************************/
+RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
+ {
+ RCODE rc;
+ PCOL colp;
+ PTDBASE tp= (PTDBASE)tdbp;
+
+ if (!tdbp)
+ return RC_FX;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return RC_FX;
+ } // endif jump_level
+
+ if (setjmp(g->jumper[++g->jump_level]) != 0) {
+ printf("%s\n", g->Message);
+ rc= RC_FX;
+ goto err;
+ } // endif rc
+
+ // Store column values in table write buffer(s)
+ for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
+ if (!colp->GetColUse(U_VIRTUAL))
+ colp->WriteColumn(g);
+
+// if (tdbp->GetMode() == MODE_INSERT)
+// tbxp->SetModified(true);
+
+ // Return result code from write operation
+ rc= (RCODE)tdbp->WriteDB(g);
+
+ err:
+ g->jump_level--;
+ return rc;
+ } // end of CntWriteRow
+
+/***********************************************************************/
+/* UpdateRow: Update a row into a table. */
+/***********************************************************************/
+RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
+ {
+ if (!tdbp || tdbp->GetMode() != MODE_UPDATE)
+ return RC_FX;
+
+ // Return result code from write operation
+ return CntWriteRow(g, tdbp);
+ } // end of CntUpdateRow
+
+/***********************************************************************/
+/* DeleteRow: Delete a row from a table. */
+/***********************************************************************/
+RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
+ {
+ RCODE rc;
+
+ if (!tdbp || tdbp->GetMode() != MODE_DELETE)
+ return RC_FX;
+ else if (tdbp->IsReadOnly())
+ return RC_NF;
+
+ if (((PTDBASE)tdbp)->GetDef()->Indexable() && all)
+ ((PTDBDOS)tdbp)->Cardinal= 0;
+
+ // Return result code from delete operation
+ // Note: if all, this call will be done when closing the table
+ rc= (RCODE)tdbp->DeleteDB(g, (all) ? RC_FX : RC_OK);
+ return rc;
+ } // end of CntDeleteRow
+
+/***********************************************************************/
+/* CLOSETAB: Close a table. */
+/***********************************************************************/
+int CntCloseTable(PGLOBAL g, PTDB tdbp)
+ {
+ int rc= RC_OK;
+ TDBDOX *tbxp= NULL;
+
+ if (!tdbp || tdbp->GetUse() != USE_OPEN)
+ return rc; // Nothing to do
+
+ if (xtrace)
+ printf("CntCloseTable: tdbp=%p mode=%d\n", tdbp, tdbp->GetMode());
+
+ if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN)
+ rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
+
+ // Prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ rc= RC_FX;
+ goto err;
+ } // endif
+
+ if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
+ g->jump_level--;
+ goto err;
+ } // endif
+
+ // This will close the table file(s) and also finalize write
+ // operations such as Insert, Update, or Delete.
+ tdbp->CloseDB(g);
+
+ g->jump_level--;
+
+ if (xtrace > 1)
+ printf("Table %s closed\n", tdbp->GetName());
+
+//if (!((PTDBDOX)tdbp)->GetModified())
+// return 0;
+
+ if (tdbp->GetMode() == MODE_READ || tdbp->GetMode() == MODE_ANY)
+ return 0;
+
+ if (xtrace > 1)
+ printf("About to reset opt\n");
+
+ // Make all the eventual indexes
+ tbxp= (TDBDOX*)tdbp;
+ tbxp->SetKindex(NULL);
+ tbxp->To_Key_Col= NULL;
+ rc= tbxp->ResetTableOpt(g, false, ((PTDBASE)tdbp)->GetDef()->Indexable());
+
+ err:
+ if (xtrace > 1)
+ printf("Done rc=%d\n", rc);
+
+ return (rc == RC_OK || rc == RC_INFO) ? 0 : rc;
+ } // end of CntCloseTable
+
+/***********************************************************************/
+/* Load and initialize the use of an index. */
+/* This is the condition(s) for doing indexing. */
+/* Note: FIX table are not reset here to Nrec= 1. */
+/***********************************************************************/
+int CntIndexInit(PGLOBAL g, PTDB ptdb, int id)
+ {
+ int k;
+ PCOL colp;
+ PVAL valp;
+ PKXBASE xp;
+ PXLOAD pxp;
+ PIXDEF xdp;
+ XKPDEF *kdp;
+ PTDBDOX tdbp;
+ PCOLDEF cdp;
+ DOXDEF *dfp;
+
+ if (!ptdb)
+ return -1;
+ else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
+ sprintf(g->Message, "CntIndexInit: Table %s is not indexable", ptdb->GetName());
+ return 0;
+ } else
+ tdbp= (PTDBDOX)ptdb;
+
+ dfp= (DOXDEF*)tdbp->To_Def;
+
+//if (!(k= colp->GetKey()))
+// if (colp->GetOpt() >= 2) {
+// strcpy(g->Message, "Not a valid indexed column");
+// return -1;
+// } else
+ // This is a pseudo indexed sorted block optimized column
+// return 0;
+
+ if (tdbp->To_Kindex)
+ if (((XXBASE*)tdbp->To_Kindex)->GetID() == id) {
+ tdbp->To_Kindex->Reset(); // Same index
+ return (tdbp->To_Kindex->IsMul()) ? 2 : 1;
+ } else {
+ tdbp->To_Kindex->Close();
+ tdbp->To_Kindex= NULL;
+ } // endif colp
+
+ for (xdp= dfp->To_Indx; xdp; xdp= xdp->GetNext())
+ if (xdp->GetID() == id)
+ break;
+
+ if (!xdp) {
+ sprintf(g->Message, "Wrong index ID %d", id);
+ return 0;
+ } // endif xdp
+
+ // Allocate the key columns definition block
+ tdbp->Knum= xdp->GetNparts();
+ tdbp->To_Key_Col= (PCOL*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PCOL));
+
+ // Get the key column description list
+ for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts(); kdp; kdp= (XKPDEF*)kdp->Next)
+ if (!(colp= tdbp->ColDB(g, kdp->Name, 0)) || colp->InitValue(g)) {
+ sprintf(g->Message, "Wrong column %s", kdp->Name);
+ return 0;
+ } else
+ tdbp->To_Key_Col[k++]= colp;
+
+#if defined(_DEBUG)
+ if (k != tdbp->Knum) {
+ sprintf(g->Message, "Key part number mismatch for %s",
+ xdp->GetName());
+ return 0;
+ } // endif k
+#endif // _DEBUG
+
+ // Allocate the pseudo constants that will contain the key values
+ tdbp->To_Link= (PXOB*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PXOB));
+
+ for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts();
+ kdp; k++, kdp= (XKPDEF*)kdp->Next) {
+ cdp= tdbp->Key(k)->GetCdp();
+ valp= AllocateValue(g, cdp->GetType(), cdp->GetLength());
+ tdbp->To_Link[k]= new(g) CONSTANT(valp);
+ } // endfor k
+
+ // Make the index on xdp
+ if (!xdp->IsAuto()) {
+ if (dfp->Huge)
+ pxp= new(g) XHUGE;
+ else
+ pxp= new(g) XFILE;
+
+ if (tdbp->Knum == 1) // Single index
+ xp= new(g) XINDXS(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link);
+ else // Multi-Column index
+ xp= new(g) XINDEX(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link);
+
+ } else // Column contains same values as ROWID
+ xp= new(g) XXROW(tdbp);
+
+ if (xp->Init(g))
+ return 0;
+
+ tdbp->To_Kindex= xp;
+ return (xp->IsMul()) ? 2 : 1;
+ } // end of CntIndexInit
+
+/***********************************************************************/
+/* IndexRead: fetch a record having the index value. */
+/***********************************************************************/
+RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
+ const void *key, int len, bool mrr)
+ {
+ char *kp= (char*)key;
+ int n;
+ short lg;
+ bool rcb;
+ RCODE rc;
+ PVAL valp;
+ PCOL colp;
+ XXBASE *xbp;
+ PTDBDOX tdbp;
+
+ if (!ptdb)
+ return RC_FX;
+ if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
+ sprintf(g->Message, "CntIndexRead: Table %s is not indexable", ptdb->GetName());
+ return RC_FX;
+ } else
+ tdbp= (PTDBDOX)ptdb;
+
+ // Set reference values and index operator
+ if (!tdbp->To_Link || !tdbp->To_Kindex) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
+ return RC_FX;
+ } else
+ xbp= (XXBASE*)tdbp->To_Kindex;
+
+ if (key) {
+ for (n= 0; n < tdbp->Knum; n++) {
+ colp= (PCOL)tdbp->To_Key_Col[n];
+
+ if (colp->GetColUse(U_NULLS))
+ kp++; // Skip null byte
+
+ valp= tdbp->To_Link[n]->GetValue();
+
+ if (!valp->IsTypeNum()) {
+ if (colp->GetColUse(U_VAR)) {
+ lg= *(short*)kp;
+ kp+= sizeof(short);
+ rcb= valp->SetValue_char(kp, (int)lg);
+ } else
+ rcb= valp->SetValue_char(kp, valp->GetClen());
+
+ if (rcb) {
+ if (tdbp->RowNumber(g))
+ sprintf(g->Message, "Out of range value for column %s at row %d",
+ colp->GetName(), tdbp->RowNumber(g));
+ else
+ sprintf(g->Message, "Out of range value for column %s",
+ colp->GetName());
+
+ PushWarning(g, tdbp);
+ } // endif b
+
+ } else
+ valp->SetBinValue((void*)kp);
+
+ kp+= valp->GetClen();
+
+ if (len == kp - (char*)key) {
+ n++;
+ break;
+ } else if (len < kp - (char*)key) {
+ strcpy(g->Message, "Key buffer is too small");
+ return RC_FX;
+ } // endif len
+
+ } // endfor n
+
+ xbp->SetNval(n);
+ } // endif key
+
+ xbp->SetOp(op);
+ xbp->SetNth(0);
+
+ if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK)
+ rc= EvalColumns(g, tdbp, mrr);
+
+ return rc;
+ } // end of CntIndexRead
+
+/***********************************************************************/
+/* Return the number of rows matching given values. */
+/***********************************************************************/
+int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
+ bool *incl, key_part_map *kmap)
+ {
+ const uchar *p, *kp;
+ int i, n, k[2];
+ short lg;
+ bool b, rcb;
+ PVAL valp;
+ PCOL colp;
+ PTDBDOX tdbp;
+ XXBASE *xbp;
+
+ if (!ptdb)
+ return -1;
+ else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
+ sprintf(g->Message, "CntIndexRange: Table %s is not indexable", ptdb->GetName());
+ DBUG_PRINT("Range", ("%s", g->Message));
+ return -1;
+ } else
+ tdbp= (PTDBDOX)ptdb;
+
+ if (!tdbp->To_Link || !tdbp->To_Kindex) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
+ DBUG_PRINT("Range", ("%s", g->Message));
+ return -1;
+ } else
+ xbp= (XXBASE*)tdbp->To_Kindex;
+
+ for (b= false, i= 0; i < 2; i++) {
+ p= kp= key[i];
+
+ if (kp) {
+ for (n= 0; n < tdbp->Knum; n++) {
+ if (kmap[i] & (key_part_map)(1 << n)) {
+ if (b == true)
+ // Cannot do indexing with missing intermediate key
+ return -1;
+
+ colp= (PCOL)tdbp->To_Key_Col[n];
+
+ if (colp->GetColUse(U_NULLS))
+ p++; // Skip null byte ???
+
+ valp= tdbp->To_Link[n]->GetValue();
+
+ if (!valp->IsTypeNum()) {
+ if (colp->GetColUse(U_VAR)) {
+ lg= *(short*)p;
+ p+= sizeof(short);
+ rcb= valp->SetValue_char((char*)p, (int)lg);
+ } else
+ rcb= valp->SetValue_char((char*)p, valp->GetClen());
+
+ if (rcb) {
+ if (tdbp->RowNumber(g))
+ sprintf(g->Message,
+ "Out of range value for column %s at row %d",
+ colp->GetName(), tdbp->RowNumber(g));
+ else
+ sprintf(g->Message, "Out of range value for column %s",
+ colp->GetName());
+
+ PushWarning(g, tdbp);
+ } // endif b
+
+ } else
+ valp->SetBinValue((void*)p);
+
+ if (xtrace) {
+ char bf[32];
+ printf("i=%d n=%d key=%s\n", i, n, valp->GetCharString(bf));
+ } // endif xtrace
+
+ p+= valp->GetClen();
+
+ if (len[i] == (unsigned)(p - kp)) {
+ n++;
+ break;
+ } else if (len[i] < (unsigned)(p - kp)) {
+ strcpy(g->Message, "Key buffer is too small");
+ return -1;
+ } // endif len
+
+ } else
+ b= true;
+
+ } // endfor n
+
+ xbp->SetNval(n);
+
+ if (xtrace)
+ printf("xbp=%p Nval=%d i=%d incl=%d\n", xbp, n, i, incl[i]);
+
+ k[i]= xbp->Range(g, i + 1, incl[i]);
+ } else
+ k[i]= (i) ? xbp->GetNum_K() : 0;
+
+ } // endfor i
+
+ if (xtrace)
+ printf("k1=%d k0=%d\n", k[1], k[0]);
+
+ return k[1] - k[0];
+ } // end of CntIndexRange
diff --git a/storage/connect/connect.h b/storage/connect/connect.h
index bf97d61b35f..be423fcb575 100644
--- a/storage/connect/connect.h
+++ b/storage/connect/connect.h
@@ -17,7 +17,6 @@
/* Name: CONNECT.H Version 2.4 */
/* This file contains the some based classes declares. */
/***********************************************************************/
-//#include "xtable.h" // Base class declares
#include "filamtxt.h"
#include "tabdos.h"
@@ -52,8 +51,6 @@ PGLOBAL CntExit(PGLOBAL g);
/* These classes purpose is chiefly to access protected items! */
/***********************************************************************/
class DOXDEF: public DOSDEF {
-//friend class TDBDOX;
-//friend int MakeIndex(PGLOBAL, PTDB, PIXDEF);
friend int CntIndexInit(PGLOBAL, PTDB, int);
}; // end of class DOXDEF
@@ -74,10 +71,7 @@ class TDBDOX: public TDBDOS {
class XKPDEF: public KPARTDEF {
friend class TDBDOX;
friend class ha_connect;
-//friend int CntMakeIndex(PGLOBAL, const char *, PIXDEF);
friend int CntIndexInit(PGLOBAL, PTDB, int);
public:
XKPDEF(const char *name, int n) : KPARTDEF((PSZ)name, n) {}
}; // end of class XKPDEF
-
-//RCODE CheckRecord(PGLOBAL g, PTDB tdbp, char *oldbuf, char *newbuf);
diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h
index b0bcc1478a5..118541d8eb0 100644
--- a/storage/connect/domdoc.h
+++ b/storage/connect/domdoc.h
@@ -20,7 +20,6 @@ typedef struct _xblock { /* Loaded XML file block */
short Type; /* TYPE_FB_XML */
int Retcode; /* Return code from Load */
MSXML2::IXMLDOMDocumentPtr Docp;/* Document interface pointer */
-//IXMLDOMNodeListPtr Nlist;
} XBLOCK, *PXBLOCK;
/******************************************************************/
@@ -123,9 +122,6 @@ class DOMATTR : public XMLATTRIBUTE {
friend class DOMDOC;
friend class DOMNODE;
public:
- // Properties
-//virtual char *GetText(void);
-
// Methods
virtual bool SetText(PGLOBAL g, char *txtp, int len);
diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp
index 64e9ed15f40..0c68a599496 100644
--- a/storage/connect/filamap.cpp
+++ b/storage/connect/filamap.cpp
@@ -1,749 +1,730 @@
-/*********** File AM Map C++ Program Source Code File (.CPP) ***********/
-/* PROGRAM NAME: FILAMAP */
-/* ------------- */
-/* Version 1.5 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the MAP file access method classes. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#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. */
-/* Note: these files are included inside the include files below. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "osutil.h"
-#include "maputil.h"
-#include "filamap.h"
-#include "tabdos.h"
-
-/* --------------------------- Class MAPFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-MAPFAM::MAPFAM(PDOSDEF tdp) : TXTFAM(tdp)
- {
- Memory = NULL;
- Mempos = NULL;
- Tpos = NULL;
- Fpos = NULL;
- Spos = NULL;
- Top = NULL;
- } // end of MAPFAM standard constructor
-
-MAPFAM::MAPFAM(PMAPFAM tmfp) : TXTFAM(tmfp)
- {
- Memory = tmfp->Memory;
- Mempos = tmfp->Mempos;
- Fpos = tmfp->Fpos;
- Spos = tmfp->Spos;
- Tpos = tmfp->Tpos;
- Top = tmfp->Top;
- } // end of MAPFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void MAPFAM::Reset(void)
- {
- TXTFAM::Reset();
- Fpos = Tpos = Spos = NULL;
- } // end of Reset
-
-/***********************************************************************/
-/* MAP GetFileLength: returns file size in number of bytes. */
-/***********************************************************************/
-int MAPFAM::GetFileLength(PGLOBAL g)
- {
- int len;
-
- len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g);
-
-#ifdef DEBTRACE
- htrc("Mapped file length=%d\n", len);
-#endif
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* OpenTableFile: Open a DOS/UNIX table file as a mapped file. */
-/***********************************************************************/
-bool MAPFAM::OpenTableFile(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- int len;
- MODE mode = Tdbp->GetMode();
- PFBLOCK fp;
- PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
-
-#if defined(_DEBUG)
- // Insert mode is no more handled using file mapping
- assert(mode != MODE_INSERT);
-#endif // _DEBUG
-
- /*********************************************************************/
- /* We used the file name relative to recorded datapath. */
- /*********************************************************************/
- PlugSetPath(filename, To_File, Tdbp->GetPath());
-
- /*********************************************************************/
- /* Under Win32 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;
-
-#ifdef DEBTRACE
- htrc("Mapping file, fp=%p\n", fp);
-#endif
- } 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;
-
- del = mode == MODE_DELETE && !Tdbp->GetNext();
-
- if (del)
- DelRows = Cardinality(g);
-
- /*******************************************************************/
- /* Create the mapping file object. */
- /*******************************************************************/
- hFile = CreateFileMap(g, filename, &mm, mode, del);
-
- if (hFile == INVALID_HANDLE_VALUE) {
- DWORD rc = GetLastError();
-
- if (!(*g->Message))
- sprintf(g->Message, MSG(OPEN_MODE_ERROR),
- "map", (int) rc, filename);
-
-#ifdef DEBTRACE
- htrc("%s\n", g->Message);
-#endif
- 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);
- Tdbp->ResetSize();
- return false;
- } // endif len
-
- if (!Memory) {
- CloseFileHandle(hFile);
- sprintf(g->Message, MSG(MAP_VIEW_ERROR),
- filename, GetLastError());
- return true;
- } // endif Memory
-
-#if defined(WIN32)
- if (mode != MODE_DELETE) {
-#else // !WIN32
- if (mode == MODE_READ) {
-#endif // !WIN32
- 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
-
- /*********************************************************************/
- /* The pseudo "buffer" is here the entire file mapping view. */
- /*********************************************************************/
- Fpos = Mempos = Memory;
- Top = Memory + len;
-
-#ifdef DEBTRACE
- htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
- fp, fp->Count, Memory, len, Top);
-#endif
-
- return AllocateBuffer(g); // Useful for DBF files
- } // end of OpenTableFile
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int MAPFAM::GetRowID(void)
- {
- return Rows;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int MAPFAM::GetPos(void)
- {
- return Fpos - Memory;
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: return the position of next record. */
-/***********************************************************************/
-int MAPFAM::GetNextPos(void)
- {
- return Mempos - Memory;
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool MAPFAM::SetPos(PGLOBAL g, int pos)
- {
- Fpos = Mempos = Memory + pos;
-
- if (Mempos >= Top || Mempos < Memory) {
- strcpy(g->Message, MSG(INV_MAP_POS));
- return true;
- } // endif Mempos
-
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/***********************************************************************/
-bool MAPFAM::RecordPos(PGLOBAL g)
- {
- Fpos = Mempos;
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int MAPFAM::SkipRecord(PGLOBAL g, bool header)
- {
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- // Skip this record
- while (*Mempos++ != '\n') ; // What about Unix ???
-
- if (Mempos >= Top)
- return RC_EF;
-
- // Update progress information
- dup->ProgCur = GetPos();
-
- if (header)
- Fpos = Tpos = Spos = Mempos; // For Delete
-
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a mapped text file. */
-/***********************************************************************/
-int MAPFAM::ReadBuffer(PGLOBAL g)
- {
- int len;
-
- // Are we at the end of the memory
- if (Mempos >= Top)
- return RC_EF;
-
- if (!Placed) {
- /*******************************************************************/
- /* Record file position in case of UPDATE or DELETE. */
- /*******************************************************************/
-#if defined(BLK_INDX)
- int rc;
-
- next:
-#endif // BLK_INDX
- Fpos = Mempos;
- CurBlk = (int)Rows++;
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* Check whether optimization on ROWID */
- /* 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:
- // Skip this record
- if ((rc = SkipRecord(g, FALSE)) != RC_OK)
- return rc;
-
- goto next;
- } // endswitch rc
-#endif // BLK_INDX
- } else
- Placed = false;
-
- // Immediately calculate next position (Used by DeleteDB)
- while (*Mempos++ != '\n') ; // What about Unix ???
-
- // Set caller line buffer
- len = (Mempos - Fpos) - Ending;
- memcpy(Tdbp->GetLine(), Fpos, len);
- Tdbp->GetLine()[len] = '\0';
- return RC_OK;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for MAP access method. */
-/***********************************************************************/
-int MAPFAM::WriteBuffer(PGLOBAL g)
- {
-#if defined(_DEBUG)
- // Insert mode is no more handled using file mapping
- if (Tdbp->GetMode() == MODE_INSERT) {
- strcpy(g->Message, MSG(NO_MAP_INSERT));
- return RC_FX;
- } // endif
-#endif // _DEBUG
-
- /*********************************************************************/
- /* Copy the updated record back into the memory mapped file. */
- /*********************************************************************/
- memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Data Base delete line routine for MAP (and FIX?) access methods. */
-/* Lines between deleted lines are moved in the mapfile view. */
-/***********************************************************************/
-int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
- {
- int n;
-
-#ifdef DEBTRACE
- fprintf(debug,
- "MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n",
- irc, Mempos, To_Buf, Tpos, Spos);
-#endif
-
- if (irc != RC_OK) {
- /*******************************************************************/
- /* EOF: position Fpos at the top of map position. */
- /*******************************************************************/
- Fpos = Top;
-#ifdef DEBTRACE
- htrc("Fpos placed at file top=%p\n", Fpos);
-#endif
- } // endif irc
-
- 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. */
- /*******************************************************************/
- memmove(Tpos, Spos, n);
- Tpos += n;
-
-#ifdef DEBTRACE
- htrc("move %d bytes\n", n);
-#endif
- } // endif n
-
- if (irc == RC_OK) {
- Spos = Mempos; // New start position
-
-#ifdef DEBTRACE
- htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
-#endif
-
- } else if (To_Fb) { // Can be NULL for deleted files
- /*******************************************************************/
- /* 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 = To_Fb;
-
- CloseMemMap(fp->Memory, (size_t)fp->Length);
- fp->Count = 0; // Avoid doing it twice
-
- /*******************************************************************/
- /* Remove extra records. */
- /*******************************************************************/
- n = Tpos - Memory;
-
-#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
-
-#ifdef DEBTRACE
- htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
-#endif
-
- 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
- } // endif irc
-
- return RC_OK; // All is correct
- } // end of DeleteRecords
-
-/***********************************************************************/
-/* Table file close routine for MAP access method. */
-/***********************************************************************/
-void MAPFAM::CloseTableFile(PGLOBAL g)
- {
- PlugCloseFile(g, To_Fb);
- To_Fb = NULL; // To get correct file size in Cardinality
-
-#ifdef DEBTRACE
- htrc("MAP Close: closing %s count=%d\n",
- To_File, (To_Fb) ? To_Fb->Count : 0);
-#endif
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for MAP access method. */
-/***********************************************************************/
-void MAPFAM::Rewind(void)
- {
- Mempos = Memory;
- } // end of Rewind
-
-/* --------------------------- Class MBKFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp)
- {
- Blocked = true;
- Block = tdp->GetBlock();
- Last = tdp->GetLast();
- Nrec = tdp->GetElemt();
-#if defined(BLK_INDX)
- BlkPos = tdp->GetTo_Pos();
-#else // !BLK_INDX
- BlkPos = NULL;
-#endif // !BLK_INDX
- CurNum = Nrec;
- } // end of MBKFAM standard constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void MBKFAM::Reset(void)
- {
- MAPFAM::Reset();
- CurNum = Nrec; // To start by a new block
- } // end of Reset
-
-/***********************************************************************/
-/* 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 MBKFAM::Cardinality(PGLOBAL g)
- {
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int MBKFAM::SkipRecord(PGLOBAL g, bool header)
- {
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int MBKFAM::GetRowID(void)
- {
- return CurNum + Nrec * CurBlk + 1;
- } // end of GetRowID
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a mapped Fix file. */
-/***********************************************************************/
-int MBKFAM::ReadBuffer(PGLOBAL g)
- {
-#if defined(BLK_INDX)
- int len;
-
- /*********************************************************************/
- /* Sequential block reading when Placed is not true. */
- /*********************************************************************/
- if (Placed) {
- Placed = false;
- } else if (Mempos >= Top) { // Are we at the end of the memory
- return RC_EF;
- } else if (++CurNum < Nrec) {
- Fpos = Mempos;
- } else {
- /*******************************************************************/
- /* New block. */
- /*******************************************************************/
- CurNum = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*******************************************************************/
- /* Before reading a new block, check whether block optimization */
- /* 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
-
- Fpos = Mempos = Memory + BlkPos[CurBlk];
- } // endif's
-
- // Immediately calculate next position (Used by DeleteDB)
- while (*Mempos++ != '\n') ; // What about Unix ???
-
- // Set caller line buffer
- len = (Mempos - Fpos) - Ending;
- memcpy(Tdbp->GetLine(), Fpos, len);
- Tdbp->GetLine()[len] = '\0';
- return RC_OK;
-#else // !BLK_POS
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
-#endif // !BLK_POS
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* Rewind routine for FIX MAP access method. */
-/***********************************************************************/
-void MBKFAM::Rewind(void)
- {
- Mempos = Memory + Headlen;
- CurBlk = -1;
- CurNum = Nrec;
- } // end of Rewind
-
-/* --------------------------- Class MPXFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp)
- {
- Blksize = tdp->GetBlksize();
- Padded = tdp->GetPadded();
-
- if (Padded && Blksize)
- Nrec = Blksize / Lrecl;
- else {
- Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
- Blksize = Nrec * Lrecl;
- Padded = false;
- } // endelse
-
- CurNum = Nrec;
- } // end of MPXFAM standard constructor
-
-#if 0 // MBKFAM routine is correct
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int MPXFAM::GetRowID(void)
- {
- return (Mempos - Memory - Headlen) / Lrecl;
- } // end of GetRowID
-#endif
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int MPXFAM::GetPos(void)
- {
- return (CurNum + Nrec * CurBlk); // Computed file index
- } // end of GetPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool MPXFAM::SetPos(PGLOBAL g, int pos)
- {
- if (pos < 0) {
- strcpy(g->Message, MSG(INV_REC_POS));
- return true;
- } // endif recpos
-
- CurBlk = pos / Nrec;
- CurNum = pos % Nrec;
- Fpos = Mempos = Memory + Headlen + pos * Lrecl;
-
- // Indicate the table position was externally set
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a mapped Fix file. */
-/***********************************************************************/
-int MPXFAM::ReadBuffer(PGLOBAL g)
- {
- /*********************************************************************/
- /* Sequential block reading when Placed is not true. */
- /*********************************************************************/
- if (Placed) {
- Placed = false;
- } else if (Mempos >= Top) { // Are we at the end of the memory
- return RC_EF;
- } else if (++CurNum < Nrec) {
- Fpos = Mempos;
- } else {
- /*******************************************************************/
- /* New block. */
- /*******************************************************************/
- CurNum = 0;
-
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (++CurBlk >= Block)
- return RC_EF;
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* Before reading a new block, check whether block optimization */
- /* 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
-#endif // BLK_INDX
-
- Fpos = Mempos = Headlen + Memory + CurBlk * Blksize;
- } // endif's
-
- Tdbp->SetLine(Mempos);
-
- // Immediately calculate next position (Used by DeleteDB)
- Mempos += Lrecl;
- return RC_OK;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for MAP access method. */
-/***********************************************************************/
-int MPXFAM::WriteBuffer(PGLOBAL g)
- {
-#if defined(_DEBUG)
- // Insert mode is no more handled using file mapping
- if (Tdbp->GetMode() == MODE_INSERT) {
- strcpy(g->Message, MSG(NO_MAP_INSERT));
- return RC_FX;
- } // endif
-#endif // _DEBUG
-
- // In Update mode, file was modified in memory
- return RC_OK;
- } // end of WriteBuffer
-
+/*********** File AM Map C++ Program Source Code File (.CPP) ***********/
+/* PROGRAM NAME: FILAMAP */
+/* ------------- */
+/* Version 1.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the MAP file access method classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#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. */
+/* Note: these files are included inside the include files below. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "osutil.h"
+#include "maputil.h"
+#include "filamap.h"
+#include "tabdos.h"
+
+/* --------------------------- Class MAPFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+MAPFAM::MAPFAM(PDOSDEF tdp) : TXTFAM(tdp)
+ {
+ Memory = NULL;
+ Mempos = NULL;
+ Tpos = NULL;
+ Fpos = NULL;
+ Spos = NULL;
+ Top = NULL;
+ } // end of MAPFAM standard constructor
+
+MAPFAM::MAPFAM(PMAPFAM tmfp) : TXTFAM(tmfp)
+ {
+ Memory = tmfp->Memory;
+ Mempos = tmfp->Mempos;
+ Fpos = tmfp->Fpos;
+ Spos = tmfp->Spos;
+ Tpos = tmfp->Tpos;
+ Top = tmfp->Top;
+ } // end of MAPFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void MAPFAM::Reset(void)
+ {
+ TXTFAM::Reset();
+ Fpos = Tpos = Spos = NULL;
+ } // end of Reset
+
+/***********************************************************************/
+/* MAP GetFileLength: returns file size in number of bytes. */
+/***********************************************************************/
+int MAPFAM::GetFileLength(PGLOBAL g)
+ {
+ int len;
+
+ len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g);
+
+#ifdef DEBTRACE
+ htrc("Mapped file length=%d\n", len);
+#endif
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file as a mapped file. */
+/***********************************************************************/
+bool MAPFAM::OpenTableFile(PGLOBAL g)
+ {
+ char filename[_MAX_PATH];
+ int len;
+ MODE mode = Tdbp->GetMode();
+ PFBLOCK fp;
+ PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
+
+#if defined(_DEBUG)
+ // Insert mode is no more handled using file mapping
+ assert(mode != MODE_INSERT);
+#endif // _DEBUG
+
+ /*********************************************************************/
+ /* We used the file name relative to recorded datapath. */
+ /*********************************************************************/
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+
+ /*********************************************************************/
+ /* Under Win32 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;
+
+#ifdef DEBTRACE
+ htrc("Mapping file, fp=%p\n", fp);
+#endif
+ } 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;
+
+ del = mode == MODE_DELETE && !Tdbp->GetNext();
+
+ if (del)
+ DelRows = Cardinality(g);
+
+ /*******************************************************************/
+ /* Create the mapping file object. */
+ /*******************************************************************/
+ hFile = CreateFileMap(g, filename, &mm, mode, del);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ DWORD rc = GetLastError();
+
+ if (!(*g->Message))
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "map", (int) rc, filename);
+
+#ifdef DEBTRACE
+ htrc("%s\n", g->Message);
+#endif
+ 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);
+ Tdbp->ResetSize();
+ return false;
+ } // endif len
+
+ if (!Memory) {
+ CloseFileHandle(hFile);
+ sprintf(g->Message, MSG(MAP_VIEW_ERROR),
+ filename, GetLastError());
+ return true;
+ } // endif Memory
+
+#if defined(WIN32)
+ if (mode != MODE_DELETE) {
+#else // !WIN32
+ if (mode == MODE_READ) {
+#endif // !WIN32
+ 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
+
+ /*********************************************************************/
+ /* The pseudo "buffer" is here the entire file mapping view. */
+ /*********************************************************************/
+ Fpos = Mempos = Memory;
+ Top = Memory + len;
+
+#ifdef DEBTRACE
+ htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
+ fp, fp->Count, Memory, len, Top);
+#endif
+
+ return AllocateBuffer(g); // Useful for DBF files
+ } // end of OpenTableFile
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int MAPFAM::GetRowID(void)
+ {
+ return Rows;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int MAPFAM::GetPos(void)
+ {
+ return Fpos - Memory;
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: return the position of next record. */
+/***********************************************************************/
+int MAPFAM::GetNextPos(void)
+ {
+ return Mempos - Memory;
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool MAPFAM::SetPos(PGLOBAL g, int pos)
+ {
+ Fpos = Mempos = Memory + pos;
+
+ if (Mempos >= Top || Mempos < Memory) {
+ strcpy(g->Message, MSG(INV_MAP_POS));
+ return true;
+ } // endif Mempos
+
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/***********************************************************************/
+bool MAPFAM::RecordPos(PGLOBAL g)
+ {
+ Fpos = Mempos;
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int MAPFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ // Skip this record
+ while (*Mempos++ != '\n') ; // What about Unix ???
+
+ if (Mempos >= Top)
+ return RC_EF;
+
+ // Update progress information
+ dup->ProgCur = GetPos();
+
+ if (header)
+ Fpos = Tpos = Spos = Mempos; // For Delete
+
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a mapped text file. */
+/***********************************************************************/
+int MAPFAM::ReadBuffer(PGLOBAL g)
+ {
+ int len;
+
+ // Are we at the end of the memory
+ if (Mempos >= Top)
+ return RC_EF;
+
+ if (!Placed) {
+ /*******************************************************************/
+ /* Record file position in case of UPDATE or DELETE. */
+ /*******************************************************************/
+ int rc;
+
+ next:
+ Fpos = Mempos;
+ CurBlk = (int)Rows++;
+
+ /*******************************************************************/
+ /* Check whether optimization on ROWID */
+ /* 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:
+ // Skip this record
+ if ((rc = SkipRecord(g, FALSE)) != RC_OK)
+ return rc;
+
+ goto next;
+ } // endswitch rc
+ } else
+ Placed = false;
+
+ // Immediately calculate next position (Used by DeleteDB)
+ while (*Mempos++ != '\n') ; // What about Unix ???
+
+ // Set caller line buffer
+ len = (Mempos - Fpos) - Ending;
+ memcpy(Tdbp->GetLine(), Fpos, len);
+ Tdbp->GetLine()[len] = '\0';
+ return RC_OK;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for MAP access method. */
+/***********************************************************************/
+int MAPFAM::WriteBuffer(PGLOBAL g)
+ {
+#if defined(_DEBUG)
+ // Insert mode is no more handled using file mapping
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ strcpy(g->Message, MSG(NO_MAP_INSERT));
+ return RC_FX;
+ } // endif
+#endif // _DEBUG
+
+ /*********************************************************************/
+ /* Copy the updated record back into the memory mapped file. */
+ /*********************************************************************/
+ memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Data Base delete line routine for MAP (and FIX?) access methods. */
+/* Lines between deleted lines are moved in the mapfile view. */
+/***********************************************************************/
+int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
+ {
+ int n;
+
+#ifdef DEBTRACE
+ fprintf(debug,
+ "MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n",
+ irc, Mempos, To_Buf, Tpos, Spos);
+#endif
+
+ if (irc != RC_OK) {
+ /*******************************************************************/
+ /* EOF: position Fpos at the top of map position. */
+ /*******************************************************************/
+ Fpos = Top;
+#ifdef DEBTRACE
+ htrc("Fpos placed at file top=%p\n", Fpos);
+#endif
+ } // endif irc
+
+ 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. */
+ /*******************************************************************/
+ memmove(Tpos, Spos, n);
+ Tpos += n;
+
+#ifdef DEBTRACE
+ htrc("move %d bytes\n", n);
+#endif
+ } // endif n
+
+ if (irc == RC_OK) {
+ Spos = Mempos; // New start position
+
+#ifdef DEBTRACE
+ htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
+#endif
+
+ } else if (To_Fb) { // Can be NULL for deleted files
+ /*******************************************************************/
+ /* 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 = To_Fb;
+
+ CloseMemMap(fp->Memory, (size_t)fp->Length);
+ fp->Count = 0; // Avoid doing it twice
+
+ /*******************************************************************/
+ /* Remove extra records. */
+ /*******************************************************************/
+ n = Tpos - Memory;
+
+#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
+
+#ifdef DEBTRACE
+ htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
+#endif
+
+ 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
+ } // endif irc
+
+ return RC_OK; // All is correct
+ } // end of DeleteRecords
+
+/***********************************************************************/
+/* Table file close routine for MAP access method. */
+/***********************************************************************/
+void MAPFAM::CloseTableFile(PGLOBAL g)
+ {
+ PlugCloseFile(g, To_Fb);
+ To_Fb = NULL; // To get correct file size in Cardinality
+
+#ifdef DEBTRACE
+ htrc("MAP Close: closing %s count=%d\n",
+ To_File, (To_Fb) ? To_Fb->Count : 0);
+#endif
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for MAP access method. */
+/***********************************************************************/
+void MAPFAM::Rewind(void)
+ {
+ Mempos = Memory;
+ } // end of Rewind
+
+/* --------------------------- Class MBKFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp)
+ {
+ Blocked = true;
+ Block = tdp->GetBlock();
+ Last = tdp->GetLast();
+ Nrec = tdp->GetElemt();
+ BlkPos = tdp->GetTo_Pos();
+ CurNum = Nrec;
+ } // end of MBKFAM standard constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void MBKFAM::Reset(void)
+ {
+ MAPFAM::Reset();
+ CurNum = Nrec; // To start by a new block
+ } // end of Reset
+
+/***********************************************************************/
+/* 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 MBKFAM::Cardinality(PGLOBAL g)
+ {
+ return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int MBKFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int MBKFAM::GetRowID(void)
+ {
+ return CurNum + Nrec * CurBlk + 1;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a mapped Fix file. */
+/***********************************************************************/
+int MBKFAM::ReadBuffer(PGLOBAL g)
+ {
+ int len;
+
+ /*********************************************************************/
+ /* Sequential block reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Placed = false;
+ } else if (Mempos >= Top) { // Are we at the end of the memory
+ return RC_EF;
+ } else if (++CurNum < Nrec) {
+ Fpos = Mempos;
+ } else {
+ /*******************************************************************/
+ /* New block. */
+ /*******************************************************************/
+ CurNum = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*******************************************************************/
+ /* Before reading a new block, check whether block optimization */
+ /* 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
+
+ Fpos = Mempos = Memory + BlkPos[CurBlk];
+ } // endif's
+
+ // Immediately calculate next position (Used by DeleteDB)
+ while (*Mempos++ != '\n') ; // What about Unix ???
+
+ // Set caller line buffer
+ len = (Mempos - Fpos) - Ending;
+ memcpy(Tdbp->GetLine(), Fpos, len);
+ Tdbp->GetLine()[len] = '\0';
+ return RC_OK;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* Rewind routine for FIX MAP access method. */
+/***********************************************************************/
+void MBKFAM::Rewind(void)
+ {
+ Mempos = Memory + Headlen;
+ CurBlk = -1;
+ CurNum = Nrec;
+ } // end of Rewind
+
+/* --------------------------- Class MPXFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp)
+ {
+ Blksize = tdp->GetBlksize();
+ Padded = tdp->GetPadded();
+
+ if (Padded && Blksize)
+ Nrec = Blksize / Lrecl;
+ else {
+ Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
+ Blksize = Nrec * Lrecl;
+ Padded = false;
+ } // endelse
+
+ CurNum = Nrec;
+ } // end of MPXFAM standard constructor
+
+#if 0 // MBKFAM routine is correct
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int MPXFAM::GetRowID(void)
+ {
+ return (Mempos - Memory - Headlen) / Lrecl;
+ } // end of GetRowID
+#endif
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int MPXFAM::GetPos(void)
+ {
+ return (CurNum + Nrec * CurBlk); // Computed file index
+ } // end of GetPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool MPXFAM::SetPos(PGLOBAL g, int pos)
+ {
+ if (pos < 0) {
+ strcpy(g->Message, MSG(INV_REC_POS));
+ return true;
+ } // endif recpos
+
+ CurBlk = pos / Nrec;
+ CurNum = pos % Nrec;
+ Fpos = Mempos = Memory + Headlen + pos * Lrecl;
+
+ // Indicate the table position was externally set
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a mapped Fix file. */
+/***********************************************************************/
+int MPXFAM::ReadBuffer(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Sequential block reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Placed = false;
+ } else if (Mempos >= Top) { // Are we at the end of the memory
+ return RC_EF;
+ } else if (++CurNum < Nrec) {
+ Fpos = Mempos;
+ } else {
+ /*******************************************************************/
+ /* New block. */
+ /*******************************************************************/
+ CurNum = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*******************************************************************/
+ /* Before reading a new block, check whether block optimization */
+ /* 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
+
+ Fpos = Mempos = Headlen + Memory + CurBlk * Blksize;
+ } // endif's
+
+ Tdbp->SetLine(Mempos);
+
+ // Immediately calculate next position (Used by DeleteDB)
+ Mempos += Lrecl;
+ return RC_OK;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for MAP access method. */
+/***********************************************************************/
+int MPXFAM::WriteBuffer(PGLOBAL g)
+ {
+#if defined(_DEBUG)
+ // Insert mode is no more handled using file mapping
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ strcpy(g->Message, MSG(NO_MAP_INSERT));
+ return RC_FX;
+ } // endif
+#endif // _DEBUG
+
+ // In Update mode, file was modified in memory
+ return RC_OK;
+ } // end of WriteBuffer
+
diff --git a/storage/connect/filamap.h b/storage/connect/filamap.h
index d3503bb0c1d..adee5816e12 100644
--- a/storage/connect/filamap.h
+++ b/storage/connect/filamap.h
@@ -1,114 +1,113 @@
-/*************** FilAMap H Declares Source Code File (.H) **************/
-/* Name: FILAMAP.H Version 1.2 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
-/* */
-/* This file contains the MAP file access method classes declares. */
-/***********************************************************************/
-#ifndef __FILAMAP_H
-#define __FILAMAP_H
-
-#include "block.h"
-#include "filamtxt.h"
-
-typedef class MAPFAM *PMAPFAM;
-
-/***********************************************************************/
-/* This is the variable file access method using file mapping. */
-/***********************************************************************/
-class DllExport MAPFAM : public TXTFAM {
- public:
- // Constructor
- MAPFAM(PDOSDEF tdp);
- MAPFAM(PMAPFAM tmfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_MAP;}
- virtual int GetPos(void);
- virtual int GetNextPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) MAPFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int GetFileLength(PGLOBAL g);
- virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
- virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
- virtual bool SetPos(PGLOBAL g, int recpos);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual bool OpenTableFile(PGLOBAL g);
- virtual bool DeferReading(void) {return false;}
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- // Members
- char *Memory; // Pointer on file mapping view.
- char *Mempos; // Position of next data to read
- char *Fpos; // Position of last read record
- char *Tpos; // Target Position for delete move
- char *Spos; // Start position for delete move
- char *Top; // Mark end of file mapping view
- }; // end of class MAPFAM
-
-/***********************************************************************/
-/* This is the blocked file access method using file mapping. */
-/***********************************************************************/
-class DllExport MBKFAM : public MAPFAM {
- public:
- // Constructor
- MBKFAM(PDOSDEF tdp);
- MBKFAM(PMAPFAM tmfp) : MAPFAM(tmfp) {}
-
- // Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) MBKFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int Cardinality(PGLOBAL g);
- virtual int MaxBlkSize(PGLOBAL g, int s)
- {return TXTFAM::MaxBlkSize(g, s);}
- virtual int GetRowID(void);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual int ReadBuffer(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- // No additional members
- }; // end of class MBKFAM
-
-/***********************************************************************/
-/* This is the fixed file access method using file mapping. */
-/***********************************************************************/
-class DllExport MPXFAM : public MBKFAM {
- public:
- // Constructor
- MPXFAM(PDOSDEF tdp);
- MPXFAM(PMAPFAM tmfp) : MBKFAM(tmfp) {}
-
- // Implementation
- virtual int GetPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) MPXFAM(this);}
-
- // Methods
- virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);}
- virtual int MaxBlkSize(PGLOBAL g, int s)
- {return TXTFAM::MaxBlkSize(g, s);}
-//virtual int GetRowID(void);
- virtual bool SetPos(PGLOBAL g, int recpos);
- virtual bool DeferReading(void) {return false;}
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
-
- protected:
- // No additional members
- }; // end of class MPXFAM
-
-#endif // __FILAMAP_H
+/*************** FilAMap H Declares Source Code File (.H) **************/
+/* Name: FILAMAP.H Version 1.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* */
+/* This file contains the MAP file access method classes declares. */
+/***********************************************************************/
+#ifndef __FILAMAP_H
+#define __FILAMAP_H
+
+#include "block.h"
+#include "filamtxt.h"
+
+typedef class MAPFAM *PMAPFAM;
+
+/***********************************************************************/
+/* This is the variable file access method using file mapping. */
+/***********************************************************************/
+class DllExport MAPFAM : public TXTFAM {
+ public:
+ // Constructor
+ MAPFAM(PDOSDEF tdp);
+ MAPFAM(PMAPFAM tmfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_MAP;}
+ virtual int GetPos(void);
+ virtual int GetNextPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) MAPFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int GetFileLength(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
+ virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
+ virtual int GetRowID(void);
+ virtual bool RecordPos(PGLOBAL g);
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual bool DeferReading(void) {return false;}
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ // Members
+ char *Memory; // Pointer on file mapping view.
+ char *Mempos; // Position of next data to read
+ char *Fpos; // Position of last read record
+ char *Tpos; // Target Position for delete move
+ char *Spos; // Start position for delete move
+ char *Top; // Mark end of file mapping view
+ }; // end of class MAPFAM
+
+/***********************************************************************/
+/* This is the blocked file access method using file mapping. */
+/***********************************************************************/
+class DllExport MBKFAM : public MAPFAM {
+ public:
+ // Constructor
+ MBKFAM(PDOSDEF tdp);
+ MBKFAM(PMAPFAM tmfp) : MAPFAM(tmfp) {}
+
+ // Implementation
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) MBKFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int MaxBlkSize(PGLOBAL g, int s)
+ {return TXTFAM::MaxBlkSize(g, s);}
+ virtual int GetRowID(void);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ // No additional members
+ }; // end of class MBKFAM
+
+/***********************************************************************/
+/* This is the fixed file access method using file mapping. */
+/***********************************************************************/
+class DllExport MPXFAM : public MBKFAM {
+ public:
+ // Constructor
+ MPXFAM(PDOSDEF tdp);
+ MPXFAM(PMAPFAM tmfp) : MBKFAM(tmfp) {}
+
+ // Implementation
+ virtual int GetPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) MPXFAM(this);}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);}
+ virtual int MaxBlkSize(PGLOBAL g, int s)
+ {return TXTFAM::MaxBlkSize(g, s);}
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual bool DeferReading(void) {return false;}
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+
+ protected:
+ // No additional members
+ }; // end of class MPXFAM
+
+#endif // __FILAMAP_H
diff --git a/storage/connect/filamdbf.h b/storage/connect/filamdbf.h
index 361f2bd8360..b85b9fc47fe 100644
--- a/storage/connect/filamdbf.h
+++ b/storage/connect/filamdbf.h
@@ -43,7 +43,6 @@ class DllExport DBFBASE {
int Nerr; /* Number of bad records */
int Maxerr; /* Maximum number of bad records */
int ReadMode; /* 1: ALL 2: DEL 0: NOT DEL */
-//PSZ Defpath; /* Default data path */
}; // end of class DBFBASE
/****************************************************************************/
@@ -63,22 +62,18 @@ class DllExport DBFFAM : public FIXFAM, public DBFBASE {
// Methods
virtual int GetNerr(void) {return Nerr;}
virtual int Cardinality(PGLOBAL g);
-//virtual int GetRowID(void); // Temporarily suppressed
virtual bool OpenTableFile(PGLOBAL g);
virtual bool AllocateBuffer(PGLOBAL g);
virtual void ResetBuffer(PGLOBAL g);
virtual int ReadBuffer(PGLOBAL g);
-//virtual int WriteBuffer(PGLOBAL g);
virtual int DeleteRecords(PGLOBAL g, int irc);
virtual void CloseTableFile(PGLOBAL g);
virtual void Rewind(void);
protected:
- // Members
virtual bool CopyHeader(PGLOBAL g);
-//int Records; in TXTFAM
-//int Headlen; in TXTFAM
+ // Members
}; // end of class DBFFAM
/****************************************************************************/
@@ -100,17 +95,13 @@ class DllExport DBMFAM : public MPXFAM, public DBFBASE {
// Methods
virtual int GetNerr(void) {return Nerr;}
virtual int Cardinality(PGLOBAL g);
-//virtual int GetRowID(void); // Temporarily suppressed
virtual bool AllocateBuffer(PGLOBAL g);
virtual int ReadBuffer(PGLOBAL g);
-//virtual int WriteBuffer(PGLOBAL g);
virtual int DeleteRecords(PGLOBAL g, int irc);
virtual void Rewind(void);
protected:
// Members
-//int Records; in TXTFAM
-//int Headlen; in TXTFAM
}; // end of class DBFFAM
#endif // __FILAMDBF_H
diff --git a/storage/connect/filamfix.cpp b/storage/connect/filamfix.cpp
index 295c281a478..daf133d4203 100644
--- a/storage/connect/filamfix.cpp
+++ b/storage/connect/filamfix.cpp
@@ -168,13 +168,10 @@ int FIXFAM::ReadBuffer(PGLOBAL g)
CurNum = 0;
Tdbp->SetLine(To_Buf);
-#if defined(BLK_INDX)
next:
-#endif // BLK_INDX
if (++CurBlk >= Block)
return RC_EF;
-#if defined(BLK_INDX)
/*****************************************************************/
/* Before reading a new block, check whether block indexing */
/* can be done, as well as for join as for local filtering. */
@@ -185,7 +182,6 @@ int FIXFAM::ReadBuffer(PGLOBAL g)
case RC_NF:
goto next;
} // endswitch rc
-#endif // BLK_INDX
} // endif's
if (OldBlk == CurBlk) {
@@ -1043,13 +1039,10 @@ int BGXFAM::ReadBuffer(PGLOBAL g)
CurNum = 0;
Tdbp->SetLine(To_Buf);
-#if defined(BLK_INDX)
next:
-#endif // BLK_INDX
if (++CurBlk >= Block)
return RC_EF;
-#if defined(BLK_INDX)
/*****************************************************************/
/* Before reading a new block, check whether block optimization */
/* can be done, as well as for join as for local filtering. */
@@ -1060,7 +1053,7 @@ int BGXFAM::ReadBuffer(PGLOBAL g)
case RC_NF:
goto next;
} // endswitch rc
-#endif // BLK_INDX
+
} // endif's
if (OldBlk == CurBlk) {
diff --git a/storage/connect/filamfix.h b/storage/connect/filamfix.h
index b2c5019c635..80523fa05e8 100644
--- a/storage/connect/filamfix.h
+++ b/storage/connect/filamfix.h
@@ -1,90 +1,88 @@
-/************** FilAMFix H Declares Source Code File (.H) **************/
-/* Name: FILAMFIX.H Version 1.2 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005 - 2012 */
-/* */
-/* This file contains the FIX file access method classes declares. */
-/***********************************************************************/
-
-#ifndef __FILAMFIX_H
-#define __FILAMFIX_H
-
-#include "filamtxt.h"
-
-typedef class FIXFAM *PFIXFAM;
-typedef class BGXFAM *PBGXFAM;
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for standard */
-/* files with fixed record format (FIX, BIN) */
-/***********************************************************************/
-class DllExport FIXFAM : public BLKFAM {
- public:
- // Constructor
- FIXFAM(PDOSDEF tdp);
- FIXFAM(PFIXFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_FIX;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) FIXFAM(this);}
-
- // Methods
- virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);}
- virtual int MaxBlkSize(PGLOBAL g, int s)
- {return TXTFAM::MaxBlkSize(g, s);}
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual void ResetBuffer(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
-
- protected:
- virtual bool CopyHeader(PGLOBAL g) {return false;}
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
-
- // No additional members
- }; // end of class FIXFAM
-
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* that are standard files with columns starting at fixed offset */
-/* This class is for fixed formatted files of more than 2 gigabytes. */
-/***********************************************************************/
-class BGXFAM : public FIXFAM {
- public:
- // Constructor
- BGXFAM(PDOSDEF tdp);
- BGXFAM(PBGXFAM txfp);
-
- // Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) BGXFAM(this);}
-
- // Methods
-//virtual void Reset(void);
- virtual int Cardinality(PGLOBAL g);
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos
- , int org = FILE_BEGIN);
- int BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req);
- bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req);
- virtual bool OpenTempFile(PGLOBAL g);
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
-
- // Members
- HANDLE Hfile; // Handle(descriptor) to big file
- HANDLE Tfile; // Handle(descriptor) to big temp file
-//BIGINT Xpos; // Current file position
- }; // end of class BGXFAM
-
-#endif // __FILAMFIX_H
+/************** FilAMFix H Declares Source Code File (.H) **************/
+/* Name: FILAMFIX.H Version 1.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005 - 2014 */
+/* */
+/* This file contains the FIX file access method classes declares. */
+/***********************************************************************/
+
+#ifndef __FILAMFIX_H
+#define __FILAMFIX_H
+
+#include "filamtxt.h"
+
+typedef class FIXFAM *PFIXFAM;
+typedef class BGXFAM *PBGXFAM;
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for standard */
+/* files with fixed record format (FIX, BIN) */
+/***********************************************************************/
+class DllExport FIXFAM : public BLKFAM {
+ public:
+ // Constructor
+ FIXFAM(PDOSDEF tdp);
+ FIXFAM(PFIXFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_FIX;}
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) FIXFAM(this);}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);}
+ virtual int MaxBlkSize(PGLOBAL g, int s)
+ {return TXTFAM::MaxBlkSize(g, s);}
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual void ResetBuffer(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+
+ protected:
+ virtual bool CopyHeader(PGLOBAL g) {return false;}
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
+
+ // No additional members
+ }; // end of class FIXFAM
+
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* that are standard files with columns starting at fixed offset */
+/* This class is for fixed formatted files of more than 2 gigabytes. */
+/***********************************************************************/
+class BGXFAM : public FIXFAM {
+ public:
+ // Constructor
+ BGXFAM(PDOSDEF tdp);
+ BGXFAM(PBGXFAM txfp);
+
+ // Implementation
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) BGXFAM(this);}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g);
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos
+ , int org = FILE_BEGIN);
+ int BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
+
+ // Members
+ HANDLE Hfile; // Handle(descriptor) to big file
+ HANDLE Tfile; // Handle(descriptor) to big temp file
+ }; // end of class BGXFAM
+
+#endif // __FILAMFIX_H
diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp
index d7310f34e6b..24459509e1f 100644
--- a/storage/connect/filamtxt.cpp
+++ b/storage/connect/filamtxt.cpp
@@ -1,1462 +1,1442 @@
-/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
-/* PROGRAM NAME: FILAMTXT */
-/* ------------- */
-/* Version 1.5 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the Text file access method classes. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-#if defined(__BORLANDC__)
-#define __MFC_COMPAT__ // To define min/max as macro
-#endif // __BORLANDC__
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX) || defined(UNIV_LINUX)
-#include <errno.h>
-#include <unistd.h>
-//#if !defined(sun) // Sun has the ftruncate fnc.
-//#define USETEMP // Force copy mode for DELETE
-//#endif // !sun
-#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 "plgdbsem.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-
-#if defined(UNIX) || defined(UNIV_LINUX)
-#include "osutil.h"
-#define _fileno fileno
-#define _O_RDONLY O_RDONLY
-#endif
-
-extern int num_read, num_there, num_eq[2]; // Statistics
-extern "C" int trace;
-
-/* --------------------------- Class TXTFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-TXTFAM::TXTFAM(PDOSDEF tdp)
- {
- Tdbp = NULL;
- To_Fb = NULL;
- To_File = tdp->Fn;
- Lrecl = tdp->Lrecl;
- Placed = false;
- IsRead = true;
- Blocked = false;
- To_Buf = NULL;
- DelBuf = NULL;
- BlkPos = NULL;
- BlkLen = 0;
- Buflen = 0;
- Dbflen = 0;
- Rows = 0;
- DelRows = 0;
- Headlen = 0;
- Block = 0;
- Last = 0;
- Nrec = 1;
- OldBlk = -1;
- CurBlk = -1;
- ReadBlks = 0;
- CurNum = 0;
- Rbuf = 0;
- Modif = 0;
- Blksize = 0;
- Padded = false;
- Eof = tdp->Eof;
- Ending = tdp->Ending;
- CrLf = (char*)(Ending == 2 ? "\r\n" : "\n");
- } // end of TXTFAM standard constructor
-
-TXTFAM::TXTFAM(PTXF txfp)
- {
- Tdbp = txfp->Tdbp;
- To_Fb = txfp->To_Fb;
- To_File = txfp->To_File;
- Lrecl = txfp->Lrecl;
- Placed = txfp->Placed;
- IsRead = txfp->IsRead;
- Blocked = txfp->Blocked;
- To_Buf = txfp->To_Buf;
- DelBuf = txfp->DelBuf;
- BlkPos = txfp->BlkPos;
- BlkLen = txfp->BlkLen;
- Buflen = txfp->Buflen;
- Dbflen = txfp->Dbflen;
- Rows = txfp->Rows;
- DelRows = txfp->DelRows;
- Headlen = txfp->Headlen;
- Block = txfp->Block;
- Last = txfp->Last;
- Nrec = txfp->Nrec;
- OldBlk = txfp->OldBlk;
- CurBlk = txfp->CurBlk;
- ReadBlks = txfp->ReadBlks;
- CurNum = txfp->CurNum;
- Rbuf = txfp->Rbuf;
- Modif = txfp->Modif;
- Blksize = txfp->Blksize;
- Padded = txfp->Padded;
- Eof = txfp->Eof;
- Ending = txfp->Ending;
- } // end of TXTFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void TXTFAM::Reset(void)
- {
- Rows = 0;
- DelRows = 0;
- OldBlk = -1;
- CurBlk = -1;
- ReadBlks = 0;
- CurNum = 0;
- Rbuf = 0;
- Modif = 0;
- Placed = false;
- } // end of Reset
-
-/***********************************************************************/
-/* TXT GetFileLength: returns file size in number of bytes. */
-/***********************************************************************/
-int TXTFAM::GetFileLength(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- int h;
- int len;
-
- PlugSetPath(filename, To_File, Tdbp->GetPath());
- h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
-
- if (trace)
- htrc("GetFileLength: fn=%s h=%d\n", filename, h);
-
- if (h == -1) {
- if (errno != ENOENT) {
- if (trace)
- htrc("%s\n", g->Message);
- len = -1;
- }
- else
- {
- len = 0; // File does not exist yet
- g->Message[0]= '\0';
- }
- } else {
- if ((len = _filelength(h)) < 0)
- sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
-
- if (Eof && len)
- len--; // Do not count the EOF character
-
- close(h);
- } // endif h
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* 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). */
-/* Note: This function is meant only for fixed length files but is */
-/* placed here to be available to FIXFAM and MPXFAM classes. */
-/***********************************************************************/
-int TXTFAM::Cardinality(PGLOBAL g)
- {
- if (g) {
- int card = -1;
- int len = GetFileLength(g);
-
- if (len >= 0) {
- if (Padded && Blksize) {
- if (!(len % Blksize))
- card = (len / Blksize) * Nrec;
- else
- sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
-
- } else {
- if (!(len % Lrecl))
- card = len / (int)Lrecl; // Fixed length file
- else
- sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
-
- } // endif Padded
-
- if (trace)
- htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
- card, len, Lrecl);
-
- } else
- card = 0;
-
- // Set number of blocks for later use
- Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
- return card;
- } else
- return 1;
-
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/* Note: This function is meant only for fixed length files but is */
-/* placed here to be available to FIXFAM and MPXFAM classes. */
-/***********************************************************************/
-int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
- {
- int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
- int size, last = s - blm1 * Nrec;
-
- // Roughly estimate the table size as the sum of blocks
- // that can contain good rows
- for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
-#if defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == blm1) ? last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == blm1) ? last : Nrec;
-#endif // !BLK_INDX
-
- CurBlk = savcur;
- return size;
- } // end of MaxBlkSize
-
-/* --------------------------- Class DOSFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
- {
- To_Fbt = NULL;
- Stream = NULL;
- T_Stream = NULL;
- Fpos = Spos = Tpos = 0;
- UseTemp = false;
- Bin = false;
- } // end of DOSFAM standard constructor
-
-DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
- {
- To_Fbt = tdfp->To_Fbt;
- Stream = tdfp->Stream;
- T_Stream = tdfp->T_Stream;
- Fpos = tdfp->Fpos;
- Spos = tdfp->Spos;
- Tpos = tdfp->Tpos;
- UseTemp = tdfp->UseTemp;
- Bin = tdfp->Bin;
- } // end of DOSFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void DOSFAM::Reset(void)
- {
- TXTFAM::Reset();
- Bin = false;
- Fpos = Tpos = Spos = 0;
- } // end of Reset
-
-/***********************************************************************/
-/* DOS GetFileLength: returns file size in number of bytes. */
-/***********************************************************************/
-int DOSFAM::GetFileLength(PGLOBAL g)
- {
- int len;
-
- if (!Stream)
- len = TXTFAM::GetFileLength(g);
- else
- if ((len = _filelength(_fileno(Stream))) < 0)
- sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
-
- if (trace)
- htrc("File length=%d\n", len);
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* 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 DOSFAM::Cardinality(PGLOBAL g)
- {
- return (g) ? -1 : 0;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/* Note: This function is not really implemented yet. */
-/***********************************************************************/
-int DOSFAM::MaxBlkSize(PGLOBAL g, int s)
- {
- return s;
- } // end of MaxBlkSize
-
-/***********************************************************************/
-/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
-/***********************************************************************/
-bool DOSFAM::OpenTableFile(PGLOBAL g)
- {
- char opmode[4], filename[_MAX_PATH];
-//int ftype = Tdbp->GetFtype();
- MODE mode = Tdbp->Mode;
- PDBUSER dbuserp = PlgGetUser(g);
-
- // This is required when using Unix files under Windows
- Bin = (Ending == 1);
-
- switch (mode) {
- case MODE_READ:
- strcpy(opmode, "r");
- break;
- case MODE_DELETE:
- if (!Tdbp->Next) {
- // Store the number of deleted lines
- DelRows = Cardinality(g);
-
- if (Blocked) {
- // Cardinality must return 0
- Block = 0;
- Last = Nrec;
- } // endif blocked
-
- // This will erase the entire file
- strcpy(opmode, "w");
- Tdbp->ResetSize();
- break;
- } // endif
-
- // Selective delete, pass thru
- Bin = true;
- case MODE_UPDATE:
- if ((UseTemp = Tdbp->IsUsingTemp(g))) {
- strcpy(opmode, "r");
- Bin = true;
- } else
- strcpy(opmode, "r+");
-
- break;
- case MODE_INSERT:
- strcpy(opmode, "a+");
- break;
- default:
- sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
- return true;
- } // endswitch Mode
-
- // For blocked I/O or for moving lines, open the table in binary
- strcat(opmode, (Blocked || Bin) ? "b" : "t");
-
- // Now open the file stream
- 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 open Stream=%p mode=%s\n", filename, Stream, opmode);
-
- To_Fb = dbuserp->Openlist; // Keep track of File block
-
- /*********************************************************************/
- /* Allocate the line buffer. For mode Delete a bigger buffer has to */
- /* be allocated because is it also used to move lines into the file.*/
- /*********************************************************************/
- return AllocateBuffer(g);
- } // end of OpenTableFile
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool DOSFAM::AllocateBuffer(PGLOBAL g)
- {
- MODE mode = Tdbp->Mode;
-
- // Lrecl does not include line ending
- Buflen = Lrecl + Ending + ((Bin) ? 1 : 0);
-
- if (trace)
- htrc("SubAllocating a buffer of %d bytes\n", Buflen);
-
- To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (UseTemp || mode == MODE_DELETE) {
- // Have a big buffer to move lines
- Dbflen = Buflen * DOS_BUFF_LEN;
- DelBuf = PlugSubAlloc(g, NULL, Dbflen);
- } else if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Prepare the buffer so eventual gaps are filled with blanks. */
- /*******************************************************************/
- memset(To_Buf, ' ', Buflen);
- To_Buf[Buflen - 2] = '\n';
- To_Buf[Buflen - 1] = '\0';
- } // endif's mode
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int DOSFAM::GetRowID(void)
- {
- return Rows;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int DOSFAM::GetPos(void)
- {
- return Fpos;
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: return the position of next record. */
-/***********************************************************************/
-int DOSFAM::GetNextPos(void)
- {
- return ftell(Stream);
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool DOSFAM::SetPos(PGLOBAL g, int pos)
- {
- Fpos = pos;
-
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
- return true;
- } // endif
-
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/***********************************************************************/
-bool DOSFAM::RecordPos(PGLOBAL g)
- {
- if ((Fpos = ftell(Stream)) < 0) {
- sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
- return true;
- } // endif Fpos
-
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int DOSFAM::SkipRecord(PGLOBAL g, bool header)
- {
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- // Skip this record
- if (!fgets(To_Buf, Buflen, Stream)) {
- if (feof(Stream))
- return RC_EF;
-
-#if defined(UNIX) || defined(UNIV_LINUX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
- return RC_FX;
- } // endif fgets
-
- // Update progress information
- dup->ProgCur = GetPos();
-
- if (header) {
- // For Delete
- Fpos = ftell(Stream);
-
- if (!UseTemp)
- Tpos = Spos = Fpos; // No need to move header
-
- } // endif header
-
-#if defined(THREAD)
- return RC_NF; // To have progress info
-#else
- return RC_OK; // To loop locally
-#endif
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a text file. */
-/***********************************************************************/
-int DOSFAM::ReadBuffer(PGLOBAL g)
- {
- char *p;
- int rc;
-
- if (!Stream)
- return RC_EF;
-
- if (trace > 1)
- htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
- Tdbp, Tdbp->To_Line, Placed);
-
- if (!Placed) {
- /*******************************************************************/
- /* Record file position in case of UPDATE or DELETE. */
- /*******************************************************************/
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (RecordPos(g))
- return RC_FX;
-
- CurBlk = (int)Rows++;
-
- if (trace > 1)
- htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* Check whether optimization on ROWID */
- /* 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:
- // Skip this record
- if ((rc = SkipRecord(g, FALSE)) != RC_OK)
- return rc;
-
- goto next;
- } // endswitch rc
-#endif // BLK_INDX
- } else
- Placed = false;
-
- if (trace > 1)
- htrc(" About to read: stream=%p To_Buf=%p Buflen=%d\n",
- Stream, To_Buf, Buflen);
-
- if (fgets(To_Buf, Buflen, Stream)) {
- p = To_Buf + strlen(To_Buf) - 1;
-
- if (trace > 1)
- htrc(" Read: To_Buf=%p p=%c\n", To_Buf, To_Buf, p);
-
-#if defined(UNIX)
- if (true) {
- // Data files can be imported from Windows (having CRLF)
-#else
- if (Bin) {
- // Data file is read in binary so CRLF remains
-#endif
- if (*p == '\n' || *p == '\r') {
- // is this enough for Unix ???
- *p = '\0'; // Eliminate ending CR or LF character
-
- if (p > To_Buf) {
- // is this enough for Unix ???
- p--;
-
- if (*p == '\n' || *p == '\r')
- *p = '\0'; // Eliminate ending CR or LF character
-
- } // endif To_Buf
-
- } // endif p
-
- } else if (*p == '\n')
- *p = '\0'; // Eliminate ending new-line character
-
- if (trace > 1)
- htrc(" To_Buf='%s'\n", To_Buf);
-
- strcpy(Tdbp->To_Line, To_Buf);
- num_read++;
- rc = RC_OK;
- } else if (feof(Stream)) {
- rc = RC_EF;
- } else {
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
-
- if (trace)
- htrc("%s\n", g->Message);
-
- rc = RC_FX;
- } // endif's fgets
-
- if (trace > 1)
- htrc("ReadBuffer: rc=%d\n", rc);
-
- IsRead = true;
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for DOS access method. */
-/***********************************************************************/
-int DOSFAM::WriteBuffer(PGLOBAL g)
- {
- char *crlf = "\n";
- int curpos = 0;
- bool moved = true;
-
- // T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
- if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
- if (OpenTempFile(g))
- return RC_FX;
-
- } else
- T_Stream = Stream;
-
- if (Tdbp->Mode == MODE_UPDATE) {
- /*******************************************************************/
- /* Here we simply rewrite a record on itself. There are two cases */
- /* were another method should be used, a/ when Update apply to */
- /* the whole file, b/ when updating the last field of a variable */
- /* length file. The method could be to rewrite a new file, then */
- /* to erase the old one and rename the new updated file. */
- /*******************************************************************/
- curpos = ftell(Stream);
-
- if (trace)
- htrc("Last : %d cur: %d\n", Fpos, curpos);
-
- if (UseTemp) {
- /*****************************************************************/
- /* We are using a temporary file. Before writing the updated */
- /* record, we must eventually copy all the intermediate records */
- /* that have not been updated. */
- /*****************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- Spos = curpos; // New start position
- } else
- // Update is directly written back into the file,
- // with this (fast) method, record size cannot change.
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif
-
- } // endif mode
-
- /*********************************************************************/
- /* Prepare the write buffer. */
- /*********************************************************************/
-#if defined(WIN32)
- if (Bin)
- crlf = "\r\n";
-#endif // WIN32
- strcat(strcpy(To_Buf, Tdbp->To_Line), crlf);
-
- /*********************************************************************/
- /* Now start the writing process. */
- /*********************************************************************/
- if ((fputs(To_Buf, T_Stream)) == EOF) {
- sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
- return RC_FX;
- } // endif EOF
-
- if (Tdbp->Mode == MODE_UPDATE && moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
- return RC_FX;
- } // endif
-
- if (trace)
- htrc("write done\n");
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Data Base delete line routine for DOS and BLK access methods. */
-/***********************************************************************/
-int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
- {
- bool moved;
- int curpos = ftell(Stream);
-
- /*********************************************************************/
- /* 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 will be experimented, but method 1 must be used for Unix as */
- /* the function needed to erase trailing records is not available. */
- /*********************************************************************/
- if (trace)
- htrc(
- "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
- irc, UseTemp, curpos, Fpos, Tpos, Spos);
-
- if (irc != RC_OK) {
- /*******************************************************************/
- /* EOF: position Fpos at the end-of-file position. */
- /*******************************************************************/
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
-
- if (trace)
- htrc("Fpos placed at file end=%d\n", Fpos);
-
- } // endif irc
-
- if (Tpos == Spos) {
- /*******************************************************************/
- /* First line to delete, Open temporary file. */
- /*******************************************************************/
- if (UseTemp) {
- 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. */
- /*****************************************************************/
- T_Stream = Stream;
- Spos = Tpos = Fpos;
- } // endif UseTemp
-
- } // endif Tpos == Spos
-
- /*********************************************************************/
- /* Move any intermediate lines. */
- /*********************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- if (irc == RC_OK) {
- /*******************************************************************/
- /* Reposition the file pointer and set Spos. */
- /*******************************************************************/
- if (!UseTemp || moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif
-
- Spos = GetNextPos(); // New start position
-
- if (trace)
- htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
-
- } else {
- /*******************************************************************/
- /* Last call after EOF has been reached. */
- /* The UseTemp case is treated in CloseTableFile. */
- /*******************************************************************/
- 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 Text files and other OS's. */
- /*****************************************************************/
- char filename[_MAX_PATH];
- int h; // File handle, return code
-
- PlugSetPath(filename, To_File, Tdbp->GetPath());
- /*rc=*/ PlugCloseFile(g, To_Fb);
-
- 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)) {
- sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#else
- if (chsize(h, Tpos)) {
- 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);
-
- } // endif !UseTemp
-
- } // endif irc
-
- return RC_OK; // All is correct
- } // end of DeleteRecords
-
-/***********************************************************************/
-/* Open a temporary file used while updating or deleting. */
-/***********************************************************************/
-bool DOSFAM::OpenTempFile(PGLOBAL g)
- {
- char 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 (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
- 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. */
-/* This works only for file open in binary mode. */
-/***********************************************************************/
-bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
- {
- int n;
- size_t req, len;
-
- for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
- if (!UseTemp || !*b)
- if (fseek(Stream, Spos, SEEK_SET)) {
- sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
- return true;
- } // endif
-
- req = (size_t)min(n, Dbflen);
- len = fread(DelBuf, 1, 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)
- if (fseek(T_Stream, Tpos, SEEK_SET)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
- sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
- return true;
- } // endif
-
- if (trace)
- htrc("after write pos=%d\n", ftell(Stream));
-
- 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 file and rename the new temp file. */
-/***********************************************************************/
-int DOSFAM::RenameTempFile(PGLOBAL g)
- {
- char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
- int rc;
-
- if (!To_Fbt)
- return RC_INFO; // Nothing to do ???
-
- // This loop is necessary because, in case of join,
- // To_File can have been open several times.
- for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
- rc = PlugCloseFile(g, fb);
-
- tempname = (char*)To_Fbt->Fname;
- PlugSetPath(filename, To_File, 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
- } else
- rc = RC_OK;
-
- return rc;
- } // end of RenameTempFile
-
-/***********************************************************************/
-/* Table file close routine for DOS access method. */
-/***********************************************************************/
-void DOSFAM::CloseTableFile(PGLOBAL g)
- {
- int rc;
-
- if (UseTemp && T_Stream) {
- if (Tdbp->Mode == MODE_UPDATE) {
- // Copy eventually remaining lines
- bool b;
-
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
- rc = MoveIntermediateLines(g, &b);
- } // endif Mode
-
- // Delete the old file and rename the new temp file.
- RenameTempFile(g); // Also close all files
- } else {
- rc = PlugCloseFile(g, To_Fb);
-
- if (trace)
- htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
-
- } // endif UseTemp
-
- Stream = NULL; // So we can know whether table is open
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for DOS access method. */
-/***********************************************************************/
-void DOSFAM::Rewind(void)
- {
- rewind(Stream);
- Rows = 0;
- OldBlk = CurBlk = -1;
- } // end of Rewind
-
-/* --------------------------- Class BLKFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
- {
- Blocked = true;
- Block = tdp->GetBlock();
- Last = tdp->GetLast();
- Nrec = tdp->GetElemt();
- Closing = false;
-#if defined(BLK_INDX)
- BlkPos = tdp->GetTo_Pos();
-#else // !BLK_INDX
- BlkPos = NULL;
-#endif // !BLK_INDX
- CurLine = NULL;
- NxtLine = NULL;
- OutBuf = NULL;
- } // end of BLKFAM standard constructor
-
-BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
- {
- Closing = txfp->Closing;
- CurLine = txfp->CurLine;
- NxtLine = txfp->NxtLine;
- OutBuf = txfp->OutBuf;
- } // end of BLKFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void BLKFAM::Reset(void)
- {
- DOSFAM::Reset();
- Closing = false;
- } // end of Reset
-
-/***********************************************************************/
-/* 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 BLKFAM::Cardinality(PGLOBAL g)
- {
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/***********************************************************************/
-int BLKFAM::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 defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == Block - 1) ? Last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == Block - 1) ? Last : Nrec;
-#endif // !BLK_INDX
-
- CurBlk = savcur;
- return size;
- } // end of MaxBlkSize
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete or when a temp file is */
-/* used another big buffer has to be allocated because is it used */
-/* to move or update the lines into the (temp) file. */
-/***********************************************************************/
-bool BLKFAM::AllocateBuffer(PGLOBAL g)
- {
- int len;
- MODE mode = Tdbp->GetMode();
-
- // For variable length files, Lrecl does not include CRLF
- len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
- Buflen = len * Nrec;
- CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (UseTemp || mode == MODE_DELETE) {
- if (mode == MODE_UPDATE)
- OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
-
- Dbflen = Buflen;
- DelBuf = PlugSubAlloc(g, NULL, Dbflen);
- } else if (mode == MODE_INSERT)
- Rbuf = Nrec; // To be used by WriteDB
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int BLKFAM::GetRowID(void)
- {
- return CurNum + Nrec * CurBlk + 1;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int BLKFAM::GetPos(void)
- {
- return (CurNum + Nrec * CurBlk); // Computed file index
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: called by DeleteRecords. */
-/***********************************************************************/
-int BLKFAM::GetNextPos(void)
- {
- return Fpos + NxtLine - CurLine;
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool BLKFAM::SetPos(PGLOBAL g, int pos)
- {
- if (pos < 0) {
- strcpy(g->Message, MSG(INV_REC_POS));
- return true;
- } // endif recpos
-
- CurBlk = pos / Nrec;
- CurNum = pos % Nrec;
-#if defined(_DEBUG)
- num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
-#endif
-
- // Indicate the table position was externally set
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/* Not used yet for blocked tables. */
-/***********************************************************************/
-bool BLKFAM::RecordPos(PGLOBAL g)
- {
- Fpos = (CurNum + Nrec * CurBlk); // Computed file index
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int BLKFAM::SkipRecord(PGLOBAL g, bool header)
- {
- if (header) {
- // For Delete
- Fpos = BlkPos[0]; // First block starts after the header
-
- if (!UseTemp)
- Tpos = Spos = Fpos; // No need to move header
-
- } // endif header
-
- OldBlk = -2; // To force fseek on first block
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a text file. */
-/***********************************************************************/
-int BLKFAM::ReadBuffer(PGLOBAL g)
- {
-#if defined(BLK_INDX)
- int i, n, rc = RC_OK;
-
- /*********************************************************************/
- /* Sequential reading when Placed is not true. */
- /*********************************************************************/
- if (Placed) {
- Placed = false;
- } else if (++CurNum < Rbuf) {
- CurLine = NxtLine;
-
- // Get the position of the next line in the buffer
- while (*NxtLine++ != '\n') ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- goto fin;
- } else if (Rbuf < Nrec && CurBlk != -1) {
- return RC_EF;
- } else {
- /*******************************************************************/
- /* New block. */
- /*******************************************************************/
- CurNum = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*******************************************************************/
- /* Before reading a new block, check whether block optimization */
- /* 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
-
- } // endif's
-
- if (OldBlk == CurBlk)
- goto ok; // Block is already there
-
- // fseek is required only in non sequential reading
- if (CurBlk != OldBlk + 1)
- if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
- return RC_FX;
- } // endif fseek
-
- // Calculate the length of block to read
- BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
-
- if (trace)
- htrc("File position is now %d\n", ftell(Stream));
-
- // Read the entire next block
- n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
-
- if (n == BlkLen) {
-// ReadBlks++;
- num_read++;
- Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
-
- ok:
- rc = RC_OK;
-
- // Get the position of the current line
- for (i = 0, CurLine = To_Buf; i < CurNum; i++)
- while (*CurLine++ != '\n') ; // What about Unix ???
-
- // Now get the position of the next line
- for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- } else if (feof(Stream)) {
- rc = RC_EF;
- } else {
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
-
- if (trace)
- htrc("%s\n", g->Message);
-
- return RC_FX;
- } // endelse
-
- OldBlk = CurBlk; // Last block actually read
- IsRead = true; // Is read indeed
-
- fin:
- // Store the current record file position for Delete and Update
- Fpos = BlkPos[CurBlk] + CurLine - To_Buf;
- return rc;
-#else // !BLK_POS
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
-#endif // !BLK_POS
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for the blocked DOS access method. */
-/* Update is directly written back into the file, */
-/* with this (fast) method, record size cannot change. */
-/***********************************************************************/
-int BLKFAM::WriteBuffer(PGLOBAL g)
- {
- if (Tdbp->GetMode() == MODE_INSERT) {
- /*******************************************************************/
- /* In Insert mode, blocks are added sequentially to the file end. */
- /*******************************************************************/
- if (!Closing) { // Add line to the write buffer
- strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
-
- if (++CurNum != Rbuf) {
- CurLine += strlen(CurLine);
- return RC_OK; // We write only full blocks
- } // endif CurNum
-
- } // endif Closing
-
- // Now start the writing process.
- NxtLine = CurLine + strlen(CurLine);
- BlkLen = NxtLine - To_Buf;
-
- if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- Closing = true; // To tell CloseDB about a Write error
- return RC_FX;
- } // endif size
-
- CurBlk++;
- CurNum = 0;
- CurLine = To_Buf;
- } else {
- /*******************************************************************/
- /* Mode == MODE_UPDATE. */
- /*******************************************************************/
- char *crlf;
- size_t len;
- int curpos = ftell(Stream);
- bool moved = true;
-
- // T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
- if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
- if (OpenTempFile(g))
- return RC_FX;
-
- } else
- T_Stream = Stream;
-
- if (UseTemp) {
- /*****************************************************************/
- /* We are using a temporary file. Before writing the updated */
- /* record, we must eventually copy all the intermediate records */
- /* that have not been updated. */
- /*****************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- Spos = GetNextPos(); // New start position
-
- // Prepare the output buffer
-#if defined(WIN32)
- crlf = "\r\n";
-#else
- crlf = "\n";
-#endif // WIN32
- strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
- len = strlen(OutBuf);
- } else {
- if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif fseek
-
- // Replace the line inside read buffer (length has not changed)
- memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
- OutBuf = CurLine;
- len = (size_t)(NxtLine - CurLine);
- } // endif UseTemp
-
- if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- return RC_FX;
- } // endif fwrite
-
- if (moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
- return RC_FX;
- } // endif
-
- } // endif Mode
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Table file close routine for DOS access method. */
-/***********************************************************************/
-void BLKFAM::CloseTableFile(PGLOBAL g)
- {
- int rc, wrc = RC_OK;
-
- if (UseTemp && T_Stream) {
- if (Tdbp->GetMode() == MODE_UPDATE) {
- // Copy eventually remaining lines
- bool b;
-
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
- rc = MoveIntermediateLines(g, &b);
- } else
- rc = RC_OK;
-
- if (rc == RC_OK)
- // Delete the old file and rename the new temp file.
- rc = RenameTempFile(g); // Also close all files
- else
- rc = PlugCloseFile(g, To_Fb);
-
- } else {
- // Closing is True if last Write was in error
- if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
- // Some more inserted lines remain to be written
- Rbuf = CurNum--;
- Closing = true;
- wrc = WriteBuffer(g);
- } else if (Modif && !Closing) {
- // Last updated block remains to be written
- Closing = true;
- wrc = ReadBuffer(g);
- } // endif's
-
- rc = PlugCloseFile(g, To_Fb);
-
- if (trace)
- htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
- To_File, Tdbp->GetMode(), wrc, rc);
-
- } // endif UseTemp
-
- Stream = NULL; // So we can know whether table is open
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for DOS access method. */
-/* Note: commenting out OldBlk = -1 has two advantages: */
-/* 1 - It forces fseek on first block, thus suppressing the need to */
-/* rewind the file, anyway unuseful when second pass if indexed. */
-/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
-/***********************************************************************/
-void BLKFAM::Rewind(void)
- {
-//rewind(Stream); will be placed by fseek
- CurBlk = -1;
- CurNum = Rbuf;
-//OldBlk = -1; commented out in case we reuse last read block
-//Rbuf = 0; commented out in case we reuse last read block
- } // end of Rewind
-
+/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
+/* PROGRAM NAME: FILAMTXT */
+/* ------------- */
+/* Version 1.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the Text file access method classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif // __BORLANDC__
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX) || defined(UNIV_LINUX)
+#include <errno.h>
+#include <unistd.h>
+//#if !defined(sun) // Sun has the ftruncate fnc.
+//#define USETEMP // Force copy mode for DELETE
+//#endif // !sun
+#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 "plgdbsem.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+#include "osutil.h"
+#define _fileno fileno
+#define _O_RDONLY O_RDONLY
+#endif
+
+extern int num_read, num_there, num_eq[2]; // Statistics
+extern "C" int trace;
+
+/* --------------------------- Class TXTFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+TXTFAM::TXTFAM(PDOSDEF tdp)
+ {
+ Tdbp = NULL;
+ To_Fb = NULL;
+ To_File = tdp->Fn;
+ Lrecl = tdp->Lrecl;
+ Placed = false;
+ IsRead = true;
+ Blocked = false;
+ To_Buf = NULL;
+ DelBuf = NULL;
+ BlkPos = NULL;
+ BlkLen = 0;
+ Buflen = 0;
+ Dbflen = 0;
+ Rows = 0;
+ DelRows = 0;
+ Headlen = 0;
+ Block = 0;
+ Last = 0;
+ Nrec = 1;
+ OldBlk = -1;
+ CurBlk = -1;
+ ReadBlks = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ Modif = 0;
+ Blksize = 0;
+ Padded = false;
+ Eof = tdp->Eof;
+ Ending = tdp->Ending;
+ CrLf = (char*)(Ending == 2 ? "\r\n" : "\n");
+ } // end of TXTFAM standard constructor
+
+TXTFAM::TXTFAM(PTXF txfp)
+ {
+ Tdbp = txfp->Tdbp;
+ To_Fb = txfp->To_Fb;
+ To_File = txfp->To_File;
+ Lrecl = txfp->Lrecl;
+ Placed = txfp->Placed;
+ IsRead = txfp->IsRead;
+ Blocked = txfp->Blocked;
+ To_Buf = txfp->To_Buf;
+ DelBuf = txfp->DelBuf;
+ BlkPos = txfp->BlkPos;
+ BlkLen = txfp->BlkLen;
+ Buflen = txfp->Buflen;
+ Dbflen = txfp->Dbflen;
+ Rows = txfp->Rows;
+ DelRows = txfp->DelRows;
+ Headlen = txfp->Headlen;
+ Block = txfp->Block;
+ Last = txfp->Last;
+ Nrec = txfp->Nrec;
+ OldBlk = txfp->OldBlk;
+ CurBlk = txfp->CurBlk;
+ ReadBlks = txfp->ReadBlks;
+ CurNum = txfp->CurNum;
+ Rbuf = txfp->Rbuf;
+ Modif = txfp->Modif;
+ Blksize = txfp->Blksize;
+ Padded = txfp->Padded;
+ Eof = txfp->Eof;
+ Ending = txfp->Ending;
+ } // end of TXTFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void TXTFAM::Reset(void)
+ {
+ Rows = 0;
+ DelRows = 0;
+ OldBlk = -1;
+ CurBlk = -1;
+ ReadBlks = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ Modif = 0;
+ Placed = false;
+ } // end of Reset
+
+/***********************************************************************/
+/* TXT GetFileLength: returns file size in number of bytes. */
+/***********************************************************************/
+int TXTFAM::GetFileLength(PGLOBAL g)
+ {
+ char filename[_MAX_PATH];
+ int h;
+ int len;
+
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+ h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
+
+ if (trace)
+ htrc("GetFileLength: fn=%s h=%d\n", filename, h);
+
+ if (h == -1) {
+ if (errno != ENOENT) {
+ if (trace)
+ htrc("%s\n", g->Message);
+ len = -1;
+ }
+ else
+ {
+ len = 0; // File does not exist yet
+ g->Message[0]= '\0';
+ }
+ } else {
+ if ((len = _filelength(h)) < 0)
+ sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
+
+ if (Eof && len)
+ len--; // Do not count the EOF character
+
+ close(h);
+ } // endif h
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* 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). */
+/* Note: This function is meant only for fixed length files but is */
+/* placed here to be available to FIXFAM and MPXFAM classes. */
+/***********************************************************************/
+int TXTFAM::Cardinality(PGLOBAL g)
+ {
+ if (g) {
+ int card = -1;
+ int len = GetFileLength(g);
+
+ if (len >= 0) {
+ if (Padded && Blksize) {
+ if (!(len % Blksize))
+ card = (len / Blksize) * Nrec;
+ else
+ sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
+
+ } else {
+ if (!(len % Lrecl))
+ card = len / (int)Lrecl; // Fixed length file
+ else
+ sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
+
+ } // endif Padded
+
+ if (trace)
+ htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
+ card, len, Lrecl);
+
+ } else
+ card = 0;
+
+ // Set number of blocks for later use
+ Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
+ return card;
+ } else
+ return 1;
+
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/* Note: This function is meant only for fixed length files but is */
+/* placed here to be available to FIXFAM and MPXFAM classes. */
+/***********************************************************************/
+int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
+ {
+ int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
+ int size, last = s - blm1 * Nrec;
+
+ // 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 == blm1) ? last : Nrec;
+ else if (rc == RC_EF)
+ break;
+
+ CurBlk = savcur;
+ return size;
+ } // end of MaxBlkSize
+
+/* --------------------------- Class DOSFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
+ {
+ To_Fbt = NULL;
+ Stream = NULL;
+ T_Stream = NULL;
+ Fpos = Spos = Tpos = 0;
+ UseTemp = false;
+ Bin = false;
+ } // end of DOSFAM standard constructor
+
+DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
+ {
+ To_Fbt = tdfp->To_Fbt;
+ Stream = tdfp->Stream;
+ T_Stream = tdfp->T_Stream;
+ Fpos = tdfp->Fpos;
+ Spos = tdfp->Spos;
+ Tpos = tdfp->Tpos;
+ UseTemp = tdfp->UseTemp;
+ Bin = tdfp->Bin;
+ } // end of DOSFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void DOSFAM::Reset(void)
+ {
+ TXTFAM::Reset();
+ Bin = false;
+ Fpos = Tpos = Spos = 0;
+ } // end of Reset
+
+/***********************************************************************/
+/* DOS GetFileLength: returns file size in number of bytes. */
+/***********************************************************************/
+int DOSFAM::GetFileLength(PGLOBAL g)
+ {
+ int len;
+
+ if (!Stream)
+ len = TXTFAM::GetFileLength(g);
+ else
+ if ((len = _filelength(_fileno(Stream))) < 0)
+ sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
+
+ if (trace)
+ htrc("File length=%d\n", len);
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* 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 DOSFAM::Cardinality(PGLOBAL g)
+ {
+ return (g) ? -1 : 0;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/* Note: This function is not really implemented yet. */
+/***********************************************************************/
+int DOSFAM::MaxBlkSize(PGLOBAL g, int s)
+ {
+ return s;
+ } // end of MaxBlkSize
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
+/***********************************************************************/
+bool DOSFAM::OpenTableFile(PGLOBAL g)
+ {
+ char opmode[4], filename[_MAX_PATH];
+//int ftype = Tdbp->GetFtype();
+ MODE mode = Tdbp->Mode;
+ PDBUSER dbuserp = PlgGetUser(g);
+
+ // This is required when using Unix files under Windows
+ Bin = (Ending == 1);
+
+ switch (mode) {
+ case MODE_READ:
+ strcpy(opmode, "r");
+ break;
+ case MODE_DELETE:
+ if (!Tdbp->Next) {
+ // Store the number of deleted lines
+ DelRows = Cardinality(g);
+
+ if (Blocked) {
+ // Cardinality must return 0
+ Block = 0;
+ Last = Nrec;
+ } // endif blocked
+
+ // This will erase the entire file
+ strcpy(opmode, "w");
+ Tdbp->ResetSize();
+ break;
+ } // endif
+
+ // Selective delete, pass thru
+ Bin = true;
+ case MODE_UPDATE:
+ if ((UseTemp = Tdbp->IsUsingTemp(g))) {
+ strcpy(opmode, "r");
+ Bin = true;
+ } else
+ strcpy(opmode, "r+");
+
+ break;
+ case MODE_INSERT:
+ strcpy(opmode, "a+");
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
+ return true;
+ } // endswitch Mode
+
+ // For blocked I/O or for moving lines, open the table in binary
+ strcat(opmode, (Blocked || Bin) ? "b" : "t");
+
+ // Now open the file stream
+ 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 open Stream=%p mode=%s\n", filename, Stream, opmode);
+
+ To_Fb = dbuserp->Openlist; // Keep track of File block
+
+ /*********************************************************************/
+ /* Allocate the line buffer. For mode Delete a bigger buffer has to */
+ /* be allocated because is it also used to move lines into the file.*/
+ /*********************************************************************/
+ return AllocateBuffer(g);
+ } // end of OpenTableFile
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool DOSFAM::AllocateBuffer(PGLOBAL g)
+ {
+ MODE mode = Tdbp->Mode;
+
+ // Lrecl does not include line ending
+ Buflen = Lrecl + Ending + ((Bin) ? 1 : 0);
+
+ if (trace)
+ htrc("SubAllocating a buffer of %d bytes\n", Buflen);
+
+ To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (UseTemp || mode == MODE_DELETE) {
+ // Have a big buffer to move lines
+ Dbflen = Buflen * DOS_BUFF_LEN;
+ DelBuf = PlugSubAlloc(g, NULL, Dbflen);
+ } else if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Prepare the buffer so eventual gaps are filled with blanks. */
+ /*******************************************************************/
+ memset(To_Buf, ' ', Buflen);
+ To_Buf[Buflen - 2] = '\n';
+ To_Buf[Buflen - 1] = '\0';
+ } // endif's mode
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int DOSFAM::GetRowID(void)
+ {
+ return Rows;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int DOSFAM::GetPos(void)
+ {
+ return Fpos;
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: return the position of next record. */
+/***********************************************************************/
+int DOSFAM::GetNextPos(void)
+ {
+ return ftell(Stream);
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool DOSFAM::SetPos(PGLOBAL g, int pos)
+ {
+ Fpos = pos;
+
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
+ return true;
+ } // endif
+
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/***********************************************************************/
+bool DOSFAM::RecordPos(PGLOBAL g)
+ {
+ if ((Fpos = ftell(Stream)) < 0) {
+ sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
+ return true;
+ } // endif Fpos
+
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int DOSFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ // Skip this record
+ if (!fgets(To_Buf, Buflen, Stream)) {
+ if (feof(Stream))
+ return RC_EF;
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+ return RC_FX;
+ } // endif fgets
+
+ // Update progress information
+ dup->ProgCur = GetPos();
+
+ if (header) {
+ // For Delete
+ Fpos = ftell(Stream);
+
+ if (!UseTemp)
+ Tpos = Spos = Fpos; // No need to move header
+
+ } // endif header
+
+#if defined(THREAD)
+ return RC_NF; // To have progress info
+#else
+ return RC_OK; // To loop locally
+#endif
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a text file. */
+/***********************************************************************/
+int DOSFAM::ReadBuffer(PGLOBAL g)
+ {
+ char *p;
+ int rc;
+
+ if (!Stream)
+ return RC_EF;
+
+ if (trace > 1)
+ htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
+ Tdbp, Tdbp->To_Line, Placed);
+
+ if (!Placed) {
+ /*******************************************************************/
+ /* Record file position in case of UPDATE or DELETE. */
+ /*******************************************************************/
+ next:
+ if (RecordPos(g))
+ return RC_FX;
+
+ CurBlk = (int)Rows++;
+
+ if (trace > 1)
+ htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
+
+ /*******************************************************************/
+ /* Check whether optimization on ROWID */
+ /* 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:
+ // Skip this record
+ if ((rc = SkipRecord(g, FALSE)) != RC_OK)
+ return rc;
+
+ goto next;
+ } // endswitch rc
+
+ } else
+ Placed = false;
+
+ if (trace > 1)
+ htrc(" About to read: stream=%p To_Buf=%p Buflen=%d\n",
+ Stream, To_Buf, Buflen);
+
+ if (fgets(To_Buf, Buflen, Stream)) {
+ p = To_Buf + strlen(To_Buf) - 1;
+
+ if (trace > 1)
+ htrc(" Read: To_Buf=%p p=%c\n", To_Buf, To_Buf, p);
+
+#if defined(UNIX)
+ if (true) {
+ // Data files can be imported from Windows (having CRLF)
+#else
+ if (Bin) {
+ // Data file is read in binary so CRLF remains
+#endif
+ if (*p == '\n' || *p == '\r') {
+ // is this enough for Unix ???
+ *p = '\0'; // Eliminate ending CR or LF character
+
+ if (p > To_Buf) {
+ // is this enough for Unix ???
+ p--;
+
+ if (*p == '\n' || *p == '\r')
+ *p = '\0'; // Eliminate ending CR or LF character
+
+ } // endif To_Buf
+
+ } // endif p
+
+ } else if (*p == '\n')
+ *p = '\0'; // Eliminate ending new-line character
+
+ if (trace > 1)
+ htrc(" To_Buf='%s'\n", To_Buf);
+
+ strcpy(Tdbp->To_Line, To_Buf);
+ num_read++;
+ rc = RC_OK;
+ } else if (feof(Stream)) {
+ rc = RC_EF;
+ } else {
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ rc = RC_FX;
+ } // endif's fgets
+
+ if (trace > 1)
+ htrc("ReadBuffer: rc=%d\n", rc);
+
+ IsRead = true;
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for DOS access method. */
+/***********************************************************************/
+int DOSFAM::WriteBuffer(PGLOBAL g)
+ {
+ char *crlf = "\n";
+ int curpos = 0;
+ bool moved = true;
+
+ // T_Stream is the temporary stream or the table file stream itself
+ if (!T_Stream)
+ if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
+ if (OpenTempFile(g))
+ return RC_FX;
+
+ } else
+ T_Stream = Stream;
+
+ if (Tdbp->Mode == MODE_UPDATE) {
+ /*******************************************************************/
+ /* Here we simply rewrite a record on itself. There are two cases */
+ /* were another method should be used, a/ when Update apply to */
+ /* the whole file, b/ when updating the last field of a variable */
+ /* length file. The method could be to rewrite a new file, then */
+ /* to erase the old one and rename the new updated file. */
+ /*******************************************************************/
+ curpos = ftell(Stream);
+
+ if (trace)
+ htrc("Last : %d cur: %d\n", Fpos, curpos);
+
+ if (UseTemp) {
+ /*****************************************************************/
+ /* We are using a temporary file. Before writing the updated */
+ /* record, we must eventually copy all the intermediate records */
+ /* that have not been updated. */
+ /*****************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ Spos = curpos; // New start position
+ } else
+ // Update is directly written back into the file,
+ // with this (fast) method, record size cannot change.
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif
+
+ } // endif mode
+
+ /*********************************************************************/
+ /* Prepare the write buffer. */
+ /*********************************************************************/
+#if defined(WIN32)
+ if (Bin)
+ crlf = "\r\n";
+#endif // WIN32
+ strcat(strcpy(To_Buf, Tdbp->To_Line), crlf);
+
+ /*********************************************************************/
+ /* Now start the writing process. */
+ /*********************************************************************/
+ if ((fputs(To_Buf, T_Stream)) == EOF) {
+ sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif EOF
+
+ if (Tdbp->Mode == MODE_UPDATE && moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif
+
+ if (trace)
+ htrc("write done\n");
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Data Base delete line routine for DOS and BLK access methods. */
+/***********************************************************************/
+int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
+ {
+ bool moved;
+ int curpos = ftell(Stream);
+
+ /*********************************************************************/
+ /* 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 will be experimented, but method 1 must be used for Unix as */
+ /* the function needed to erase trailing records is not available. */
+ /*********************************************************************/
+ if (trace)
+ htrc(
+ "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
+ irc, UseTemp, curpos, Fpos, Tpos, Spos);
+
+ if (irc != RC_OK) {
+ /*******************************************************************/
+ /* EOF: position Fpos at the end-of-file position. */
+ /*******************************************************************/
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+
+ if (trace)
+ htrc("Fpos placed at file end=%d\n", Fpos);
+
+ } // endif irc
+
+ if (Tpos == Spos) {
+ /*******************************************************************/
+ /* First line to delete, Open temporary file. */
+ /*******************************************************************/
+ if (UseTemp) {
+ 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. */
+ /*****************************************************************/
+ T_Stream = Stream;
+ Spos = Tpos = Fpos;
+ } // endif UseTemp
+
+ } // endif Tpos == Spos
+
+ /*********************************************************************/
+ /* Move any intermediate lines. */
+ /*********************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ if (irc == RC_OK) {
+ /*******************************************************************/
+ /* Reposition the file pointer and set Spos. */
+ /*******************************************************************/
+ if (!UseTemp || moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif
+
+ Spos = GetNextPos(); // New start position
+
+ if (trace)
+ htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
+
+ } else {
+ /*******************************************************************/
+ /* Last call after EOF has been reached. */
+ /* The UseTemp case is treated in CloseTableFile. */
+ /*******************************************************************/
+ 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 Text files and other OS's. */
+ /*****************************************************************/
+ char filename[_MAX_PATH];
+ int h; // File handle, return code
+
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+ /*rc=*/ PlugCloseFile(g, To_Fb);
+
+ 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)) {
+ sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#else
+ if (chsize(h, Tpos)) {
+ 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);
+
+ } // endif !UseTemp
+
+ } // endif irc
+
+ return RC_OK; // All is correct
+ } // end of DeleteRecords
+
+/***********************************************************************/
+/* Open a temporary file used while updating or deleting. */
+/***********************************************************************/
+bool DOSFAM::OpenTempFile(PGLOBAL g)
+ {
+ char 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 (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
+ 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. */
+/* This works only for file open in binary mode. */
+/***********************************************************************/
+bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
+ {
+ int n;
+ size_t req, len;
+
+ for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
+ if (!UseTemp || !*b)
+ if (fseek(Stream, Spos, SEEK_SET)) {
+ sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ req = (size_t)min(n, Dbflen);
+ len = fread(DelBuf, 1, 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)
+ if (fseek(T_Stream, Tpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
+ sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ if (trace)
+ htrc("after write pos=%d\n", ftell(Stream));
+
+ 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 file and rename the new temp file. */
+/***********************************************************************/
+int DOSFAM::RenameTempFile(PGLOBAL g)
+ {
+ char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
+ int rc;
+
+ if (!To_Fbt)
+ return RC_INFO; // Nothing to do ???
+
+ // This loop is necessary because, in case of join,
+ // To_File can have been open several times.
+ for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
+ rc = PlugCloseFile(g, fb);
+
+ tempname = (char*)To_Fbt->Fname;
+ PlugSetPath(filename, To_File, 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
+ } else
+ rc = RC_OK;
+
+ return rc;
+ } // end of RenameTempFile
+
+/***********************************************************************/
+/* Table file close routine for DOS access method. */
+/***********************************************************************/
+void DOSFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc;
+
+ if (UseTemp && T_Stream) {
+ if (Tdbp->Mode == MODE_UPDATE) {
+ // Copy eventually remaining lines
+ bool b;
+
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+ rc = MoveIntermediateLines(g, &b);
+ } // endif Mode
+
+ // Delete the old file and rename the new temp file.
+ RenameTempFile(g); // Also close all files
+ } else {
+ rc = PlugCloseFile(g, To_Fb);
+
+ if (trace)
+ htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
+
+ } // endif UseTemp
+
+ Stream = NULL; // So we can know whether table is open
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for DOS access method. */
+/***********************************************************************/
+void DOSFAM::Rewind(void)
+ {
+ rewind(Stream);
+ Rows = 0;
+ OldBlk = CurBlk = -1;
+ } // end of Rewind
+
+/* --------------------------- Class BLKFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
+ {
+ Blocked = true;
+ Block = tdp->GetBlock();
+ Last = tdp->GetLast();
+ Nrec = tdp->GetElemt();
+ Closing = false;
+ BlkPos = tdp->GetTo_Pos();
+ CurLine = NULL;
+ NxtLine = NULL;
+ OutBuf = NULL;
+ } // end of BLKFAM standard constructor
+
+BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
+ {
+ Closing = txfp->Closing;
+ CurLine = txfp->CurLine;
+ NxtLine = txfp->NxtLine;
+ OutBuf = txfp->OutBuf;
+ } // end of BLKFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void BLKFAM::Reset(void)
+ {
+ DOSFAM::Reset();
+ Closing = false;
+ } // end of Reset
+
+/***********************************************************************/
+/* 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 BLKFAM::Cardinality(PGLOBAL g)
+ {
+ // Should not be called in this version
+ return (g) ? -1 : 0;
+//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/***********************************************************************/
+int BLKFAM::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
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete or when a temp file is */
+/* used another big buffer has to be allocated because is it used */
+/* to move or update the lines into the (temp) file. */
+/***********************************************************************/
+bool BLKFAM::AllocateBuffer(PGLOBAL g)
+ {
+ int len;
+ MODE mode = Tdbp->GetMode();
+
+ // For variable length files, Lrecl does not include CRLF
+ len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
+ Buflen = len * Nrec;
+ CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (UseTemp || mode == MODE_DELETE) {
+ if (mode == MODE_UPDATE)
+ OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
+
+ Dbflen = Buflen;
+ DelBuf = PlugSubAlloc(g, NULL, Dbflen);
+ } else if (mode == MODE_INSERT)
+ Rbuf = Nrec; // To be used by WriteDB
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int BLKFAM::GetRowID(void)
+ {
+ return CurNum + Nrec * CurBlk + 1;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int BLKFAM::GetPos(void)
+ {
+ return (CurNum + Nrec * CurBlk); // Computed file index
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: called by DeleteRecords. */
+/***********************************************************************/
+int BLKFAM::GetNextPos(void)
+ {
+ return Fpos + NxtLine - CurLine;
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool BLKFAM::SetPos(PGLOBAL g, int pos)
+ {
+ if (pos < 0) {
+ strcpy(g->Message, MSG(INV_REC_POS));
+ return true;
+ } // endif recpos
+
+ CurBlk = pos / Nrec;
+ CurNum = pos % Nrec;
+#if defined(_DEBUG)
+ num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
+#endif
+
+ // Indicate the table position was externally set
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/* Not used yet for blocked tables. */
+/***********************************************************************/
+bool BLKFAM::RecordPos(PGLOBAL g)
+ {
+ Fpos = (CurNum + Nrec * CurBlk); // Computed file index
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int BLKFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ if (header) {
+ // For Delete
+ Fpos = BlkPos[0]; // First block starts after the header
+
+ if (!UseTemp)
+ Tpos = Spos = Fpos; // No need to move header
+
+ } // endif header
+
+ OldBlk = -2; // To force fseek on first block
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a text file. */
+/***********************************************************************/
+int BLKFAM::ReadBuffer(PGLOBAL g)
+ {
+ int i, n, rc = RC_OK;
+
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Placed = false;
+ } else if (++CurNum < Rbuf) {
+ CurLine = NxtLine;
+
+ // Get the position of the next line in the buffer
+ while (*NxtLine++ != '\n') ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ goto fin;
+ } else if (Rbuf < Nrec && CurBlk != -1) {
+ return RC_EF;
+ } else {
+ /*******************************************************************/
+ /* New block. */
+ /*******************************************************************/
+ CurNum = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*******************************************************************/
+ /* Before reading a new block, check whether block optimization */
+ /* 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
+
+ } // endif's
+
+ if (OldBlk == CurBlk)
+ goto ok; // Block is already there
+
+ // fseek is required only in non sequential reading
+ if (CurBlk != OldBlk + 1)
+ if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
+ return RC_FX;
+ } // endif fseek
+
+ // Calculate the length of block to read
+ BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
+
+ if (trace)
+ htrc("File position is now %d\n", ftell(Stream));
+
+ // Read the entire next block
+ n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
+
+ if (n == BlkLen) {
+// ReadBlks++;
+ num_read++;
+ Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
+
+ ok:
+ rc = RC_OK;
+
+ // Get the position of the current line
+ for (i = 0, CurLine = To_Buf; i < CurNum; i++)
+ while (*CurLine++ != '\n') ; // What about Unix ???
+
+ // Now get the position of the next line
+ for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ } else if (feof(Stream)) {
+ rc = RC_EF;
+ } else {
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return RC_FX;
+ } // endelse
+
+ OldBlk = CurBlk; // Last block actually read
+ IsRead = true; // Is read indeed
+
+ fin:
+ // Store the current record file position for Delete and Update
+ Fpos = BlkPos[CurBlk] + CurLine - To_Buf;
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for the blocked DOS access method. */
+/* Update is directly written back into the file, */
+/* with this (fast) method, record size cannot change. */
+/***********************************************************************/
+int BLKFAM::WriteBuffer(PGLOBAL g)
+ {
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ /*******************************************************************/
+ /* In Insert mode, blocks are added sequentially to the file end. */
+ /*******************************************************************/
+ if (!Closing) { // Add line to the write buffer
+ strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
+
+ if (++CurNum != Rbuf) {
+ CurLine += strlen(CurLine);
+ return RC_OK; // We write only full blocks
+ } // endif CurNum
+
+ } // endif Closing
+
+ // Now start the writing process.
+ NxtLine = CurLine + strlen(CurLine);
+ BlkLen = NxtLine - To_Buf;
+
+ if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ Closing = true; // To tell CloseDB about a Write error
+ return RC_FX;
+ } // endif size
+
+ CurBlk++;
+ CurNum = 0;
+ CurLine = To_Buf;
+ } else {
+ /*******************************************************************/
+ /* Mode == MODE_UPDATE. */
+ /*******************************************************************/
+ char *crlf;
+ size_t len;
+ int curpos = ftell(Stream);
+ bool moved = true;
+
+ // T_Stream is the temporary stream or the table file stream itself
+ if (!T_Stream)
+ if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
+ if (OpenTempFile(g))
+ return RC_FX;
+
+ } else
+ T_Stream = Stream;
+
+ if (UseTemp) {
+ /*****************************************************************/
+ /* We are using a temporary file. Before writing the updated */
+ /* record, we must eventually copy all the intermediate records */
+ /* that have not been updated. */
+ /*****************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ Spos = GetNextPos(); // New start position
+
+ // Prepare the output buffer
+#if defined(WIN32)
+ crlf = "\r\n";
+#else
+ crlf = "\n";
+#endif // WIN32
+ strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
+ len = strlen(OutBuf);
+ } else {
+ if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif fseek
+
+ // Replace the line inside read buffer (length has not changed)
+ memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
+ OutBuf = CurLine;
+ len = (size_t)(NxtLine - CurLine);
+ } // endif UseTemp
+
+ if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif fwrite
+
+ if (moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif
+
+ } // endif Mode
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Table file close routine for DOS access method. */
+/***********************************************************************/
+void BLKFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc, wrc = RC_OK;
+
+ if (UseTemp && T_Stream) {
+ if (Tdbp->GetMode() == MODE_UPDATE) {
+ // Copy eventually remaining lines
+ bool b;
+
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+ rc = MoveIntermediateLines(g, &b);
+ } else
+ rc = RC_OK;
+
+ if (rc == RC_OK)
+ // Delete the old file and rename the new temp file.
+ rc = RenameTempFile(g); // Also close all files
+ else
+ rc = PlugCloseFile(g, To_Fb);
+
+ } else {
+ // Closing is True if last Write was in error
+ if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
+ // Some more inserted lines remain to be written
+ Rbuf = CurNum--;
+ Closing = true;
+ wrc = WriteBuffer(g);
+ } else if (Modif && !Closing) {
+ // Last updated block remains to be written
+ Closing = true;
+ wrc = ReadBuffer(g);
+ } // endif's
+
+ rc = PlugCloseFile(g, To_Fb);
+
+ if (trace)
+ htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
+ To_File, Tdbp->GetMode(), wrc, rc);
+
+ } // endif UseTemp
+
+ Stream = NULL; // So we can know whether table is open
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for DOS access method. */
+/* Note: commenting out OldBlk = -1 has two advantages: */
+/* 1 - It forces fseek on first block, thus suppressing the need to */
+/* rewind the file, anyway unuseful when second pass if indexed. */
+/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
+/***********************************************************************/
+void BLKFAM::Rewind(void)
+ {
+//rewind(Stream); will be placed by fseek
+ CurBlk = -1;
+ CurNum = Rbuf;
+//OldBlk = -1; commented out in case we reuse last read block
+//Rbuf = 0; commented out in case we reuse last read block
+ } // end of Rewind
+
diff --git a/storage/connect/filamtxt.h b/storage/connect/filamtxt.h
index a6105d0fe66..04375d9daa5 100644
--- a/storage/connect/filamtxt.h
+++ b/storage/connect/filamtxt.h
@@ -1,196 +1,196 @@
-/************** FilAMTxt H Declares Source Code File (.H) **************/
-/* Name: FILAMTXT.H Version 1.2 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
-/* */
-/* This file contains the file access method classes declares. */
-/***********************************************************************/
-
-#ifndef __FILAMTXT_H
-#define __FILAMTXT_H
-
-#include "block.h"
-
-typedef class TXTFAM *PTXF;
-typedef class DOSFAM *PDOSFAM;
-typedef class BLKFAM *PBLKFAM;
-typedef class DOSDEF *PDOSDEF;
-typedef class TDBDOS *PTDBDOS;
-
-/***********************************************************************/
-/* This is the base class for all file access method classes. */
-/***********************************************************************/
-class DllExport TXTFAM : public BLOCK {
- friend class TDBDOS;
- friend class TDBCSV;
- friend class TDBFIX;
- friend class TDBVCT;
- friend class DOSCOL;
- friend class BINCOL;
- friend class VCTCOL;
- public:
- // Constructor
- TXTFAM(PDOSDEF tdp);
- TXTFAM(PTXF txfp);
-
- // Implementation
- virtual AMT GetAmType(void) = 0;
- virtual int GetPos(void) = 0;
- virtual int GetNextPos(void) = 0;
- virtual PTXF Duplicate(PGLOBAL g) = 0;
- virtual bool GetUseTemp(void) {return false;}
- virtual int GetDelRows(void) {return DelRows;}
- int GetCurBlk(void) {return CurBlk;}
- void SetTdbp(PTDBDOS tdbp) {Tdbp = tdbp;}
- int GetBlock(void) {return Block;}
- void SetBlkPos(int *bkp) {BlkPos = bkp;}
- void SetNrec(int n) {Nrec = n;}
- char *GetBuf(void) {return To_Buf;}
- int GetRows(void) {return Rows;}
- bool IsBlocked(void) {return Blocked;}
-
- // Methods
- virtual void Reset(void);
- virtual int GetFileLength(PGLOBAL g);
- virtual int Cardinality(PGLOBAL g);
- virtual int MaxBlkSize(PGLOBAL g, int s);
- virtual bool AllocateBuffer(PGLOBAL g) {return false;}
- virtual void ResetBuffer(PGLOBAL g) {}
- virtual int GetNerr(void) {return 0;}
- virtual int GetRowID(void) = 0;
- virtual bool RecordPos(PGLOBAL g) = 0;
- virtual bool SetPos(PGLOBAL g, int recpos) = 0;
- virtual int SkipRecord(PGLOBAL g, bool header) = 0;
- virtual bool OpenTableFile(PGLOBAL g) = 0;
- virtual bool DeferReading(void) {IsRead = false; return true;}
- virtual int ReadBuffer(PGLOBAL g) = 0;
- virtual int WriteBuffer(PGLOBAL g) = 0;
- virtual int DeleteRecords(PGLOBAL g, int irc) = 0;
- virtual void CloseTableFile(PGLOBAL g) = 0;
- virtual void Rewind(void) = 0;
-
- protected:
- // Members
- PTDBDOS Tdbp; // To table class
- PSZ To_File; // Points to table file name
- PFBLOCK To_Fb; // Pointer to file block
- bool Placed; // true if Recpos was externally set
- bool IsRead; // false for deferred reading
- bool Blocked; // true if using blocked I/O
- char *To_Buf; // Points to I/O buffer
- void *DelBuf; // Buffer used to move lines in Delete
- int *BlkPos; // To array of block positions
- int BlkLen; // Current block length
- int Buflen; // Buffer length
- int Dbflen; // Delete buffer length
- int Rows; // Number of rows read so far
- int DelRows; // Number of deleted rows
- int Headlen; // Number of bytes in header
- int Lrecl; // Logical Record Length
- int Block; // Number of blocks in table
- int Last; // Number of elements of last block
- int Nrec; // Number of records in buffer
- int OldBlk; // Index of last read block
- int CurBlk; // Index of current block
- int CurNum; // Current buffer line number
- int ReadBlks; // Number of blocks read (selected)
- int Rbuf; // Number of lines read in buffer
- int Modif; // Number of modified lines in block
- int Blksize; // Size of padded blocks
- int Ending; // Length of line end
- bool Padded; // true if fixed size blocks are padded
- bool Eof; // true if an EOF (0xA) character exists
- char *CrLf; // End of line character(s)
- }; // end of class TXTFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for standard */
-/* text files with variable record format (DOS, CSV, FMT) */
-/***********************************************************************/
-class DllExport DOSFAM : public TXTFAM {
- public:
- // Constructor
- DOSFAM(PDOSDEF tdp);
- DOSFAM(PDOSFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_DOS;}
- virtual bool GetUseTemp(void) {return UseTemp;}
- virtual int GetPos(void);
- virtual int GetNextPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) DOSFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int GetFileLength(PGLOBAL g);
- virtual int Cardinality(PGLOBAL g);
- virtual int MaxBlkSize(PGLOBAL g, int s);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
- virtual bool SetPos(PGLOBAL g, int recpos);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- virtual bool OpenTempFile(PGLOBAL g);
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
- virtual int RenameTempFile(PGLOBAL g);
-
- // Members
- FILE *Stream; // Points to Dos file structure
- FILE *T_Stream; // Points to temporary file structure
- PFBLOCK To_Fbt; // Pointer to temp file block
- int Fpos; // Position of last read record
- int Tpos; // Target Position for delete move
- int Spos; // Start position for delete move
- bool UseTemp; // True to use a temporary file in Delete
- bool Bin; // True to force binary mode
- }; // end of class DOSFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for standard */
-/* text files with variable record format (DOS, CSV, FMT) */
-/***********************************************************************/
-class DllExport BLKFAM : public DOSFAM {
- public:
- // Constructor
- BLKFAM(PDOSDEF tdp);
- BLKFAM(PBLKFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_BLK;}
- virtual int GetPos(void);
- virtual int GetNextPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) BLKFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int Cardinality(PGLOBAL g);
- virtual int MaxBlkSize(PGLOBAL g, int s);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
- virtual bool SetPos(PGLOBAL g, int recpos);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- // Members
- char *CurLine; // Position of current line in buffer
- char *NxtLine; // Position of Next line in buffer
- char *OutBuf; // Buffer to write in temporary file
- bool Closing; // True when closing on Update
- }; // end of class BLKFAM
-
-#endif // __FILAMTXT_H
+/************** FilAMTxt H Declares Source Code File (.H) **************/
+/* Name: FILAMTXT.H Version 1.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* */
+/* This file contains the file access method classes declares. */
+/***********************************************************************/
+
+#ifndef __FILAMTXT_H
+#define __FILAMTXT_H
+
+#include "block.h"
+
+typedef class TXTFAM *PTXF;
+typedef class DOSFAM *PDOSFAM;
+typedef class BLKFAM *PBLKFAM;
+typedef class DOSDEF *PDOSDEF;
+typedef class TDBDOS *PTDBDOS;
+
+/***********************************************************************/
+/* This is the base class for all file access method classes. */
+/***********************************************************************/
+class DllExport TXTFAM : public BLOCK {
+ friend class TDBDOS;
+ friend class TDBCSV;
+ friend class TDBFIX;
+ friend class TDBVCT;
+ friend class DOSCOL;
+ friend class BINCOL;
+ friend class VCTCOL;
+ public:
+ // Constructor
+ TXTFAM(PDOSDEF tdp);
+ TXTFAM(PTXF txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) = 0;
+ virtual int GetPos(void) = 0;
+ virtual int GetNextPos(void) = 0;
+ virtual PTXF Duplicate(PGLOBAL g) = 0;
+ virtual bool GetUseTemp(void) {return false;}
+ virtual int GetDelRows(void) {return DelRows;}
+ int GetCurBlk(void) {return CurBlk;}
+ void SetTdbp(PTDBDOS tdbp) {Tdbp = tdbp;}
+ int GetBlock(void) {return Block;}
+ void SetBlkPos(int *bkp) {BlkPos = bkp;}
+ void SetNrec(int n) {Nrec = n;}
+ char *GetBuf(void) {return To_Buf;}
+ int GetRows(void) {return Rows;}
+ bool IsBlocked(void) {return Blocked;}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int GetFileLength(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int MaxBlkSize(PGLOBAL g, int s);
+ virtual bool AllocateBuffer(PGLOBAL g) {return false;}
+ virtual void ResetBuffer(PGLOBAL g) {}
+ virtual int GetNerr(void) {return 0;}
+ virtual int GetRowID(void) = 0;
+ virtual bool RecordPos(PGLOBAL g) = 0;
+ virtual bool SetPos(PGLOBAL g, int recpos) = 0;
+ virtual int SkipRecord(PGLOBAL g, bool header) = 0;
+ virtual bool OpenTableFile(PGLOBAL g) = 0;
+ virtual bool DeferReading(void) {IsRead = false; return true;}
+ virtual int ReadBuffer(PGLOBAL g) = 0;
+ virtual int WriteBuffer(PGLOBAL g) = 0;
+ virtual int DeleteRecords(PGLOBAL g, int irc) = 0;
+ virtual void CloseTableFile(PGLOBAL g) = 0;
+ virtual void Rewind(void) = 0;
+
+ protected:
+ // Members
+ PTDBDOS Tdbp; // To table class
+ PSZ To_File; // Points to table file name
+ PFBLOCK To_Fb; // Pointer to file block
+ bool Placed; // true if Recpos was externally set
+ bool IsRead; // false for deferred reading
+ bool Blocked; // true if using blocked I/O
+ char *To_Buf; // Points to I/O buffer
+ void *DelBuf; // Buffer used to move lines in Delete
+ int *BlkPos; // To array of block positions
+ int BlkLen; // Current block length
+ int Buflen; // Buffer length
+ int Dbflen; // Delete buffer length
+ int Rows; // Number of rows read so far
+ int DelRows; // Number of deleted rows
+ int Headlen; // Number of bytes in header
+ int Lrecl; // Logical Record Length
+ int Block; // Number of blocks in table
+ int Last; // Number of elements of last block
+ int Nrec; // Number of records in buffer
+ int OldBlk; // Index of last read block
+ int CurBlk; // Index of current block
+ int CurNum; // Current buffer line number
+ int ReadBlks; // Number of blocks read (selected)
+ int Rbuf; // Number of lines read in buffer
+ int Modif; // Number of modified lines in block
+ int Blksize; // Size of padded blocks
+ int Ending; // Length of line end
+ bool Padded; // true if fixed size blocks are padded
+ bool Eof; // true if an EOF (0xA) character exists
+ char *CrLf; // End of line character(s)
+ }; // end of class TXTFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for standard */
+/* text files with variable record format (DOS, CSV, FMT) */
+/***********************************************************************/
+class DllExport DOSFAM : public TXTFAM {
+ public:
+ // Constructor
+ DOSFAM(PDOSDEF tdp);
+ DOSFAM(PDOSFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_DOS;}
+ virtual bool GetUseTemp(void) {return UseTemp;}
+ virtual int GetPos(void);
+ virtual int GetNextPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) DOSFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int GetFileLength(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int MaxBlkSize(PGLOBAL g, int s);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int GetRowID(void);
+ virtual bool RecordPos(PGLOBAL g);
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
+ virtual int RenameTempFile(PGLOBAL g);
+
+ // Members
+ FILE *Stream; // Points to Dos file structure
+ FILE *T_Stream; // Points to temporary file structure
+ PFBLOCK To_Fbt; // Pointer to temp file block
+ int Fpos; // Position of last read record
+ int Tpos; // Target Position for delete move
+ int Spos; // Start position for delete move
+ bool UseTemp; // True to use a temporary file in Delete
+ bool Bin; // True to force binary mode
+ }; // end of class DOSFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for standard */
+/* text files with variable record format (DOS, CSV, FMT) */
+/***********************************************************************/
+class DllExport BLKFAM : public DOSFAM {
+ public:
+ // Constructor
+ BLKFAM(PDOSDEF tdp);
+ BLKFAM(PBLKFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_BLK;}
+ virtual int GetPos(void);
+ virtual int GetNextPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) BLKFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int MaxBlkSize(PGLOBAL g, int s);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int GetRowID(void);
+ virtual bool RecordPos(PGLOBAL g);
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ // Members
+ char *CurLine; // Position of current line in buffer
+ char *NxtLine; // Position of Next line in buffer
+ char *OutBuf; // Buffer to write in temporary file
+ bool Closing; // True when closing on Update
+ }; // end of class BLKFAM
+
+#endif // __FILAMTXT_H
diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp
index 4887d7e52fd..586c7bd7c54 100755
--- a/storage/connect/filamvct.cpp
+++ b/storage/connect/filamvct.cpp
@@ -1,4242 +1,4234 @@
-/*********** 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 defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == Block - 1) ? Last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == Block - 1) ? Last : Nrec;
-#endif // !BLK_INDX
-
- 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;
-
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (++CurBlk == Block)
- return RC_EF; // End of file
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* 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
-#endif // BLK_INDX
-
- 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();
+ 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 --------------------------- */
diff --git a/storage/connect/filamvct.h b/storage/connect/filamvct.h
index 22665c6cd23..f528f00372b 100644
--- a/storage/connect/filamvct.h
+++ b/storage/connect/filamvct.h
@@ -1,249 +1,249 @@
-/************** FilAMVct H Declares Source Code File (.H) **************/
-/* Name: FILAMVCT.H Version 1.5 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
-/* */
-/* This file contains the VCT file access method classes declares. */
-/***********************************************************************/
-#ifndef __FILAMVCT__
-#define __FILAMVCT__
-
-#include "filamfix.h"
-
-typedef class VCTFAM *PVCTFAM;
-typedef class VCTCOL *PVCTCOL;
-typedef class VCMFAM *PVCMFAM;
-typedef class VECFAM *PVECFAM;
-typedef class VMPFAM *PVMPFAM;
-typedef class BGVFAM *PBGVFAM;
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in vector format. If MaxBlk=0, each block containing "Elements" */
-/* records, values of each columns are consecutively stored (vector). */
-/* Otherwise, data is arranged by column in the file and MaxBlk is */
-/* used to set the maximum number of blocks. This leave some white */
-/* space allowing to insert new values up to this maximum size. */
-/***********************************************************************/
-class DllExport VCTFAM : public FIXFAM {
- friend class TDBVCT;
- friend class VCTCOL;
- public:
- // Constructor
- VCTFAM(PVCTDEF tdp);
- VCTFAM(PVCTFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) VCTFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int MaxBlkSize(PGLOBAL g, int s);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual bool InitInsert(PGLOBAL g);
- virtual void ResetBuffer(PGLOBAL g) {}
- virtual int Cardinality(PGLOBAL g);
- virtual int GetRowID(void);
-
- // Database routines
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- // Specific functions
- virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
- virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
-
- protected:
- virtual bool MakeEmptyFile(PGLOBAL g, char *fn);
- virtual bool OpenTempFile(PGLOBAL g);
- virtual bool MoveLines(PGLOBAL g) {return false;}
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
- virtual bool CleanUnusedSpace(PGLOBAL g);
- virtual int GetBlockInfo(PGLOBAL g);
- virtual bool SetBlockInfo(PGLOBAL g);
- bool ResetTableSize(PGLOBAL g, int block, int last);
-
- // Members
- char *NewBlock; // To block written on Insert
- char *Colfn; // Pattern for column file names (VER)
- char *Tempat; // Pattern for temp file names (VER)
- int *Clens; // Pointer to col size array
- int *Deplac; // Pointer to col start position array
- bool *Isnum; // Pointer to buffer type isnum result
- bool AddBlock; // True when adding new blocks on Insert
- bool Split; // true: split column file vector format
- int Header; // 0: no, 1: separate, 2: in data file
- int MaxBlk; // Max number of blocks (True vector format)
- int Bsize; // Because Nrec can be modified
- int Ncol; // The number of columns;
- }; // end of class VCTFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in vector format accessed using file mapping. */
-/***********************************************************************/
-class DllExport VCMFAM : public VCTFAM {
- friend class TDBVCT;
- friend class VCTCOL;
- public:
- // Constructor
- VCMFAM(PVCTDEF tdp);
- VCMFAM(PVCMFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_VMP;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) VCMFAM(this);}
-
- // Methods
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual bool InitInsert(PGLOBAL g);
-
- // Database routines
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
-
- // Specific functions
- virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
- virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
-
- // Members
- char* Memory; // Pointer on file mapping view.
- char* *Memcol; // Pointer on column start.
- }; // end of class VCMFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in full vertical format. Each column is contained in a separate */
-/* file whose name is the table name followed by the column number. */
-/***********************************************************************/
-class DllExport VECFAM : public VCTFAM {
- friend class TDBVCT;
- friend class VCTCOL;
- public:
- // Constructor
- VECFAM(PVCTDEF tdp);
- VECFAM(PVECFAM txfp);
-
- // Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) VECFAM(this);}
-
- // Methods
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual bool InitInsert(PGLOBAL g);
- virtual void ResetBuffer(PGLOBAL g);
-
- // Database routines
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
-
- // Specific functions
- virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
- virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
-
- protected:
- virtual bool OpenTempFile(PGLOBAL g);
- virtual bool MoveLines(PGLOBAL g);
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
- virtual int RenameTempFile(PGLOBAL g);
- bool OpenColumnFile(PGLOBAL g, char *opmode, int i);
-
- // Members
- FILE* *Streams; // Points to Dos file structure array
- FILE* *T_Streams; // Points to temp file structure array
- PFBLOCK *To_Fbs; // Pointer to file block array
- PFBLOCK *T_Fbs; // Pointer to temp file block array
- void* *To_Bufs; // Pointer to col val block array
- bool InitUpdate; // Used to initialize updating
- }; // end of class VECFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in full vertical format accessed using file mapping. */
-/***********************************************************************/
-class DllExport VMPFAM : public VCMFAM {
- friend class TDBVCT;
- friend class VCTCOL;
- public:
- // Constructor
- VMPFAM(PVCTDEF tdp);
- VMPFAM(PVMPFAM txfp);
-
- // Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) VMPFAM(this);}
-
- // Methods
- virtual bool AllocateBuffer(PGLOBAL g);
-
- // Database routines
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
-
- protected:
- bool MapColumnFile(PGLOBAL g, MODE mode, int i);
-
- // Members
- PFBLOCK *To_Fbs; // Pointer to file block array
- }; // end of class VMPFAM
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in (possibly blocked) vector format that can be larger than 2GB. */
-/***********************************************************************/
-class BGVFAM : public VCTFAM {
- friend class VCTCOL;
- public:
- // Constructors
- BGVFAM(PVCTDEF tdp);
- BGVFAM(PBGVFAM txfp);
-
- // Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) BGVFAM(this);}
-
- // Methods
- virtual bool AllocateBuffer(PGLOBAL g);
-
- // Database routines
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- // Specific functions
- virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
- virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
-
- protected:
- bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b = false);
- bool BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req);
- bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req);
- virtual bool MakeEmptyFile(PGLOBAL g, char *fn);
- virtual bool OpenTempFile(PGLOBAL g);
- virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
- virtual bool CleanUnusedSpace(PGLOBAL g);
- virtual bool SetBlockInfo(PGLOBAL g);
- virtual int GetBlockInfo(PGLOBAL g);
-
- // Members
- HANDLE Hfile; // Handle to big file
- HANDLE Tfile; // Handle to temporary file
- BIGINT *BigDep; // Pointer to col start position array
- }; // end of class BGVFAM
-
-#endif // __FILAMVCT__
-
+/************** FilAMVct H Declares Source Code File (.H) **************/
+/* Name: FILAMVCT.H Version 1.5 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* */
+/* This file contains the VCT file access method classes declares. */
+/***********************************************************************/
+#ifndef __FILAMVCT__
+#define __FILAMVCT__
+
+#include "filamfix.h"
+
+typedef class VCTFAM *PVCTFAM;
+typedef class VCTCOL *PVCTCOL;
+typedef class VCMFAM *PVCMFAM;
+typedef class VECFAM *PVECFAM;
+typedef class VMPFAM *PVMPFAM;
+typedef class BGVFAM *PBGVFAM;
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in vector format. If MaxBlk=0, each block containing "Elements" */
+/* records, values of each columns are consecutively stored (vector). */
+/* Otherwise, data is arranged by column in the file and MaxBlk is */
+/* used to set the maximum number of blocks. This leave some white */
+/* space allowing to insert new values up to this maximum size. */
+/***********************************************************************/
+class DllExport VCTFAM : public FIXFAM {
+ friend class TDBVCT;
+ friend class VCTCOL;
+ public:
+ // Constructor
+ VCTFAM(PVCTDEF tdp);
+ VCTFAM(PVCTFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) VCTFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int MaxBlkSize(PGLOBAL g, int s);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual bool InitInsert(PGLOBAL g);
+ virtual void ResetBuffer(PGLOBAL g) {}
+ virtual int Cardinality(PGLOBAL g);
+ virtual int GetRowID(void);
+
+ // Database routines
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ // Specific functions
+ virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
+ virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
+
+ protected:
+ virtual bool MakeEmptyFile(PGLOBAL g, char *fn);
+ virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool MoveLines(PGLOBAL g) {return false;}
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
+ virtual bool CleanUnusedSpace(PGLOBAL g);
+ virtual int GetBlockInfo(PGLOBAL g);
+ virtual bool SetBlockInfo(PGLOBAL g);
+ bool ResetTableSize(PGLOBAL g, int block, int last);
+
+ // Members
+ char *NewBlock; // To block written on Insert
+ char *Colfn; // Pattern for column file names (VER)
+ char *Tempat; // Pattern for temp file names (VER)
+ int *Clens; // Pointer to col size array
+ int *Deplac; // Pointer to col start position array
+ bool *Isnum; // Pointer to buffer type isnum result
+ bool AddBlock; // True when adding new blocks on Insert
+ bool Split; // true: split column file vector format
+ int Header; // 0: no, 1: separate, 2: in data file
+ int MaxBlk; // Max number of blocks (True vector format)
+ int Bsize; // Because Nrec can be modified
+ int Ncol; // The number of columns;
+ }; // end of class VCTFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in vector format accessed using file mapping. */
+/***********************************************************************/
+class DllExport VCMFAM : public VCTFAM {
+ friend class TDBVCT;
+ friend class VCTCOL;
+ public:
+ // Constructor
+ VCMFAM(PVCTDEF tdp);
+ VCMFAM(PVCMFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_VMP;}
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) VCMFAM(this);}
+
+ // Methods
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual bool InitInsert(PGLOBAL g);
+
+ // Database routines
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+
+ // Specific functions
+ virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
+ virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
+
+ // Members
+ char* Memory; // Pointer on file mapping view.
+ char* *Memcol; // Pointer on column start.
+ }; // end of class VCMFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in full vertical format. Each column is contained in a separate */
+/* file whose name is the table name followed by the column number. */
+/***********************************************************************/
+class DllExport VECFAM : public VCTFAM {
+ friend class TDBVCT;
+ friend class VCTCOL;
+ public:
+ // Constructor
+ VECFAM(PVCTDEF tdp);
+ VECFAM(PVECFAM txfp);
+
+ // Implementation
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) VECFAM(this);}
+
+ // Methods
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual bool InitInsert(PGLOBAL g);
+ virtual void ResetBuffer(PGLOBAL g);
+
+ // Database routines
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+
+ // Specific functions
+ virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
+ virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
+
+ protected:
+ virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool MoveLines(PGLOBAL g);
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
+ virtual int RenameTempFile(PGLOBAL g);
+ bool OpenColumnFile(PGLOBAL g, char *opmode, int i);
+
+ // Members
+ FILE* *Streams; // Points to Dos file structure array
+ FILE* *T_Streams; // Points to temp file structure array
+ PFBLOCK *To_Fbs; // Pointer to file block array
+ PFBLOCK *T_Fbs; // Pointer to temp file block array
+ void* *To_Bufs; // Pointer to col val block array
+ bool InitUpdate; // Used to initialize updating
+ }; // end of class VECFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in full vertical format accessed using file mapping. */
+/***********************************************************************/
+class DllExport VMPFAM : public VCMFAM {
+ friend class TDBVCT;
+ friend class VCTCOL;
+ public:
+ // Constructor
+ VMPFAM(PVCTDEF tdp);
+ VMPFAM(PVMPFAM txfp);
+
+ // Implementation
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) VMPFAM(this);}
+
+ // Methods
+ virtual bool AllocateBuffer(PGLOBAL g);
+
+ // Database routines
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+
+ protected:
+ bool MapColumnFile(PGLOBAL g, MODE mode, int i);
+
+ // Members
+ PFBLOCK *To_Fbs; // Pointer to file block array
+ }; // end of class VMPFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in (possibly blocked) vector format that can be larger than 2GB. */
+/***********************************************************************/
+class BGVFAM : public VCTFAM {
+ friend class VCTCOL;
+ public:
+ // Constructors
+ BGVFAM(PVCTDEF tdp);
+ BGVFAM(PBGVFAM txfp);
+
+ // Implementation
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) BGVFAM(this);}
+
+ // Methods
+ virtual bool AllocateBuffer(PGLOBAL g);
+
+ // Database routines
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ // Specific functions
+ virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
+ virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
+
+ protected:
+ bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b = false);
+ bool BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ virtual bool MakeEmptyFile(PGLOBAL g, char *fn);
+ virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
+ virtual bool CleanUnusedSpace(PGLOBAL g);
+ virtual bool SetBlockInfo(PGLOBAL g);
+ virtual int GetBlockInfo(PGLOBAL g);
+
+ // Members
+ HANDLE Hfile; // Handle to big file
+ HANDLE Tfile; // Handle to temporary file
+ BIGINT *BigDep; // Pointer to col start position array
+ }; // end of class BGVFAM
+
+#endif // __FILAMVCT__
+
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index 04184bdda71..8ae7e5863af 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -1,1424 +1,1405 @@
-/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/
-/* PROGRAM NAME: FILAMZIP */
-/* ------------- */
-/* Version 1.4 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the ZLIB compressed files classes. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* 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
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX)
-#include <errno.h>
-#else // !UNIX
-#include <io.h>
-#endif
-#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 "plgdbsem.h"
-//#include "catalog.h"
-//#include "reldef.h"
-//#include "xobject.h"
-//#include "kindex.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-#if defined(UNIX)
-#include "osutil.h"
-#endif
-
-/***********************************************************************/
-/* This define prepares ZLIB function declarations. */
-/***********************************************************************/
-//#define ZLIB_DLL
-
-#include "filamzip.h"
-
-/***********************************************************************/
-/* DB static variables. */
-/***********************************************************************/
-extern int num_read, num_there, num_eq[]; // Statistics
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Implementation of the ZIPFAM class. */
-/***********************************************************************/
-ZIPFAM::ZIPFAM(PZIPFAM txfp) : TXTFAM(txfp)
- {
- Zfile = txfp->Zfile;
- Zpos = txfp->Zpos;
- } // end of ZIPFAM copy constructor
-
-/***********************************************************************/
-/* Zerror: Error function for gz calls. */
-/* gzerror returns the error message for the last error which occurred*/
-/* on the given compressed file. errnum is set to zlib error number. */
-/* If an error occurred in the file system and not in the compression */
-/* library, errnum is set to Z_ERRNO and the application may consult */
-/* errno to get the exact error code. */
-/***********************************************************************/
-int ZIPFAM::Zerror(PGLOBAL g)
- {
- int errnum;
-
- strcpy(g->Message, gzerror(Zfile, &errnum));
-
- if (errnum == Z_ERRNO)
-#if defined(WIN32)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(NULL));
-#else // !WIN32
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
-#endif // !WIN32
-
- return (errnum == Z_STREAM_END) ? RC_EF : RC_FX;
- } // end of Zerror
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void ZIPFAM::Reset(void)
- {
- TXTFAM::Reset();
-//gzrewind(Zfile); // Useful ?????
- Zpos = 0;
- } // end of Reset
-
-/***********************************************************************/
-/* ZIP GetFileLength: returns an estimate of what would be the */
-/* uncompressed file size in number of bytes. */
-/***********************************************************************/
-int ZIPFAM::GetFileLength(PGLOBAL g)
- {
- int len = TXTFAM::GetFileLength(g);
-
- if (len > 0)
- // Estimate size reduction to a max of 6
- len *= 6;
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* ZIP Access Method opening routine. */
-/***********************************************************************/
-bool ZIPFAM::OpenTableFile(PGLOBAL g)
- {
- char opmode[4], filename[_MAX_PATH];
- MODE mode = Tdbp->GetMode();
-
- switch (mode) {
- case MODE_READ:
- strcpy(opmode, "r");
- break;
- case MODE_UPDATE:
- /*****************************************************************/
- /* Updating ZIP files not implemented yet. */
- /*****************************************************************/
- strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP));
- return true;
- case MODE_DELETE:
- if (!Tdbp->GetNext()) {
- // Store the number of deleted lines
- DelRows = Cardinality(g);
-
- // This will erase the entire file
- strcpy(opmode, "w");
-// Block = 0; // For ZBKFAM
-// Last = Nrec; // For ZBKFAM
- Tdbp->ResetSize();
- } else {
- sprintf(g->Message, MSG(NO_PART_DEL), "ZIP");
- return true;
- } // endif filter
-
- break;
- case MODE_INSERT:
- strcpy(opmode, "a+");
- break;
- default:
- sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
- return true;
- } // endswitch Mode
-
- /*********************************************************************/
- /* Open according to logical input/output mode required. */
- /* Use specific zlib functions. */
- /* Treat files as binary. */
- /*********************************************************************/
- strcat(opmode, "b");
- Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode);
-
- if (Zfile == NULL) {
- sprintf(g->Message, MSG(GZOPEN_ERROR),
- opmode, (int)errno, filename);
- strcat(strcat(g->Message, ": "), strerror(errno));
- return (mode == MODE_READ && errno == ENOENT)
- ? PushWarning(g, Tdbp) : true;
- } // endif Zfile
-
- /*********************************************************************/
- /* Something to be done here. >>>>>>>> NOT DONE <<<<<<<< */
- /*********************************************************************/
-//To_Fb = dbuserp->Openlist; // Keep track of File block
-
- /*********************************************************************/
- /* Allocate the line buffer. */
- /*********************************************************************/
- return AllocateBuffer(g);
- } // end of OpenTableFile
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool ZIPFAM::AllocateBuffer(PGLOBAL g)
- {
- MODE mode = Tdbp->GetMode();
-
- Buflen = Lrecl + 2; // Lrecl does not include CRLF
-//Buflen *= ((Mode == MODE_DELETE) ? DOS_BUFF_LEN : 1); NIY
-
-#ifdef DEBTRACE
- htrc("SubAllocating a buffer of %d bytes\n", Buflen);
-#endif
-
- To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* For Insert buffer must be prepared. */
- /*******************************************************************/
- memset(To_Buf, ' ', Buflen);
- To_Buf[Buflen - 2] = '\n';
- To_Buf[Buflen - 1] = '\0';
- } // endif Insert
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int ZIPFAM::GetRowID(void)
- {
- return Rows;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int ZIPFAM::GetPos(void)
- {
- return (int)Zpos;
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: return the position of next record. */
-/***********************************************************************/
-int ZIPFAM::GetNextPos(void)
- {
- return gztell(Zfile);
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool ZIPFAM::SetPos(PGLOBAL g, int pos)
- {
- sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP");
- return true;
-#if 0
- Fpos = pos;
-
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
- return true;
- } // endif
-
- Placed = true;
- return false;
-#endif // 0
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/***********************************************************************/
-bool ZIPFAM::RecordPos(PGLOBAL g)
- {
- Zpos = gztell(Zfile);
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int ZIPFAM::SkipRecord(PGLOBAL g, bool header)
- {
- // Skip this record
- if (gzeof(Zfile))
- return RC_EF;
- else if (gzgets(Zfile, To_Buf, Buflen) == Z_NULL)
- return Zerror(g);
-
- if (header)
- RecordPos(g);
-
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line from a compressed text file. */
-/***********************************************************************/
-int ZIPFAM::ReadBuffer(PGLOBAL g)
- {
- char *p;
- int rc;
-
- if (!Zfile)
- return RC_EF;
-
- if (!Placed) {
- /*******************************************************************/
- /* Record file position in case of UPDATE or DELETE. */
- /*******************************************************************/
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (RecordPos(g))
- return RC_FX;
-
- CurBlk = Rows++; // Update RowID
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* Check whether optimization on ROWID */
- /* 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:
- // Skip this record
- if ((rc = SkipRecord(g, FALSE)) != RC_OK)
- return rc;
-
- goto next;
- } // endswitch rc
-#endif // BLK_INDX
- } else
- Placed = false;
-
- if (gzeof(Zfile)) {
- rc = RC_EF;
- } else if (gzgets(Zfile, To_Buf, Buflen) != Z_NULL) {
- p = To_Buf + strlen(To_Buf) - 1;
-
- if (*p == '\n')
- *p = '\0'; // Eliminate ending new-line character
-
- if (*(--p) == '\r')
- *p = '\0'; // Eliminate eventuel carriage return
-
- strcpy(Tdbp->GetLine(), To_Buf);
- IsRead = true;
- rc = RC_OK;
- num_read++;
- } else
- rc = Zerror(g);
-
-#ifdef DEBTRACE
- htrc(" Read: '%s' rc=%d\n", To_Buf, rc);
-#endif
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for ZDOS access method. */
-/* Update is not possible without using a temporary file (NIY). */
-/***********************************************************************/
-int ZIPFAM::WriteBuffer(PGLOBAL g)
- {
- /*********************************************************************/
- /* Prepare the write buffer. */
- /*********************************************************************/
- strcat(strcpy(To_Buf, Tdbp->GetLine()), CrLf);
-
- /*********************************************************************/
- /* Now start the writing process. */
- /*********************************************************************/
- if (gzputs(Zfile, To_Buf) < 0)
- return Zerror(g);
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Data Base delete line routine for ZDOS access method. (NIY) */
-/***********************************************************************/
-int ZIPFAM::DeleteRecords(PGLOBAL g, int irc)
- {
- strcpy(g->Message, MSG(NO_ZIP_DELETE));
- return RC_FX;
- } // end of DeleteRecords
-
-/***********************************************************************/
-/* Data Base close routine for DOS access method. */
-/***********************************************************************/
-void ZIPFAM::CloseTableFile(PGLOBAL g)
- {
- int rc = gzclose(Zfile);
-
-#ifdef DEBTRACE
- htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc);
-#endif
-
- Zfile = NULL; // So we can know whether table is open
-//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for ZIP access method. */
-/***********************************************************************/
-void ZIPFAM::Rewind(void)
- {
- gzrewind(Zfile);
- } // end of Rewind
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp)
- {
- Blocked = true;
- Block = tdp->GetBlock();
- Last = tdp->GetLast();
- Nrec = tdp->GetElemt();
- CurLine = NULL;
- NxtLine = NULL;
- Closing = false;
-#if defined(BLK_INDX)
- BlkPos = tdp->GetTo_Pos();
-#else // !BLK_INDX
- BlkPos = NULL;
-#endif // !BLK_INDX
- } // end of ZBKFAM standard constructor
-
-ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp)
- {
- CurLine = txfp->CurLine;
- NxtLine = txfp->NxtLine;
- Closing = txfp->Closing;
- } // end of ZBKFAM copy constructor
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/***********************************************************************/
-int ZBKFAM::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 defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == Block - 1) ? Last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == Block - 1) ? Last : Nrec;
-#endif // !BLK_INDX
-
- CurBlk = savcur;
- return size;
- } // end of MaxBlkSize
-
-/***********************************************************************/
-/* ZBK 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 ZBKFAM::Cardinality(PGLOBAL g)
- {
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool ZBKFAM::AllocateBuffer(PGLOBAL g)
- {
- Buflen = Nrec * (Lrecl + 2);
- CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (Tdbp->GetMode() == MODE_INSERT) {
- // Set values so Block and Last can be recalculated
- if (Last == Nrec) {
- CurBlk = Block;
- Rbuf = Nrec; // To be used by WriteDB
- } else {
- // The last block must be completed
- CurBlk = Block - 1;
- Rbuf = Nrec - Last; // To be used by WriteDB
- } // endif Last
-
- } // endif Insert
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int ZBKFAM::GetRowID(void)
- {
- return CurNum + Nrec * CurBlk + 1;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int ZBKFAM::GetPos(void)
- {
- return CurNum + Nrec * CurBlk; // Computed file index
- } // end of GetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/* Not used yet for fixed tables. */
-/***********************************************************************/
-bool ZBKFAM::RecordPos(PGLOBAL g)
- {
-//strcpy(g->Message, "RecordPos not implemented for zip blocked tables");
-//return true;
- return RC_OK;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int ZBKFAM::SkipRecord(PGLOBAL g, bool header)
- {
-//strcpy(g->Message, "SkipRecord not implemented for zip blocked tables");
-//return RC_FX;
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line from a compressed text file. */
-/***********************************************************************/
-int ZBKFAM::ReadBuffer(PGLOBAL g)
- {
-#if defined(BLK_INDX)
- int n, skip, rc = RC_OK;
-
- /*********************************************************************/
- /* Sequential reading when Placed is not true. */
- /*********************************************************************/
- if (++CurNum < Rbuf) {
- CurLine = NxtLine;
-
- // Get the position of the next line in the buffer
- while (*NxtLine++ != '\n') ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- return RC_OK;
- } else if (Rbuf < Nrec && CurBlk != -1)
- return RC_EF;
-
- /*********************************************************************/
- /* New block. */
- /*********************************************************************/
- CurNum = 0;
- skip = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*********************************************************************/
- /* Before using the new block, check whether block optimization */
- /* 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:
- skip++;
- goto next;
- } // endswitch rc
-
- if (skip)
- // Skip blocks rejected by block optimization
- for (int i = CurBlk - skip; i < CurBlk; i++) {
- BlkLen = BlkPos[i + 1] - BlkPos[i];
-
- if (gzseek(Zfile, (z_off_t)BlkLen, SEEK_CUR) < 0)
- return Zerror(g);
-
- } // endfor i
-
- BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
-
- if (!(n = gzread(Zfile, To_Buf, BlkLen))) {
- rc = RC_EF;
- } else if (n > 0) {
- // Get the position of the current line
- CurLine = To_Buf;
-
- // Now get the position of the next line
- for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
- IsRead = true;
- rc = RC_OK;
- num_read++;
- } else
- rc = Zerror(g);
-
- return rc;
-#else // !BLK_POS
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
-#endif // !BLK_POS
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for ZDOS access method. */
-/* Update is not possible without using a temporary file (NIY). */
-/***********************************************************************/
-int ZBKFAM::WriteBuffer(PGLOBAL g)
- {
- /*********************************************************************/
- /* Prepare the write buffer. */
- /*********************************************************************/
- if (!Closing)
- strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
-
- /*********************************************************************/
- /* In Insert mode, blocs are added sequentialy to the file end. */
- /* Note: Update mode is not handled for zip files. */
- /*********************************************************************/
- if (++CurNum == Rbuf) {
- /*******************************************************************/
- /* New block, start the writing process. */
- /*******************************************************************/
- BlkLen = CurLine + strlen(CurLine) - To_Buf;
-
- if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
- gzflush(Zfile, Z_FULL_FLUSH)) {
- Closing = true;
- return Zerror(g);
- } // endif gzwrite
-
- Rbuf = Nrec;
- CurBlk++;
- CurNum = 0;
- CurLine = To_Buf;
- } else
- CurLine += strlen(CurLine);
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Data Base delete line routine for ZBK access method. */
-/* Implemented only for total deletion of the table, which is done */
-/* by opening the file in mode "wb". */
-/***********************************************************************/
-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)) {
- sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
- return RC_FX;
- } else
- return RC_OK;
-
- } else
- return irc;
-
- } // end of DeleteRecords
-
-/***********************************************************************/
-/* Data Base close routine for ZBK access method. */
-/***********************************************************************/
-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();
-
- if (CurNum && !Closing) {
- // Some more inserted lines remain to be written
- Last = (Nrec - Rbuf) + CurNum;
- Block = CurBlk + 1;
- Rbuf = CurNum--;
- Closing = true;
- rc = WriteBuffer(g);
- } else if (Rbuf == Nrec) {
- Last = Nrec;
- Block = CurBlk;
- } // endif CurNum
-
- if (rc != RC_FX) {
- defp->SetBlock(Block);
- defp->SetLast(Last);
- cat->SetIntCatInfo("Blocks", Block);
- cat->SetIntCatInfo("Last", Last);
- } // endif
-
- gzclose(Zfile);
- } else if (Tdbp->GetMode() == MODE_DELETE) {
- rc = DeleteRecords(g, RC_EF);
- gzclose(Zfile);
- } else
- rc = gzclose(Zfile);
-
-#ifdef DEBTRACE
- htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc);
-#endif
-
- Zfile = NULL; // So we can know whether table is open
-//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for ZBK access method. */
-/***********************************************************************/
-void ZBKFAM::Rewind(void)
- {
- gzrewind(Zfile);
- CurBlk = -1;
- CurNum = Rbuf;
- } // end of Rewind
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-ZIXFAM::ZIXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
- {
-//Block = tdp->GetBlock();
-//Last = tdp->GetLast();
- Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
- Blksize = Nrec * Lrecl;
- } // end of ZIXFAM standard constructor
-
-/***********************************************************************/
-/* ZIX 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 ZIXFAM::Cardinality(PGLOBAL g)
- {
- if (Last)
- return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
- else // Last and Block not defined, cannot do it yet
- return 0;
-
- } // end of Cardinality
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool ZIXFAM::AllocateBuffer(PGLOBAL g)
- {
- Buflen = Blksize;
- To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (Tdbp->GetMode() == MODE_INSERT) {
- /*******************************************************************/
- /* For Insert the buffer must be prepared. */
- /*******************************************************************/
- memset(To_Buf, ' ', Buflen);
-
- if (Tdbp->GetFtype() < 2)
- // if not binary, the file is physically a text file
- for (int len = Lrecl; len <= Buflen; len += Lrecl) {
-#if defined(WIN32)
- To_Buf[len - 2] = '\r';
-#endif // WIN32
- To_Buf[len - 1] = '\n';
- } // endfor len
-
- // Set values so Block and Last can be recalculated
- if (Last == Nrec) {
- CurBlk = Block;
- Rbuf = Nrec; // To be used by WriteDB
- } else {
- // The last block must be completed
- CurBlk = Block - 1;
- Rbuf = Nrec - Last; // To be used by WriteDB
- } // endif Last
-
- } // endif Insert
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* ReadBuffer: Read one line from a compressed text file. */
-/***********************************************************************/
-int ZIXFAM::ReadBuffer(PGLOBAL g)
- {
- int n, rc = RC_OK;
-
- /*********************************************************************/
- /* Sequential reading when Placed is not true. */
- /*********************************************************************/
- if (++CurNum < Rbuf) {
- Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
- return RC_OK;
- } else if (Rbuf < Nrec && CurBlk != -1)
- return RC_EF;
-
- /*********************************************************************/
- /* New block. */
- /*********************************************************************/
- CurNum = 0;
- Tdbp->SetLine(To_Buf);
-
-#if defined(BLK_INDX)
- int skip = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*********************************************************************/
- /* Before using the new block, check whether block optimization */
- /* 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:
- skip++;
- goto next;
- } // endswitch rc
-
- if (skip)
- // Skip blocks rejected by block optimization
- for (int i = 0; i < skip; i++) {
- if (gzseek(Zfile, (z_off_t)Buflen, SEEK_CUR) < 0)
- return Zerror(g);
-
- } // endfor i
-#endif // BLK_INDX
-
- if (!(n = gzread(Zfile, To_Buf, Buflen))) {
- rc = RC_EF;
- } else if (n > 0) {
- Rbuf = n / Lrecl;
- IsRead = true;
- rc = RC_OK;
- num_read++;
- } else
- rc = Zerror(g);
-
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for ZDOS access method. */
-/* Update is not possible without using a temporary file (NIY). */
-/***********************************************************************/
-int ZIXFAM::WriteBuffer(PGLOBAL g)
- {
- /*********************************************************************/
- /* In Insert mode, blocs are added sequentialy to the file end. */
- /* Note: Update mode is not handled for zip files. */
- /*********************************************************************/
- if (++CurNum == Rbuf) {
- /*******************************************************************/
- /* New block, start the writing process. */
- /*******************************************************************/
- BlkLen = Rbuf * Lrecl;
-
- if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
- gzflush(Zfile, Z_FULL_FLUSH)) {
- Closing = true;
- return Zerror(g);
- } // endif gzwrite
-
- Rbuf = Nrec;
- CurBlk++;
- CurNum = 0;
- Tdbp->SetLine(To_Buf);
- } else
- Tdbp->IncLine(Lrecl); // Used by FIXCOL functions
-
- return RC_OK;
- } // end of WriteBuffer
-
-#if defined(BLK_INDX)
-/* --------------------------- Class ZLBFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-ZLBFAM::ZLBFAM(PDOSDEF tdp) : BLKFAM(tdp)
- {
- Zstream = NULL;
- Zbuffer = NULL;
- Zlenp = NULL;
- Optimized = tdp->IsOptimized();
- } // end of ZLBFAM standard constructor
-
-ZLBFAM::ZLBFAM(PZLBFAM txfp) : BLKFAM(txfp)
- {
- Zstream = txfp->Zstream;
- Zbuffer = txfp->Zbuffer;
- Zlenp = txfp->Zlenp;
- Optimized = txfp->Optimized;
- } // end of ZLBFAM (dummy?) copy constructor
-
-/***********************************************************************/
-/* ZLB GetFileLength: returns an estimate of what would be the */
-/* uncompressed file size in number of bytes. */
-/***********************************************************************/
-int ZLBFAM::GetFileLength(PGLOBAL g)
- {
- int len = (Optimized) ? BlkPos[Block] : BLKFAM::GetFileLength(g);
-
- if (len > 0)
- // Estimate size reduction to a max of 5
- len *= 5;
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool ZLBFAM::AllocateBuffer(PGLOBAL g)
- {
- char *msg;
- int n, zrc;
-
-#if 0
- if (!Optimized && Tdbp->NeedIndexing(g)) {
- strcpy(g->Message, MSG(NOP_ZLIB_INDEX));
- return TRUE;
- } // endif indexing
-#endif // 0
-
-#if defined(NOLIB)
- if (!zlib && LoadZlib()) {
- sprintf(g->Message, MSG(DLL_LOAD_ERROR), GetLastError(), "zlib.dll");
- return TRUE;
- } // endif zlib
-#endif
-
- BLKFAM::AllocateBuffer(g);
-//Buflen = Nrec * (Lrecl + 2);
-//Rbuf = Nrec;
-
- // Allocate the compressed buffer
- n = Buflen + 16; // ?????????????????????????????????
- Zlenp = (int*)PlugSubAlloc(g, NULL, n);
- Zbuffer = (Byte*)(Zlenp + 1);
-
- // Allocate and initialize the Z stream
- Zstream = (z_streamp)PlugSubAlloc(g, NULL, sizeof(z_stream));
- Zstream->zalloc = (alloc_func)0;
- Zstream->zfree = (free_func)0;
- Zstream->opaque = (voidpf)0;
- Zstream->next_in = NULL;
- Zstream->avail_in = 0;
-
- if (Tdbp->GetMode() == MODE_READ) {
- msg = "inflateInit";
- zrc = inflateInit(Zstream);
- } else {
- msg = "deflateInit";
- zrc = deflateInit(Zstream, Z_DEFAULT_COMPRESSION);
- } // endif Mode
-
- if (zrc != Z_OK) {
- if (Zstream->msg)
- sprintf(g->Message, "%s error: %s", msg, Zstream->msg);
- else
- sprintf(g->Message, "%s error: %d", msg, zrc);
-
- return TRUE;
- } // endif zrc
-
- if (Tdbp->GetMode() == MODE_INSERT) {
- // Write the file header block
- if (Last == Nrec) {
- CurBlk = Block;
- CurNum = 0;
-
- if (!GetFileLength(g)) {
- // Write the zlib header as an extra block
- strcpy(To_Buf, "PlugDB");
- BlkLen = strlen("PlugDB") + 1;
-
- if (WriteCompressedBuffer(g))
- return TRUE;
-
- } // endif void file
-
- } else {
- // In mode insert, if Last != Nrec, last block must be updated
- CurBlk = Block - 1;
- CurNum = Last;
-
- strcpy(g->Message, MSG(NO_PAR_BLK_INS));
- return TRUE;
- } // endif Last
-
- } else { // MODE_READ
- // First thing to do is to read the header block
- void *rdbuf;
-
- if (Optimized) {
- BlkLen = BlkPos[0];
- rdbuf = Zlenp;
- } else {
- // Get the stored length from the file itself
- if (fread(Zlenp, sizeof(int), 1, Stream) != 1)
- return FALSE; // Empty file
-
- BlkLen = *Zlenp;
- rdbuf = Zbuffer;
- } // endif Optimized
-
- switch (ReadCompressedBuffer(g, rdbuf)) {
- case RC_EF:
- return FALSE;
- case RC_FX:
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
- case RC_NF:
- return TRUE;
- } // endswitch
-
- // Some old tables can have PlugDB in their header
- if (strcmp(To_Buf, "PlugDB")) {
- sprintf(g->Message, MSG(BAD_HEADER), Tdbp->GetFile(g));
- return TRUE;
- } // endif strcmp
-
- } // endif Mode
-
- return FALSE;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int ZLBFAM::GetPos(void)
- {
- return (Optimized) ? (CurNum + Nrec * CurBlk) : Fpos;
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: should not be called for this class. */
-/***********************************************************************/
-int ZLBFAM::GetNextPos(void)
- {
- if (Optimized) {
- assert(FALSE);
- return 0;
- } else
- return ftell(Stream);
-
- } // end of GetNextPos
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a text file. */
-/***********************************************************************/
-int ZLBFAM::ReadBuffer(PGLOBAL g)
- {
- int n;
- void *rdbuf;
-
- /*********************************************************************/
- /* Sequential reading when Placed is not true. */
- /*********************************************************************/
- if (Placed) {
- Placed = FALSE;
- } else if (++CurNum < Rbuf) {
- CurLine = NxtLine;
-
- // Get the position of the next line in the buffer
- if (Tdbp->GetFtype() == RECFM_VAR)
- while (*NxtLine++ != '\n') ;
- else
- NxtLine += Lrecl;
-
- // Set caller line buffer
- n = NxtLine - CurLine - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- return RC_OK;
- } else if (Rbuf < Nrec && CurBlk != -1) {
- CurNum--; // To have a correct Last value when optimizing
- return RC_EF;
- } else {
- /*******************************************************************/
- /* New block. */
- /*******************************************************************/
- CurNum = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*******************************************************************/
- /* Before reading a new block, check whether block optimization */
- /* can be done, as well as for join as for local filtering. */
- /*******************************************************************/
- if (Optimized) switch (Tdbp->TestBlock(g)) {
- case RC_EF:
- return RC_EF;
- case RC_NF:
- goto next;
- } // endswitch rc
-
- } // endif's
-
- if (OldBlk == CurBlk)
- goto ok; // Block is already there
-
- if (Optimized) {
- // Store the position of next block
- Fpos = BlkPos[CurBlk];
-
- // fseek is required only in non sequential reading
- if (CurBlk != OldBlk + 1)
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
- return RC_FX;
- } // endif fseek
-
- // Calculate the length of block to read
- BlkLen = BlkPos[CurBlk + 1] - Fpos;
- rdbuf = Zlenp;
- } else { // !Optimized
- if (CurBlk != OldBlk + 1) {
- strcpy(g->Message, MSG(INV_RAND_ACC));
- return RC_FX;
- } else
- Fpos = ftell(Stream); // Used when optimizing
-
- // Get the stored length from the file itself
- if (fread(Zlenp, sizeof(int), 1, Stream) != 1) {
- if (feof(Stream))
- return RC_EF;
-
- goto err;
- } // endif fread
-
- BlkLen = *Zlenp;
- rdbuf = Zbuffer;
- } // endif Optimized
-
- // Read the next block
- switch (ReadCompressedBuffer(g, rdbuf)) {
- case RC_FX: goto err;
- case RC_NF: return RC_FX;
- case RC_EF: return RC_EF;
- default: Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
- } // endswitch ReadCompressedBuffer
-
- ok:
- if (Tdbp->GetFtype() == RECFM_VAR) {
- int i;
-
- // Get the position of the current line
- for (i = 0, CurLine = To_Buf; i < CurNum; i++)
- while (*CurLine++ != '\n') ; // What about Unix ???
-
- // Now get the position of the next line
- for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- } else {
- CurLine = To_Buf + CurNum * Lrecl;
- NxtLine = CurLine + Lrecl;
- n = Lrecl - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
- } // endif Ftype
-
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
-
- OldBlk = CurBlk; // Last block actually read
- IsRead = TRUE; // Is read indeed
- return RC_OK;
-
- err:
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
- return RC_FX;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* Read and decompress a block from the stream. */
-/***********************************************************************/
-int ZLBFAM::ReadCompressedBuffer(PGLOBAL g, void *rdbuf)
- {
- if (fread(rdbuf, 1, (size_t)BlkLen, Stream) == (unsigned)BlkLen) {
- int zrc;
-
- num_read++;
-
- if (Optimized && BlkLen != signed(*Zlenp + sizeof(int))) {
- sprintf(g->Message, MSG(BAD_BLK_SIZE), CurBlk + 1);
- return RC_NF;
- } // endif BlkLen
-
- // HERE WE MUST INFLATE THE BLOCK
- Zstream->next_in = Zbuffer;
- Zstream->avail_in = (uInt)(*Zlenp);
- Zstream->next_out = (Byte*)To_Buf;
- Zstream->avail_out = Buflen;
- zrc = inflate(Zstream, Z_SYNC_FLUSH);
-
- if (zrc != Z_OK) {
- if (Zstream->msg)
- sprintf(g->Message, MSG(FUNC_ERR_S), "inflate", Zstream->msg);
- else
- sprintf(g->Message, MSG(FUNCTION_ERROR), "inflate", (int)zrc);
-
- return RC_NF;
- } // endif zrc
-
- } else if (feof(Stream)) {
- return RC_EF;
- } else
- return RC_FX;
-
- return RC_OK;
- } // end of ReadCompressedBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for DOS access method. */
-/* Update is directly written back into the file, */
-/* with this (fast) method, record size cannot change. */
-/***********************************************************************/
-int ZLBFAM::WriteBuffer(PGLOBAL g)
- {
- assert (Tdbp->GetMode() == MODE_INSERT);
-
- /*********************************************************************/
- /* Prepare the write buffer. */
- /*********************************************************************/
- if (!Closing) {
- if (Tdbp->GetFtype() == RECFM_BIN)
- memcpy(CurLine, Tdbp->GetLine(), Lrecl);
- else
- strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
-
-#if defined(_DEBUG)
- if (Tdbp->GetFtype() == RECFM_FIX &&
- (signed)strlen(CurLine) != Lrecl + (signed)strlen(CrLf)) {
- strcpy(g->Message, MSG(BAD_LINE_LEN));
- Closing = TRUE;
- return RC_FX;
- } // endif Lrecl
-#endif // _DEBUG
- } // endif Closing
-
- /*********************************************************************/
- /* In Insert mode, blocs are added sequentialy to the file end. */
- /*********************************************************************/
- if (++CurNum != Rbuf) {
- if (Tdbp->GetFtype() == RECFM_VAR)
- CurLine += strlen(CurLine);
- else
- CurLine += Lrecl;
-
- return RC_OK; // We write only full blocks
- } // endif CurNum
-
- // HERE WE MUST DEFLATE THE BLOCK
- if (Tdbp->GetFtype() == RECFM_VAR)
- NxtLine = CurLine + strlen(CurLine);
- else
- NxtLine = CurLine + Lrecl;
-
- BlkLen = NxtLine - To_Buf;
-
- if (WriteCompressedBuffer(g)) {
- Closing = TRUE; // To tell CloseDB about a Write error
- return RC_FX;
- } // endif WriteCompressedBuffer
-
- CurBlk++;
- CurNum = 0;
- CurLine = To_Buf;
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Compress the buffer and write the deflated output to stream. */
-/***********************************************************************/
-bool ZLBFAM::WriteCompressedBuffer(PGLOBAL g)
- {
- int zrc;
-
- Zstream->next_in = (Byte*)To_Buf;
- Zstream->avail_in = (uInt)BlkLen;
- Zstream->next_out = Zbuffer;
- Zstream->avail_out = Buflen + 16;
- Zstream->total_out = 0;
- zrc = deflate(Zstream, Z_FULL_FLUSH);
-
- if (zrc != Z_OK) {
- if (Zstream->msg)
- sprintf(g->Message, MSG(FUNC_ERR_S), "deflate", Zstream->msg);
- else
- sprintf(g->Message, MSG(FUNCTION_ERROR), "deflate", (int)zrc);
-
- return TRUE;
- } else
- *Zlenp = Zstream->total_out;
-
- // Now start the writing process.
- BlkLen = *Zlenp + sizeof(int);
-
- if (fwrite(Zlenp, 1, BlkLen, Stream) != (size_t)BlkLen) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- return TRUE;
- } // endif size
-
- return FALSE;
- } // end of WriteCompressedBuffer
-
-/***********************************************************************/
-/* Table file close routine for DOS access method. */
-/***********************************************************************/
-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();
-
- // Closing is True if last Write was in error
- if (CurNum && !Closing) {
- // Some more inserted lines remain to be written
- Last = (Nrec - Rbuf) + CurNum;
- Block = CurBlk + 1;
- Rbuf = CurNum--;
- Closing = TRUE;
- rc = WriteBuffer(g);
- } else if (Rbuf == Nrec) {
- Last = Nrec;
- Block = CurBlk;
- } // endif CurNum
-
- if (rc != RC_FX) {
- defp->SetBlock(Block);
- defp->SetLast(Last);
- cat->SetIntCatInfo("Blocks", Block);
- cat->SetIntCatInfo("Last", Last);
- } // endif
-
- fclose(Stream);
- } else
- rc = fclose(Stream);
-
-#ifdef DEBTRACE
- htrc("ZLB CloseTableFile: closing %s mode=%d rc=%d\n",
- To_File, Tdbp->GetMode(), rc);
-#endif
-
- Stream = NULL; // So we can know whether table is open
- To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
-
- if (Tdbp->GetMode() == MODE_READ)
- rc = inflateEnd(Zstream);
- else
- rc = deflateEnd(Zstream);
-
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for ZLIB access method. */
-/***********************************************************************/
-void ZLBFAM::Rewind(void)
- {
- // We must be positioned after the header block
- if (CurBlk >= 0) { // Nothing to do if no block read yet
- if (!Optimized) { // If optimized, fseek will be done in ReadBuffer
- rewind(Stream);
- fread(Zlenp, sizeof(int), 1, Stream);
- fseek(Stream, *Zlenp + sizeof(int), SEEK_SET);
- OldBlk = -1;
- } // endif Optimized
-
- CurBlk = -1;
- CurNum = Rbuf;
- } // endif CurBlk
-
-//OldBlk = -1;
-//Rbuf = 0; commented out in case we reuse last read block
- } // end of Rewind
-#endif // BLK_INDX
-/* ------------------------ End of ZipFam ---------------------------- */
+/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/
+/* PROGRAM NAME: FILAMZIP */
+/* ------------- */
+/* Version 1.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the ZLIB compressed files classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* 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
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX)
+#include <errno.h>
+#else // !UNIX
+#include <io.h>
+#endif
+#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 "plgdbsem.h"
+//#include "catalog.h"
+//#include "reldef.h"
+//#include "xobject.h"
+//#include "kindex.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+#if defined(UNIX)
+#include "osutil.h"
+#endif
+
+/***********************************************************************/
+/* This define prepares ZLIB function declarations. */
+/***********************************************************************/
+//#define ZLIB_DLL
+
+#include "filamzip.h"
+
+/***********************************************************************/
+/* DB static variables. */
+/***********************************************************************/
+extern int num_read, num_there, num_eq[]; // Statistics
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the ZIPFAM class. */
+/***********************************************************************/
+ZIPFAM::ZIPFAM(PZIPFAM txfp) : TXTFAM(txfp)
+ {
+ Zfile = txfp->Zfile;
+ Zpos = txfp->Zpos;
+ } // end of ZIPFAM copy constructor
+
+/***********************************************************************/
+/* Zerror: Error function for gz calls. */
+/* gzerror returns the error message for the last error which occurred*/
+/* on the given compressed file. errnum is set to zlib error number. */
+/* If an error occurred in the file system and not in the compression */
+/* library, errnum is set to Z_ERRNO and the application may consult */
+/* errno to get the exact error code. */
+/***********************************************************************/
+int ZIPFAM::Zerror(PGLOBAL g)
+ {
+ int errnum;
+
+ strcpy(g->Message, gzerror(Zfile, &errnum));
+
+ if (errnum == Z_ERRNO)
+#if defined(WIN32)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(NULL));
+#else // !WIN32
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+#endif // !WIN32
+
+ return (errnum == Z_STREAM_END) ? RC_EF : RC_FX;
+ } // end of Zerror
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void ZIPFAM::Reset(void)
+ {
+ TXTFAM::Reset();
+//gzrewind(Zfile); // Useful ?????
+ Zpos = 0;
+ } // end of Reset
+
+/***********************************************************************/
+/* ZIP GetFileLength: returns an estimate of what would be the */
+/* uncompressed file size in number of bytes. */
+/***********************************************************************/
+int ZIPFAM::GetFileLength(PGLOBAL g)
+ {
+ int len = TXTFAM::GetFileLength(g);
+
+ if (len > 0)
+ // Estimate size reduction to a max of 6
+ len *= 6;
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* ZIP Access Method opening routine. */
+/***********************************************************************/
+bool ZIPFAM::OpenTableFile(PGLOBAL g)
+ {
+ char opmode[4], filename[_MAX_PATH];
+ MODE mode = Tdbp->GetMode();
+
+ switch (mode) {
+ case MODE_READ:
+ strcpy(opmode, "r");
+ break;
+ case MODE_UPDATE:
+ /*****************************************************************/
+ /* Updating ZIP files not implemented yet. */
+ /*****************************************************************/
+ strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP));
+ return true;
+ case MODE_DELETE:
+ if (!Tdbp->GetNext()) {
+ // Store the number of deleted lines
+ DelRows = Cardinality(g);
+
+ // This will erase the entire file
+ strcpy(opmode, "w");
+// Block = 0; // For ZBKFAM
+// Last = Nrec; // For ZBKFAM
+ Tdbp->ResetSize();
+ } else {
+ sprintf(g->Message, MSG(NO_PART_DEL), "ZIP");
+ return true;
+ } // endif filter
+
+ break;
+ case MODE_INSERT:
+ strcpy(opmode, "a+");
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
+ return true;
+ } // endswitch Mode
+
+ /*********************************************************************/
+ /* Open according to logical input/output mode required. */
+ /* Use specific zlib functions. */
+ /* Treat files as binary. */
+ /*********************************************************************/
+ strcat(opmode, "b");
+ Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode);
+
+ if (Zfile == NULL) {
+ sprintf(g->Message, MSG(GZOPEN_ERROR),
+ opmode, (int)errno, filename);
+ strcat(strcat(g->Message, ": "), strerror(errno));
+ return (mode == MODE_READ && errno == ENOENT)
+ ? PushWarning(g, Tdbp) : true;
+ } // endif Zfile
+
+ /*********************************************************************/
+ /* Something to be done here. >>>>>>>> NOT DONE <<<<<<<< */
+ /*********************************************************************/
+//To_Fb = dbuserp->Openlist; // Keep track of File block
+
+ /*********************************************************************/
+ /* Allocate the line buffer. */
+ /*********************************************************************/
+ return AllocateBuffer(g);
+ } // end of OpenTableFile
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool ZIPFAM::AllocateBuffer(PGLOBAL g)
+ {
+ MODE mode = Tdbp->GetMode();
+
+ Buflen = Lrecl + 2; // Lrecl does not include CRLF
+//Buflen *= ((Mode == MODE_DELETE) ? DOS_BUFF_LEN : 1); NIY
+
+#ifdef DEBTRACE
+ htrc("SubAllocating a buffer of %d bytes\n", Buflen);
+#endif
+
+ To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* For Insert buffer must be prepared. */
+ /*******************************************************************/
+ memset(To_Buf, ' ', Buflen);
+ To_Buf[Buflen - 2] = '\n';
+ To_Buf[Buflen - 1] = '\0';
+ } // endif Insert
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int ZIPFAM::GetRowID(void)
+ {
+ return Rows;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int ZIPFAM::GetPos(void)
+ {
+ return (int)Zpos;
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: return the position of next record. */
+/***********************************************************************/
+int ZIPFAM::GetNextPos(void)
+ {
+ return gztell(Zfile);
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool ZIPFAM::SetPos(PGLOBAL g, int pos)
+ {
+ sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP");
+ return true;
+#if 0
+ Fpos = pos;
+
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
+ return true;
+ } // endif
+
+ Placed = true;
+ return false;
+#endif // 0
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/***********************************************************************/
+bool ZIPFAM::RecordPos(PGLOBAL g)
+ {
+ Zpos = gztell(Zfile);
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int ZIPFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ // Skip this record
+ if (gzeof(Zfile))
+ return RC_EF;
+ else if (gzgets(Zfile, To_Buf, Buflen) == Z_NULL)
+ return Zerror(g);
+
+ if (header)
+ RecordPos(g);
+
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line from a compressed text file. */
+/***********************************************************************/
+int ZIPFAM::ReadBuffer(PGLOBAL g)
+ {
+ char *p;
+ int rc;
+
+ if (!Zfile)
+ return RC_EF;
+
+ if (!Placed) {
+ /*******************************************************************/
+ /* Record file position in case of UPDATE or DELETE. */
+ /*******************************************************************/
+ next:
+ if (RecordPos(g))
+ return RC_FX;
+
+ CurBlk = Rows++; // Update RowID
+
+ /*******************************************************************/
+ /* Check whether optimization on ROWID */
+ /* 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:
+ // Skip this record
+ if ((rc = SkipRecord(g, FALSE)) != RC_OK)
+ return rc;
+
+ goto next;
+ } // endswitch rc
+
+ } else
+ Placed = false;
+
+ if (gzeof(Zfile)) {
+ rc = RC_EF;
+ } else if (gzgets(Zfile, To_Buf, Buflen) != Z_NULL) {
+ p = To_Buf + strlen(To_Buf) - 1;
+
+ if (*p == '\n')
+ *p = '\0'; // Eliminate ending new-line character
+
+ if (*(--p) == '\r')
+ *p = '\0'; // Eliminate eventuel carriage return
+
+ strcpy(Tdbp->GetLine(), To_Buf);
+ IsRead = true;
+ rc = RC_OK;
+ num_read++;
+ } else
+ rc = Zerror(g);
+
+#ifdef DEBTRACE
+ htrc(" Read: '%s' rc=%d\n", To_Buf, rc);
+#endif
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for ZDOS access method. */
+/* Update is not possible without using a temporary file (NIY). */
+/***********************************************************************/
+int ZIPFAM::WriteBuffer(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Prepare the write buffer. */
+ /*********************************************************************/
+ strcat(strcpy(To_Buf, Tdbp->GetLine()), CrLf);
+
+ /*********************************************************************/
+ /* Now start the writing process. */
+ /*********************************************************************/
+ if (gzputs(Zfile, To_Buf) < 0)
+ return Zerror(g);
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Data Base delete line routine for ZDOS access method. (NIY) */
+/***********************************************************************/
+int ZIPFAM::DeleteRecords(PGLOBAL g, int irc)
+ {
+ strcpy(g->Message, MSG(NO_ZIP_DELETE));
+ return RC_FX;
+ } // end of DeleteRecords
+
+/***********************************************************************/
+/* Data Base close routine for DOS access method. */
+/***********************************************************************/
+void ZIPFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc = gzclose(Zfile);
+
+#ifdef DEBTRACE
+ htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc);
+#endif
+
+ Zfile = NULL; // So we can know whether table is open
+//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for ZIP access method. */
+/***********************************************************************/
+void ZIPFAM::Rewind(void)
+ {
+ gzrewind(Zfile);
+ } // end of Rewind
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp)
+ {
+ Blocked = true;
+ Block = tdp->GetBlock();
+ Last = tdp->GetLast();
+ Nrec = tdp->GetElemt();
+ CurLine = NULL;
+ NxtLine = NULL;
+ Closing = false;
+ BlkPos = tdp->GetTo_Pos();
+ } // end of ZBKFAM standard constructor
+
+ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp)
+ {
+ CurLine = txfp->CurLine;
+ NxtLine = txfp->NxtLine;
+ Closing = txfp->Closing;
+ } // end of ZBKFAM copy constructor
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/***********************************************************************/
+int ZBKFAM::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
+
+/***********************************************************************/
+/* ZBK 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 ZBKFAM::Cardinality(PGLOBAL g)
+ {
+ // Should not be called in this version
+ return (g) ? -1 : 0;
+//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool ZBKFAM::AllocateBuffer(PGLOBAL g)
+ {
+ Buflen = Nrec * (Lrecl + 2);
+ CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ // Set values so Block and Last can be recalculated
+ if (Last == Nrec) {
+ CurBlk = Block;
+ Rbuf = Nrec; // To be used by WriteDB
+ } else {
+ // The last block must be completed
+ CurBlk = Block - 1;
+ Rbuf = Nrec - Last; // To be used by WriteDB
+ } // endif Last
+
+ } // endif Insert
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int ZBKFAM::GetRowID(void)
+ {
+ return CurNum + Nrec * CurBlk + 1;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int ZBKFAM::GetPos(void)
+ {
+ return CurNum + Nrec * CurBlk; // Computed file index
+ } // end of GetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/* Not used yet for fixed tables. */
+/***********************************************************************/
+bool ZBKFAM::RecordPos(PGLOBAL g)
+ {
+//strcpy(g->Message, "RecordPos not implemented for zip blocked tables");
+//return true;
+ return RC_OK;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int ZBKFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+//strcpy(g->Message, "SkipRecord not implemented for zip blocked tables");
+//return RC_FX;
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line from a compressed text file. */
+/***********************************************************************/
+int ZBKFAM::ReadBuffer(PGLOBAL g)
+ {
+ int n, skip, rc = RC_OK;
+
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (++CurNum < Rbuf) {
+ CurLine = NxtLine;
+
+ // Get the position of the next line in the buffer
+ while (*NxtLine++ != '\n') ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ return RC_OK;
+ } else if (Rbuf < Nrec && CurBlk != -1)
+ return RC_EF;
+
+ /*********************************************************************/
+ /* New block. */
+ /*********************************************************************/
+ CurNum = 0;
+ skip = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*********************************************************************/
+ /* Before using the new block, check whether block optimization */
+ /* 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:
+ skip++;
+ goto next;
+ } // endswitch rc
+
+ if (skip)
+ // Skip blocks rejected by block optimization
+ for (int i = CurBlk - skip; i < CurBlk; i++) {
+ BlkLen = BlkPos[i + 1] - BlkPos[i];
+
+ if (gzseek(Zfile, (z_off_t)BlkLen, SEEK_CUR) < 0)
+ return Zerror(g);
+
+ } // endfor i
+
+ BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
+
+ if (!(n = gzread(Zfile, To_Buf, BlkLen))) {
+ rc = RC_EF;
+ } else if (n > 0) {
+ // Get the position of the current line
+ CurLine = To_Buf;
+
+ // Now get the position of the next line
+ for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
+ IsRead = true;
+ rc = RC_OK;
+ num_read++;
+ } else
+ rc = Zerror(g);
+
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for ZDOS access method. */
+/* Update is not possible without using a temporary file (NIY). */
+/***********************************************************************/
+int ZBKFAM::WriteBuffer(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Prepare the write buffer. */
+ /*********************************************************************/
+ if (!Closing)
+ strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
+
+ /*********************************************************************/
+ /* In Insert mode, blocs are added sequentialy to the file end. */
+ /* Note: Update mode is not handled for zip files. */
+ /*********************************************************************/
+ if (++CurNum == Rbuf) {
+ /*******************************************************************/
+ /* New block, start the writing process. */
+ /*******************************************************************/
+ BlkLen = CurLine + strlen(CurLine) - To_Buf;
+
+ if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
+ gzflush(Zfile, Z_FULL_FLUSH)) {
+ Closing = true;
+ return Zerror(g);
+ } // endif gzwrite
+
+ Rbuf = Nrec;
+ CurBlk++;
+ CurNum = 0;
+ CurLine = To_Buf;
+ } else
+ CurLine += strlen(CurLine);
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Data Base delete line routine for ZBK access method. */
+/* Implemented only for total deletion of the table, which is done */
+/* by opening the file in mode "wb". */
+/***********************************************************************/
+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)) {
+ sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
+ return RC_FX;
+ } else
+ return RC_OK;
+
+ } else
+ return irc;
+
+ } // end of DeleteRecords
+
+/***********************************************************************/
+/* Data Base close routine for ZBK access method. */
+/***********************************************************************/
+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();
+
+ if (CurNum && !Closing) {
+ // Some more inserted lines remain to be written
+ Last = (Nrec - Rbuf) + CurNum;
+ Block = CurBlk + 1;
+ Rbuf = CurNum--;
+ Closing = true;
+ rc = WriteBuffer(g);
+ } else if (Rbuf == Nrec) {
+ Last = Nrec;
+ Block = CurBlk;
+ } // endif CurNum
+
+ if (rc != RC_FX) {
+ defp->SetBlock(Block);
+ defp->SetLast(Last);
+ cat->SetIntCatInfo("Blocks", Block);
+ cat->SetIntCatInfo("Last", Last);
+ } // endif
+
+ gzclose(Zfile);
+ } else if (Tdbp->GetMode() == MODE_DELETE) {
+ rc = DeleteRecords(g, RC_EF);
+ gzclose(Zfile);
+ } else
+ rc = gzclose(Zfile);
+
+#ifdef DEBTRACE
+ htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc);
+#endif
+
+ Zfile = NULL; // So we can know whether table is open
+//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for ZBK access method. */
+/***********************************************************************/
+void ZBKFAM::Rewind(void)
+ {
+ gzrewind(Zfile);
+ CurBlk = -1;
+ CurNum = Rbuf;
+ } // end of Rewind
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+ZIXFAM::ZIXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
+ {
+//Block = tdp->GetBlock();
+//Last = tdp->GetLast();
+ Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
+ Blksize = Nrec * Lrecl;
+ } // end of ZIXFAM standard constructor
+
+/***********************************************************************/
+/* ZIX 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 ZIXFAM::Cardinality(PGLOBAL g)
+ {
+ if (Last)
+ return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ else // Last and Block not defined, cannot do it yet
+ return 0;
+
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool ZIXFAM::AllocateBuffer(PGLOBAL g)
+ {
+ Buflen = Blksize;
+ To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ /*******************************************************************/
+ /* For Insert the buffer must be prepared. */
+ /*******************************************************************/
+ memset(To_Buf, ' ', Buflen);
+
+ if (Tdbp->GetFtype() < 2)
+ // if not binary, the file is physically a text file
+ for (int len = Lrecl; len <= Buflen; len += Lrecl) {
+#if defined(WIN32)
+ To_Buf[len - 2] = '\r';
+#endif // WIN32
+ To_Buf[len - 1] = '\n';
+ } // endfor len
+
+ // Set values so Block and Last can be recalculated
+ if (Last == Nrec) {
+ CurBlk = Block;
+ Rbuf = Nrec; // To be used by WriteDB
+ } else {
+ // The last block must be completed
+ CurBlk = Block - 1;
+ Rbuf = Nrec - Last; // To be used by WriteDB
+ } // endif Last
+
+ } // endif Insert
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* ReadBuffer: Read one line from a compressed text file. */
+/***********************************************************************/
+int ZIXFAM::ReadBuffer(PGLOBAL g)
+ {
+ int n, rc = RC_OK;
+
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (++CurNum < Rbuf) {
+ Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
+ return RC_OK;
+ } else if (Rbuf < Nrec && CurBlk != -1)
+ return RC_EF;
+
+ /*********************************************************************/
+ /* New block. */
+ /*********************************************************************/
+ CurNum = 0;
+ Tdbp->SetLine(To_Buf);
+
+ int skip = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*********************************************************************/
+ /* Before using the new block, check whether block optimization */
+ /* 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:
+ skip++;
+ goto next;
+ } // endswitch rc
+
+ if (skip)
+ // Skip blocks rejected by block optimization
+ for (int i = 0; i < skip; i++) {
+ if (gzseek(Zfile, (z_off_t)Buflen, SEEK_CUR) < 0)
+ return Zerror(g);
+
+ } // endfor i
+
+ if (!(n = gzread(Zfile, To_Buf, Buflen))) {
+ rc = RC_EF;
+ } else if (n > 0) {
+ Rbuf = n / Lrecl;
+ IsRead = true;
+ rc = RC_OK;
+ num_read++;
+ } else
+ rc = Zerror(g);
+
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for ZDOS access method. */
+/* Update is not possible without using a temporary file (NIY). */
+/***********************************************************************/
+int ZIXFAM::WriteBuffer(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* In Insert mode, blocs are added sequentialy to the file end. */
+ /* Note: Update mode is not handled for zip files. */
+ /*********************************************************************/
+ if (++CurNum == Rbuf) {
+ /*******************************************************************/
+ /* New block, start the writing process. */
+ /*******************************************************************/
+ BlkLen = Rbuf * Lrecl;
+
+ if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
+ gzflush(Zfile, Z_FULL_FLUSH)) {
+ Closing = true;
+ return Zerror(g);
+ } // endif gzwrite
+
+ Rbuf = Nrec;
+ CurBlk++;
+ CurNum = 0;
+ Tdbp->SetLine(To_Buf);
+ } else
+ Tdbp->IncLine(Lrecl); // Used by FIXCOL functions
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/* --------------------------- Class ZLBFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+ZLBFAM::ZLBFAM(PDOSDEF tdp) : BLKFAM(tdp)
+ {
+ Zstream = NULL;
+ Zbuffer = NULL;
+ Zlenp = NULL;
+ Optimized = tdp->IsOptimized();
+ } // end of ZLBFAM standard constructor
+
+ZLBFAM::ZLBFAM(PZLBFAM txfp) : BLKFAM(txfp)
+ {
+ Zstream = txfp->Zstream;
+ Zbuffer = txfp->Zbuffer;
+ Zlenp = txfp->Zlenp;
+ Optimized = txfp->Optimized;
+ } // end of ZLBFAM (dummy?) copy constructor
+
+/***********************************************************************/
+/* ZLB GetFileLength: returns an estimate of what would be the */
+/* uncompressed file size in number of bytes. */
+/***********************************************************************/
+int ZLBFAM::GetFileLength(PGLOBAL g)
+ {
+ int len = (Optimized) ? BlkPos[Block] : BLKFAM::GetFileLength(g);
+
+ if (len > 0)
+ // Estimate size reduction to a max of 5
+ len *= 5;
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool ZLBFAM::AllocateBuffer(PGLOBAL g)
+ {
+ char *msg;
+ int n, zrc;
+
+#if 0
+ if (!Optimized && Tdbp->NeedIndexing(g)) {
+ strcpy(g->Message, MSG(NOP_ZLIB_INDEX));
+ return TRUE;
+ } // endif indexing
+#endif // 0
+
+#if defined(NOLIB)
+ if (!zlib && LoadZlib()) {
+ sprintf(g->Message, MSG(DLL_LOAD_ERROR), GetLastError(), "zlib.dll");
+ return TRUE;
+ } // endif zlib
+#endif
+
+ BLKFAM::AllocateBuffer(g);
+//Buflen = Nrec * (Lrecl + 2);
+//Rbuf = Nrec;
+
+ // Allocate the compressed buffer
+ n = Buflen + 16; // ?????????????????????????????????
+ Zlenp = (int*)PlugSubAlloc(g, NULL, n);
+ Zbuffer = (Byte*)(Zlenp + 1);
+
+ // Allocate and initialize the Z stream
+ Zstream = (z_streamp)PlugSubAlloc(g, NULL, sizeof(z_stream));
+ Zstream->zalloc = (alloc_func)0;
+ Zstream->zfree = (free_func)0;
+ Zstream->opaque = (voidpf)0;
+ Zstream->next_in = NULL;
+ Zstream->avail_in = 0;
+
+ if (Tdbp->GetMode() == MODE_READ) {
+ msg = "inflateInit";
+ zrc = inflateInit(Zstream);
+ } else {
+ msg = "deflateInit";
+ zrc = deflateInit(Zstream, Z_DEFAULT_COMPRESSION);
+ } // endif Mode
+
+ if (zrc != Z_OK) {
+ if (Zstream->msg)
+ sprintf(g->Message, "%s error: %s", msg, Zstream->msg);
+ else
+ sprintf(g->Message, "%s error: %d", msg, zrc);
+
+ return TRUE;
+ } // endif zrc
+
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ // Write the file header block
+ if (Last == Nrec) {
+ CurBlk = Block;
+ CurNum = 0;
+
+ if (!GetFileLength(g)) {
+ // Write the zlib header as an extra block
+ strcpy(To_Buf, "PlugDB");
+ BlkLen = strlen("PlugDB") + 1;
+
+ if (WriteCompressedBuffer(g))
+ return TRUE;
+
+ } // endif void file
+
+ } else {
+ // In mode insert, if Last != Nrec, last block must be updated
+ CurBlk = Block - 1;
+ CurNum = Last;
+
+ strcpy(g->Message, MSG(NO_PAR_BLK_INS));
+ return TRUE;
+ } // endif Last
+
+ } else { // MODE_READ
+ // First thing to do is to read the header block
+ void *rdbuf;
+
+ if (Optimized) {
+ BlkLen = BlkPos[0];
+ rdbuf = Zlenp;
+ } else {
+ // Get the stored length from the file itself
+ if (fread(Zlenp, sizeof(int), 1, Stream) != 1)
+ return FALSE; // Empty file
+
+ BlkLen = *Zlenp;
+ rdbuf = Zbuffer;
+ } // endif Optimized
+
+ switch (ReadCompressedBuffer(g, rdbuf)) {
+ case RC_EF:
+ return FALSE;
+ case RC_FX:
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+ case RC_NF:
+ return TRUE;
+ } // endswitch
+
+ // Some old tables can have PlugDB in their header
+ if (strcmp(To_Buf, "PlugDB")) {
+ sprintf(g->Message, MSG(BAD_HEADER), Tdbp->GetFile(g));
+ return TRUE;
+ } // endif strcmp
+
+ } // endif Mode
+
+ return FALSE;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int ZLBFAM::GetPos(void)
+ {
+ return (Optimized) ? (CurNum + Nrec * CurBlk) : Fpos;
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: should not be called for this class. */
+/***********************************************************************/
+int ZLBFAM::GetNextPos(void)
+ {
+ if (Optimized) {
+ assert(FALSE);
+ return 0;
+ } else
+ return ftell(Stream);
+
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a text file. */
+/***********************************************************************/
+int ZLBFAM::ReadBuffer(PGLOBAL g)
+ {
+ int n;
+ void *rdbuf;
+
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Placed = FALSE;
+ } else if (++CurNum < Rbuf) {
+ CurLine = NxtLine;
+
+ // Get the position of the next line in the buffer
+ if (Tdbp->GetFtype() == RECFM_VAR)
+ while (*NxtLine++ != '\n') ;
+ else
+ NxtLine += Lrecl;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ return RC_OK;
+ } else if (Rbuf < Nrec && CurBlk != -1) {
+ CurNum--; // To have a correct Last value when optimizing
+ return RC_EF;
+ } else {
+ /*******************************************************************/
+ /* New block. */
+ /*******************************************************************/
+ CurNum = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*******************************************************************/
+ /* Before reading a new block, check whether block optimization */
+ /* can be done, as well as for join as for local filtering. */
+ /*******************************************************************/
+ if (Optimized) switch (Tdbp->TestBlock(g)) {
+ case RC_EF:
+ return RC_EF;
+ case RC_NF:
+ goto next;
+ } // endswitch rc
+
+ } // endif's
+
+ if (OldBlk == CurBlk)
+ goto ok; // Block is already there
+
+ if (Optimized) {
+ // Store the position of next block
+ Fpos = BlkPos[CurBlk];
+
+ // fseek is required only in non sequential reading
+ if (CurBlk != OldBlk + 1)
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
+ return RC_FX;
+ } // endif fseek
+
+ // Calculate the length of block to read
+ BlkLen = BlkPos[CurBlk + 1] - Fpos;
+ rdbuf = Zlenp;
+ } else { // !Optimized
+ if (CurBlk != OldBlk + 1) {
+ strcpy(g->Message, MSG(INV_RAND_ACC));
+ return RC_FX;
+ } else
+ Fpos = ftell(Stream); // Used when optimizing
+
+ // Get the stored length from the file itself
+ if (fread(Zlenp, sizeof(int), 1, Stream) != 1) {
+ if (feof(Stream))
+ return RC_EF;
+
+ goto err;
+ } // endif fread
+
+ BlkLen = *Zlenp;
+ rdbuf = Zbuffer;
+ } // endif Optimized
+
+ // Read the next block
+ switch (ReadCompressedBuffer(g, rdbuf)) {
+ case RC_FX: goto err;
+ case RC_NF: return RC_FX;
+ case RC_EF: return RC_EF;
+ default: Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
+ } // endswitch ReadCompressedBuffer
+
+ ok:
+ if (Tdbp->GetFtype() == RECFM_VAR) {
+ int i;
+
+ // Get the position of the current line
+ for (i = 0, CurLine = To_Buf; i < CurNum; i++)
+ while (*CurLine++ != '\n') ; // What about Unix ???
+
+ // Now get the position of the next line
+ for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ } else {
+ CurLine = To_Buf + CurNum * Lrecl;
+ NxtLine = CurLine + Lrecl;
+ n = Lrecl - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
+ } // endif Ftype
+
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+
+ OldBlk = CurBlk; // Last block actually read
+ IsRead = TRUE; // Is read indeed
+ return RC_OK;
+
+ err:
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+ return RC_FX;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* Read and decompress a block from the stream. */
+/***********************************************************************/
+int ZLBFAM::ReadCompressedBuffer(PGLOBAL g, void *rdbuf)
+ {
+ if (fread(rdbuf, 1, (size_t)BlkLen, Stream) == (unsigned)BlkLen) {
+ int zrc;
+
+ num_read++;
+
+ if (Optimized && BlkLen != signed(*Zlenp + sizeof(int))) {
+ sprintf(g->Message, MSG(BAD_BLK_SIZE), CurBlk + 1);
+ return RC_NF;
+ } // endif BlkLen
+
+ // HERE WE MUST INFLATE THE BLOCK
+ Zstream->next_in = Zbuffer;
+ Zstream->avail_in = (uInt)(*Zlenp);
+ Zstream->next_out = (Byte*)To_Buf;
+ Zstream->avail_out = Buflen;
+ zrc = inflate(Zstream, Z_SYNC_FLUSH);
+
+ if (zrc != Z_OK) {
+ if (Zstream->msg)
+ sprintf(g->Message, MSG(FUNC_ERR_S), "inflate", Zstream->msg);
+ else
+ sprintf(g->Message, MSG(FUNCTION_ERROR), "inflate", (int)zrc);
+
+ return RC_NF;
+ } // endif zrc
+
+ } else if (feof(Stream)) {
+ return RC_EF;
+ } else
+ return RC_FX;
+
+ return RC_OK;
+ } // end of ReadCompressedBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for DOS access method. */
+/* Update is directly written back into the file, */
+/* with this (fast) method, record size cannot change. */
+/***********************************************************************/
+int ZLBFAM::WriteBuffer(PGLOBAL g)
+ {
+ assert (Tdbp->GetMode() == MODE_INSERT);
+
+ /*********************************************************************/
+ /* Prepare the write buffer. */
+ /*********************************************************************/
+ if (!Closing) {
+ if (Tdbp->GetFtype() == RECFM_BIN)
+ memcpy(CurLine, Tdbp->GetLine(), Lrecl);
+ else
+ strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
+
+#if defined(_DEBUG)
+ if (Tdbp->GetFtype() == RECFM_FIX &&
+ (signed)strlen(CurLine) != Lrecl + (signed)strlen(CrLf)) {
+ strcpy(g->Message, MSG(BAD_LINE_LEN));
+ Closing = TRUE;
+ return RC_FX;
+ } // endif Lrecl
+#endif // _DEBUG
+ } // endif Closing
+
+ /*********************************************************************/
+ /* In Insert mode, blocs are added sequentialy to the file end. */
+ /*********************************************************************/
+ if (++CurNum != Rbuf) {
+ if (Tdbp->GetFtype() == RECFM_VAR)
+ CurLine += strlen(CurLine);
+ else
+ CurLine += Lrecl;
+
+ return RC_OK; // We write only full blocks
+ } // endif CurNum
+
+ // HERE WE MUST DEFLATE THE BLOCK
+ if (Tdbp->GetFtype() == RECFM_VAR)
+ NxtLine = CurLine + strlen(CurLine);
+ else
+ NxtLine = CurLine + Lrecl;
+
+ BlkLen = NxtLine - To_Buf;
+
+ if (WriteCompressedBuffer(g)) {
+ Closing = TRUE; // To tell CloseDB about a Write error
+ return RC_FX;
+ } // endif WriteCompressedBuffer
+
+ CurBlk++;
+ CurNum = 0;
+ CurLine = To_Buf;
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Compress the buffer and write the deflated output to stream. */
+/***********************************************************************/
+bool ZLBFAM::WriteCompressedBuffer(PGLOBAL g)
+ {
+ int zrc;
+
+ Zstream->next_in = (Byte*)To_Buf;
+ Zstream->avail_in = (uInt)BlkLen;
+ Zstream->next_out = Zbuffer;
+ Zstream->avail_out = Buflen + 16;
+ Zstream->total_out = 0;
+ zrc = deflate(Zstream, Z_FULL_FLUSH);
+
+ if (zrc != Z_OK) {
+ if (Zstream->msg)
+ sprintf(g->Message, MSG(FUNC_ERR_S), "deflate", Zstream->msg);
+ else
+ sprintf(g->Message, MSG(FUNCTION_ERROR), "deflate", (int)zrc);
+
+ return TRUE;
+ } else
+ *Zlenp = Zstream->total_out;
+
+ // Now start the writing process.
+ BlkLen = *Zlenp + sizeof(int);
+
+ if (fwrite(Zlenp, 1, BlkLen, Stream) != (size_t)BlkLen) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ return TRUE;
+ } // endif size
+
+ return FALSE;
+ } // end of WriteCompressedBuffer
+
+/***********************************************************************/
+/* Table file close routine for DOS access method. */
+/***********************************************************************/
+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();
+
+ // Closing is True if last Write was in error
+ if (CurNum && !Closing) {
+ // Some more inserted lines remain to be written
+ Last = (Nrec - Rbuf) + CurNum;
+ Block = CurBlk + 1;
+ Rbuf = CurNum--;
+ Closing = TRUE;
+ rc = WriteBuffer(g);
+ } else if (Rbuf == Nrec) {
+ Last = Nrec;
+ Block = CurBlk;
+ } // endif CurNum
+
+ if (rc != RC_FX) {
+ defp->SetBlock(Block);
+ defp->SetLast(Last);
+ cat->SetIntCatInfo("Blocks", Block);
+ cat->SetIntCatInfo("Last", Last);
+ } // endif
+
+ fclose(Stream);
+ } else
+ rc = fclose(Stream);
+
+#ifdef DEBTRACE
+ htrc("ZLB CloseTableFile: closing %s mode=%d rc=%d\n",
+ To_File, Tdbp->GetMode(), rc);
+#endif
+
+ Stream = NULL; // So we can know whether table is open
+ To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
+
+ if (Tdbp->GetMode() == MODE_READ)
+ rc = inflateEnd(Zstream);
+ else
+ rc = deflateEnd(Zstream);
+
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for ZLIB access method. */
+/***********************************************************************/
+void ZLBFAM::Rewind(void)
+ {
+ // We must be positioned after the header block
+ if (CurBlk >= 0) { // Nothing to do if no block read yet
+ if (!Optimized) { // If optimized, fseek will be done in ReadBuffer
+ rewind(Stream);
+ fread(Zlenp, sizeof(int), 1, Stream);
+ fseek(Stream, *Zlenp + sizeof(int), SEEK_SET);
+ OldBlk = -1;
+ } // endif Optimized
+
+ CurBlk = -1;
+ CurNum = Rbuf;
+ } // endif CurBlk
+
+//OldBlk = -1;
+//Rbuf = 0; commented out in case we reuse last read block
+ } // end of Rewind
+
+/* ------------------------ End of ZipFam ---------------------------- */
diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h
index 67d9553a4e6..8111bc1ad97 100644
--- a/storage/connect/filamzip.h
+++ b/storage/connect/filamzip.h
@@ -1,171 +1,169 @@
-/************** FilAmZip H Declares Source Code File (.H) **************/
-/* Name: FILAMZIP.H Version 1.1 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
-/* */
-/* This file contains the GZIP access method classes declares. */
-/***********************************************************************/
-#ifndef __FILAMZIP_H
-#define __FILAMZIP_H
-
-#include "zlib.h"
-
-typedef class ZIPFAM *PZIPFAM;
-typedef class ZBKFAM *PZBKFAM;
-typedef class ZIXFAM *PZIXFAM;
-typedef class ZLBFAM *PZLBFAM;
-
-/***********************************************************************/
-/* This is the access method class declaration for not optimized */
-/* variable record length files compressed using the gzip library */
-/* functions. File is accessed record by record (row). */
-/***********************************************************************/
-class DllExport ZIPFAM : public TXTFAM {
-// friend class DOSCOL;
- public:
- // Constructor
- ZIPFAM(PDOSDEF tdp) : TXTFAM(tdp) {Zfile = NULL; Zpos = 0;}
- ZIPFAM(PZIPFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_ZIP;}
- virtual int GetPos(void);
- virtual int GetNextPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) ZIPFAM(this);}
-
- // Methods
- virtual void Reset(void);
- virtual int GetFileLength(PGLOBAL g);
- virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
- virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
- virtual bool SetPos(PGLOBAL g, int recpos);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual bool OpenTableFile(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- int Zerror(PGLOBAL g); // GZ error function
-
- // Members
- gzFile Zfile; // Points to GZ file structure
- z_off_t Zpos; // Uncompressed file position
- }; // end of class ZIPFAM
-
-/***********************************************************************/
-/* This is the access method class declaration for optimized variable */
-/* record length files compressed using the gzip library functions. */
-/* The File is accessed by block (requires an opt file). */
-/***********************************************************************/
-class DllExport ZBKFAM : public ZIPFAM {
- public:
- // Constructor
- ZBKFAM(PDOSDEF tdp);
- ZBKFAM(PZBKFAM txfp);
-
- // Implementation
- virtual int GetPos(void);
- virtual int GetNextPos(void) {return 0;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) ZBKFAM(this);}
-
- // Methods
- virtual int Cardinality(PGLOBAL g);
- virtual int MaxBlkSize(PGLOBAL g, int s);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
- virtual int SkipRecord(PGLOBAL g, bool header);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- // Members
- char *CurLine; // Position of current line in buffer
- char *NxtLine; // Position of Next line in buffer
- bool Closing; // True when closing on Insert
- }; // end of class ZBKFAM
-
-/***********************************************************************/
-/* This is the access method class declaration for fixed record */
-/* length files compressed using the gzip library functions. */
-/* The file is always accessed by block. */
-/***********************************************************************/
-class DllExport ZIXFAM : public ZBKFAM {
- public:
- // Constructor
- ZIXFAM(PDOSDEF tdp);
- ZIXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {}
-
- // Implementation
- virtual int GetNextPos(void) {return 0;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) ZIXFAM(this);}
-
- // Methods
- virtual int Cardinality(PGLOBAL g);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
-
- protected:
- // No additional Members
- }; // end of class ZIXFAM
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for PlugDB */
-/* fixed/variable files compressed using the zlib library functions. */
-/* Physically these are written and read using the same technique */
-/* than blocked variable files, only the contain of each block is */
-/* compressed using the deflate zlib function. The purpose of this */
-/* specific format is to have a fast mechanism for direct access of */
-/* records so blocked optimization is fast and direct access (joins) */
-/* is allowed. Note that the block length is written ahead of each */
-/* block to enable reading when optimization file is not available. */
-/***********************************************************************/
-class DllExport ZLBFAM : public BLKFAM {
- public:
- // Constructor
- ZLBFAM(PDOSDEF tdp);
- ZLBFAM(PZLBFAM txfp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_ZLIB;}
- virtual int GetPos(void);
- virtual int GetNextPos(void);
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) ZLBFAM(this);}
- inline void SetOptimized(bool b) {Optimized = b;}
-
- // Methods
- virtual int GetFileLength(PGLOBAL g);
- virtual bool AllocateBuffer(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g);
- virtual int WriteBuffer(PGLOBAL g);
- virtual void CloseTableFile(PGLOBAL g);
- virtual void Rewind(void);
-
- protected:
- bool WriteCompressedBuffer(PGLOBAL g);
- int ReadCompressedBuffer(PGLOBAL g, void *rdbuf);
-
- // Members
- z_streamp Zstream; // Compression/decompression stream
- Byte *Zbuffer; // Compressed block buffer
- int *Zlenp; // Pointer to block length
- bool Optimized; // true when opt file is available
- }; // end of class ZLBFAM
-#endif // BLK_INDX
-
-#endif // __FILAMZIP_H
+/************** FilAmZip H Declares Source Code File (.H) **************/
+/* Name: FILAMZIP.H Version 1.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* This file contains the GZIP access method classes declares. */
+/***********************************************************************/
+#ifndef __FILAMZIP_H
+#define __FILAMZIP_H
+
+#include "zlib.h"
+
+typedef class ZIPFAM *PZIPFAM;
+typedef class ZBKFAM *PZBKFAM;
+typedef class ZIXFAM *PZIXFAM;
+typedef class ZLBFAM *PZLBFAM;
+
+/***********************************************************************/
+/* This is the access method class declaration for not optimized */
+/* variable record length files compressed using the gzip library */
+/* functions. File is accessed record by record (row). */
+/***********************************************************************/
+class DllExport ZIPFAM : public TXTFAM {
+// friend class DOSCOL;
+ public:
+ // Constructor
+ ZIPFAM(PDOSDEF tdp) : TXTFAM(tdp) {Zfile = NULL; Zpos = 0;}
+ ZIPFAM(PZIPFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_ZIP;}
+ virtual int GetPos(void);
+ virtual int GetNextPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) ZIPFAM(this);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual int GetFileLength(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
+ virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int GetRowID(void);
+ virtual bool RecordPos(PGLOBAL g);
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ int Zerror(PGLOBAL g); // GZ error function
+
+ // Members
+ gzFile Zfile; // Points to GZ file structure
+ z_off_t Zpos; // Uncompressed file position
+ }; // end of class ZIPFAM
+
+/***********************************************************************/
+/* This is the access method class declaration for optimized variable */
+/* record length files compressed using the gzip library functions. */
+/* The File is accessed by block (requires an opt file). */
+/***********************************************************************/
+class DllExport ZBKFAM : public ZIPFAM {
+ public:
+ // Constructor
+ ZBKFAM(PDOSDEF tdp);
+ ZBKFAM(PZBKFAM txfp);
+
+ // Implementation
+ virtual int GetPos(void);
+ virtual int GetNextPos(void) {return 0;}
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) ZBKFAM(this);}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g);
+ virtual int MaxBlkSize(PGLOBAL g, int s);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int GetRowID(void);
+ virtual bool RecordPos(PGLOBAL g);
+ virtual int SkipRecord(PGLOBAL g, bool header);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ // Members
+ char *CurLine; // Position of current line in buffer
+ char *NxtLine; // Position of Next line in buffer
+ bool Closing; // True when closing on Insert
+ }; // end of class ZBKFAM
+
+/***********************************************************************/
+/* This is the access method class declaration for fixed record */
+/* length files compressed using the gzip library functions. */
+/* The file is always accessed by block. */
+/***********************************************************************/
+class DllExport ZIXFAM : public ZBKFAM {
+ public:
+ // Constructor
+ ZIXFAM(PDOSDEF tdp);
+ ZIXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {}
+
+ // Implementation
+ virtual int GetNextPos(void) {return 0;}
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) ZIXFAM(this);}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+
+ protected:
+ // No additional Members
+ }; // end of class ZIXFAM
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for PlugDB */
+/* fixed/variable files compressed using the zlib library functions. */
+/* Physically these are written and read using the same technique */
+/* than blocked variable files, only the contain of each block is */
+/* compressed using the deflate zlib function. The purpose of this */
+/* specific format is to have a fast mechanism for direct access of */
+/* records so blocked optimization is fast and direct access (joins) */
+/* is allowed. Note that the block length is written ahead of each */
+/* block to enable reading when optimization file is not available. */
+/***********************************************************************/
+class DllExport ZLBFAM : public BLKFAM {
+ public:
+ // Constructor
+ ZLBFAM(PDOSDEF tdp);
+ ZLBFAM(PZLBFAM txfp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_ZLIB;}
+ virtual int GetPos(void);
+ virtual int GetNextPos(void);
+ virtual PTXF Duplicate(PGLOBAL g)
+ {return (PTXF)new(g) ZLBFAM(this);}
+ inline void SetOptimized(bool b) {Optimized = b;}
+
+ // Methods
+ virtual int GetFileLength(PGLOBAL g);
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g);
+ virtual void Rewind(void);
+
+ protected:
+ bool WriteCompressedBuffer(PGLOBAL g);
+ int ReadCompressedBuffer(PGLOBAL g, void *rdbuf);
+
+ // Members
+ z_streamp Zstream; // Compression/decompression stream
+ Byte *Zbuffer; // Compressed block buffer
+ int *Zlenp; // Pointer to block length
+ bool Optimized; // true when opt file is available
+ }; // end of class ZLBFAM
+
+#endif // __FILAMZIP_H
diff --git a/storage/connect/filter.cpp b/storage/connect/filter.cpp
index c2747d00948..62453b7b17b 100644
--- a/storage/connect/filter.cpp
+++ b/storage/connect/filter.cpp
@@ -68,7 +68,7 @@ PPARM MakeParm(PGLOBAL g, PXOB xp)
bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
//bool ReadSubQuery(PGLOBAL, PSUBQ);
//PSUBQ OpenSubQuery(PGLOBAL, PSQL);
-void PlugCloseDB(PGLOBAL, PSQL);
+//void PlugCloseDB(PGLOBAL, PSQL);
BYTE OpBmp(PGLOBAL g, OPVAL opc);
PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
@@ -201,6 +201,7 @@ FILTER::FILTER(PFIL fil1)
Test[1] = fil1->Test[1];
} // end of FILTER copy constructor
+#if 0
/***********************************************************************/
/* Linearize: Does the linearization of the filter tree: */
/* Independent filters (not implied in OR/NOT) will be separated */
@@ -388,7 +389,6 @@ int FILTER::RefNum(PSQL sqlp)
return n;
} // end of RefNum
-#if 0
/***********************************************************************/
/* CheckSubQuery: see SUBQUERY::CheckSubQuery for comment. */
/***********************************************************************/
@@ -747,7 +747,6 @@ bool FILTER::CheckHaving(PGLOBAL g, PSQL sqlp)
sqlp->SetOk(FALSE);
return FALSE;
} // end of CheckHaving
-#endif // 0
/***********************************************************************/
/* Used while building a table index. This function split the filter */
@@ -924,6 +923,7 @@ int FILTER::CheckSpcCol(PTDB tdbp, int n)
return max(n1, n2);
} // end of CheckSpcCol
+#endif // 0
/***********************************************************************/
/* Reset the filter arguments to non evaluated yet. */
diff --git a/storage/connect/filter.h b/storage/connect/filter.h
index a24ca18dc59..364c0260ae1 100644
--- a/storage/connect/filter.h
+++ b/storage/connect/filter.h
@@ -53,25 +53,25 @@ class DllExport FILTER : public XOBJECT { /* Filter description block */
virtual bool Init(PGLOBAL);
virtual bool Eval(PGLOBAL);
virtual bool SetFormat(PGLOBAL, FORMAT&) {return TRUE;} // NUY
- virtual int CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &xp, int &ag);
- virtual int RefNum(PSQL);
- virtual PXOB SetSelect(PGLOBAL, PSQL, bool) {return NULL;} // NUY
+//virtual int CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &xp, int &ag);
+//virtual int RefNum(PSQL);
+//virtual PXOB SetSelect(PGLOBAL, PSQL, bool) {return NULL;} // NUY
//virtual PXOB CheckSubQuery(PGLOBAL, PSQL);
- virtual bool CheckLocal(PTDB);
- virtual int CheckSpcCol(PTDB tdbp, int n);
+//virtual bool CheckLocal(PTDB);
+//virtual int CheckSpcCol(PTDB tdbp, int n);
virtual void Print(PGLOBAL g, FILE *f, UINT n);
virtual void Print(PGLOBAL g, char *ps, UINT z);
- PFIL Linearize(bool nosep);
- PFIL Link(PGLOBAL g, PFIL fil2);
- PFIL RemoveLastSep(void);
+// PFIL Linearize(bool nosep);
+// PFIL Link(PGLOBAL g, PFIL fil2);
+// PFIL RemoveLastSep(void);
// PFIL SortJoin(PGLOBAL g);
// bool FindJoinFilter(POPJOIN opj, PFIL fprec, bool teq,
// bool tek, bool tk2, bool tc2, bool tix, bool thx);
// bool CheckHaving(PGLOBAL g, PSQL sqlp);
bool Convert(PGLOBAL g, bool having);
- int SplitFilter(PFIL *fp);
- int SplitFilter(PFIL *fp, PTDB tp, int n);
- PFIL LinkFilter(PGLOBAL g, PFIL fp2);
+// int SplitFilter(PFIL *fp);
+// int SplitFilter(PFIL *fp, PTDB tp, int n);
+// PFIL LinkFilter(PGLOBAL g, PFIL fp2);
// PFIL Copy(PTABS t);
protected:
diff --git a/storage/connect/global.h b/storage/connect/global.h
index 374ba6e4177..4f94651b0b5 100644
--- a/storage/connect/global.h
+++ b/storage/connect/global.h
@@ -1,260 +1,258 @@
-/***********************************************************************/
-/* GLOBAL.H: Declaration file used by all CONNECT implementations. */
-/* (C) Copyright Olivier Bertrand 1993-2012 */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Included C-definition files common to all Plug routines */
-/***********************************************************************/
-#include <string.h> /* String manipulation declares */
-#include <stdlib.h> /* C standard library */
-#include <ctype.h> /* C language specific types */
-#include <stdio.h> /* FOPEN_MAX declaration */
-#include <time.h> /* time_t type declaration */
-#include <setjmp.h> /* Long jump declarations */
-
-#if defined(WIN32) && !defined(NOEX)
-#define DllExport __declspec( dllexport )
-#else // !WIN32
-#define DllExport
-#endif // !WIN32
-
-#if defined(DOMDOC_SUPPORT) || defined(LIBXML2_SUPPORT)
-#define XML_SUPPORT 1
-#endif
-
-#if defined(XMSG)
-// Definition used to read messages from message file.
-#include "msgid.h"
-#define MSG(I) PlugReadMessage(NULL, MSG_##I, #I)
-#define STEP(I) PlugReadMessage(g, MSG_##I, #I)
-#elif defined(NEWMSG)
-// Definition used to get messages from resource.
-#include "msgid.h"
-#define MSG(I) PlugGetMessage(NULL, MSG_##I)
-#define STEP(I) PlugGetMessage(g, MSG_##I)
-#else // !XMSG and !NEWMSG
-// Definition used to replace messages ID's by their definition.
-#include "messages.h"
-#define MSG(I) MSG_##I
-#define STEP(I) MSG_##I
-#endif // !XMSG and !NEWMSG
-
-#if defined(WIN32)
-#define CRLF 2
-#else // !WIN32
-#define CRLF 1
-#endif // !WIN32
-
-/***********************************************************************/
-/* Miscellaneous Constants */
-/***********************************************************************/
-#define NO_IVAL -95684275 /* Used by GetIntegerOption */
-#define VMLANG 370 /* Size of olf VM lang blocks */
-#define MAX_JUMP 24 /* Maximum jump level number */
-#define MAX_STR 1024 /* Maximum string length */
-#define STR_SIZE 501 /* Length of char strings. */
-#define STD_INPUT 0 /* Standard language input */
-#define STD_OUTPUT 1 /* Standard language output */
-#define ERROR_OUTPUT 2 /* Error message output */
-#define DEBUG_OUTPUT 3 /* Debug info output */
-#define PROMPT_OUTPUT 4 /* Prompt message output */
-#define COPY_OUTPUT 5 /* Copy of language input */
-#define STD_MSG 6 /* System message file */
-#define DEBUG_MSG 7 /* Debug message file */
-#define DUMMY 0 /* Dummy file index in Ldm block */
-#define STDIN 1 /* stdin file index in Ldm block */
-#define STDOUT 2 /* stdout file index in Ldm block */
-#define STDERR 3 /* stderr file index in Ldm block */
-#define STDEBUG 4 /* debug file index in Ldm block */
-#define STDPRN 5 /* stdprn file index in Ldm block */
-#define STDFREE 6 /* Free file index in Ldm block */
-
-#define TYPE_SEM -2 /* Returned semantic function */
-#define TYPE_DFONC -2 /* Indirect sem ref in FPARM */
-#define TYPE_VOID -1
-#define TYPE_SBPAR -1 /* Phrase reference in FPARM */
-#define TYPE_SEMX 0 /* Initial semantic function type? */
-#define TYPE_ERROR 0
-#define TYPE_STRING 1
-#define TYPE_DOUBLE 2
-#define TYPE_SHORT 3
-#define TYPE_TINY 4
-#define TYPE_BIGINT 5
-#define TYPE_LIST 6
-#define TYPE_INT 7
-#define TYPE_DECIM 9
-
-#if defined(OS32)
- #define SYS_STAMP "OS32"
-#elif defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX)
- #define SYS_STAMP "UNIX"
-#elif defined(OS16)
- #define SYS_STAMP "OS16"
-#elif defined(DOSR)
- #define SYS_STAMP "DOSR"
-#elif defined(WIN)
- #define SYS_STAMP "WIN1"
-#elif defined(WIN32)
- #define SYS_STAMP "WIN2"
-#else
- #define SYS_STAMP "XXXX"
-#endif
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-/***********************************************************************/
-/* Static variables */
-/***********************************************************************/
-#if defined(STORAGE)
- char sys_stamp[4] = SYS_STAMP;
-#else
- extern char sys_stamp[];
-#endif
-
-/***********************************************************************/
-/* File-Selection Indicators */
-/***********************************************************************/
-#define PAT_LOG "log"
-
-#if defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX)
- /*********************************************************************/
- /* printf does not accept null pointer for %s target. */
- /*********************************************************************/
- #define SVP(S) ((S) ? S : "<null>")
-#else
- /*********************************************************************/
- /* printf accepts null pointer for %s target. */
- /*********************************************************************/
- #define SVP(S) S
-#endif
-
-#if defined(STORAGE)
- FILE *debug;
-#else
- extern FILE *debug;
-#endif
-
-
-/***********************************************************************/
-/* General purpose type definitions. */
-/***********************************************************************/
-#include "os.h"
-
-typedef uint OFFSET;
-typedef char NAME[9];
-
-typedef struct {
- ushort Length;
- char String[2];
- } VARSTR;
-
-#if !defined(PGLOBAL_DEFINED)
-typedef struct _global *PGLOBAL;
-#define PGLOBAL_DEFINED
-#endif
-typedef struct _globplg *PGS;
-typedef struct _activity *PACTIVITY;
-typedef struct _parm *PPARM;
-
-/***********************************************************************/
-/* Segment Sub-Allocation block structure declares. */
-/* Next block is an implementation dependent segment suballoc save */
-/* structure used to keep the suballocation system offsets and to */
-/* restore them if needed. This scheme implies that no SubFree be used */
-/***********************************************************************/
-typedef struct { /* Plug Area SubAlloc header */
- OFFSET To_Free; /* Offset of next free block */
- uint FreeBlk; /* Size of remaining free memory */
- } POOLHEADER, *PPOOLHEADER;
-
-/***********************************************************************/
-/* Language block. Containing all global information for the language */
-/* this block is saved and retrieved with the language. Information */
-/* in this block can be set and modified under Grammar editing. */
-/***********************************************************************/
-#if defined(BIT64)
-typedef int TIME_T; /* Lang block size must not change */
-#else // BIT32
-typedef time_t TIME_T; /* time_t */
-#endif // BIT32
-
-typedef struct {
- uint Memsize;
- uint Size;
- } AREADEF;
-
-typedef struct Lang_block {
- NAME LangName; /* Language name */
- NAME Application; /* Application name */
- } LANG, *PLANG;
-
-/***********************************************************************/
-/* Application block. It contains all global information for the */
-/* current parse and execution using the corresponding language. */
-/* This block is dynamically allocated and set at language init. */
-/***********************************************************************/
-typedef struct _activity { /* Describes activity and language */
- void *Aptr; /* Points to user work area(s) */
- NAME Ap_Name; /* Current application name */
- } ACTIVITY;
-
-/*---------------- UNIT ?????????? VERSION ? ----------------------*/
-typedef struct _parm {
- void *Value;
- short Type, Domain;
- PPARM Next;
- } PARM;
-
-/***********************************************************************/
-/* Global Structure Block. This block contains, or points to, all */
-/* information used by CONNECT tables. Passed as an argument */
-/* to any routine allows it to have access to the entire information */
-/* currently available for the whole set of loaded languages. */
-/***********************************************************************/
-typedef struct _global { /* Global structure */
- void *Sarea; /* Points to work area */
- uint Sarea_Size; /* Work area size */
- PACTIVITY Activityp, ActivityStart;
- char Message[MAX_STR];
- int Createas; /* To pass info to created table */
- void *Xchk; /* indexes in create/alter */
- short Alchecked; /* Checked for ALTER */
-#if defined(MRRBKA_SUPPORT)
- short Mrr; /* True when doing mrr */
-#endif // MRRBKA_SUPPORT
- short Trace;
- int jump_level;
- jmp_buf jumper[MAX_JUMP + 2];
- } GLOBAL;
-
-/***********************************************************************/
-/* Exported routine declarations. */
-/***********************************************************************/
-#if defined(XMSG)
-DllExport char *PlugReadMessage(PGLOBAL, int, char *);
-#elif defined(NEWMSG)
-DllExport char *PlugGetMessage(PGLOBAL, int);
-#endif // XMSG || NEWMSG
-#if defined(WIN32)
-DllExport short GetLineLength(PGLOBAL); // Console line length
-#endif // WIN32
-DllExport PGLOBAL PlugInit(LPCSTR, uint); // Plug global initialization
-DllExport int PlugExit(PGLOBAL); // Plug global termination
-DllExport LPSTR PlugRemoveType(LPSTR, LPCSTR);
-DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR prefix, LPCSTR name, LPCSTR dir);
-DllExport BOOL PlugIsAbsolutePath(LPCSTR path);
-DllExport void *PlugAllocMem(PGLOBAL, uint);
-DllExport BOOL PlugSubSet(PGLOBAL, void *, uint);
-DllExport void *PlugSubAlloc(PGLOBAL, void *, size_t);
-DllExport char *PlugDup(PGLOBAL g, const char *str);
-DllExport void *MakePtr(void *, OFFSET);
-DllExport void htrc(char const *fmt, ...);
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-/*-------------------------- End of Global.H --------------------------*/
+/***********************************************************************/
+/* GLOBAL.H: Declaration file used by all CONNECT implementations. */
+/* (C) Copyright Olivier Bertrand 1993-2012 */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Included C-definition files common to all Plug routines */
+/***********************************************************************/
+#include <string.h> /* String manipulation declares */
+#include <stdlib.h> /* C standard library */
+#include <ctype.h> /* C language specific types */
+#include <stdio.h> /* FOPEN_MAX declaration */
+#include <time.h> /* time_t type declaration */
+#include <setjmp.h> /* Long jump declarations */
+
+#if defined(WIN32) && !defined(NOEX)
+#define DllExport __declspec( dllexport )
+#else // !WIN32
+#define DllExport
+#endif // !WIN32
+
+#if defined(DOMDOC_SUPPORT) || defined(LIBXML2_SUPPORT)
+#define XML_SUPPORT 1
+#endif
+
+#if defined(XMSG)
+// Definition used to read messages from message file.
+#include "msgid.h"
+#define MSG(I) PlugReadMessage(NULL, MSG_##I, #I)
+#define STEP(I) PlugReadMessage(g, MSG_##I, #I)
+#elif defined(NEWMSG)
+// Definition used to get messages from resource.
+#include "msgid.h"
+#define MSG(I) PlugGetMessage(NULL, MSG_##I)
+#define STEP(I) PlugGetMessage(g, MSG_##I)
+#else // !XMSG and !NEWMSG
+// Definition used to replace messages ID's by their definition.
+#include "messages.h"
+#define MSG(I) MSG_##I
+#define STEP(I) MSG_##I
+#endif // !XMSG and !NEWMSG
+
+#if defined(WIN32)
+#define CRLF 2
+#else // !WIN32
+#define CRLF 1
+#endif // !WIN32
+
+/***********************************************************************/
+/* Miscellaneous Constants */
+/***********************************************************************/
+#define NO_IVAL -95684275 /* Used by GetIntegerOption */
+#define VMLANG 370 /* Size of olf VM lang blocks */
+#define MAX_JUMP 24 /* Maximum jump level number */
+#define MAX_STR 1024 /* Maximum string length */
+#define STR_SIZE 501 /* Length of char strings. */
+#define STD_INPUT 0 /* Standard language input */
+#define STD_OUTPUT 1 /* Standard language output */
+#define ERROR_OUTPUT 2 /* Error message output */
+#define DEBUG_OUTPUT 3 /* Debug info output */
+#define PROMPT_OUTPUT 4 /* Prompt message output */
+#define COPY_OUTPUT 5 /* Copy of language input */
+#define STD_MSG 6 /* System message file */
+#define DEBUG_MSG 7 /* Debug message file */
+#define DUMMY 0 /* Dummy file index in Ldm block */
+#define STDIN 1 /* stdin file index in Ldm block */
+#define STDOUT 2 /* stdout file index in Ldm block */
+#define STDERR 3 /* stderr file index in Ldm block */
+#define STDEBUG 4 /* debug file index in Ldm block */
+#define STDPRN 5 /* stdprn file index in Ldm block */
+#define STDFREE 6 /* Free file index in Ldm block */
+
+#define TYPE_SEM -2 /* Returned semantic function */
+#define TYPE_DFONC -2 /* Indirect sem ref in FPARM */
+#define TYPE_VOID -1
+#define TYPE_SBPAR -1 /* Phrase reference in FPARM */
+#define TYPE_SEMX 0 /* Initial semantic function type? */
+#define TYPE_ERROR 0
+#define TYPE_STRING 1
+#define TYPE_DOUBLE 2
+#define TYPE_SHORT 3
+#define TYPE_TINY 4
+#define TYPE_BIGINT 5
+#define TYPE_LIST 6
+#define TYPE_INT 7
+#define TYPE_DECIM 9
+
+#if defined(OS32)
+ #define SYS_STAMP "OS32"
+#elif defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX)
+ #define SYS_STAMP "UNIX"
+#elif defined(OS16)
+ #define SYS_STAMP "OS16"
+#elif defined(DOSR)
+ #define SYS_STAMP "DOSR"
+#elif defined(WIN)
+ #define SYS_STAMP "WIN1"
+#elif defined(WIN32)
+ #define SYS_STAMP "WIN2"
+#else
+ #define SYS_STAMP "XXXX"
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/***********************************************************************/
+/* Static variables */
+/***********************************************************************/
+#if defined(STORAGE)
+ char sys_stamp[4] = SYS_STAMP;
+#else
+ extern char sys_stamp[];
+#endif
+
+/***********************************************************************/
+/* File-Selection Indicators */
+/***********************************************************************/
+#define PAT_LOG "log"
+
+#if defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX)
+ /*********************************************************************/
+ /* printf does not accept null pointer for %s target. */
+ /*********************************************************************/
+ #define SVP(S) ((S) ? S : "<null>")
+#else
+ /*********************************************************************/
+ /* printf accepts null pointer for %s target. */
+ /*********************************************************************/
+ #define SVP(S) S
+#endif
+
+#if defined(STORAGE)
+ FILE *debug;
+#else
+ extern FILE *debug;
+#endif
+
+
+/***********************************************************************/
+/* General purpose type definitions. */
+/***********************************************************************/
+#include "os.h"
+
+typedef uint OFFSET;
+typedef char NAME[9];
+
+typedef struct {
+ ushort Length;
+ char String[2];
+ } VARSTR;
+
+#if !defined(PGLOBAL_DEFINED)
+typedef struct _global *PGLOBAL;
+#define PGLOBAL_DEFINED
+#endif
+typedef struct _globplg *PGS;
+typedef struct _activity *PACTIVITY;
+typedef struct _parm *PPARM;
+
+/***********************************************************************/
+/* Segment Sub-Allocation block structure declares. */
+/* Next block is an implementation dependent segment suballoc save */
+/* structure used to keep the suballocation system offsets and to */
+/* restore them if needed. This scheme implies that no SubFree be used */
+/***********************************************************************/
+typedef struct { /* Plug Area SubAlloc header */
+ OFFSET To_Free; /* Offset of next free block */
+ uint FreeBlk; /* Size of remaining free memory */
+ } POOLHEADER, *PPOOLHEADER;
+
+/***********************************************************************/
+/* Language block. Containing all global information for the language */
+/* this block is saved and retrieved with the language. Information */
+/* in this block can be set and modified under Grammar editing. */
+/***********************************************************************/
+#if defined(BIT64)
+typedef int TIME_T; /* Lang block size must not change */
+#else // BIT32
+typedef time_t TIME_T; /* time_t */
+#endif // BIT32
+
+typedef struct {
+ uint Memsize;
+ uint Size;
+ } AREADEF;
+
+typedef struct Lang_block {
+ NAME LangName; /* Language name */
+ NAME Application; /* Application name */
+ } LANG, *PLANG;
+
+/***********************************************************************/
+/* Application block. It contains all global information for the */
+/* current parse and execution using the corresponding language. */
+/* This block is dynamically allocated and set at language init. */
+/***********************************************************************/
+typedef struct _activity { /* Describes activity and language */
+ void *Aptr; /* Points to user work area(s) */
+ NAME Ap_Name; /* Current application name */
+ } ACTIVITY;
+
+/*---------------- UNIT ?????????? VERSION ? ----------------------*/
+typedef struct _parm {
+ void *Value;
+ short Type, Domain;
+ PPARM Next;
+ } PARM;
+
+/***********************************************************************/
+/* Global Structure Block. This block contains, or points to, all */
+/* information used by CONNECT tables. Passed as an argument */
+/* to any routine allows it to have access to the entire information */
+/* currently available for the whole set of loaded languages. */
+/***********************************************************************/
+typedef struct _global { /* Global structure */
+ void *Sarea; /* Points to work area */
+ uint Sarea_Size; /* Work area size */
+ PACTIVITY Activityp, ActivityStart;
+ char Message[MAX_STR];
+ int Createas; /* To pass info to created table */
+ void *Xchk; /* indexes in create/alter */
+ short Alchecked; /* Checked for ALTER */
+ short Mrr; /* True when doing mrr */
+ short Trace;
+ int jump_level;
+ jmp_buf jumper[MAX_JUMP + 2];
+ } GLOBAL;
+
+/***********************************************************************/
+/* Exported routine declarations. */
+/***********************************************************************/
+#if defined(XMSG)
+DllExport char *PlugReadMessage(PGLOBAL, int, char *);
+#elif defined(NEWMSG)
+DllExport char *PlugGetMessage(PGLOBAL, int);
+#endif // XMSG || NEWMSG
+#if defined(WIN32)
+DllExport short GetLineLength(PGLOBAL); // Console line length
+#endif // WIN32
+DllExport PGLOBAL PlugInit(LPCSTR, uint); // Plug global initialization
+DllExport int PlugExit(PGLOBAL); // Plug global termination
+DllExport LPSTR PlugRemoveType(LPSTR, LPCSTR);
+DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR prefix, LPCSTR name, LPCSTR dir);
+DllExport BOOL PlugIsAbsolutePath(LPCSTR path);
+DllExport void *PlugAllocMem(PGLOBAL, uint);
+DllExport BOOL PlugSubSet(PGLOBAL, void *, uint);
+DllExport void *PlugSubAlloc(PGLOBAL, void *, size_t);
+DllExport char *PlugDup(PGLOBAL g, const char *str);
+DllExport void *MakePtr(void *, OFFSET);
+DllExport void htrc(char const *fmt, ...);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+/*-------------------------- End of Global.H --------------------------*/
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index f301ed2dcae..c12cee58c40 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1,5733 +1,5688 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2014
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/**
- @file ha_connect.cc
-
- @brief
- The ha_connect engine is a stubbed storage engine that enables to create tables
- based on external data. Principally they are based on plain files of many
- different types, but also on collections of such files, collection of tables,
- ODBC tables retrieving data from other DBMS having an ODBC server, and even
- virtual tables.
-
- @details
- ha_connect will let you create/open/delete tables, the created table can be
- done specifying an already existing file, the drop table command will just
- suppress the table definition but not the eventual data file.
- Indexes are not supported for all table types but data can be inserted,
- updated or deleted.
-
- You can enable the CONNECT storage engine in your build by doing the
- following during your build process:<br> ./configure
- --with-connect-storage-engine
-
- You can install the CONNECT handler as all other storage handlers.
-
- Once this is done, MySQL will let you create tables with:<br>
- CREATE TABLE <table name> (...) ENGINE=CONNECT;
-
- The example storage engine does not use table locks. It
- implements an example "SHARE" that is inserted into a hash by table
- name. This is not used yet.
-
- Please read the object definition in ha_connect.h before reading the rest
- of this file.
-
- @note
- This MariaDB CONNECT handler is currently an adaptation of the XDB handler
- that was written for MySQL version 4.1.2-alpha. Its overall design should
- be enhanced in the future to meet MariaDB requirements.
-
- @note
- It was written also from the Brian's ha_example handler and contains parts
- of it that are there but not currently used, such as table variables.
-
- @note
- When you create an CONNECT table, the MySQL Server creates a table .frm
- (format) file in the database directory, using the table name as the file
- name as is customary with MySQL. No other files are created. To get an idea
- of what occurs, here is an example select that would do a scan of an entire
- table:
-
- @code
- ha-connect::open
- ha_connect::store_lock
- ha_connect::external_lock
- ha_connect::info
- ha_connect::rnd_init
- ha_connect::extra
- ENUM HA_EXTRA_CACHE Cache record in HA_rrnd()
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::rnd_next
- ha_connect::extra
- ENUM HA_EXTRA_NO_CACHE End caching of records (def)
- ha_connect::external_lock
- ha_connect::extra
- ENUM HA_EXTRA_RESET Reset database to after open
- @endcode
-
- Here you see that the connect storage engine has 9 rows called before
- rnd_next signals that it has reached the end of its data. Calls to
- ha_connect::extra() are hints as to what will be occuring to the request.
-
- Happy use!<br>
- -Olivier
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#define MYSQL_SERVER 1
-#define DONT_DEFINE_VOID
-//#include "sql_partition.h"
-#include "sql_class.h"
-#include "create_options.h"
-#include "mysql_com.h"
-#include "field.h"
-#include "sql_parse.h"
-#include "sql_base.h"
-#include <sys/stat.h>
-#if defined(NEW_WAY)
-#include "sql_table.h"
-#endif // NEW_WAY
-#undef OFFSET
-
-#define NOPARSE
-#if defined(UNIX)
-#include "osutil.h"
-#endif // UNIX
-#include "global.h"
-#include "plgdbsem.h"
-#if defined(ODBC_SUPPORT)
-#include "odbccat.h"
-#endif // ODBC_SUPPORT
-#if defined(MYSQL_SUPPORT)
-#include "xtable.h"
-#include "tabmysql.h"
-#endif // MYSQL_SUPPORT
-#include "filamdbf.h"
-#include "tabxcl.h"
-#include "tabfmt.h"
-#include "reldef.h"
-#include "tabcol.h"
-#include "xindex.h"
-#if defined(WIN32)
-#include <io.h>
-#include "tabwmi.h"
-#endif // WIN32
-#include "connect.h"
-#include "user_connect.h"
-#include "ha_connect.h"
-#include "mycat.h"
-#include "myutil.h"
-#include "preparse.h"
-#include "inihandl.h"
-
-#define PLGXINI "plgcnx.ini" /* Configuration settings file */
-#define my_strupr(p) my_caseup_str(default_charset_info, (p));
-#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
-#define my_stricmp(a,b) my_strcasecmp(default_charset_info, (a), (b))
-
-#ifdef LIBXML2_SUPPORT
-#include "libdoc.h"
-#endif // LIBXML2_SUPPORT
-
-#include "taboccur.h"
-#include "tabpivot.h"
-
-
-/***********************************************************************/
-/* DB static variables. */
-/***********************************************************************/
-extern "C" char plgxini[];
-extern "C" char plgini[];
-extern "C" char nmfile[];
-extern "C" char pdebug[];
-
-/***********************************************************************/
-/* Initialize the ha_connect static members. */
-/***********************************************************************/
-#define CONNECT_INI "connect.ini"
-extern "C" {
- char connectini[_MAX_PATH]= CONNECT_INI;
- char version[]= "Version 1.02.0001 February 03, 2014";
-
-#if defined(XMSG)
- char msglang[]; // Default message language
-#endif
- int trace= 0; // The general trace value
-} // extern "C"
-
-int xtrace= 0;
-ulong ha_connect::num= 0;
-//int DTVAL::Shift= 0;
-
-/***********************************************************************/
-/* Utility functions. */
-/***********************************************************************/
-PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
-
-static PCONNECT GetUser(THD *thd, PCONNECT xp);
-static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp);
-
-static handler *connect_create_handler(handlerton *hton,
- TABLE_SHARE *table,
- MEM_ROOT *mem_root);
-
-static int connect_assisted_discovery(handlerton *hton, THD* thd,
- TABLE_SHARE *table_s,
- HA_CREATE_INFO *info);
-
-handlerton *connect_hton;
-
-/**
- CREATE TABLE option list (table options)
-
- These can be specified in the CREATE TABLE:
- CREATE TABLE ( ... ) {...here...}
-*/
-ha_create_table_option connect_table_option_list[]=
-{
- HA_TOPTION_STRING("TABLE_TYPE", type),
- HA_TOPTION_STRING("FILE_NAME", filename),
- HA_TOPTION_STRING("XFILE_NAME", optname),
-//HA_TOPTION_STRING("CONNECT_STRING", connect),
- HA_TOPTION_STRING("TABNAME", tabname),
- HA_TOPTION_STRING("TABLE_LIST", tablist),
- HA_TOPTION_STRING("DBNAME", dbname),
- HA_TOPTION_STRING("SEP_CHAR", separator),
- HA_TOPTION_STRING("QCHAR", qchar),
- HA_TOPTION_STRING("MODULE", module),
- HA_TOPTION_STRING("SUBTYPE", subtype),
- HA_TOPTION_STRING("CATFUNC", catfunc),
- HA_TOPTION_STRING("SRCDEF", srcdef),
- HA_TOPTION_STRING("COLIST", colist),
- HA_TOPTION_STRING("OPTION_LIST", oplist),
- HA_TOPTION_STRING("DATA_CHARSET", data_charset),
- HA_TOPTION_NUMBER("LRECL", lrecl, 0, 0, INT_MAX32, 1),
- HA_TOPTION_NUMBER("BLOCK_SIZE", elements, 0, 0, INT_MAX32, 1),
-//HA_TOPTION_NUMBER("ESTIMATE", estimate, 0, 0, INT_MAX32, 1),
- HA_TOPTION_NUMBER("MULTIPLE", multiple, 0, 0, 2, 1),
- HA_TOPTION_NUMBER("HEADER", header, 0, 0, 3, 1),
- HA_TOPTION_NUMBER("QUOTED", quoted, (ulonglong) -1, 0, 3, 1),
- HA_TOPTION_NUMBER("ENDING", ending, (ulonglong) -1, 0, INT_MAX32, 1),
- HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1),
-//HA_TOPTION_BOOL("COMPRESS", compressed, 0),
- HA_TOPTION_BOOL("MAPPED", mapped, 0),
- HA_TOPTION_BOOL("HUGE", huge, 0),
- HA_TOPTION_BOOL("SPLIT", split, 0),
- HA_TOPTION_BOOL("READONLY", readonly, 0),
- HA_TOPTION_BOOL("SEPINDEX", sepindex, 0),
- HA_TOPTION_END
-};
-
-
-/**
- CREATE TABLE option list (field options)
-
- These can be specified in the CREATE TABLE per field:
- CREATE TABLE ( field ... {...here...}, ... )
-*/
-ha_create_table_option connect_field_option_list[]=
-{
- HA_FOPTION_NUMBER("FLAG", offset, (ulonglong) -1, 0, INT_MAX32, 1),
- HA_FOPTION_NUMBER("MAX_DIST", freq, 0, 0, INT_MAX32, 1), // BLK_INDX
- HA_FOPTION_NUMBER("DISTRIB", opt, 0, 0, 2, 1), // used for BLK_INDX
- HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1),
- HA_FOPTION_STRING("DATE_FORMAT", dateformat),
- HA_FOPTION_STRING("FIELD_FORMAT", fieldformat),
- HA_FOPTION_STRING("SPECIAL", special),
- HA_FOPTION_END
-};
-
-/***********************************************************************/
-/* Push G->Message as a MySQL warning. */
-/***********************************************************************/
-bool PushWarning(PGLOBAL g, PTDBASE tdbp, int level)
- {
- PHC phc;
- THD *thd;
- MYCAT *cat= (MYCAT*)tdbp->GetDef()->GetCat();
- Sql_condition::enum_warning_level wlvl;
-
-
- if (!cat || !(phc= cat->GetHandler()) || !phc->GetTable() ||
- !(thd= (phc->GetTable())->in_use))
- return true;
-
-//push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- wlvl= (Sql_condition::enum_warning_level)level;
- push_warning(thd, wlvl, 0, g->Message);
- return false;
- } // end of PushWarning
-
-#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key con_key_mutex_CONNECT_SHARE_mutex;
-
-static PSI_mutex_info all_connect_mutexes[]=
-{
- { &con_key_mutex_CONNECT_SHARE_mutex, "CONNECT_SHARE::mutex", 0}
-};
-
-static void init_connect_psi_keys()
-{
- const char* category= "connect";
- int count;
-
- if (PSI_server == NULL)
- return;
-
- count= array_elements(all_connect_mutexes);
- PSI_server->register_mutex(category, all_connect_mutexes, count);
-}
-#else
-static void init_connect_psi_keys() {}
-#endif
-
-
-DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir)
-{
- const char *res= PlugSetPath(to, mysql_data_home, name, dir);
- return res;
-}
-
-
-/**
- @brief
- If frm_error() is called then we will use this to determine
- the file extensions that exist for the storage engine. This is also
- used by the default rename_table and delete_table method in
- handler.cc.
-
- For engines that have two file name extentions (separate meta/index file
- and data file), the order of elements is relevant. First element of engine
- file name extentions array should be meta/index file extention. Second
- element - data file extention. This order is assumed by
- prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
-
- @see
- rename_table method in handler.cc and
- delete_table method in handler.cc
-*/
-static const char *ha_connect_exts[]= {
- ".dos", ".fix", ".csv", ".bin", ".fmt", ".dbf", ".xml", ".ini", ".vec",
- ".dnx", ".fnx", ".bnx", ".vnx", ".dbx",
-#if defined(BLK_INDX)
- ".dop", ".fop", ".bop", ".vop",
-#endif // BLK_INDX
- NULL};
-
-/**
- @brief
- Plugin initialization
-*/
-static int connect_init_func(void *p)
-{
- DBUG_ENTER("connect_init_func");
- char dir[_MAX_PATH - sizeof(CONNECT_INI) - 1];
-
-#ifdef LIBXML2_SUPPORT
- XmlInitParserLib();
-#endif // LIBXML2_SUPPORT
-
- /* Build connect.ini file name */
- my_getwd(dir, sizeof(dir) - 1, MYF(0));
- snprintf(connectini, sizeof(connectini), "%s%s", dir, CONNECT_INI);
- sql_print_information("CONNECT: %s=%s", CONNECT_INI, connectini);
-
- if ((xtrace= GetPrivateProfileInt("CONNECT", "Trace", 0, connectini)))
- {
- sql_print_information("CONNECT: xtrace=%d", xtrace);
- sql_print_information("CONNECT: plgini=%s", plgini);
- sql_print_information("CONNECT: plgxini=%s", plgxini);
- sql_print_information("CONNECT: nmfile=%s", nmfile);
- sql_print_information("CONNECT: pdebug=%s", pdebug);
- sql_print_information("CONNECT: version=%s", version);
- trace= xtrace;
- } // endif xtrace
-
-#if !defined(WIN32)
- PROFILE_Close(connectini);
-#endif // !WIN32
-
- init_connect_psi_keys();
-
- connect_hton= (handlerton *)p;
- connect_hton->state= SHOW_OPTION_YES;
- connect_hton->create= connect_create_handler;
- connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_NO_PARTITION;
- connect_hton->table_options= connect_table_option_list;
- connect_hton->field_options= connect_field_option_list;
- connect_hton->tablefile_extensions= ha_connect_exts;
- connect_hton->discover_table_structure= connect_assisted_discovery;
-
- if (xtrace)
- sql_print_information("connect_init: hton=%p", p);
-
- DTVAL::SetTimeShift(); // Initialize time zone shift once for all
- DBUG_RETURN(0);
-}
-
-
-/**
- @brief
- Plugin clean up
-*/
-static int connect_done_func(void *p)
-{
- int error= 0;
- PCONNECT pc, pn;
- DBUG_ENTER("connect_done_func");
-
-#ifdef LIBXML2_SUPPORT
- XmlCleanupParserLib();
-#endif // LIBXML2_SUPPORT
-
-#if !defined(WIN32)
- PROFILE_End();
-#endif // !WIN32
-
- for (pc= user_connect::to_users; pc; pc= pn) {
- if (pc->g)
- PlugCleanup(pc->g, true);
-
- pn= pc->next;
- delete pc;
- } // endfor pc
-
- DBUG_RETURN(error);
-}
-
-
-/**
- @brief
- Example of simple lock controls. The "share" it creates is a
- structure we will pass to each example handler. Do you have to have
- one of these? Well, you have pieces that are used for locking, and
- they are needed to function.
-*/
-
-CONNECT_SHARE *ha_connect::get_share()
-{
- CONNECT_SHARE *tmp_share;
- lock_shared_ha_data();
- if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr())))
- {
- tmp_share= new CONNECT_SHARE;
- if (!tmp_share)
- goto err;
- mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
- &tmp_share->mutex, MY_MUTEX_INIT_FAST);
- set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
- }
-err:
- unlock_shared_ha_data();
- return tmp_share;
-}
-
-
-static handler* connect_create_handler(handlerton *hton,
- TABLE_SHARE *table,
- MEM_ROOT *mem_root)
-{
- handler *h= new (mem_root) ha_connect(hton, table);
-
- if (xtrace)
- htrc("New CONNECT %p, table: %s\n",
- h, table ? table->table_name.str : "<null>");
-
- return h;
-} // end of connect_create_handler
-
-/****************************************************************************/
-/* ha_connect constructor. */
-/****************************************************************************/
-ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
- :handler(hton, table_arg)
-{
- hnum= ++num;
- xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
- if (xp)
- xp->SetHandler(this);
- tdbp= NULL;
- sdvalin= NULL;
- sdvalout= NULL;
- xmod= MODE_ANY;
- istable= false;
-//*tname= '\0';
- bzero((char*) &xinfo, sizeof(XINFO));
- valid_info= false;
- valid_query_id= 0;
- creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0;
- stop= false;
- alter= false;
- mrr= false;
- indexing= -1;
- locked= 0;
- data_file_name= NULL;
- index_file_name= NULL;
- enable_activate_all_index= 0;
- int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS);
- ref_length= sizeof(int);
- share= NULL;
- tshp= NULL;
-} // end of ha_connect constructor
-
-
-/****************************************************************************/
-/* ha_connect destructor. */
-/****************************************************************************/
-ha_connect::~ha_connect(void)
-{
- if (xtrace)
- htrc("Delete CONNECT %p, table: %s, xp=%p count=%d\n", this,
- table ? table->s->table_name.str : "<null>",
- xp, xp ? xp->count : 0);
-
- if (xp) {
- PCONNECT p;
-
- xp->count--;
-
- for (p= user_connect::to_users; p; p= p->next)
- if (p == xp)
- break;
-
- if (p && !p->count) {
- if (p->next)
- p->next->previous= p->previous;
-
- if (p->previous)
- p->previous->next= p->next;
- else
- user_connect::to_users= p->next;
-
- } // endif p
-
- if (!xp->count) {
- PlugCleanup(xp->g, true);
- delete xp;
- } // endif count
-
- } // endif xp
-
-} // end of ha_connect destructor
-
-
-/****************************************************************************/
-/* Get a pointer to the user of this handler. */
-/****************************************************************************/
-static PCONNECT GetUser(THD *thd, PCONNECT xp)
-{
- const char *dbn= NULL;
-
- if (!thd)
- return NULL;
-
- if (xp && thd == xp->thdp)
- return xp;
-
- for (xp= user_connect::to_users; xp; xp= xp->next)
- if (thd == xp->thdp)
- break;
-
- if (!xp) {
- xp= new user_connect(thd, dbn);
-
- if (xp->user_init()) {
- delete xp;
- xp= NULL;
- } // endif user_init
-
- } else
- xp->count++;
-
- return xp;
-} // end of GetUser
-
-
-/****************************************************************************/
-/* Get the global pointer of the user of this handler. */
-/****************************************************************************/
-static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp)
-{
- lxp= GetUser(thd, lxp);
- return (lxp) ? lxp->g : NULL;
-} // end of GetPlug
-
-/****************************************************************************/
-/* Get the implied table type. */
-/****************************************************************************/
-TABTYPE ha_connect::GetRealType(PTOS pos)
-{
- TABTYPE type= GetTypeID(pos->type);
-
- if (type == TAB_UNDEF)
- type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS;
-
- return type;
-} // end of GetRealType
-
-/** @brief
- This is a list of flags that indicate what functionality the storage
- engine implements. The current table flags are documented in handler.h
-*/
-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_NULL_IN_KEY | not implemented yet
-// HA_FAST_KEY_READ | causes error when sorting (???)
- HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
- HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
- ha_connect *hp= (ha_connect*)this;
- PTOS pos= hp->GetTableOptionStruct(table);
-
- if (pos) {
- TABTYPE type= hp->GetRealType(pos);
-
- if (IsFileType(type))
- flags|= HA_FILE_BASED;
-
- if (IsExactType(type))
- flags|= (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
-
- // No data change on ALTER for outward tables
- if (!IsFileType(type) || hp->FileExists(pos->filename))
- flags|= HA_NO_COPY_ON_ALTER;
-
- } // endif pos
-
- return flags;
-} // end of table_flags
-
-/****************************************************************************/
-/* Return the value of an option specified in the option list. */
-/****************************************************************************/
-char *GetListOption(PGLOBAL g, const char *opname,
- const char *oplist, const char *def)
-{
- char key[16], val[256];
- char *pk, *pv, *pn;
- char *opval= (char*) def;
- int n;
-
- for (pk= (char*)oplist; pk; pk= ++pn) {
- pn= strchr(pk, ',');
- pv= strchr(pk, '=');
-
- if (pv && (!pn || pv < pn)) {
- n= pv - pk;
- memcpy(key, pk, n);
- key[n]= 0;
- pv++;
-
- if (pn) {
- n= pn - pv;
- memcpy(val, pv, n);
- val[n]= 0;
- } else
- strcpy(val, pv);
-
- } else {
- if (pn) {
- n= min(pn - pk, 15);
- memcpy(key, pk, n);
- key[n]= 0;
- } else
- strcpy(key, pk);
-
- val[0]= 0;
- } // endif pv
-
- if (!stricmp(opname, key)) {
- opval= (char*)PlugSubAlloc(g, NULL, strlen(val) + 1);
- strcpy(opval, val);
- break;
- } else if (!pn)
- break;
-
- } // endfor pk
-
- return opval;
-} // end of GetListOption
-
-/****************************************************************************/
-/* Return the table option structure. */
-/****************************************************************************/
-PTOS ha_connect::GetTableOptionStruct(TABLE *tab)
-{
- return (tshp) ? tshp->option_struct :
- (tab) ? tab->s->option_struct : NULL;
-} // end of GetTableOptionStruct
-
-/****************************************************************************/
-/* Return the value of a string option or NULL if not specified. */
-/****************************************************************************/
-char *ha_connect::GetStringOption(char *opname, char *sdef)
-{
- char *opval= NULL;
- PTOS options= GetTableOptionStruct(table);
-
- if (!options)
- ;
- else if (!stricmp(opname, "Type"))
- opval= (char*)options->type;
- else if (!stricmp(opname, "Filename"))
- opval= (char*)options->filename;
- else if (!stricmp(opname, "Optname"))
- opval= (char*)options->optname;
- else if (!stricmp(opname, "Tabname"))
- opval= (char*)options->tabname;
- else if (!stricmp(opname, "Tablist"))
- opval= (char*)options->tablist;
- else if (!stricmp(opname, "Database") ||
- !stricmp(opname, "DBname"))
- opval= (char*)options->dbname;
- else if (!stricmp(opname, "Separator"))
- opval= (char*)options->separator;
- else if (!stricmp(opname, "Connect"))
- opval= (tshp) ? tshp->connect_string.str : table->s->connect_string.str;
- else if (!stricmp(opname, "Qchar"))
- opval= (char*)options->qchar;
- else if (!stricmp(opname, "Module"))
- opval= (char*)options->module;
- else if (!stricmp(opname, "Subtype"))
- opval= (char*)options->subtype;
- else if (!stricmp(opname, "Catfunc"))
- opval= (char*)options->catfunc;
- else if (!stricmp(opname, "Srcdef"))
- opval= (char*)options->srcdef;
- else if (!stricmp(opname, "Colist"))
- opval= (char*)options->colist;
- else if (!stricmp(opname, "Data_charset"))
- opval= (char*)options->data_charset;
- else if (!stricmp(opname, "Query_String"))
- opval= thd_query_string(table->in_use)->str;
-
- if (!opval && options && options->oplist)
- opval= GetListOption(xp->g, opname, options->oplist);
-
- if (!opval) {
- if (sdef && !strcmp(sdef, "*")) {
- // Return the handler default value
- if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database"))
- opval= (char*)GetDBName(NULL); // Current database
- else if (!stricmp(opname, "Type")) // Default type
- opval= (!options) ? NULL :
- (options->srcdef) ? (char*)"MYSQL" :
- (options->tabname) ? (char*)"PROXY" : (char*)"DOS";
- else if (!stricmp(opname, "User")) // Connected user
- opval= (char *) "root";
- else if (!stricmp(opname, "Host")) // Connected user host
- opval= (char *) "localhost";
- else
- opval= sdef; // Caller default
-
- } else
- opval= sdef; // Caller default
-
- } // endif !opval
-
- return opval;
-} // end of GetStringOption
-
-/****************************************************************************/
-/* Return the value of a Boolean option or bdef if not specified. */
-/****************************************************************************/
-bool ha_connect::GetBooleanOption(char *opname, bool bdef)
-{
- bool opval= bdef;
- char *pv;
- PTOS options= GetTableOptionStruct(table);
-
- if (!stricmp(opname, "View"))
- opval= (tshp) ? tshp->is_view : table->s->is_view;
- else if (!options)
- ;
- else if (!stricmp(opname, "Mapped"))
- opval= options->mapped;
- else if (!stricmp(opname, "Huge"))
- opval= options->huge;
-//else if (!stricmp(opname, "Compressed"))
-// opval= options->compressed;
- else if (!stricmp(opname, "Split"))
- opval= options->split;
- else if (!stricmp(opname, "Readonly"))
- opval= options->readonly;
- else if (!stricmp(opname, "SepIndex"))
- opval= options->sepindex;
- else if (options->oplist)
- if ((pv= GetListOption(xp->g, opname, options->oplist)))
- opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
-
- return opval;
-} // end of GetBooleanOption
-
-/****************************************************************************/
-/* Set the value of the opname option (does not work for oplist options) */
-/* Currently used only to set the Sepindex value. */
-/****************************************************************************/
-bool ha_connect::SetBooleanOption(char *opname, bool b)
-{
- PTOS options= GetTableOptionStruct(table);
-
- if (!options)
- return true;
-
- if (!stricmp(opname, "SepIndex"))
- options->sepindex= b;
- else
- return true;
-
- return false;
-} // end of SetBooleanOption
-
-/****************************************************************************/
-/* Return the value of an integer option or NO_IVAL if not specified. */
-/****************************************************************************/
-int ha_connect::GetIntegerOption(char *opname)
-{
- ulonglong opval= NO_IVAL;
- char *pv;
- PTOS options= GetTableOptionStruct(table);
-
- if (!options)
- ;
- else if (!stricmp(opname, "Lrecl"))
- opval= options->lrecl;
- else if (!stricmp(opname, "Elements"))
- opval= options->elements;
- else if (!stricmp(opname, "Estimate"))
-// opval= options->estimate;
- opval= (int)table->s->max_rows;
- else if (!stricmp(opname, "Avglen"))
- opval= (int)table->s->avg_row_length;
- else if (!stricmp(opname, "Multiple"))
- opval= options->multiple;
- else if (!stricmp(opname, "Header"))
- opval= options->header;
- else if (!stricmp(opname, "Quoted"))
- opval= options->quoted;
- else if (!stricmp(opname, "Ending"))
- opval= options->ending;
- else if (!stricmp(opname, "Compressed"))
- opval= (options->compressed);
-
- if (opval == (ulonglong)NO_IVAL && options && options->oplist)
- if ((pv= GetListOption(xp->g, opname, options->oplist)))
- opval= CharToNumber(pv, strlen(pv), ULONGLONG_MAX, true);
-
- return (int)opval;
-} // end of GetIntegerOption
-
-/****************************************************************************/
-/* Set the value of the opname option (does not work for oplist options) */
-/* Currently used only to set the Lrecl value. */
-/****************************************************************************/
-bool ha_connect::SetIntegerOption(char *opname, int n)
-{
- PTOS options= GetTableOptionStruct(table);
-
- if (!options)
- return true;
-
- if (!stricmp(opname, "Lrecl"))
- options->lrecl= n;
- else if (!stricmp(opname, "Elements"))
- options->elements= n;
-//else if (!stricmp(opname, "Estimate"))
-// options->estimate= n;
- else if (!stricmp(opname, "Multiple"))
- options->multiple= n;
- else if (!stricmp(opname, "Header"))
- options->header= n;
- else if (!stricmp(opname, "Quoted"))
- options->quoted= n;
- else if (!stricmp(opname, "Ending"))
- options->ending= n;
- else if (!stricmp(opname, "Compressed"))
- options->compressed= n;
- else
- return true;
-//else if (options->oplist)
-// SetListOption(opname, options->oplist, n);
-
- return false;
-} // end of SetIntegerOption
-
-/****************************************************************************/
-/* Return a field option structure. */
-/****************************************************************************/
-PFOS ha_connect::GetFieldOptionStruct(Field *fdp)
-{
- return fdp->option_struct;
-} // end of GetFildOptionStruct
-
-/****************************************************************************/
-/* Returns the column description structure used to make the column. */
-/****************************************************************************/
-void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
-{
- const char *cp;
- ha_field_option_struct *fop;
- Field* fp;
- Field* *fldp;
-
- // Double test to be on the safe side
- if (!table)
- return NULL;
-
- // Find the column to describe
- if (field) {
- fldp= (Field**)field;
- fldp++;
- } else
- fldp= (tshp) ? tshp->field : table->field;
-
- if (!fldp || !(fp= *fldp))
- return NULL;
-
- // Get the CONNECT field options structure
- fop= GetFieldOptionStruct(fp);
- pcf->Flags= 0;
-
- // Now get column information
- pcf->Name= (char*)fp->field_name;
-
- if (fop && fop->special) {
- pcf->Fieldfmt= (char*)fop->special;
- pcf->Flags= U_SPECIAL;
- return fldp;
- } // endif special
-
- pcf->Scale= 0;
- pcf->Opt= (fop) ? (int)fop->opt : 0;
-
- if ((pcf->Length= fp->field_length) < 0)
- pcf->Length= 256; // BLOB?
-
- pcf->Precision= pcf->Length;
-
- if (fop) {
- pcf->Offset= (int)fop->offset;
- pcf->Freq= (int)fop->freq;
- pcf->Datefmt= (char*)fop->dateformat;
- pcf->Fieldfmt= (char*)fop->fieldformat;
- } else {
- pcf->Offset= -1;
- pcf->Freq= 0;
- pcf->Datefmt= NULL;
- pcf->Fieldfmt= NULL;
- } // endif fop
-
- switch (fp->type()) {
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- pcf->Flags |= U_VAR;
- /* no break */
- default:
- pcf->Type= MYSQLtoPLG(fp->type());
- break;
- } // endswitch SQL type
-
- switch (pcf->Type) {
- case TYPE_STRING:
- // Do something for case
- cp= fp->charset()->name;
-
- // Find if collation name ends by _ci
- if (!strcmp(cp + strlen(cp) - 3, "_ci")) {
- pcf->Scale= 1; // Case insensitive
- pcf->Opt= 0; // Prevent index opt until it is safe
- } // endif ci
-
- break;
- case TYPE_DOUBLE:
- pcf->Scale= max(min(fp->decimals(), ((unsigned)pcf->Length - 2)), 0);
- break;
- case TYPE_DECIM:
- pcf->Precision= ((Field_new_decimal*)fp)->precision;
- pcf->Scale= fp->decimals();
- break;
- case TYPE_DATE:
- // Field_length is only used for DATE columns
- if (fop->fldlen)
- pcf->Length= (int)fop->fldlen;
- else {
- int len;
-
- if (pcf->Datefmt) {
- // Find the (max) length produced by the date format
- char buf[256];
- PGLOBAL g= GetPlug(table->in_use, xp);
- PDTP pdtp= MakeDateFormat(g, pcf->Datefmt, false, true, 0);
- struct tm datm;
- bzero(&datm, sizeof(datm));
- datm.tm_mday= 12;
- datm.tm_mon= 11;
- datm.tm_year= 112;
- len= strftime(buf, 256, pdtp->OutFmt, &datm);
- } else
- len= 0;
-
- // 11 is for signed numeric representation of the date
- pcf->Length= (len) ? len : 11;
- } // endelse
-
- break;
- default:
- break;
- } // endswitch type
-
- if (fp->flags & UNSIGNED_FLAG)
- pcf->Flags |= U_UNSIGNED;
-
- if (fp->flags & ZEROFILL_FLAG)
- pcf->Flags |= U_ZEROFILL;
-
- // This is used to skip null bit
- if (fp->real_maybe_null())
- pcf->Flags |= U_NULLS;
-
- // Mark virtual columns as such
- if (fp->vcol_info && !fp->stored_in_db)
- pcf->Flags |= U_VIRTUAL;
-
- pcf->Key= 0; // Not used when called from MySQL
-
- // Get the comment if any
- if (fp->comment.str && fp->comment.length) {
- pcf->Remark= (char*)PlugSubAlloc(g, NULL, fp->comment.length + 1);
- memcpy(pcf->Remark, fp->comment.str, fp->comment.length);
- pcf->Remark[fp->comment.length]= 0;
- } else
- pcf->Remark= NULL;
-
- return fldp;
-} // end of GetColumnOption
-
-/****************************************************************************/
-/* Returns the index description structure used to make the index. */
-/****************************************************************************/
-PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
-{
- char *name, *pn;
- bool unique;
- PIXDEF xdp, pxd=NULL, toidx= NULL;
- PKPDEF kpp, pkp;
- KEY kp;
- PGLOBAL& g= xp->g;
-
- if (!s)
- s= table->s;
-
- for (int n= 0; (unsigned)n < s->keynames.count; n++) {
- if (xtrace)
- htrc("Getting created index %d info\n", n + 1);
-
- // Find the index to describe
- kp= s->key_info[n];
-
- // Now get index information
- pn= (char*)s->keynames.type_names[n];
- name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
- strcpy(name, pn); // This is probably unuseful
- unique= (kp.flags & 1) != 0;
- pkp= NULL;
-
- // Allocate the index description block
- xdp= new(g) INDEXDEF(name, unique, n);
-
- // Get the the key parts info
- for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
- pn= (char*)kp.key_part[k].field->field_name;
- name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
- strcpy(name, pn); // This is probably unuseful
-
- // Allocate the key part description block
- kpp= new(g) KPARTDEF(name, k + 1);
- kpp->SetKlen(kp.key_part[k].length);
-
-#if 0 // NIY
- // Index on auto increment column can be an XXROW index
- if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
- kp.uder_defined_key_parts == 1) {
- char *type= GetStringOption("Type", "DOS");
- TABTYPE typ= GetTypeID(type);
-
- xdp->SetAuto(IsTypeFixed(typ));
- } // endif AUTO_INCREMENT
-#endif // 0
-
- if (pkp)
- pkp->SetNext(kpp);
- else
- xdp->SetToKeyParts(kpp);
-
- pkp= kpp;
- } // endfor k
-
- xdp->SetNParts(kp.user_defined_key_parts);
-
- if (pxd)
- pxd->SetNext(xdp);
- else
- toidx= xdp;
-
- pxd= xdp;
- } // endfor n
-
- return toidx;
-} // end of GetIndexInfo
-
-const char *ha_connect::GetDBName(const char* name)
-{
- return (name) ? name : table->s->db.str;
-} // end of GetDBName
-
-const char *ha_connect::GetTableName(void)
-{
- return (tshp) ? tshp->table_name.str : table->s->table_name.str;
-} // end of GetTableName
-
-#if 0
-/****************************************************************************/
-/* Returns the column real or special name length of a field. */
-/****************************************************************************/
-int ha_connect::GetColNameLen(Field *fp)
-{
- int n;
- PFOS fop= GetFieldOptionStruct(fp);
-
- // Now get the column name length
- if (fop && fop->special)
- n= strlen(fop->special) + 1;
- else
- n= strlen(fp->field_name);
-
- return n;
-} // end of GetColNameLen
-
-/****************************************************************************/
-/* Returns the column real or special name of a field. */
-/****************************************************************************/
-char *ha_connect::GetColName(Field *fp)
-{
- PFOS fop= GetFieldOptionStruct(fp);
-
- return (fop && fop->special) ? fop->special : (char*)fp->field_name;
-} // end of GetColName
-
-/****************************************************************************/
-/* Adds the column real or special name of a field to a string. */
-/****************************************************************************/
-void ha_connect::AddColName(char *cp, Field *fp)
-{
- PFOS fop= GetFieldOptionStruct(fp);
-
- // Now add the column name
- if (fop && fop->special)
- // The prefix * mark the column as "special"
- strcat(strcpy(cp, "*"), strupr(fop->special));
- else
- strcpy(cp, (char*)fp->field_name);
-
-} // end of AddColName
-#endif // 0
-
-/****************************************************************************/
-/* Get the table description block of a CONNECT table. */
-/****************************************************************************/
-PTDB ha_connect::GetTDB(PGLOBAL g)
-{
- const char *table_name;
- PTDB tp;
-
- // Double test to be on the safe side
- if (!g || !table)
- return NULL;
-
- table_name= GetTableName();
-
- if (!xp->CheckQuery(valid_query_id) && tdbp
- && !stricmp(tdbp->GetName(), table_name)
- && (tdbp->GetMode() == xmod
- || tdbp->GetAmType() == TYPE_AM_XML)) {
- tp= tdbp;
-// tp->SetMode(xmod);
- } else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
- valid_query_id= xp->last_query_id;
- tp->SetMode(xmod);
- } else
- htrc("GetTDB: %s\n", g->Message);
-
- return tp;
-} // end of GetTDB
-
-/****************************************************************************/
-/* Open a CONNECT table, restricting column list if cols is true. */
-/****************************************************************************/
-int ha_connect::OpenTable(PGLOBAL g, bool del)
-{
- bool rc= false;
- char *c1= NULL, *c2=NULL;
-
- // Double test to be on the safe side
- if (!g || !table) {
- htrc("OpenTable logical error; g=%p table=%p\n", g, table);
- return HA_ERR_INITIALIZATION;
- } // endif g
-
- if (!(tdbp= GetTDB(g)))
- return RC_FX;
- else if (tdbp->IsReadOnly())
- switch (xmod) {
- case MODE_WRITE:
- case MODE_INSERT:
- case MODE_UPDATE:
- case MODE_DELETE:
- strcpy(g->Message, MSG(READ_ONLY));
- return HA_ERR_TABLE_READONLY;
- default:
- break;
- } // endswitch xmode
-
- if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC
- || tdbp->GetAmType() == TYPE_AM_MYSQL) {
- // Get the list of used fields (columns)
- char *p;
- unsigned int k1, k2, n1, n2;
- Field* *field;
- Field* fp;
- MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
- MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
-
- k1= k2= 0;
- n1= n2= 1; // 1 is space for final null character
-
- for (field= table->field; fp= *field; field++) {
- if (bitmap_is_set(map, fp->field_index)) {
- n1+= (strlen(fp->field_name) + 1);
- k1++;
- } // endif
-
- if (ump && bitmap_is_set(ump, fp->field_index)) {
- n2+= (strlen(fp->field_name) + 1);
- k2++;
- } // endif
-
- } // endfor field
-
- if (k1) {
- p= c1= (char*)PlugSubAlloc(g, NULL, n1);
-
- for (field= table->field; fp= *field; field++)
- if (bitmap_is_set(map, fp->field_index)) {
- strcpy(p, (char*)fp->field_name);
- p+= (strlen(p) + 1);
- } // endif used field
-
- *p= '\0'; // mark end of list
- } // endif k1
-
- if (k2) {
- p= c2= (char*)PlugSubAlloc(g, NULL, n2);
-
- for (field= table->field; fp= *field; field++)
- if (bitmap_is_set(ump, fp->field_index)) {
- strcpy(p, (char*)fp->field_name);
- p+= (strlen(p) + 1);
- } // endif used field
-
- *p= '\0'; // mark end of list
- } // endif k2
-
- } // endif xmod
-
- // Open the table
- if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) {
- istable= true;
-// strmake(tname, table_name, sizeof(tname)-1);
-
- // We may be in a create index query
- if (xmod == MODE_ANY && *tdbp->GetName() != '#') {
- // The current indexes
- PIXDEF oldpix= GetIndexInfo();
- } // endif xmod
-
- } else
- htrc("OpenTable: %s\n", g->Message);
-
- if (rc) {
- tdbp= NULL;
- valid_info= false;
- } // endif rc
-
- return (rc) ? HA_ERR_INITIALIZATION : 0;
-} // end of OpenTable
-
-
-/****************************************************************************/
-/* IsOpened: returns true if the table is already opened. */
-/****************************************************************************/
-bool ha_connect::IsOpened(void)
-{
- return (!xp->CheckQuery(valid_query_id) && tdbp
- && tdbp->GetUse() == USE_OPEN);
-} // end of IsOpened
-
-
-/****************************************************************************/
-/* Close a CONNECT table. */
-/****************************************************************************/
-int ha_connect::CloseTable(PGLOBAL g)
-{
- int rc= CntCloseTable(g, tdbp);
- tdbp= NULL;
- sdvalin=NULL;
- sdvalout=NULL;
- valid_info= false;
- indexing= -1;
- return rc;
-} // end of CloseTable
-
-
-/***********************************************************************/
-/* Make a pseudo record from current row values. Specific to MySQL. */
-/***********************************************************************/
-int ha_connect::MakeRecord(char *buf)
-{
- char *p, *fmt, val[32];
- int rc= 0;
- Field* *field;
- Field *fp;
- my_bitmap_map *org_bitmap;
- CHARSET_INFO *charset= tdbp->data_charset();
-//MY_BITMAP readmap;
- MY_BITMAP *map;
- PVAL value;
- PCOL colp= NULL;
- DBUG_ENTER("ha_connect::MakeRecord");
-
- if (xtrace > 1)
- htrc("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n",
- *table->read_set->bitmap, *table->write_set->bitmap,
- *table->vcol_set->bitmap,
- *table->def_read_set.bitmap, *table->def_write_set.bitmap);
-
- // Avoid asserts in field::store() for columns that are not updated
- org_bitmap= dbug_tmp_use_all_columns(table, table->write_set);
-
- // This is for variable_length rows
- memset(buf, 0, table->s->null_bytes);
-
- // When sorting read_set selects all columns, so we use def_read_set
- map= (MY_BITMAP *)&table->def_read_set;
-
- // Make the pseudo record from field values
- for (field= table->field; *field && !rc; field++) {
- fp= *field;
-
- if (fp->vcol_info && !fp->stored_in_db)
- continue; // This is a virtual column
-
- if (bitmap_is_set(map, fp->field_index) || alter) {
- // This is a used field, fill the buffer with value
- for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
-#if defined(MRRBKA_SUPPORT)
- if ((!mrr || colp->GetKcol()) &&
- !stricmp(colp->GetName(), (char*)fp->field_name))
- break;
-#else // !MRRBKA_SUPPORT
- if (!stricmp(colp->GetName(), (char*)fp->field_name))
- break;
-#endif // !MRRBKA_SUPPORT
-
- if (!colp) {
-#if defined(MRRBKA_SUPPORT)
- if (mrr)
- continue;
-#endif // MRRBKA_SUPPORT
- htrc("Column %s not found\n", fp->field_name);
- dbug_tmp_restore_column_map(table->write_set, org_bitmap);
- DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
- } // endif colp
-
- value= colp->GetValue();
-
- // All this could be better optimized
- if (!value->IsNull()) {
- switch (value->GetType()) {
- case TYPE_DATE:
- if (!sdvalout)
- sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
-
- switch (fp->type()) {
- case MYSQL_TYPE_DATE:
- fmt= "%Y-%m-%d";
- break;
- case MYSQL_TYPE_TIME:
- fmt= "%H:%M:%S";
- break;
- case MYSQL_TYPE_YEAR:
- fmt= "%Y";
- break;
- default:
- fmt= "%Y-%m-%d %H:%M:%S";
- break;
- } // endswitch type
-
- // Get date in the format required by MySQL fields
- value->FormatValue(sdvalout, fmt);
- p= sdvalout->GetCharValue();
- break;
- case TYPE_DOUBLE:
- p= NULL;
- break;
- case TYPE_STRING:
- // Passthru
- default:
- p= value->GetCharString(val);
- 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;
-
- DBUG_PRINT("MakeRecord", ("%s", p));
- } // endif store
-
- } 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
-
- fp->set_notnull();
- } else
- fp->set_null();
-
- } // endif bitmap
-
- } // endfor field
-
- // This is copied from ha_tina and is necessary to avoid asserts
- dbug_tmp_restore_column_map(table->write_set, org_bitmap);
- DBUG_RETURN(rc);
-} // end of MakeRecord
-
-
-/***********************************************************************/
-/* Set row values from a MySQL pseudo record. Specific to MySQL. */
-/***********************************************************************/
-int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
-{
- char attr_buffer[1024];
- char data_buffer[1024];
- char *fmt;
- int rc= 0;
- PCOL colp;
- PVAL value;
- Field *fp;
- PTDBASE tp= (PTDBASE)tdbp;
- String attribute(attr_buffer, sizeof(attr_buffer),
- table->s->table_charset);
- my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set);
- const CHARSET_INFO *charset= tdbp->data_charset();
- String data_charset_value(data_buffer, sizeof(data_buffer), charset);
-
- // Scan the pseudo record for field values and set column values
- for (Field **field=table->field ; *field ; field++) {
- fp= *field;
-
- if ((fp->vcol_info && !fp->stored_in_db) ||
- fp->option_struct->special)
- continue; // Is a virtual column possible here ???
-
- if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
- && tdbp->GetAmType() != TYPE_AM_ODBC) ||
- bitmap_is_set(table->write_set, fp->field_index)) {
- for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
- if (!stricmp(colp->GetName(), fp->field_name))
- break;
-
- if (!colp) {
- htrc("Column %s not found\n", fp->field_name);
- rc= HA_ERR_WRONG_IN_RECORD;
- goto err;
- } else
- value= colp->GetValue();
-
- // This is a used field, fill the value from the row buffer
- // All this could be better optimized
- if (fp->is_null()) {
- if (colp->IsNullable())
- value->SetNull(true);
-
- value->Reset();
- } else switch (value->GetType()) {
- case TYPE_DOUBLE:
- value->SetValue(fp->val_real());
- break;
- case TYPE_DATE:
- if (!sdvalin)
- sdvalin= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
-
- // Get date in the format produced by MySQL fields
- switch (fp->type()) {
- case MYSQL_TYPE_DATE:
- fmt= "YYYY-MM-DD";
- break;
- case MYSQL_TYPE_TIME:
- fmt= "hh:mm:ss";
- break;
- case MYSQL_TYPE_YEAR:
- fmt= "YYYY";
- break;
- default:
- fmt= "YYYY-MM-DD hh:mm:ss";
- } // endswitch type
-
- ((DTVAL*)sdvalin)->SetFormat(g, fmt, strlen(fmt));
- fp->val_str(&attribute);
- sdvalin->SetValue_psz(attribute.c_ptr_safe());
- value->SetValue_pval(sdvalin);
- break;
- default:
- fp->val_str(&attribute);
-
- if (charset != &my_charset_bin) {
- // Convert from SQL field charset to DATA_CHARSET
- uint cnv_errors;
-
- data_charset_value.copy(attribute.ptr(), attribute.length(),
- attribute.charset(), charset, &cnv_errors);
- value->SetValue_psz(data_charset_value.c_ptr_safe());
- } else
- value->SetValue_psz(attribute.c_ptr_safe());
-
- break;
- } // endswitch Type
-
-#ifdef NEWCHANGE
- } else if (xmod == MODE_UPDATE) {
- PCOL cp;
-
- for (cp= tp->GetColumns(); cp; cp= cp->GetNext())
- if (!stricmp(colp->GetName(), cp->GetName()))
- break;
-
- if (!cp) {
- rc= HA_ERR_WRONG_IN_RECORD;
- goto err;
- } // endif cp
-
- value->SetValue_pval(cp->GetValue());
- } else // mode Insert
- value->Reset();
-#else
- } // endif bitmap_is_set
-#endif
-
- } // endfor field
-
- err:
- dbug_tmp_restore_column_map(table->read_set, bmap);
- return rc;
-} // end of ScanRecord
-
-
-/***********************************************************************/
-/* Check change in index column. Specific to MySQL. */
-/* Should be elaborated to check for real changes. */
-/***********************************************************************/
-int ha_connect::CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf)
-{
- return ScanRecord(g, newbuf);
-} // end of dummy CheckRecord
-
-
-/***********************************************************************/
-/* Return the string representing an operator. */
-/***********************************************************************/
-const char *ha_connect::GetValStr(OPVAL vop, bool neg)
-{
- const char *val;
-
- switch (vop) {
- case OP_EQ:
- val= " = ";
- break;
- case OP_NE:
- val= " <> ";
- break;
- case OP_GT:
- val= " > ";
- break;
- case OP_GE:
- val= " >= ";
- break;
- case OP_LT:
- val= " < ";
- break;
- case OP_LE:
- val= " <= ";
- break;
- case OP_IN:
- val= (neg) ? " NOT IN (" : " IN (";
- break;
- case OP_NULL:
- val= (neg) ? " IS NOT NULL" : " IS NULL";
- break;
- case OP_LIKE:
- val= " LIKE ";
- break;
- case OP_XX:
- val= (neg) ? " NOT BETWEEN " : " BETWEEN ";
- break;
- case OP_EXIST:
- val= (neg) ? " NOT EXISTS " : " EXISTS ";
- break;
- case OP_AND:
- val= " AND ";
- break;
- case OP_OR:
- val= " OR ";
- break;
- case OP_NOT:
- val= " NOT ";
- break;
- case OP_CNC:
- val= " || ";
- break;
- case OP_ADD:
- val= " + ";
- break;
- case OP_SUB:
- val= " - ";
- break;
- case OP_MULT:
- val= " * ";
- break;
- case OP_DIV:
- val= " / ";
- break;
- default:
- val= " ? ";
- break;
- } /* endswitch */
-
- return val;
-} // end of GetValStr
-
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Check the WHERE condition and return a CONNECT filter. */
-/***********************************************************************/
-PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
-{
- unsigned int i;
- bool ismul= false;
- OPVAL vop= OP_XX;
- PFIL filp= NULL;
-
- if (!cond)
- return NULL;
-
- if (xtrace)
- htrc("Cond type=%d\n", cond->type());
-
- if (cond->type() == COND::COND_ITEM) {
- PFIL fp;
- Item_cond *cond_item= (Item_cond *)cond;
-
- if (xtrace)
- htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
- cond_item->func_name());
-
- switch (cond_item->functype()) {
- case Item_func::COND_AND_FUNC: vop= OP_AND; break;
- case Item_func::COND_OR_FUNC: vop= OP_OR; break;
- default: return NULL;
- } // endswitch functype
-
- List<Item>* arglist= cond_item->argument_list();
- List_iterator<Item> li(*arglist);
- Item *subitem;
-
- for (i= 0; i < arglist->elements; i++)
- if ((subitem= li++)) {
- if (!(fp= CondFilter(g, subitem))) {
- if (vop == OP_OR)
- return NULL;
- } else
- filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
-
- } else
- return NULL;
-
- } else if (cond->type() == COND::FUNC_ITEM) {
- unsigned int i;
- bool iscol, neg= FALSE;
- PCOL colp[2]= {NULL,NULL};
- PPARM pfirst= NULL, pprec= NULL;
- POPER pop;
- Item_func *condf= (Item_func *)cond;
- Item* *args= condf->arguments();
-
- if (xtrace)
- htrc("Func type=%d argnum=%d\n", condf->functype(),
- condf->argument_count());
-
- switch (condf->functype()) {
- case Item_func::EQUAL_FUNC:
- case Item_func::EQ_FUNC: vop= OP_EQ; break;
- case Item_func::NE_FUNC: vop= OP_NE; break;
- case Item_func::LT_FUNC: vop= OP_LT; break;
- case Item_func::LE_FUNC: vop= OP_LE; break;
- case Item_func::GE_FUNC: vop= OP_GE; break;
- case Item_func::GT_FUNC: vop= OP_GT; break;
- case Item_func::IN_FUNC: vop= OP_IN;
- case Item_func::BETWEEN:
- ismul= true;
- neg= ((Item_func_opt_neg *)condf)->negated;
- break;
- default: return NULL;
- } // endswitch functype
-
- pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
- pop->Name= NULL;
- pop->Val=vop;
- pop->Mod= 0;
-
- if (condf->argument_count() < 2)
- return NULL;
-
- for (i= 0; i < condf->argument_count(); i++) {
- if (xtrace)
- htrc("Argtype(%d)=%d\n", i, args[i]->type());
-
- if (i >= 2 && !ismul) {
- if (xtrace)
- htrc("Unexpected arg for vop=%d\n", vop);
-
- continue;
- } // endif i
-
- if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
- Item_field *pField= (Item_field *)args[i];
-
- // IN and BETWEEN clauses should be col VOP list
- if (i && ismul)
- return NULL;
-
- if (pField->field->table != table ||
- !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name, 0)))
- return NULL; // Column does not belong to this table
-
- if (xtrace) {
- htrc("Field index=%d\n", pField->field->field_index);
- htrc("Field name=%s\n", pField->field->field_name);
- } // endif xtrace
-
- } else {
- char buff[256];
- String *res, tmp(buff, sizeof(buff), &my_charset_bin);
- Item_basic_constant *pval= (Item_basic_constant *)args[i];
- PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
-
- // IN and BETWEEN clauses should be col VOP list
- if (!i && (ismul))
- return NULL;
-
- if ((res= pval->val_str(&tmp)) == NULL)
- return NULL; // To be clarified
-
- switch (args[i]->real_type()) {
- case COND::STRING_ITEM:
- pp->Type= TYPE_STRING;
- pp->Value= PlugSubAlloc(g, NULL, res->length() + 1);
- strncpy((char*)pp->Value, res->ptr(), res->length() + 1);
- break;
- case COND::INT_ITEM:
- pp->Type= TYPE_INT;
- pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
- *((int*)pp->Value)= (int)pval->val_int();
- break;
- case COND::DATE_ITEM:
- pp->Type= TYPE_DATE;
- pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
- *((int*)pp->Value)= (int)pval->val_int_from_date();
- break;
- case COND::REAL_ITEM:
- pp->Type= TYPE_DOUBLE;
- pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
- *((double*)pp->Value)= pval->val_real();
- break;
- case COND::DECIMAL_ITEM:
- pp->Type= TYPE_DOUBLE;
- pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
- *((double*)pp->Value)= pval->val_real_from_decimal();
- break;
- case COND::CACHE_ITEM: // Possible ???
- case COND::NULL_ITEM: // TODO: handle this
- default:
- return NULL;
- } // endswitch type
-
- if (xtrace)
- htrc("Value=%.*s\n", res->length(), res->ptr());
-
- // Append the value to the argument list
- if (pprec)
- pprec->Next= pp;
- else
- pfirst= pp;
-
- pp->Domain= i;
- pp->Next= NULL;
- pprec= pp;
- } // endif type
-
- } // endfor i
-
- filp= MakeFilter(g, colp, pop, pfirst, neg);
- } else {
- if (xtrace)
- htrc("Unsupported condition\n");
-
- return NULL;
- } // endif's type
-
- return filp;
-} // end of CondFilter
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
-/***********************************************************************/
-PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
-{
- char *body= filp->Body;
- unsigned int i;
- bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
- OPVAL vop= OP_XX;
-
- if (!cond)
- return NULL;
-
- if (xtrace)
- htrc("Cond type=%d\n", cond->type());
-
- if (cond->type() == COND::COND_ITEM) {
- char *p1, *p2;
- Item_cond *cond_item= (Item_cond *)cond;
-
- if (x)
- return NULL;
-
- if (xtrace)
- htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
- cond_item->func_name());
-
- switch (cond_item->functype()) {
- case Item_func::COND_AND_FUNC: vop= OP_AND; break;
- case Item_func::COND_OR_FUNC: vop= OP_OR; break;
- default: return NULL;
- } // endswitch functype
-
- List<Item>* arglist= cond_item->argument_list();
- List_iterator<Item> li(*arglist);
- Item *subitem;
-
- p1= body + strlen(body);
- strcpy(p1, "(");
- p2= p1 + 1;
-
- for (i= 0; i < arglist->elements; i++)
- if ((subitem= li++)) {
- if (!CheckCond(g, filp, tty, subitem)) {
- if (vop == OP_OR)
- return NULL;
- else
- *p2= 0;
-
- } else {
- p1= p2 + strlen(p2);
- strcpy(p1, GetValStr(vop, FALSE));
- p2= p1 + strlen(p1);
- } // endif CheckCond
-
- } else
- return NULL;
-
- if (*p1 != '(')
- strcpy(p1, ")");
- else
- return NULL;
-
- } else if (cond->type() == COND::FUNC_ITEM) {
- unsigned int i;
-// int n;
- bool iscol, neg= FALSE;
- Item_func *condf= (Item_func *)cond;
- Item* *args= condf->arguments();
-
- if (xtrace)
- htrc("Func type=%d argnum=%d\n", condf->functype(),
- condf->argument_count());
-
-// neg= condf->
-
- switch (condf->functype()) {
- case Item_func::EQUAL_FUNC:
- case Item_func::EQ_FUNC: vop= OP_EQ; break;
- case Item_func::NE_FUNC: vop= OP_NE; break;
- case Item_func::LT_FUNC: vop= OP_LT; break;
- case Item_func::LE_FUNC: vop= OP_LE; break;
- case Item_func::GE_FUNC: vop= OP_GE; break;
- case Item_func::GT_FUNC: vop= OP_GT; break;
- case Item_func::IN_FUNC: vop= OP_IN;
- case Item_func::BETWEEN:
- ismul= true;
- neg= ((Item_func_opt_neg *)condf)->negated;
- break;
- default: return NULL;
- } // endswitch functype
-
- if (condf->argument_count() < 2)
- return NULL;
- else if (ismul && tty == TYPE_AM_WMI)
- return NULL; // Not supported by WQL
-
- if (x && (neg || !(vop == OP_EQ || vop == OP_IN)))
- return NULL;
-
- for (i= 0; i < condf->argument_count(); i++) {
- if (xtrace)
- htrc("Argtype(%d)=%d\n", i, args[i]->type());
-
- if (i >= 2 && !ismul) {
- if (xtrace)
- htrc("Unexpected arg for vop=%d\n", vop);
-
- continue;
- } // endif i
-
- if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
- const char *fnm;
- ha_field_option_struct *fop;
- Item_field *pField= (Item_field *)args[i];
-
- if (x && i)
- return NULL;
-
- if (pField->field->table != table)
- return NULL; // Field does not belong to this table
- else
- fop= GetFieldOptionStruct(pField->field);
-
- if (fop && fop->special) {
- if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID"))
- fnm= "TABID";
- else if (tty == TYPE_AM_PLG)
- fnm= fop->special;
- else
- return NULL;
-
- } else if (tty == TYPE_AM_TBL)
- return NULL;
- else
- fnm= pField->field->field_name;
-
- if (xtrace) {
- htrc("Field index=%d\n", pField->field->field_index);
- htrc("Field name=%s\n", pField->field->field_name);
- } // endif xtrace
-
- // IN and BETWEEN clauses should be col VOP list
- if (i && ismul)
- return NULL;
-
- strcat(body, fnm);
- } else if (args[i]->type() == COND::FUNC_ITEM) {
- if (tty == TYPE_AM_MYSQL) {
- if (!CheckCond(g, filp, tty, args[i]))
- return NULL;
-
- } else
- return NULL;
-
- } else {
- char buff[256];
- String *res, tmp(buff, sizeof(buff), &my_charset_bin);
- Item_basic_constant *pval= (Item_basic_constant *)args[i];
-
- switch (args[i]->real_type()) {
- case COND::STRING_ITEM:
- case COND::INT_ITEM:
- case COND::REAL_ITEM:
- case COND::NULL_ITEM:
- case COND::DECIMAL_ITEM:
- case COND::DATE_ITEM:
- case COND::CACHE_ITEM:
- break;
- default:
- return NULL;
- } // endswitch type
-
- if ((res= pval->val_str(&tmp)) == NULL)
- return NULL; // To be clarified
-
- if (xtrace)
- htrc("Value=%.*s\n", res->length(), res->ptr());
-
- // IN and BETWEEN clauses should be col VOP list
- if (!i && (x || ismul))
- return NULL;
-
- if (!x) {
- // Append the value to the filter
- if (args[i]->field_type() == MYSQL_TYPE_VARCHAR)
- strcat(strcat(strcat(body, "'"), res->ptr()), "'");
- else
- strncat(body, res->ptr(), res->length());
-
- } else {
- if (args[i]->field_type() == MYSQL_TYPE_VARCHAR) {
- // Add the command to the list
- PCMD *ncp, cmdp= new(g) CMD(g, (char*)res->ptr());
-
- for (ncp= &filp->Cmds; *ncp; ncp= &(*ncp)->Next) ;
-
- *ncp= cmdp;
- } else
- return NULL;
-
- } // endif x
-
- } // endif
-
- if (!x) {
- if (!i)
- strcat(body, GetValStr(vop, neg));
- else if (vop == OP_XX && i == 1)
- strcat(body, " AND ");
- else if (vop == OP_IN)
- strcat(body, (i == condf->argument_count() - 1) ? ")" : ",");
-
- } // endif x
-
- } // endfor i
-
- if (x)
- filp->Op= vop;
-
- } else {
- if (xtrace)
- htrc("Unsupported condition\n");
-
- return NULL;
- } // endif's type
-
- return filp;
-} // end of CheckCond
-
-
- /**
- Push condition down to the table handler.
-
- @param cond Condition to be pushed. The condition tree must not be
- modified by the caller.
-
- @return
- The 'remainder' condition that caller must use to filter out records.
- NULL means the handler will not return rows that do not match the
- passed condition.
-
- @note
- CONNECT handles the filtering only for table types that construct
- an SQL or WQL query, but still leaves it to MySQL because only some
- parts of the filter may be relevant.
- The first suballocate finds the position where the string will be
- constructed in the sarea. The second one does make the suballocation
- with the proper length.
- */
-const COND *ha_connect::cond_push(const COND *cond)
-{
- DBUG_ENTER("ha_connect::cond_push");
-
- if (tdbp) {
- AMT tty= tdbp->GetAmType();
- bool x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
- bool b= (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC ||
- tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL ||
- tty == TYPE_AM_PLG || x);
-#if defined(BLK_INDX)
- bool go= true;
-#else // !BLK_INDX)
- bool go= b;
-#endif // !BLK_INDX
-
- if (go) {
- PGLOBAL& g= xp->g;
-
- if (b) {
- PCFIL filp= (PCFIL)PlugSubAlloc(g, NULL, sizeof(CONDFIL));
-
- filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
- *filp->Body= 0;
- filp->Op= OP_XX;
- filp->Cmds= NULL;
-
- if (CheckCond(g, filp, tty, (Item *)cond)) {
- if (xtrace)
- htrc("cond_push: %s\n", filp->Body);
-
- if (!x)
- PlugSubAlloc(g, NULL, strlen(filp->Body) + 1);
- else
- cond= NULL; // Does this work?
-
- tdbp->SetCondFil(filp);
- } else if (x && cond)
- tdbp->SetCondFil(filp); // Wrong filter
-
- } // endif b
-#if defined(BLK_INDX)
- else
- tdbp->SetFilter(CondFilter(g, (Item *)cond));
-#endif // BLK_INDX
- } // endif go
-
- } // endif tdbp
-
- // Let MySQL do the filtering
- DBUG_RETURN(cond);
-} // end of cond_push
-
-/**
- Number of rows in table. It will only be called if
- (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
-*/
-ha_rows ha_connect::records()
-{
- if (!valid_info)
- info(HA_STATUS_VARIABLE);
-
- if (tdbp && tdbp->Cardinality(NULL))
- return stats.records;
- else
- return HA_POS_ERROR;
-
-} // end of records
-
-
-/**
- Return an error message specific to this handler.
-
- @param error error code previously returned by handler
- @param buf pointer to String where to add error message
-
- @return
- Returns true if this is a temporary error
-*/
-bool ha_connect::get_error_message(int error, String* buf)
-{
- DBUG_ENTER("ha_connect::get_error_message");
-
- if (xp && xp->g) {
- PGLOBAL g= xp->g;
- char *msg= (char*)PlugSubAlloc(g, NULL, strlen(g->Message) * 3);
- uint dummy_errors;
- uint32 len= copy_and_convert(msg, strlen(g->Message) * 3,
- system_charset_info,
- g->Message, strlen(g->Message),
- &my_charset_latin1,
- &dummy_errors);
- msg[len]= '\0';
- buf->copy(msg, (uint)strlen(msg), system_charset_info);
- } else
- buf->copy("Cannot retrieve msg", 19, system_charset_info);
-
- DBUG_RETURN(false);
-} // end of get_error_message
-
-
-/**
- @brief
- Used for opening tables. The name will be the name of the file.
-
- @details
- A table is opened when it needs to be opened; e.g. when a request comes in
- for a SELECT on the table (tables are not open and closed for each request,
- they are cached).
-
- Called from handler.cc by handler::ha_open(). The server opens all tables by
- calling ha_open() which then calls the handler specific open().
-
- @note
- For CONNECT no open can be done here because field information is not yet
- updated. >>>>> TO BE CHECKED <<<<<
- (Thread information could be get by using 'ha_thd')
-
- @see
- handler::ha_open() in handler.cc
-*/
-int ha_connect::open(const char *name, int mode, uint test_if_locked)
-{
- int rc= 0;
- DBUG_ENTER("ha_connect::open");
-
- if (xtrace)
- htrc("open: name=%s mode=%d test=%u\n", name, mode, test_if_locked);
-
- if (!(share= get_share()))
- DBUG_RETURN(1);
-
- thr_lock_data_init(&share->lock,&lock,NULL);
-
- // Try to get the user if possible
- xp= GetUser(ha_thd(), xp);
- PGLOBAL g= (xp) ? xp->g : NULL;
-
- // Try to set the database environment
- if (g) {
- rc= (CntCheckDB(g, this, name)) ? (-2) : 0;
-#if defined(MRRBKA_SUPPORT)
- if (g->Mrr) {
- // This should only happen for the mrr secondary handler
- mrr= true;
- g->Mrr= false;
- } else
- mrr= false;
-#endif // MRRBKA_SUPPORT
- } else
- rc= HA_ERR_INTERNAL_ERROR;
-
- DBUG_RETURN(rc);
-} // end of open
-
-/**
- @brief
- Make the indexes for this table
-*/
-int ha_connect::optimize(THD* thd, HA_CHECK_OPT* check_opt)
-{
- int rc= 0;
- PGLOBAL& g= xp->g;
- PDBUSER dup= PlgGetUser(g);
-
- // Ignore error on the opt file
- dup->Check &= ~CHK_OPT;
- tdbp= GetTDB(g);
- dup->Check |= CHK_OPT;
-
- if (tdbp || (tdbp= GetTDB(g))) {
-#if defined(BLK_INDX)
- bool b= ((PTDBASE)tdbp)->GetDef()->Indexable();
-
- if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, true, b))) {
- if (rc == RC_INFO) {
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- rc= 0;
- } else
- rc= HA_ERR_INTERNAL_ERROR;
-
- } // endif rc
-#else // !BLK_INDX
- if (!((PTDBASE)tdbp)->GetDef()->Indexable()) {
- sprintf(g->Message, "optimize: Table %s is not indexable", tdbp->GetName());
- my_message(ER_INDEX_REBUILD, g->Message, MYF(0));
- rc= HA_ERR_UNSUPPORTED;
- } else if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, false, true))) {
- if (rc == RC_INFO) {
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- rc= 0;
- } else
- rc= HA_ERR_INTERNAL_ERROR;
-
- } // endif's
-#endif // !BLK_INDX
-
-
- } else
- rc= HA_ERR_INTERNAL_ERROR;
-
- return rc;
-} // end of optimize
-
-/**
- @brief
- Closes a table.
-
- @details
- Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is
- only used to close up temporary tables or during the process where a
- temporary table is converted over to being a myisam table.
-
- For sql_base.cc look at close_data_tables().
-
- @see
- sql_base.cc, sql_select.cc and table.cc
-*/
-int ha_connect::close(void)
-{
- int rc= 0;
- DBUG_ENTER("ha_connect::close");
-
- // If this is called by a later query, the table may have
- // been already closed and the tdbp is not valid anymore.
- if (tdbp && xp->last_query_id == valid_query_id)
- rc= CloseTable(xp->g);
-
- DBUG_RETURN(rc);
-} // end of close
-
-
-/**
- @brief
- write_row() inserts a row. No extra() hint is given currently if a bulk load
- is happening. buf() is a byte array of data. You can use the field
- information to extract the data from the native byte array type.
-
- @details
- Example of this would be:
- @code
- for (Field **field=table->field ; *field ; field++)
- {
- ...
- }
- @endcode
-
- See ha_tina.cc for an example of extracting all of the data as strings.
- ha_berekly.cc has an example of how to store it intact by "packing" it
- for ha_berkeley's own native storage type.
-
- See the note for update_row() on auto_increments and timestamps. This
- case also applies to write_row().
-
- Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
- sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
-
- @see
- item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
- sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
-*/
-int ha_connect::write_row(uchar *buf)
-{
- int rc= 0;
- PGLOBAL& g= xp->g;
- DBUG_ENTER("ha_connect::write_row");
-
- // This is not tested yet
- if (xmod == MODE_ALTER)
- xmod= MODE_INSERT;
-
- // Open the table if it was not opened yet (locked)
- if (!IsOpened() || xmod != tdbp->GetMode()) {
- if (IsOpened())
- CloseTable(g);
-
- if ((rc= OpenTable(g)))
- DBUG_RETURN(rc);
-
- } // endif isopened
-
- if (tdbp->GetMode() == MODE_ANY)
- DBUG_RETURN(0);
-
-#if 0 // AUTO_INCREMENT NIY
- if (table->next_number_field && buf == table->record[0]) {
- int error;
-
- if ((error= update_auto_increment()))
- return error;
-
- } // endif nex_number_field
-#endif // 0
-
- // Set column values from the passed pseudo record
- if ((rc= ScanRecord(g, buf)))
- DBUG_RETURN(rc);
-
- // Return result code from write operation
- if (CntWriteRow(g, tdbp)) {
- DBUG_PRINT("write_row", ("%s", g->Message));
- htrc("write_row: %s\n", g->Message);
- rc= HA_ERR_INTERNAL_ERROR;
- } // endif RC
-
- DBUG_RETURN(rc);
-} // end of write_row
-
-
-/**
- @brief
- Yes, update_row() does what you expect, it updates a row. old_data will have
- the previous row record in it, while new_data will have the newest data in it.
- Keep in mind that the server can do updates based on ordering if an ORDER BY
- clause was used. Consecutive ordering is not guaranteed.
-
- @details
- Currently new_data will not have an updated auto_increament record, or
- and updated timestamp field. You can do these for example by doing:
- @code
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- if (table->next_number_field && record == table->record[0])
- update_auto_increment();
- @endcode
-
- Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
-
- @see
- sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
-*/
-int ha_connect::update_row(const uchar *old_data, uchar *new_data)
-{
- int rc= 0;
- PGLOBAL& g= xp->g;
- DBUG_ENTER("ha_connect::update_row");
-
- if (xtrace > 1)
- htrc("update_row: old=%s new=%s\n", old_data, new_data);
-
- // Check values for possible change in indexed column
- if ((rc= CheckRecord(g, old_data, new_data)))
- return rc;
-
- if (CntUpdateRow(g, tdbp)) {
- DBUG_PRINT("update_row", ("%s", g->Message));
- htrc("update_row CONNECT: %s\n", g->Message);
- rc= HA_ERR_INTERNAL_ERROR;
- } // endif RC
-
- DBUG_RETURN(rc);
-} // end of update_row
-
-
-/**
- @brief
- This will delete a row. buf will contain a copy of the row to be deleted.
- The server will call this right after the current row has been called (from
- either a previous rnd_nexT() or index call).
-
- @details
- If you keep a pointer to the last row or can access a primary key it will
- make doing the deletion quite a bit easier. Keep in mind that the server does
- not guarantee consecutive deletions. ORDER BY clauses can be used.
-
- Called in sql_acl.cc and sql_udf.cc to manage internal table
- information. Called in sql_delete.cc, sql_insert.cc, and
- sql_select.cc. In sql_select it is used for removing duplicates
- while in insert it is used for REPLACE calls.
-
- @see
- sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc
-*/
-int ha_connect::delete_row(const uchar *buf)
-{
- int rc= 0;
- DBUG_ENTER("ha_connect::delete_row");
-
- if (CntDeleteRow(xp->g, tdbp, false)) {
- rc= HA_ERR_INTERNAL_ERROR;
- htrc("delete_row CONNECT: %s\n", xp->g->Message);
- } // endif DeleteRow
-
- DBUG_RETURN(rc);
-} // end of delete_row
-
-
-/****************************************************************************/
-/* We seem to come here at the begining of an index use. */
-/****************************************************************************/
-int ha_connect::index_init(uint idx, bool sorted)
-{
- int rc;
- PGLOBAL& g= xp->g;
- DBUG_ENTER("index_init");
-
- if (xtrace)
- htrc("index_init: this=%p idx=%u sorted=%d\n", this, idx, sorted);
-
- if ((rc= rnd_init(0)))
- return rc;
-
- if (locked == 2) {
- // Indexes are not updated in lock write mode
- active_index= MAX_KEY;
- indexing= 0;
- DBUG_RETURN(0);
- } // endif locked
-
- indexing= CntIndexInit(g, tdbp, (signed)idx);
-
- if (indexing <= 0) {
- DBUG_PRINT("index_init", ("%s", g->Message));
- htrc("index_init CONNECT: %s\n", g->Message);
- active_index= MAX_KEY;
- rc= HA_ERR_INTERNAL_ERROR;
- } else {
- if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) {
- if (((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
- ((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g);
-
- active_index= idx;
- } else // Void table
- indexing= 0;
-
- rc= 0;
- } // endif indexing
-
- if (xtrace)
- htrc("index_init: rc=%d indexing=%d active_index=%d\n",
- rc, indexing, active_index);
-
- DBUG_RETURN(rc);
-} // end of index_init
-
-/****************************************************************************/
-/* We seem to come here at the end of an index use. */
-/****************************************************************************/
-int ha_connect::index_end()
-{
- DBUG_ENTER("index_end");
- active_index= MAX_KEY;
-#if defined(MRRBKA_SUPPORT)
- ds_mrr.dsmrr_close();
-#endif // MRRBKA_SUPPORT
- DBUG_RETURN(rnd_end());
-} // end of index_end
-
-
-/****************************************************************************/
-/* This is internally called by all indexed reading functions. */
-/****************************************************************************/
-int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len)
-{
- int rc;
-
-//statistic_increment(ha_read_key_count, &LOCK_status);
-
- switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) {
- case RC_OK:
- xp->fnd++;
- rc= MakeRecord((char*)buf);
- break;
- case RC_EF: // End of file
- rc= HA_ERR_END_OF_FILE;
- break;
- case RC_NF: // Not found
- xp->nfd++;
- rc= (op == OP_SAME) ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
- break;
- default: // Read error
- DBUG_PRINT("ReadIndexed", ("%s", xp->g->Message));
- htrc("ReadIndexed: %s\n", xp->g->Message);
- rc= HA_ERR_INTERNAL_ERROR;
- break;
- } // endswitch RC
-
- if (xtrace > 1)
- htrc("ReadIndexed: op=%d rc=%d\n", op, rc);
-
- table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND;
- return rc;
-} // end of ReadIndexed
-
-
-#ifdef NOT_USED
-/**
- @brief
- Positions an index cursor to the index specified in the handle. Fetches the
- row if available. If the key value is null, begin at the first key of the
- index.
-*/
-int ha_connect::index_read_map(uchar *buf, const uchar *key,
- key_part_map keypart_map __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
-{
- DBUG_ENTER("ha_connect::index_read");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-#endif // NOT_USED
-
-
-/****************************************************************************/
-/* This is called by handler::index_read_map. */
-/****************************************************************************/
-int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
- enum ha_rkey_function find_flag)
-{
- int rc;
- OPVAL op= OP_XX;
- DBUG_ENTER("ha_connect::index_read");
-
- switch(find_flag) {
- case HA_READ_KEY_EXACT: op= OP_EQ; break;
- case HA_READ_AFTER_KEY: op= OP_GT; break;
- case HA_READ_KEY_OR_NEXT: op= OP_GE; break;
- default: DBUG_RETURN(-1); break;
- } // endswitch find_flag
-
- if (xtrace > 1)
- htrc("%p index_read: op=%d\n", this, op);
-
- if (indexing > 0)
- rc= ReadIndexed(buf, op, key, key_len);
- else
- rc= HA_ERR_INTERNAL_ERROR;
-
- DBUG_RETURN(rc);
-} // end of index_read
-
-
-/**
- @brief
- Used to read forward through the index.
-*/
-int ha_connect::index_next(uchar *buf)
-{
- int rc;
- DBUG_ENTER("ha_connect::index_next");
- //statistic_increment(ha_read_next_count, &LOCK_status);
-
- if (indexing > 0)
- rc= ReadIndexed(buf, OP_NEXT);
- else if (!indexing)
- rc= rnd_next(buf);
- else
- rc= HA_ERR_INTERNAL_ERROR;
-
- DBUG_RETURN(rc);
-} // end of index_next
-
-
-#ifdef NOT_USED
-/**
- @brief
- Used to read backwards through the index.
-*/
-int ha_connect::index_prev(uchar *buf)
-{
- DBUG_ENTER("ha_connect::index_prev");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-#endif // NOT_USED
-
-
-/**
- @brief
- index_first() asks for the first key in the index.
-
- @details
- Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
-
- @see
- opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
-*/
-int ha_connect::index_first(uchar *buf)
-{
- int rc;
- DBUG_ENTER("ha_connect::index_first");
-
- if (indexing > 0)
- rc= ReadIndexed(buf, OP_FIRST);
- else if (indexing < 0)
- rc= HA_ERR_INTERNAL_ERROR;
- else if (CntRewindTable(xp->g, tdbp)) {
- table->status= STATUS_NOT_FOUND;
- rc= HA_ERR_INTERNAL_ERROR;
- } else
- rc= rnd_next(buf);
-
- DBUG_RETURN(rc);
-} // end of index_first
-
-
-#ifdef NOT_USED
-/**
- @brief
- index_last() asks for the last key in the index.
-
- @details
- Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
-
- @see
- opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
-*/
-int ha_connect::index_last(uchar *buf)
-{
- DBUG_ENTER("ha_connect::index_last");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-#endif // NOT_USED
-
-
-/****************************************************************************/
-/* This is called to get more rows having the same index value. */
-/****************************************************************************/
-int ha_connect::index_next_same(uchar *buf, const uchar *key, uint keylen)
-{
- int rc;
- DBUG_ENTER("ha_connect::index_next_same");
-//statistic_increment(ha_read_next_count, &LOCK_status);
-
- if (!indexing)
- rc= rnd_next(buf);
- else if (indexing > 0)
- rc= ReadIndexed(buf, OP_SAME);
- else
- rc= HA_ERR_INTERNAL_ERROR;
-
- DBUG_RETURN(rc);
-} // end of index_next_same
-
-
-/**
- @brief
- rnd_init() is called when the system wants the storage engine to do a table
- scan. See the example in the introduction at the top of this file to see when
- rnd_init() is called.
-
- @details
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
- and sql_update.cc.
-
- @note
- We always call open and extern_lock/start_stmt before comming here.
-
- @see
- filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
-*/
-int ha_connect::rnd_init(bool scan)
-{
- int rc;
- PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use, xp) :
- (xp) ? xp->g : NULL);
- DBUG_ENTER("ha_connect::rnd_init");
-
- // This is not tested yet
- if (xmod == MODE_ALTER) {
- xmod= MODE_READ;
- alter= 1;
- } // endif xmod
-
- if (xtrace)
- htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
- this, scan, xmod, alter);
-
- if (!g || !table || xmod == MODE_INSERT)
- DBUG_RETURN(HA_ERR_INITIALIZATION);
-
- // Do not close the table if it was opened yet (locked?)
- if (IsOpened())
- DBUG_RETURN(0);
-// CloseTable(g); Was done before making things done twice
- else if (xp->CheckQuery(valid_query_id))
- tdbp= NULL; // Not valid anymore
-
- // When updating, to avoid skipped update, force the table
- // handler to retrieve write-only fields to be able to compare
- // records and detect data change.
- if (xmod == MODE_UPDATE)
- bitmap_union(table->read_set, table->write_set);
-
- if ((rc= OpenTable(g, xmod == MODE_DELETE)))
- DBUG_RETURN(rc);
-
- xp->nrd= xp->fnd= xp->nfd= 0;
- xp->tb1= my_interval_timer();
- DBUG_RETURN(0);
-} // end of rnd_init
-
-/**
- @brief
- Not described.
-
- @note
- The previous version said:
- Stop scanning of table. Note that this may be called several times during
- execution of a sub select.
- =====> This has been moved to external lock to avoid closing subselect tables.
-*/
-int ha_connect::rnd_end()
-{
- int rc= 0;
- DBUG_ENTER("ha_connect::rnd_end");
-
- // If this is called by a later query, the table may have
- // been already closed and the tdbp is not valid anymore.
-// if (tdbp && xp->last_query_id == valid_query_id)
-// rc= CloseTable(xp->g);
-
-#if defined(MRRBKA_SUPPORT)
- ds_mrr.dsmrr_close();
-#endif // MRRBKA_SUPPORT
- DBUG_RETURN(rc);
-} // end of rnd_end
-
-
-/**
- @brief
- This is called for each row of the table scan. When you run out of records
- you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
- The Field structure for the table is the key to getting data into buf
- in a manner that will allow the server to understand it.
-
- @details
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
- and sql_update.cc.
-
- @see
- filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
-*/
-int ha_connect::rnd_next(uchar *buf)
-{
- int rc;
- DBUG_ENTER("ha_connect::rnd_next");
-//statistic_increment(ha_read_rnd_next_count, &LOCK_status);
-
- if (tdbp->GetMode() == MODE_ANY) {
- // We will stop on next read
- if (!stop) {
- stop= true;
- DBUG_RETURN(RC_OK);
- } else
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- } // endif Mode
-
- switch (CntReadNext(xp->g, tdbp)) {
- case RC_OK:
- rc= MakeRecord((char*)buf);
- break;
- case RC_EF: // End of file
- rc= HA_ERR_END_OF_FILE;
- break;
- case RC_NF: // Not found
- rc= HA_ERR_RECORD_DELETED;
- break;
- default: // Read error
- htrc("rnd_next CONNECT: %s\n", xp->g->Message);
- rc= (records()) ? HA_ERR_INTERNAL_ERROR : HA_ERR_END_OF_FILE;
- break;
- } // endswitch RC
-
- if (xtrace > 1 && (rc || !(xp->nrd++ % 16384))) {
- ulonglong tb2= my_interval_timer();
- double elapsed= (double) (tb2 - xp->tb1) / 1000000000ULL;
- DBUG_PRINT("rnd_next", ("rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
- rc, (uint)xp->nrd, (uint)xp->fnd,
- (uint)xp->nfd, elapsed));
- htrc("rnd_next: rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
- rc, (uint)xp->nrd, (uint)xp->fnd,
- (uint)xp->nfd, elapsed);
- xp->tb1= tb2;
- xp->fnd= xp->nfd= 0;
- } // endif nrd
-
- table->status= (!rc) ? 0 : STATUS_NOT_FOUND;
- DBUG_RETURN(rc);
-} // end of rnd_next
-
-
-/**
- @brief
- position() is called after each call to rnd_next() if the data needs
- to be ordered. You can do something like the following to store
- the position:
- @code
- my_store_ptr(ref, ref_length, current_position);
- @endcode
-
- @details
- The server uses ref to store data. ref_length in the above case is
- the size needed to store current_position. ref is just a byte array
- that the server will maintain. If you are using offsets to mark rows, then
- current_position should be the offset. If it is a primary key like in
- BDB, then it needs to be a primary key.
-
- Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
-
- @see
- filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
-*/
-void ha_connect::position(const uchar *record)
-{
- DBUG_ENTER("ha_connect::position");
-//if (((PTDBASE)tdbp)->GetDef()->Indexable())
- my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos());
- DBUG_VOID_RETURN;
-} // end of position
-
-
-/**
- @brief
- This is like rnd_next, but you are given a position to use
- to determine the row. The position will be of the type that you stored in
- ref. You can use my_get_ptr(pos,ref_length) to retrieve whatever key
- or position you saved when position() was called.
-
- @details
- Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc.
-
- @note
- Is this really useful? It was never called even when sorting.
-
- @see
- filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
-*/
-int ha_connect::rnd_pos(uchar *buf, uchar *pos)
-{
- int rc;
- PTDBASE tp= (PTDBASE)tdbp;
- DBUG_ENTER("ha_connect::rnd_pos");
-
- if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length)))
- rc= rnd_next(buf);
- else
- rc= HA_ERR_KEY_NOT_FOUND;
-
- DBUG_RETURN(rc);
-} // end of rnd_pos
-
-
-/**
- @brief
- ::info() is used to return information to the optimizer. See my_base.h for
- the complete description.
-
- @details
- Currently this table handler doesn't implement most of the fields really needed.
- SHOW also makes use of this data.
-
- You will probably want to have the following in your code:
- @code
- if (records < 2)
- records= 2;
- @endcode
- The reason is that the server will optimize for cases of only a single
- record. If, in a table scan, you don't know the number of records, it
- will probably be better to set records to two so you can return as many
- records as you need. Along with records, a few more variables you may wish
- to set are:
- records
- deleted
- data_file_length
- index_file_length
- delete_length
- check_time
- Take a look at the public variables in handler.h for more information.
-
- Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
- sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
- sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc,
- sql_table.cc, sql_union.cc, and sql_update.cc.
-
- @see
- filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc,
- sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc,
- sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc,
- sql_union.cc and sql_update.cc
-*/
-int ha_connect::info(uint flag)
-{
- bool pure= false;
- PGLOBAL g= GetPlug((table) ? table->in_use : NULL, xp);
-
- DBUG_ENTER("ha_connect::info");
-
- if (xtrace)
- htrc("%p In info: flag=%u valid_info=%d\n", this, flag, valid_info);
-
- if (!valid_info) {
- // tdbp must be available to get updated info
- if (xp->CheckQuery(valid_query_id) || !tdbp) {
- if (xmod == MODE_ANY || xmod == MODE_ALTER) {
- // Pure info, not a query
- pure= true;
- xp->CheckCleanup();
- } // endif xmod
-
- tdbp= GetTDB(g);
- } // endif tdbp
-
- valid_info= CntInfo(g, tdbp, &xinfo);
- } // endif valid_info
-
- if (flag & HA_STATUS_VARIABLE) {
- stats.records= xinfo.records;
- stats.deleted= 0;
- stats.data_file_length= xinfo.data_file_length;
- stats.index_file_length= 0;
- stats.delete_length= 0;
- stats.check_time= 0;
- stats.mean_rec_length= xinfo.mean_rec_length;
- } // endif HA_STATUS_VARIABLE
-
- if (flag & HA_STATUS_CONST) {
- // This is imported from the previous handler and must be reconsidered
- stats.max_data_file_length= 4294967295;
- stats.max_index_file_length= 4398046510080;
- stats.create_time= 0;
- data_file_name= xinfo.data_file_name;
- index_file_name= NULL;
-// sortkey= (uint) - 1; // Table is not sorted
- ref_length= sizeof(int); // Pointer size to row
- table->s->db_options_in_use= 03;
- stats.block_size= 1024;
- table->s->keys_in_use.set_prefix(table->s->keys);
- table->s->keys_for_keyread= table->s->keys_in_use;
-// table->s->keys_for_keyread.subtract(table->s->read_only_keys);
- table->s->db_record_offset= 0;
- } // endif HA_STATUS_CONST
-
- if (flag & HA_STATUS_ERRKEY) {
- errkey= 0;
- } // endif HA_STATUS_ERRKEY
-
- if (flag & HA_STATUS_TIME)
- stats.update_time= 0;
-
- if (flag & HA_STATUS_AUTO)
- stats.auto_increment_value= 1;
-
- if (tdbp && pure)
- CloseTable(g); // Not used anymore
-
- DBUG_RETURN(0);
-} // end of info
-
-
-/**
- @brief
- extra() is called whenever the server wishes to send a hint to
- the storage engine. The myisam engine implements the most hints.
- ha_innodb.cc has the most exhaustive list of these hints.
-
- @note
- This is not yet implemented for CONNECT.
-
- @see
- ha_innodb.cc
-*/
-int ha_connect::extra(enum ha_extra_function operation)
-{
- DBUG_ENTER("ha_connect::extra");
- DBUG_RETURN(0);
-} // end of extra
-
-
-/**
- @brief
- Used to delete all rows in a table, including cases of truncate and cases where
- the optimizer realizes that all rows will be removed as a result of an SQL statement.
-
- @details
- Called from item_sum.cc by Item_func_group_concat::clear(),
- Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
- Called from sql_delete.cc by mysql_delete().
- Called from sql_select.cc by JOIN::reinit().
- Called from sql_union.cc by st_select_lex_unit::exec().
-
- @see
- Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
- Item_func_group_concat::clear() in item_sum.cc;
- mysql_delete() in sql_delete.cc;
- JOIN::reinit() in sql_select.cc and
- st_select_lex_unit::exec() in sql_union.cc.
-*/
-int ha_connect::delete_all_rows()
-{
- int rc= 0;
- PGLOBAL g= xp->g;
- DBUG_ENTER("ha_connect::delete_all_rows");
-
- if (tdbp && tdbp->GetUse() == USE_OPEN &&
- tdbp->GetAmType() != TYPE_AM_XML &&
- ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
- // Close and reopen the table so it will be deleted
- rc= CloseTable(g);
-
- if (!(rc= OpenTable(g))) {
- if (CntDeleteRow(g, tdbp, true)) {
- htrc("%s\n", g->Message);
- rc= HA_ERR_INTERNAL_ERROR;
- } // endif
-
- } // endif rc
-
- DBUG_RETURN(rc);
-} // end of delete_all_rows
-
-
-bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
-{
- const char *db= (dbn && *dbn) ? dbn : NULL;
- TABTYPE type=GetRealType(options);
-
- switch (type) {
- case TAB_UNDEF:
-// case TAB_CATLG:
- case TAB_PLG:
- case TAB_JCT:
- case TAB_DMY:
- case TAB_NIY:
- my_printf_error(ER_UNKNOWN_ERROR,
- "Unsupported table type %s", MYF(0), options->type);
- return true;
-
- case TAB_DOS:
- case TAB_FIX:
- case TAB_BIN:
- case TAB_CSV:
- case TAB_FMT:
- case TAB_DBF:
- case TAB_XML:
- case TAB_INI:
- case TAB_VEC:
- if (options->filename && *options->filename) {
- char *s, path[FN_REFLEN], dbpath[FN_REFLEN];
-#if defined(WIN32)
- s= "\\";
-#else // !WIN32
- s= "/";
-#endif // !WIN32
- strcpy(dbpath, mysql_real_data_home);
-
- if (db)
- strcat(strcat(dbpath, db), s);
-
- (void) fn_format(path, options->filename, dbpath, "",
- MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
-
- if (!is_secure_file_path(path)) {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
- return true;
- } // endif path
-
- } else
- return false;
-
- /* Fall through to check FILE_ACL */
- case TAB_ODBC:
- case TAB_MYSQL:
- case TAB_DIR:
- case TAB_MAC:
- case TAB_WMI:
- case TAB_OEM:
- return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
-
- // This is temporary until a solution is found
- case TAB_TBL:
- case TAB_XCL:
- case TAB_PRX:
- case TAB_OCCUR:
- case TAB_PIVOT:
- return false;
- } // endswitch type
-
- my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
- return true;
-} // end of check_privileges
-
-// Check that two indexes are equivalent
-bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
-{
- bool b= true;
- PKPDEF kp1, kp2;
-
- if (stricmp(xp1->Name, xp2->Name))
- b= false;
- else if (xp1->Nparts != xp2->Nparts ||
- xp1->MaxSame != xp2->MaxSame ||
- xp1->Unique != xp2->Unique)
- b= false;
- else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts;
- b && (kp1 || kp2);
- kp1= kp1->Next, kp2= kp2->Next)
- if (!kp1 || !kp2)
- b= false;
- else if (stricmp(kp1->Name, kp2->Name))
- b= false;
- else if (kp1->Klen != kp2->Klen)
- b= false;
-
- return b;
-} // end of IsSameIndex
-
-MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
- MODE newmode, bool *chk, bool *cras)
-{
- if (xtrace) {
- LEX_STRING *query_string= thd_query_string(thd);
- htrc("%p check_mode: cmdtype=%d\n", this, thd_sql_command(thd));
- htrc("Cmd=%.*s\n", (int) query_string->length, query_string->str);
- } // endif xtrace
-
- // Next code is temporarily replaced until sql_command is set
- stop= false;
-
- if (newmode == MODE_WRITE) {
- switch (thd_sql_command(thd)) {
- case SQLCOM_LOCK_TABLES:
- locked= 2;
- case SQLCOM_CREATE_TABLE:
- case SQLCOM_INSERT:
- case SQLCOM_LOAD:
- case SQLCOM_INSERT_SELECT:
- newmode= MODE_INSERT;
- break;
-// case SQLCOM_REPLACE:
-// case SQLCOM_REPLACE_SELECT:
-// newmode= MODE_UPDATE; // To be checked
-// break;
- case SQLCOM_DELETE:
- case SQLCOM_DELETE_MULTI:
- case SQLCOM_TRUNCATE:
- newmode= MODE_DELETE;
- break;
- case SQLCOM_UPDATE:
- case SQLCOM_UPDATE_MULTI:
- newmode= MODE_UPDATE;
- break;
- case SQLCOM_SELECT:
- case SQLCOM_OPTIMIZE:
- newmode= MODE_READ;
- break;
- case SQLCOM_DROP_TABLE:
- case SQLCOM_RENAME_TABLE:
- newmode= MODE_ANY;
- break;
- case SQLCOM_DROP_INDEX:
- case SQLCOM_CREATE_INDEX:
- newmode= MODE_ANY;
-// stop= true;
- break;
- case SQLCOM_CREATE_VIEW:
- case SQLCOM_DROP_VIEW:
- newmode= MODE_ANY;
- break;
- case SQLCOM_ALTER_TABLE:
- newmode= MODE_ALTER;
- break;
- default:
- htrc("Unsupported sql_command=%d", thd_sql_command(thd));
- strcpy(g->Message, "CONNECT Unsupported command");
- my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
- newmode= MODE_ERROR;
- break;
- } // endswitch newmode
-
- } else if (newmode == MODE_READ) {
- switch (thd_sql_command(thd)) {
- case SQLCOM_CREATE_TABLE:
- *chk= true;
- *cras= true;
- case SQLCOM_INSERT:
- case SQLCOM_LOAD:
- case SQLCOM_INSERT_SELECT:
-// case SQLCOM_REPLACE:
-// case SQLCOM_REPLACE_SELECT:
- case SQLCOM_DELETE:
- case SQLCOM_DELETE_MULTI:
- case SQLCOM_TRUNCATE:
- case SQLCOM_UPDATE:
- case SQLCOM_UPDATE_MULTI:
- case SQLCOM_SELECT:
- case SQLCOM_OPTIMIZE:
- break;
- case SQLCOM_LOCK_TABLES:
- locked= 1;
- break;
- case SQLCOM_DROP_INDEX:
- case SQLCOM_CREATE_INDEX:
- *chk= true;
-// stop= true;
- case SQLCOM_DROP_TABLE:
- case SQLCOM_RENAME_TABLE:
- newmode= MODE_ANY;
- break;
- case SQLCOM_CREATE_VIEW:
- case SQLCOM_DROP_VIEW:
- newmode= MODE_ANY;
- break;
- case SQLCOM_ALTER_TABLE:
- *chk= true;
- newmode= MODE_ALTER;
- break;
- default:
- htrc("Unsupported sql_command=%d", thd_sql_command(thd));
- strcpy(g->Message, "CONNECT Unsupported command");
- my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
- newmode= MODE_ERROR;
- break;
- } // endswitch newmode
-
- } // endif's newmode
-
- if (xtrace)
- htrc("New mode=%d\n", newmode);
-
- return newmode;
-} // end of check_mode
-
-int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
-{
- int rc= 0;
- bool chk=false, cras= false;
- MODE newmode;
- PGLOBAL g= GetPlug(thd, xp);
- DBUG_ENTER("ha_connect::start_stmt");
-
- // Action will depend on lock_type
- switch (lock_type) {
- case TL_WRITE_ALLOW_WRITE:
- case TL_WRITE_CONCURRENT_INSERT:
- case TL_WRITE_DELAYED:
- case TL_WRITE_DEFAULT:
- case TL_WRITE_LOW_PRIORITY:
- case TL_WRITE:
- case TL_WRITE_ONLY:
- newmode= MODE_WRITE;
- break;
- case TL_READ:
- case TL_READ_WITH_SHARED_LOCKS:
- case TL_READ_HIGH_PRIORITY:
- case TL_READ_NO_INSERT:
- case TL_READ_DEFAULT:
- newmode= MODE_READ;
- break;
- case TL_UNLOCK:
- default:
- newmode= MODE_ANY;
- break;
- } // endswitch mode
-
- xmod= CheckMode(g, thd, newmode, &chk, &cras);
- DBUG_RETURN((xmod == MODE_ERROR) ? HA_ERR_INTERNAL_ERROR : 0);
-} // end of start_stmt
-
-/**
- @brief
- This create a lock on the table. If you are implementing a storage engine
- that can handle transacations look at ha_berkely.cc to see how you will
- want to go about doing this. Otherwise you should consider calling flock()
- here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
- this.
-
- @details
- Called from lock.cc by lock_external() and unlock_external(). Also called
- from sql_table.cc by copy_data_between_tables().
-
- @note
- Following what we did in the MySQL XDB handler, we use this call to actually
- physically open the table. This could be reconsider when finalizing this handler
- design, which means we have a better understanding of what MariaDB does.
-
- @see
- lock.cc by lock_external() and unlock_external() in lock.cc;
- the section "locking functions for mysql" in lock.cc;
- copy_data_between_tables() in sql_table.cc.
-*/
-int ha_connect::external_lock(THD *thd, int lock_type)
-{
- int rc= 0;
- bool xcheck=false, cras= false;
- MODE newmode;
- PTOS options= GetTableOptionStruct(table);
- PGLOBAL g= GetPlug(thd, xp);
- DBUG_ENTER("ha_connect::external_lock");
-
- DBUG_ASSERT(thd == current_thd);
-
- if (xtrace)
- htrc("external_lock: this=%p thd=%p xp=%p g=%p lock_type=%d\n",
- this, thd, xp, g, lock_type);
-
- if (!g)
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
-
- // Action will depend on lock_type
- switch (lock_type) {
- case F_WRLCK:
- newmode= MODE_WRITE;
- break;
- case F_RDLCK:
- newmode= MODE_READ;
- break;
- case F_UNLCK:
- default:
- newmode= MODE_ANY;
- break;
- } // endswitch mode
-
- if (newmode == MODE_ANY) {
- int sqlcom= thd_sql_command(thd);
-
- // This is unlocking, do it by closing the table
- if (xp->CheckQueryID() && sqlcom != SQLCOM_UNLOCK_TABLES
- && sqlcom != SQLCOM_LOCK_TABLES)
- rc= 2; // Logical error ???
-// else if (g->Xchk && (sqlcom == SQLCOM_CREATE_INDEX ||
-// sqlcom == SQLCOM_DROP_INDEX)) {
- else if (g->Xchk) {
- if (!tdbp) {
- if (!(tdbp= GetTDB(g)))
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- else if (!((PTDBASE)tdbp)->GetDef()->Indexable()) {
- sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
-// DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- DBUG_RETURN(0);
- } // endif Indexable
-
- bool oldsep= ((PCHK)g->Xchk)->oldsep;
- bool newsep= ((PCHK)g->Xchk)->newsep;
- PTDBDOS tdp= (PTDBDOS)tdbp;
-
- PDOSDEF ddp= (PDOSDEF)tdp->GetDef();
- PIXDEF xp, xp1, xp2, drp=NULL, adp= NULL;
- PIXDEF oldpix= ((PCHK)g->Xchk)->oldpix;
- PIXDEF newpix= ((PCHK)g->Xchk)->newpix;
- PIXDEF *xlst, *xprc;
-
- ddp->SetIndx(oldpix);
-
- if (oldsep != newsep) {
- // All indexes have to be remade
- ddp->DeleteIndexFile(g, NULL);
- oldpix= NULL;
- ddp->SetIndx(NULL);
- SetBooleanOption("Sepindex", newsep);
- } else if (newsep) {
- // Make the list of dropped indexes
- xlst= &drp; xprc= &oldpix;
-
- for (xp2= oldpix; xp2; xp2= xp) {
- for (xp1= newpix; xp1; xp1= xp1->Next)
- if (IsSameIndex(xp1, xp2))
- break; // Index not to drop
-
- xp= xp2->GetNext();
-
- if (!xp1) {
- *xlst= xp2;
- *xprc= xp;
- *(xlst= &xp2->Next)= NULL;
- } else
- xprc= &xp2->Next;
-
- } // endfor xp2
-
- if (drp) {
- // Here we erase the index files
- ddp->DeleteIndexFile(g, drp);
- } // endif xp1
-
- } else if (oldpix) {
- // TODO: optimize the case of just adding new indexes
- if (!newpix)
- ddp->DeleteIndexFile(g, NULL);
-
- oldpix= NULL; // To remake all indexes
- ddp->SetIndx(NULL);
- } // endif sepindex
-
- // Make the list of new created indexes
- xlst= &adp; xprc= &newpix;
-
- for (xp1= newpix; xp1; xp1= xp) {
- for (xp2= oldpix; xp2; xp2= xp2->Next)
- if (IsSameIndex(xp1, xp2))
- break; // Index already made
-
- xp= xp1->Next;
-
- if (!xp2) {
- *xlst= xp1;
- *xprc= xp;
- *(xlst= &xp1->Next)= NULL;
- } else
- xprc= &xp1->Next;
-
- } // endfor xp1
-
- if (adp)
- // Here we do make the new indexes
- if (tdp->MakeIndex(g, adp, true) == RC_FX) {
- // Make it a warning to avoid crash
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- 0, g->Message);
- rc= 0;
- } // endif MakeIndex
-
- } // endif Tdbp
-
- } // endelse Xchk
-
- if (CloseTable(g)) {
- // This is an error while builing index
- // Make it a warning to avoid crash
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- rc= 0;
- } // endif Close
-
- locked= 0;
- DBUG_RETURN(rc);
- } // endif MODE_ANY
-
- DBUG_ASSERT(table && table->s);
-
- if (check_privileges(thd, options, table->s->db.str)) {
- strcpy(g->Message, "This operation requires the FILE privilege");
- htrc("%s\n", g->Message);
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif check_privileges
-
- // Table mode depends on the query type
- newmode= CheckMode(g, thd, newmode, &xcheck, &cras);
-
- if (newmode == MODE_ERROR)
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
-
- // If this is the start of a new query, cleanup the previous one
- if (xp->CheckCleanup()) {
- tdbp= NULL;
- valid_info= false;
- } // endif CheckCleanup
-
-#if 0
- if (xcheck) {
- // This must occur after CheckCleanup
- if (!g->Xchk) {
- g->Xchk= new(g) XCHK;
- ((PCHK)g->Xchk)->oldsep= GetBooleanOption("Sepindex", false);
- ((PCHK)g->Xchk)->oldpix= GetIndexInfo();
- } // endif Xchk
-
- } else
- g->Xchk= NULL;
-#endif // 0
-
- if (cras)
- g->Createas= 1; // To tell created table to ignore FLAG
-
- if (xtrace) {
-#if 0
- htrc("xcheck=%d cras=%d\n", xcheck, cras);
-
- if (xcheck)
- htrc("oldsep=%d oldpix=%p\n",
- ((PCHK)g->Xchk)->oldsep, ((PCHK)g->Xchk)->oldpix);
-#endif // 0
- htrc("Calling CntCheckDB db=%s cras=%d\n", GetDBName(NULL), cras);
- } // endif xtrace
-
- // Set or reset the good database environment
- if (CntCheckDB(g, this, GetDBName(NULL))) {
- htrc("%p external_lock: %s\n", this, g->Message);
- rc= HA_ERR_INTERNAL_ERROR;
- // This can NOT be called without open called first, but
- // the table can have been closed since then
- } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) {
- if (tdbp) {
- // If this is called by a later query, the table may have
- // been already closed and the tdbp is not valid anymore.
- if (xp->last_query_id == valid_query_id)
- rc= CloseTable(g);
- else
- tdbp= NULL;
-
- } // endif tdbp
-
- xmod= newmode;
-
- // Delay open until used fields are known
- } // endif tdbp
-
- if (xtrace)
- htrc("external_lock: rc=%d\n", rc);
-
- DBUG_RETURN(rc);
-} // end of external_lock
-
-
-/**
- @brief
- The idea with handler::store_lock() is: The statement decides which locks
- should be needed for the table. For updates/deletes/inserts we get WRITE
- locks, for SELECT... we get read locks.
-
- @details
- Before adding the lock into the table lock handler (see thr_lock.c),
- mysqld calls store lock with the requested locks. Store lock can now
- modify a write lock to a read lock (or some other lock), ignore the
- lock (if we don't want to use MySQL table locks at all), or add locks
- for many tables (like we do when we are using a MERGE handler).
-
- Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE
- (which signals that we are doing WRITES, but are still allowing other
- readers and writers).
-
- When releasing locks, store_lock() is also called. In this case one
- usually doesn't have to do anything.
-
- In some exceptional cases MySQL may send a request for a TL_IGNORE;
- This means that we are requesting the same lock as last time and this
- should also be ignored. (This may happen when someone does a flush
- table when we have opened a part of the tables, in which case mysqld
- closes and reopens the tables and tries to get the same locks at last
- time). In the future we will probably try to remove this.
-
- Called from lock.cc by get_lock_data().
-
- @note
- In this method one should NEVER rely on table->in_use, it may, in fact,
- refer to a different thread! (this happens if get_lock_data() is called
- from mysql_lock_abort_for_thread() function)
-
- @see
- get_lock_data() in lock.cc
-*/
-THR_LOCK_DATA **ha_connect::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- lock.type=lock_type;
- *to++ = &lock;
- return to;
-}
-
-
-/**
- Searches for a pointer to the last occurrence of the
- character c in the string src.
- Returns true on failure, false on success.
-*/
-static bool
-strnrchr(LEX_CSTRING *ls, const char *src, size_t length, int c)
-{
- const char *srcend, *s;
- for (s= srcend= src + length; s > src; s--)
- {
- if (s[-1] == c)
- {
- ls->str= s;
- ls->length= srcend - s;
- return false;
- }
- }
- return true;
-}
-
-
-/**
- Split filename into database and table name.
-*/
-static bool
-filename_to_dbname_and_tablename(const char *filename,
- char *database, size_t database_size,
- char *table, size_t table_size)
-{
-#if defined(WIN32)
- char slash= '\\';
-#else // !WIN32
- char slash= '/';
-#endif // !WIN32
- LEX_CSTRING d, t;
- size_t length= strlen(filename);
-
- /* Find filename - the rightmost directory part */
- if (strnrchr(&t, filename, length, slash) || t.length + 1 > table_size)
- return true;
- memcpy(table, t.str, t.length);
- table[t.length]= '\0';
- if (!(length-= t.length))
- return true;
-
- length--; /* Skip slash */
-
- /* Find database name - the second rightmost directory part */
- if (strnrchr(&d, filename, length, slash) || d.length + 1 > database_size)
- return true;
- memcpy(database, d.str, d.length);
- database[d.length]= '\0';
- return false;
-} // end of filename_to_dbname_and_tablename
-
-/**
- @brief
- Used to delete or rename a table. By the time delete_table() has been
- called all opened references to this table will have been closed
- (and your globally shared references released) ===> too bad!!!
- The variable name will just be the name of the table.
- You will need to remove or rename any files you have created at
- this point.
-
- @details
- If you do not implement this, the default delete_table() is called from
- handler.cc and it will delete all files with the file extensions returned
- by bas_ext().
-
- Called from handler.cc by delete_table and ha_create_table(). Only used
- during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
- the storage engine.
-
- @see
- delete_table and ha_create_table() in handler.cc
-*/
-int ha_connect::delete_or_rename_table(const char *name, const char *to)
-{
- DBUG_ENTER("ha_connect::delete_or_rename_table");
- char db[128], tabname[128];
- int rc= 0;
- bool ok= false;
- THD *thd= current_thd;
- int sqlcom= thd_sql_command(thd);
-
- if (xtrace) {
- if (to)
- htrc("rename_table: this=%p thd=%p sqlcom=%d from=%s to=%s\n",
- this, thd, sqlcom, name, to);
- else
- htrc("delete_table: this=%p thd=%p sqlcom=%d name=%s\n",
- this, thd, sqlcom, name);
-
- } // endif xtrace
-
- if (to && (filename_to_dbname_and_tablename(to, db, sizeof(db),
- tabname, sizeof(tabname))
- || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX)))
- DBUG_RETURN(0);
-
- if (filename_to_dbname_and_tablename(name, db, sizeof(db),
- tabname, sizeof(tabname))
- || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX))
- DBUG_RETURN(0);
-
- // If a temporary file exists, all the tests below were passed
- // successfully when making it, so they are not needed anymore
- // in particular because they sometimes cause DBUG_ASSERT crash.
- if (*tabname != '#') {
- // We have to retrieve the information about this table options.
- ha_table_option_struct *pos;
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- TABLE_SHARE *share;
-
- key_length= tdc_create_key(key, db, tabname);
-
- // share contains the option struct that we need
- if (!(share= alloc_table_share(db, tabname, key, key_length)))
- DBUG_RETURN(rc);
-
-#if 0
- if (*tabname == '#') {
- // These are in ???? charset after renaming
- char *p= strchr(share->path.str, '@');
- strcpy(p, share->table_name.str);
- share->path.length= strlen(share->path.str);
- share->normalized_path.length= share->path.length;
- } // endif tabname
-#endif // 0
-
- // Get the share info from the .frm file
- if (!open_table_def(thd, share)) {
- // Now we can work
- if ((pos= share->option_struct)) {
- if (check_privileges(thd, pos, db))
- rc= HA_ERR_INTERNAL_ERROR; // ???
- else
- if (IsFileType(GetRealType(pos)) && !pos->filename)
- ok= true;
-
- } // endif pos
-
- } else // Avoid infamous DBUG_ASSERT
- thd->get_stmt_da()->reset_diagnostics_area();
-
- free_table_share(share);
- } else // Temporary file
- ok= true;
-
- if (ok) {
- // Let the base handler do the job
- if (to)
- rc= handler::rename_table(name, to);
- else if ((rc= handler::delete_table(name)) == ENOENT)
- rc= 0; // No files is not an error for CONNECT
-
- } // endif ok
-
- DBUG_RETURN(rc);
-} // end of delete_or_rename_table
-
-int ha_connect::delete_table(const char *name)
-{
- return delete_or_rename_table(name, NULL);
-} // end of delete_table
-
-int ha_connect::rename_table(const char *from, const char *to)
-{
- return delete_or_rename_table(from, to);
-} // end of rename_table
-
-/**
- @brief
- Given a starting key and an ending key, estimate the number of rows that
- will exist between the two keys.
-
- @details
- end_key may be empty, in which case determine if start_key matches any rows.
-
- Called from opt_range.cc by check_quick_keys().
-
- @see
- check_quick_keys() in opt_range.cc
-*/
-ha_rows ha_connect::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- ha_rows rows;
- DBUG_ENTER("ha_connect::records_in_range");
-
- if (indexing < 0 || inx != active_index)
- index_init(inx, false);
-
- if (xtrace)
- htrc("records_in_range: inx=%d indexing=%d\n", inx, indexing);
-
- if (indexing > 0) {
- int nval;
- uint len[2];
- const uchar *key[2];
- bool incl[2];
- key_part_map kmap[2];
-
- key[0]= (min_key) ? min_key->key : NULL;
- key[1]= (max_key) ? max_key->key : NULL;
- len[0]= (min_key) ? min_key->length : 0;
- len[1]= (max_key) ? max_key->length : 0;
- incl[0]= (min_key) ? (min_key->flag == HA_READ_KEY_EXACT) : false;
- incl[1]= (max_key) ? (max_key->flag == HA_READ_AFTER_KEY) : false;
- kmap[0]= (min_key) ? min_key->keypart_map : 0;
- kmap[1]= (max_key) ? max_key->keypart_map : 0;
-
- if ((nval= CntIndexRange(xp->g, tdbp, key, len, incl, kmap)) < 0)
- rows= HA_POS_ERROR;
- else
- rows= (ha_rows)nval;
-
- } else if (indexing < 0)
- rows= HA_POS_ERROR;
- else
- rows= 100000000; // Don't use missing index
-
- DBUG_RETURN(rows);
-} // end of records_in_range
-
-/**
- Convert an ISO-8859-1 column name to UTF-8
-*/
-static char *encode(PGLOBAL g, char *cnm)
- {
- char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3);
- uint dummy_errors;
- uint32 len= copy_and_convert(buf, strlen(cnm) * 3,
- &my_charset_utf8_general_ci,
- cnm, strlen(cnm),
- &my_charset_latin1,
- &dummy_errors);
- buf[len]= '\0';
- return buf;
- } // end of Encode
-
-/**
- Store field definition for create.
-
- @return
- Return 0 if ok
-*/
-#if defined(NEW_WAY)
-static bool add_fields(PGLOBAL g,
- THD *thd,
- Alter_info *alter_info,
- char *name,
- int typ, int len, int dec,
- uint type_modifier,
- char *rem,
-// CHARSET_INFO *cs,
-// void *vcolinfo,
-// engine_option_value *create_options,
- int flg,
- bool dbf,
- char v)
-{
- register Create_field *new_field;
- char *length, *decimals= NULL;
- enum_field_types type;
-//Virtual_column_info *vcol_info= (Virtual_column_info *)vcolinfo;
- engine_option_value *crop;
- LEX_STRING *comment;
- LEX_STRING *field_name;
-
- DBUG_ENTER("ha_connect::add_fields");
-
- if (len) {
- if (!v && typ == TYPE_STRING && len > 255)
- v= 'V'; // Change CHAR to VARCHAR
-
- length= (char*)PlugSubAlloc(g, NULL, 8);
- sprintf(length, "%d", len);
-
- if (typ == TYPE_DOUBLE) {
- decimals= (char*)PlugSubAlloc(g, NULL, 8);
- sprintf(decimals, "%d", min(dec, (min(len, 31) - 1)));
- } // endif dec
-
- } else
- length= NULL;
-
- if (!rem)
- rem= "";
-
- type= PLGtoMYSQL(typ, dbf, v);
- comment= thd->make_lex_string(rem, strlen(rem));
- field_name= thd->make_lex_string(name, strlen(name));
-
- switch (v) {
- case 'Z': type_modifier|= ZEROFILL_FLAG;
- case 'U': type_modifier|= UNSIGNED_FLAG; break;
- } // endswitch v
-
- if (flg) {
- engine_option_value *start= NULL, *end= NULL;
- LEX_STRING *flag= thd->make_lex_string("flag", 4);
-
- crop= new(thd->mem_root) engine_option_value(*flag, (ulonglong)flg,
- &start, &end, thd->mem_root);
- } else
- crop= NULL;
-
- if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
- system_charset_info, 1)) {
- my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- } // endif field_name
-
- if (!(new_field= new Create_field()) ||
- new_field->init(thd, field_name->str, type, length, decimals,
- type_modifier, NULL, NULL, comment, NULL,
- NULL, NULL, 0, NULL, crop, true))
- DBUG_RETURN(1);
-
- alter_info->create_list.push_back(new_field);
- DBUG_RETURN(0);
-} // end of add_fields
-#else // !NEW_WAY
-static bool add_field(String *sql, const char *field_name, int typ,
- int len, int dec, uint tm, const char *rem,
- char *dft, char *xtra, int flag, bool dbf, char v)
-{
- char var = (len > 255) ? 'V' : v;
- bool error= false;
- const char *type= PLGtoMYSQLtype(typ, dbf, var);
-
- error|= sql->append('`');
- error|= sql->append(field_name);
- error|= sql->append("` ");
- error|= sql->append(type);
-
- if (len) {
- error|= sql->append('(');
- error|= sql->append_ulonglong(len);
-
- if (!strcmp(type, "DOUBLE")) {
- error|= sql->append(',');
- // dec must be < len and < 31
- error|= sql->append_ulonglong(min(dec, (min(len, 31) - 1)));
- } else if (dec > 0 && !strcmp(type, "DECIMAL")) {
- error|= sql->append(',');
- // dec must be < len
- error|= sql->append_ulonglong(min(dec, len - 1));
- } // endif dec
-
- error|= sql->append(')');
- } // endif len
-
- if (v == 'U')
- error|= sql->append(" UNSIGNED");
- else if (v == 'Z')
- error|= sql->append(" ZEROFILL");
-
- if (tm)
- error|= sql->append(STRING_WITH_LEN(" NOT NULL"), system_charset_info);
-
- if (dft && *dft) {
- error|= sql->append(" DEFAULT ");
-
- if (!IsTypeNum(typ)) {
- error|= sql->append("'");
- error|= sql->append_for_single_quote(dft, strlen(dft));
- error|= sql->append("'");
- } else
- error|= sql->append(dft);
-
- } // endif dft
-
- if (xtra && *xtra) {
- error|= sql->append(" ");
- error|= sql->append(xtra);
- } // endif rem
-
- if (rem && *rem) {
- error|= sql->append(" COMMENT '");
- error|= sql->append_for_single_quote(rem, strlen(rem));
- error|= sql->append("'");
- } // endif rem
-
- if (flag) {
- error|= sql->append(" FLAG=");
- error|= sql->append_ulonglong(flag);
- } // endif flag
-
- error|= sql->append(',');
- return error;
-} // end of add_field
-#endif // !NEW_WAY
-
-/**
- Initialise the table share with the new columns.
-
- @return
- Return 0 if ok
-*/
-#if defined(NEW_WAY)
-//static bool sql_unusable_for_discovery(THD *thd, const char *sql);
-
-static int init_table_share(THD *thd,
- TABLE_SHARE *table_s,
- HA_CREATE_INFO *create_info,
- Alter_info *alter_info)
-{
- KEY *not_used_1;
- uint not_used_2;
- int rc= 0;
- handler *file;
- LEX_CUSTRING frm= {0,0};
-
- DBUG_ENTER("init_table_share");
-
-#if 0
- ulonglong saved_mode= thd->variables.sql_mode;
- CHARSET_INFO *old_cs= thd->variables.character_set_client;
- Parser_state parser_state;
- char *sql_copy;
- LEX *old_lex;
- Query_arena *arena, backup;
- LEX tmp_lex;
-
- /*
- Ouch. Parser may *change* the string it's working on.
- Currently (2013-02-26) it is used to permanently disable
- conditional comments.
- Anyway, let's copy the caller's string...
- */
- if (!(sql_copy= thd->strmake(sql, sql_length)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-
- if (parser_state.init(thd, sql_copy, sql_length))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-
- thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE;
- thd->variables.character_set_client= system_charset_info;
- old_lex= thd->lex;
- thd->lex= &tmp_lex;
-
- arena= thd->stmt_arena;
-
- if (arena->is_conventional())
- arena= 0;
- else
- thd->set_n_backup_active_arena(arena, &backup);
-
- lex_start(thd);
-
- if ((error= parse_sql(thd, & parser_state, NULL)))
- goto ret;
-
- if (table_s->sql_unusable_for_discovery(thd, NULL)) {
- my_error(ER_SQL_DISCOVER_ERROR, MYF(0), plugin_name(db_plugin)->str,
- db.str, table_name.str, sql_copy);
- goto ret;
- } // endif unusable
-
- thd->lex->create_info.db_type= plugin_data(db_plugin, handlerton *);
-
- if (tabledef_version.str)
- thd->lex->create_info.tabledef_version= tabledef_version;
-#endif // 0
-
- tmp_disable_binlog(thd);
-
- file= mysql_create_frm_image(thd, table_s->db.str, table_s->table_name.str,
- create_info, alter_info, C_ORDINARY_CREATE,
- &not_used_1, &not_used_2, &frm);
- if (file)
- delete file;
- else
- rc= OPEN_FRM_CORRUPTED;
-
- if (!rc && frm.str) {
- table_s->option_list= 0; // cleanup existing options ...
- table_s->option_struct= 0; // ... if it's an assisted discovery
- rc= table_s->init_from_binary_frm_image(thd, true, frm.str, frm.length);
- } // endif frm
-
-//ret:
- my_free(const_cast<uchar*>(frm.str));
- reenable_binlog(thd);
-#if 0
- lex_end(thd->lex);
- thd->lex= old_lex;
- if (arena)
- thd->restore_active_arena(arena, &backup);
- thd->variables.sql_mode= saved_mode;
- thd->variables.character_set_client= old_cs;
-#endif // 0
-
- if (thd->is_error() || rc) {
- thd->clear_error();
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_s->db.str,
- table_s->table_name.str);
- DBUG_RETURN(HA_ERR_NOT_A_TABLE);
- } else
- DBUG_RETURN(0);
-
-} // end of init_table_share
-#else // !NEW_WAY
-static int init_table_share(THD* thd,
- TABLE_SHARE *table_s,
- HA_CREATE_INFO *create_info,
-// char *dsn,
- String *sql)
-{
- bool oom= false;
- PTOS topt= table_s->option_struct;
-
- sql->length(sql->length()-1); // remove the trailing comma
- sql->append(')');
-
- for (ha_create_table_option *opt= connect_table_option_list;
- opt->name; opt++) {
- ulonglong vull;
- const char *vstr;
-
- switch (opt->type) {
- case HA_OPTION_TYPE_ULL:
- vull= *(ulonglong*)(((char*)topt) + opt->offset);
-
- if (vull != opt->def_value) {
- oom|= sql->append(' ');
- oom|= sql->append(opt->name);
- oom|= sql->append('=');
- oom|= sql->append_ulonglong(vull);
- } // endif vull
-
- break;
- case HA_OPTION_TYPE_STRING:
- vstr= *(char**)(((char*)topt) + opt->offset);
-
- if (vstr) {
- oom|= sql->append(' ');
- oom|= sql->append(opt->name);
- oom|= sql->append("='");
- oom|= sql->append_for_single_quote(vstr, strlen(vstr));
- oom|= sql->append('\'');
- } // endif vstr
-
- break;
- case HA_OPTION_TYPE_BOOL:
- vull= *(bool*)(((char*)topt) + opt->offset);
-
- if (vull != opt->def_value) {
- oom|= sql->append(' ');
- oom|= sql->append(opt->name);
- oom|= sql->append('=');
- oom|= sql->append(vull ? "ON" : "OFF");
- } // endif vull
-
- break;
- default: // no enums here, good :)
- break;
- } // endswitch type
-
- if (oom)
- return HA_ERR_OUT_OF_MEM;
-
- } // endfor opt
-
- if (create_info->connect_string.length) {
-//if (dsn) {
- oom|= sql->append(' ');
- oom|= sql->append("CONNECTION='");
- oom|= sql->append_for_single_quote(create_info->connect_string.str,
- create_info->connect_string.length);
-// oom|= sql->append_for_single_quote(dsn, strlen(dsn));
- oom|= sql->append('\'');
-
- if (oom)
- return HA_ERR_OUT_OF_MEM;
-
- } // endif string
-
- if (create_info->default_table_charset) {
- oom|= sql->append(' ');
- oom|= sql->append("CHARSET=");
- oom|= sql->append(create_info->default_table_charset->csname);
-
- if (oom)
- return HA_ERR_OUT_OF_MEM;
-
- } // endif charset
-
- if (xtrace)
- htrc("s_init: %.*s\n", sql->length(), sql->ptr());
-
- return table_s->init_from_sql_statement_string(thd, true,
- sql->ptr(), sql->length());
-} // end of init_table_share
-#endif // !NEW_WAY
-
-// Add an option to the create_info option list
-static void add_option(THD* thd, HA_CREATE_INFO *create_info,
- const char *opname, const char *opval)
-{
-#if defined(NEW_WAY)
- LEX_STRING *opn= thd->make_lex_string(opname, strlen(opname));
- LEX_STRING *val= thd->make_lex_string(opval, strlen(opval));
- engine_option_value *pov, **start= &create_info->option_list, *end= NULL;
-
- for (pov= *start; pov; pov= pov->next)
- end= pov;
-
- pov= new(thd->mem_root) engine_option_value(*opn, *val, false, start, &end);
-#endif // NEW_WAY
-} // end of add_option
-
-// Used to check whether a MYSQL table is created on itself
-static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
- const char *db, char *tab, const char *src, int port)
-{
- if (src)
- return false;
- else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
- return false;
- else if (db && stricmp(db, s->db.str))
- return false;
- else if (tab && stricmp(tab, s->table_name.str))
- return false;
- else if (port && port != (signed)GetDefaultPort())
- return false;
-
- strcpy(g->Message, "This MySQL table is defined on itself");
- return true;
-} // end of CheckSelf
-
-/**
- @brief
- connect_assisted_discovery() is called when creating a table with no columns.
-
- @details
- When assisted discovery is used the .frm file have not already been
- created. You can overwrite some definitions at this point but the
- main purpose of it is to define the columns for some table types.
-
- @note
- this function is no more called in case of CREATE .. SELECT
-*/
-static int connect_assisted_discovery(handlerton *hton, THD* thd,
- TABLE_SHARE *table_s,
- HA_CREATE_INFO *create_info)
-{
- 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;
-#if defined(WIN32)
- char *nsp= NULL, *cls= NULL;
-#endif // WIN32
- int port= 0, hdr= 0, mxr __attribute__((unused))= 0, mxe= 0, rc= 0;
- int cop __attribute__((unused)) = 0;
- uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
- bool bif, ok= false, dbf= false;
- TABTYPE ttp= TAB_UNDEF;
- PQRYRES qrp= NULL;
- PCOLRES crp;
- PCONNECT xp= NULL;
- PGLOBAL g= GetPlug(thd, xp);
- PTOS topt= table_s->option_struct;
-#if defined(NEW_WAY)
-//CHARSET_INFO *cs;
- Alter_info alter_info;
-#else // !NEW_WAY
- char buf[1024];
- String sql(buf, sizeof(buf), system_charset_info);
-
- sql.copy(STRING_WITH_LEN("CREATE TABLE whatever ("), system_charset_info);
-#endif // !NEW_WAY
-
- if (!g)
- return HA_ERR_INTERNAL_ERROR;
-
- user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
-
- // Get the useful create options
- ttp= GetTypeID(topt->type);
- fn= topt->filename;
- tab= (char*)topt->tabname;
- src= topt->srcdef;
- db= topt->dbname;
- fncn= topt->catfunc;
- fnc= GetFuncID(fncn);
- sep= topt->separator;
- spc= (!sep || !strcmp(sep, "\\t")) ? '\t' : *sep;
- qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0;
- hdr= (int)topt->header;
- tbl= topt->tablist;
- col= topt->colist;
-
- if (topt->oplist) {
- host= GetListOption(g, "host", topt->oplist, "localhost");
- user= GetListOption(g, "user", topt->oplist, "root");
- // Default value db can come from the DBNAME=xxx option.
- db= GetListOption(g, "database", topt->oplist, db);
- col= GetListOption(g, "colist", topt->oplist, col);
- ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
- pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
- fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
- rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
- pwd= GetListOption(g, "password", topt->oplist);
-#if defined(WIN32)
- nsp= GetListOption(g, "namespace", topt->oplist);
- cls= GetListOption(g, "class", topt->oplist);
-#endif // WIN32
- port= atoi(GetListOption(g, "port", topt->oplist, "0"));
- mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
- mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
-#if defined(PROMPT_OK)
- cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
-#endif // PROMPT_OK
- } else {
- host= "localhost";
- user= "root";
- } // endif option_list
-
- if (!(shm= (char*)db))
- db= table_s->db.str; // Default value
-
- // Check table type
- if (ttp == TAB_UNDEF) {
- topt->type= (src) ? "MYSQL" : (tab) ? "PROXY" : "DOS";
- ttp= GetTypeID(topt->type);
- sprintf(g->Message, "No table_type. Was set to %s", topt->type);
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- add_option(thd, create_info, "table_type", topt->type);
- } else if (ttp == TAB_NIY) {
- sprintf(g->Message, "Unsupported table type %s", topt->type);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } // endif ttp
-
- if (!tab) {
- if (ttp == TAB_TBL) {
- // Make tab the first table of the list
- char *p;
-
- if (!tbl) {
- strcpy(g->Message, "Missing table list");
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } // endif tbl
-
- tab= (char*)PlugSubAlloc(g, NULL, strlen(tbl) + 1);
- strcpy(tab, tbl);
-
- if ((p= strchr(tab, ',')))
- *p= 0;
-
- if ((p=strchr(tab, '.'))) {
- *p= 0;
- db= tab;
- tab= p + 1;
- } // endif p
-
- } else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
- tab= table_s->table_name.str; // Default value
-
-#if defined(NEW_WAY)
-// add_option(thd, create_info, "tabname", tab);
-#endif // NEW_WAY
- } // endif tab
-
- switch (ttp) {
-#if defined(ODBC_SUPPORT)
- case TAB_ODBC:
- dsn= create_info->connect_string.str;
-
- if (fnc & (FNC_DSN | FNC_DRIVER)) {
- ok= true;
-#if defined(PROMPT_OK)
- } else if (!stricmp(thd->main_security_ctx.host, "localhost")
- && cop == 1) {
- if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
- thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
- ok= true;
- } // endif dsn
-#endif // PROMPT_OK
-
- } else if (!dsn)
- sprintf(g->Message, "Missing %s connection string", topt->type);
- else
- ok= true;
-
- supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
- break;
-#endif // ODBC_SUPPORT
- case TAB_DBF:
- dbf= true;
- // Passthru
- case TAB_CSV:
- if (!fn && fnc != FNC_NO)
- sprintf(g->Message, "Missing %s file name", topt->type);
- else
- ok= true;
-
- break;
-#if defined(MYSQL_SUPPORT)
- case TAB_MYSQL:
- ok= true;
-
- if (create_info->connect_string.str) {
- int len= create_info->connect_string.length;
- PMYDEF mydef= new(g) MYSQLDEF();
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL;
-
- dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
- strncpy(dsn, create_info->connect_string.str, len);
- dsn[len]= 0;
- mydef->SetName(create_info->alias);
- mydef->SetCat(cat);
-
- if (!mydef->ParseURL(g, dsn, false)) {
- if (mydef->GetHostname())
- host= mydef->GetHostname();
-
- if (mydef->GetUsername())
- user= mydef->GetUsername();
-
- if (mydef->GetPassword())
- pwd= mydef->GetPassword();
-
- if (mydef->GetDatabase())
- db= mydef->GetDatabase();
-
- if (mydef->GetTabname())
- tab= mydef->GetTabname();
-
- if (mydef->GetPortnumber())
- port= mydef->GetPortnumber();
-
- } else
- ok= false;
-
- } else if (!user)
- user= "root";
-
- if (CheckSelf(g, table_s, host, db, tab, src, port))
- ok= false;
-
- break;
-#endif // MYSQL_SUPPORT
-#if defined(WIN32)
- case TAB_WMI:
- ok= true;
- break;
-#endif // WIN32
- case TAB_PIVOT:
- supfnc= FNC_NO;
- case TAB_PRX:
- case TAB_TBL:
- case TAB_XCL:
- case TAB_OCCUR:
- if (!src && !stricmp(tab, create_info->alias) &&
- (!db || !stricmp(db, table_s->db.str)))
- sprintf(g->Message, "A %s table cannot refer to itself", topt->type);
- else
- ok= true;
-
- break;
- case TAB_OEM:
- if (topt->module && topt->subtype)
- ok= true;
- else
- strcpy(g->Message, "Missing OEM module or subtype");
-
- break;
- default:
- sprintf(g->Message, "Cannot get column info for table type %s", topt->type);
- break;
- } // endif ttp
-
- // Check for supported catalog function
- if (ok && !(supfnc & fnc)) {
- sprintf(g->Message, "Unsupported catalog function %s for table type %s",
- fncn, topt->type);
- ok= false;
- } // endif supfnc
-
- if (src && fnc != FNC_NO) {
- strcpy(g->Message, "Cannot make catalog table from srcdef");
- ok= false;
- } // endif src
-
- if (ok) {
- char *cnm, *rem, *dft, *xtra;
- int i, len, prec, dec, typ, flg;
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL;
-
- if (cat)
- cat->SetDataPath(g, table_s->db.str);
- else
- return HA_ERR_INTERNAL_ERROR; // Should never happen
-
- if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
- qrp= SrcColumns(g, host, db, user, pwd, src, port);
-
- if (qrp && ttp == TAB_OCCUR)
- if (OcrSrcCols(g, qrp, col, ocl, rnk)) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } // endif OcrSrcCols
-
- } else switch (ttp) {
- case TAB_DBF:
- qrp= DBFColumns(g, fn, fnc == FNC_COL);
- break;
-#if defined(ODBC_SUPPORT)
- case TAB_ODBC:
- switch (fnc) {
- case FNC_NO:
- case FNC_COL:
- if (src) {
- qrp= ODBCSrcCols(g, dsn, (char*)src);
- src= NULL; // for next tests
- } else
- qrp= ODBCColumns(g, dsn, shm, tab, NULL, mxr, fnc == FNC_COL);
-
- break;
- case FNC_TABLE:
- qrp= ODBCTables(g, dsn, shm, tab, mxr, true);
- break;
- case FNC_DSN:
- qrp= ODBCDataSources(g, mxr, true);
- break;
- case FNC_DRIVER:
- qrp= ODBCDrivers(g, mxr, true);
- break;
- default:
- sprintf(g->Message, "invalid catfunc %s", fncn);
- break;
- } // endswitch info
-
- break;
-#endif // ODBC_SUPPORT
-#if defined(MYSQL_SUPPORT)
- case TAB_MYSQL:
- qrp= MyColumns(g, host, db, user, pwd, tab,
- NULL, port, fnc == FNC_COL);
- break;
-#endif // MYSQL_SUPPORT
- case TAB_CSV:
- qrp= CSVColumns(g, fn, spc, qch, hdr, mxe, fnc == FNC_COL);
- break;
-#if defined(WIN32)
- case TAB_WMI:
- qrp= WMIColumns(g, nsp, cls, fnc == FNC_COL);
- break;
-#endif // WIN32
- case TAB_PRX:
- case TAB_TBL:
- case TAB_XCL:
- case TAB_OCCUR:
- bif= fnc == FNC_COL;
- qrp= TabColumns(g, thd, db, tab, bif);
-
- if (!qrp && bif && fnc != FNC_COL) // tab is a view
- qrp= MyColumns(g, host, db, user, pwd, tab, NULL, port, false);
-
- if (qrp && ttp == TAB_OCCUR && fnc != FNC_COL)
- if (OcrColumns(g, qrp, col, ocl, rnk)) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } // endif OcrColumns
-
- break;
- case TAB_PIVOT:
- qrp= PivotColumns(g, tab, src, pic, fcl, host, db, user, pwd, port);
- break;
- case TAB_OEM:
- qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
- break;
- default:
- strcpy(g->Message, "System error during assisted discovery");
- break;
- } // endswitch ttp
-
- if (!qrp) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } // endif qrp
-
- if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
- // Catalog like table
- for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
- cnm= encode(g, crp->Name);
- typ= crp->Type;
- len= crp->Length;
- dec= crp->Prec;
- flg= crp->Flag;
-
- if (!len && typ == TYPE_STRING)
- len= 256; // STRBLK's have 0 length
-
-#if defined(NEW_WAY)
- // Now add the field
- rc= add_fields(g, thd, &alter_info, cnm, typ, len, dec,
- NOT_NULL_FLAG, "", flg, dbf, 0);
-#else // !NEW_WAY
- // Now add the field
- if (add_field(&sql, cnm, typ, len, dec, NOT_NULL_FLAG,
- NULL, NULL, NULL, flg, dbf, 0))
- rc= HA_ERR_OUT_OF_MEM;
-#endif // !NEW_WAY
- } // endfor crp
-
- } else // Not a catalog table
- for (i= 0; !rc && i < qrp->Nblin; i++) {
- typ= len= prec= dec= 0;
- tm= NOT_NULL_FLAG;
- cnm= (char*)"noname";
- dft= xtra= NULL;
-#if defined(NEW_WAY)
- rem= "";
-// cs= NULL;
-#else // !NEW_WAY
- rem= NULL;
-#endif // !NEW_WAY
-
- for (crp= qrp->Colresp; crp; crp= crp->Next)
- switch (crp->Fld) {
- case FLD_NAME:
- cnm= encode(g, crp->Kdata->GetCharValue(i));
- break;
- case FLD_TYPE:
- typ= crp->Kdata->GetIntValue(i);
- v = (crp->Nulls) ? crp->Nulls[i] : 0;
- break;
- case FLD_PREC:
- // PREC must be always before LENGTH
- len= prec= crp->Kdata->GetIntValue(i);
- break;
- case FLD_LENGTH:
- len= crp->Kdata->GetIntValue(i);
- break;
- case FLD_SCALE:
- dec= crp->Kdata->GetIntValue(i);
- break;
- case FLD_NULL:
- if (crp->Kdata->GetIntValue(i))
- tm= 0; // Nullable
-
- break;
- case FLD_REM:
- rem= crp->Kdata->GetCharValue(i);
- break;
-// case FLD_CHARSET:
- // No good because remote table is already translated
-// if (*(csn= crp->Kdata->GetCharValue(i)))
-// cs= get_charset_by_name(csn, 0);
-
-// break;
- case FLD_DEFAULT:
- dft= crp->Kdata->GetCharValue(i);
- break;
- case FLD_EXTRA:
- xtra= crp->Kdata->GetCharValue(i);
- break;
- default:
- break; // Ignore
- } // endswitch Fld
-
-#if defined(ODBC_SUPPORT)
- if (ttp == TAB_ODBC) {
- int plgtyp;
-
- // typ must be PLG type, not SQL type
- if (!(plgtyp= TranslateSQLType(typ, dec, prec, v))) {
- sprintf(g->Message, "Unsupported SQL type %d", typ);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
- } else
- typ= plgtyp;
-
- switch (typ) {
- case TYPE_DOUBLE:
- // Some data sources do not count dec in length (prec)
- prec += (dec + 2); // To be safe
- case TYPE_DECIM:
- break;
- default:
- dec= 0;
- } // endswitch typ
-
- } // endif ttp
-#endif // ODBC_SUPPORT
-
- // Make the arguments as required by add_fields
- if (typ == TYPE_DATE)
- prec= 0;
- else if (typ == TYPE_DOUBLE)
- prec= len;
-
- // Now add the field
-#if defined(NEW_WAY)
- rc= add_fields(g, thd, &alter_info, cnm, typ, prec, dec,
- tm, rem, 0, dbf, v);
-#else // !NEW_WAY
- if (add_field(&sql, cnm, typ, prec, dec, tm, rem, dft, xtra,
- 0, dbf, v))
- rc= HA_ERR_OUT_OF_MEM;
-#endif // !NEW_WAY
- } // endfor i
-
-#if defined(NEW_WAY)
- rc= init_table_share(thd, table_s, create_info, &alter_info);
-#else // !NEW_WAY
- if (!rc)
- rc= init_table_share(thd, table_s, create_info, &sql);
-// rc= init_table_share(thd, table_s, create_info, dsn, &sql);
-#endif // !NEW_WAY
-
- return rc;
- } // endif ok
-
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- return HA_ERR_INTERNAL_ERROR;
-} // end of connect_assisted_discovery
-
-/**
- Get the database name from a qualified table name.
-*/
-char *ha_connect::GetDBfromName(const char *name)
-{
- char *db, dbname[128], tbname[128];
-
- if (filename_to_dbname_and_tablename(name, dbname, sizeof(dbname),
- tbname, sizeof(tbname)))
- *dbname= 0;
-
- if (*dbname) {
- assert(xp && xp->g);
- db= (char*)PlugSubAlloc(xp->g, NULL, strlen(dbname + 1));
- strcpy(db, dbname);
- } else
- db= NULL;
-
- return db;
-} // end of GetDBfromName
-
-
-/**
- @brief
- create() is called to create a database. The variable name will have the name
- of the table.
-
- @details
- When create() is called you do not need to worry about
- opening the table. Also, the .frm file will have already been
- created so adjusting create_info is not necessary. You can overwrite
- the .frm file at this point if you wish to change the table
- definition, but there are no methods currently provided for doing
- so.
-
- Called from handle.cc by ha_create_table().
-
- @note
- Currently we do some checking on the create definitions and stop
- creating if an error is found. We wish we could change the table
- definition such as providing a default table type. However, as said
- above, there are no method to do so.
-
- @see
- ha_create_table() in handle.cc
-*/
-
-int ha_connect::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- int rc= RC_OK;
- bool dbf;
- Field* *field;
- Field *fp;
- TABTYPE type;
- TABLE *st= table; // Probably unuseful
- THD *thd= ha_thd();
- xp= GetUser(thd, xp);
- PGLOBAL g= xp->g;
-
- DBUG_ENTER("ha_connect::create");
- int sqlcom= thd_sql_command(table_arg->in_use);
- PTOS options= GetTableOptionStruct(table_arg);
-
- table= table_arg; // Used by called functions
-
- if (xtrace)
- htrc("create: this=%p thd=%p xp=%p g=%p sqlcom=%d name=%s\n",
- this, thd, xp, g, sqlcom, GetTableName());
-
- // CONNECT engine specific table options:
- DBUG_ASSERT(options);
- type= GetTypeID(options->type);
-
- // Check table type
- if (type == TAB_UNDEF) {
- options->type= (options->srcdef) ? "MYSQL" :
- (options->tabname) ? "PROXY" : "DOS";
- type= GetTypeID(options->type);
- sprintf(g->Message, "No table_type. Will be set to %s", options->type);
-
- if (sqlcom == SQLCOM_CREATE_TABLE)
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
-
- } else if (type == TAB_NIY) {
- sprintf(g->Message, "Unsupported table type %s", options->type);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif ttp
-
- if (check_privileges(thd, options, GetDBfromName(name)))
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
-
- if (options->data_charset) {
- const CHARSET_INFO *data_charset;
-
- if (!(data_charset= get_charset_by_csname(options->data_charset,
- MY_CS_PRIMARY, MYF(0)))) {
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), options->data_charset);
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif charset
-
- if (type == TAB_XML && data_charset != &my_charset_utf8_general_ci) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "DATA_CHARSET='%s' is not supported for TABLE_TYPE=XML",
- MYF(0), options->data_charset);
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif utf8
-
- } // endif charset
-
- if (!g) {
- rc= HA_ERR_INTERNAL_ERROR;
- DBUG_RETURN(rc);
- } else
- dbf= (GetTypeID(options->type) == TAB_DBF && !options->catfunc);
-
- // Can be null in ALTER TABLE
- if (create_info->alias)
- // Check whether a table is defined on itself
- switch (type) {
- case TAB_PRX:
- case TAB_XCL:
- case TAB_PIVOT:
- case TAB_OCCUR:
- if (options->srcdef) {
- strcpy(g->Message, "Cannot check looping reference");
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- } else if (options->tabname) {
- if (!stricmp(options->tabname, create_info->alias) &&
- (!options->dbname || !stricmp(options->dbname, table_arg->s->db.str))) {
- sprintf(g->Message, "A %s table cannot refer to itself",
- options->type);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif tab
-
- } else {
- strcpy(g->Message, "Missing object table name or definition");
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif tabname
-
- case TAB_MYSQL:
- {const char *src= options->srcdef;
- char *host, *db, *tab= (char*)options->tabname;
- int port;
-
- host= GetListOption(g, "host", options->oplist, NULL);
- db= GetListOption(g, "database", options->oplist, NULL);
- port= atoi(GetListOption(g, "port", options->oplist, "0"));
-
- if (create_info->connect_string.str) {
- char *dsn;
- int len= create_info->connect_string.length;
- PMYDEF mydef= new(g) MYSQLDEF();
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL;
-
- dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
- strncpy(dsn, create_info->connect_string.str, len);
- dsn[len]= 0;
- mydef->SetName(create_info->alias);
- mydef->SetCat(cat);
-
- if (!mydef->ParseURL(g, dsn, false)) {
- if (mydef->GetHostname())
- host= mydef->GetHostname();
-
- if (mydef->GetDatabase())
- db= mydef->GetDatabase();
-
- if (mydef->GetTabname())
- tab= mydef->GetTabname();
-
- if (mydef->GetPortnumber())
- port= mydef->GetPortnumber();
-
- } else {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif ParseURL
-
- } // endif connect_string
-
- if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif CheckSelf
-
- }break;
- default: /* do nothing */;
- break;
- } // endswitch ttp
-
- if (type == TAB_XML) {
- bool dom; // True: MS-DOM, False libxml2
- char *xsup= GetListOption(g, "Xmlsup", options->oplist, "*");
-
- // Note that if no support is specified, the default is MS-DOM
- // on Windows and libxml2 otherwise
- switch (*xsup) {
- case '*':
-#if defined(WIN32)
- dom= true;
-#else // !WIN32
- dom= false;
-#endif // !WIN32
- break;
- case 'M':
- case 'D':
- dom= true;
- break;
- default:
- dom= false;
- break;
- } // endswitch xsup
-
-#if !defined(DOMDOC_SUPPORT)
- if (dom) {
- strcpy(g->Message, "MS-DOM not supported by this version");
- xsup= NULL;
- } // endif DomDoc
-#endif // !DOMDOC_SUPPORT
-
-#if !defined(LIBXML2_SUPPORT)
- if (!dom) {
- strcpy(g->Message, "libxml2 not supported by this version");
- xsup= NULL;
- } // endif Libxml2
-#endif // !LIBXML2_SUPPORT
-
- if (!xsup) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- DBUG_RETURN(rc);
- } // endif xsup
-
- } // endif type
-
- // Check column types
- for (field= table_arg->field; *field; field++) {
- fp= *field;
-
- if (fp->vcol_info && !fp->stored_in_db)
- continue; // This is a virtual column
-
- if (fp->flags & AUTO_INCREMENT_FLAG) {
- strcpy(g->Message, "Auto_increment is not supported yet");
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- DBUG_RETURN(rc);
- } // endif flags
-
- if (fp->flags & (BLOB_FLAG | ENUM_FLAG | SET_FLAG)) {
- sprintf(g->Message, "Unsupported type for column %s",
- fp->field_name);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- DBUG_RETURN(rc);
- } // endif flags
-
- switch (fp->type()) {
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_TINY:
- break; // Ok
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- case MYSQL_TYPE_INT24:
- break; // To be checked
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_NULL:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_GEOMETRY:
- default:
-// fprintf(stderr, "Unsupported type column %s\n", fp->field_name);
- sprintf(g->Message, "Unsupported type for column %s",
- fp->field_name);
- rc= HA_ERR_INTERNAL_ERROR;
- my_printf_error(ER_UNKNOWN_ERROR,
- "Unsupported type for column '%s'",
- MYF(0), fp->field_name);
- DBUG_RETURN(rc);
- break;
- } // endswitch type
-
- if ((fp)->real_maybe_null() && !IsTypeNullable(type)) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "Table type %s does not support nullable columns",
- MYF(0), options->type);
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } // endif !nullable
-
- if (dbf) {
- bool b= false;
-
- if ((b= strlen(fp->field_name) > 10))
- sprintf(g->Message, "DBF: Column name '%s' is too long (max=10)",
- fp->field_name);
- else if ((b= fp->field_length > 255))
- sprintf(g->Message, "DBF: Column length too big for '%s' (max=255)",
- fp->field_name);
-
- if (b) {
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- DBUG_RETURN(rc);
- } // endif b
-
- } // endif dbf
-
- } // endfor field
-
- if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#')
- && IsFileType(type) && !options->filename) {
- // The file name is not specified, create a default file in
- // the database directory named table_name.table_type.
- // (temporarily not done for XML because a void file causes
- // the XML parsers to report an error on the first Insert)
- char buf[256], fn[_MAX_PATH], dbpath[128], lwt[12];
- int h;
-
- strcpy(buf, GetTableName());
-
- // Check for incompatible options
- if (options->sepindex) {
- my_message(ER_UNKNOWN_ERROR,
- "SEPINDEX is incompatible with unspecified file name",
- MYF(0));
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } else if (GetTypeID(options->type) == TAB_VEC)
- if (!table->s->max_rows || options->split) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "%s tables whose file name is unspecified cannot be split",
- MYF(0), options->type);
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } else if (options->header == 2) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "header=2 is not allowed for %s tables whose file name is unspecified",
- MYF(0), options->type);
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } // endif's
-
- // Fold type to lower case
- for (int i= 0; i < 12; i++)
- if (!options->type[i]) {
- lwt[i]= 0;
- break;
- } else
- lwt[i]= tolower(options->type[i]);
-
- strcat(strcat(buf, "."), lwt);
- sprintf(g->Message, "No file name. Table will use %s", buf);
-
- if (sqlcom == SQLCOM_CREATE_TABLE)
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
-
- strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
- PlugSetPath(fn, buf, dbpath);
-
- if ((h= ::open(fn, O_CREAT | O_EXCL, 0666)) == -1) {
- if (errno == EEXIST)
- sprintf(g->Message, "Default file %s already exists", fn);
- else
- sprintf(g->Message, "Error %d creating file %s", errno, fn);
-
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
- } else
- ::close(h);
-
- if (type == TAB_FMT || options->readonly)
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
- "Congratulation, you just created a read-only void table!");
-
- } // endif
-
- if (xtrace)
- htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
-
- // To check whether indices have to be made or remade
- if (!g->Xchk) {
- PIXDEF xdp;
-
- // We should be in CREATE TABLE or ALTER_TABLE
- if (sqlcom != SQLCOM_CREATE_TABLE && sqlcom != SQLCOM_ALTER_TABLE)
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
- "Wrong command in create, please contact CONNECT team");
-
- if (sqlcom == SQLCOM_ALTER_TABLE && g->Alchecked == 0 &&
- (!IsFileType(type) || FileExists(options->filename))) {
- // This is an ALTER to CONNECT from another engine.
- // It cannot be accepted because the table data would be lost
- // except when the target file does not exist.
- strcpy(g->Message, "Operation denied. Table data would be lost.");
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- } // endif outward
-
- // Get the index definitions
- if (xdp= GetIndexInfo()) {
- if (IsTypeIndexable(type)) {
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL;
-
- if (cat) {
- cat->SetDataPath(g, table_arg->s->db.str);
-
- if ((rc= optimize(table->in_use, NULL))) {
- htrc("Create rc=%d %s\n", rc, g->Message);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- } else
- CloseTable(g);
-
- } // endif cat
-
- } else {
- sprintf(g->Message, "Table type %s is not indexable", options->type);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_UNSUPPORTED;
- } // endif Indexable
-
- } // endif xdp
-
- } else {
- // This should not happen anymore with indexing new way
- my_message(ER_UNKNOWN_ERROR,
- "CONNECT index modification should be in-place", MYF(0));
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
-#if 0
- PIXDEF xdp= GetIndexInfo();
- PCHK xcp= (PCHK)g->Xchk;
-
- if (xdp) {
- if (!IsTypeIndexable(type)) {
- g->Xchk= NULL;
- sprintf(g->Message, "Table type %s is not indexable", options->type);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- rc= HA_ERR_INTERNAL_ERROR;
- } else {
- xcp->newpix= xdp;
- xcp->newsep= GetBooleanOption("Sepindex", false);
- } // endif Indexable
-
- } else if (!xcp->oldpix)
- g->Xchk= NULL;
-
- if (xtrace && g->Xchk)
- htrc("oldsep=%d newsep=%d oldpix=%p newpix=%p\n",
- xcp->oldsep, xcp->newsep, xcp->oldpix, xcp->newpix);
-
-// if (g->Xchk &&
-// (sqlcom != SQLCOM_CREATE_INDEX && sqlcom != SQLCOM_DROP_INDEX)) {
- if (g->Xchk) {
- PIXDEF xp1, xp2;
- bool b= false; // true if index changes
-
- if (xcp->oldsep == xcp->newsep) {
- for (xp1= xcp->newpix, xp2= xcp->oldpix;
- xp1 || xp2;
- xp1= xp1->Next, xp2= xp2->Next)
- if (!xp1 || !xp2 || !IsSameIndex(xp1, xp2)) {
- b= true;
- break;
- } // endif xp1
-
- } else
- b= true;
-
- if (!b)
- g->Xchk= NULL;
-
-#if 0
- if (b) {
- // CONNECT does not support indexing via ALTER TABLE
- my_message(ER_UNKNOWN_ERROR,
- "CONNECT does not support index modification via ALTER TABLE",
- MYF(0));
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } // endif b
-#endif // 0
-
- } // endif Xchk
-
-#endif // 0
- } // endif Xchk
-
- table= st;
- DBUG_RETURN(rc);
-} // end of create
-
-/**
- Used to check whether a file based outward table can be populated by
- an ALTER TABLE command. The conditions are:
- - file does not exist or is void
- - user has file privilege
-*/
-bool ha_connect::FileExists(const char *fn)
-{
- if (!fn || !*fn)
- return false;
-
- if (table) {
- char *s, filename[_MAX_PATH], path[128];
- int n;
- struct stat info;
-
- if (check_access(ha_thd(), FILE_ACL, table->s->db.str,
- NULL, NULL, 0, 0))
- return true;
-
-#if defined(WIN32)
- s= "\\";
-#else // !WIN32
- s= "/";
-#endif // !WIN32
-
- strcat(strcat(strcat(strcpy(path, "."), s), table->s->db.str), s);
- PlugSetPath(filename, fn, path);
- n= stat(filename, &info);
-
- if (n < 0) {
- if (errno != ENOENT) {
- char buf[_MAX_PATH + 20];
-
- sprintf(buf, "Error %d for file %s", errno, filename);
- push_warning(table->in_use, Sql_condition::WARN_LEVEL_WARN, 0, buf);
- return true;
- } else
- return false;
-
- } else
- return (info.st_size) ? true : false;
-
- } // endif table
-
- return true;
-} // end of FileExists
-
-// Called by SameString and NoFieldOptionChange
-bool ha_connect::CheckString(const char *str1, const char *str2)
-{
- bool b1= (!str1 || !*str1), b2= (!str2 || !*str2);
-
- if (b1 && b2)
- return true;
- else if ((b1 && !b2) || (!b1 && b2) || stricmp(str1, str2))
- return false;
-
- return true;
-} // end of CheckString
-
-/**
- check whether a string option have changed
- */
-bool ha_connect::SameString(TABLE *tab, char *opn)
-{
- char *str1, *str2;
-
- tshp= tab->s; // The altered table
- str1= GetStringOption(opn);
- tshp= NULL;
- str2= GetStringOption(opn);
- return CheckString(str1, str2);
-} // end of SameString
-
-/**
- check whether a Boolean option have changed
- */
-bool ha_connect::SameBool(TABLE *tab, char *opn)
-{
- bool b1, b2;
-
- tshp= tab->s; // The altered table
- b1= GetBooleanOption(opn, false);
- tshp= NULL;
- b2= GetBooleanOption(opn, false);
- return (b1 == b2);
-} // end of SameBool
-
-/**
- check whether an integer option have changed
- */
-bool ha_connect::SameInt(TABLE *tab, char *opn)
-{
- int i1, i2;
-
- tshp= tab->s; // The altered table
- i1= GetIntegerOption(opn);
- tshp= NULL;
- i2= GetIntegerOption(opn);
-
- if (!stricmp(opn, "lrecl"))
- return (i1 == i2 || !i1 || !i2);
- else if (!stricmp(opn, "ending"))
- return (i1 == i2 || i1 <= 0 || i2 <= 0);
- else
- return (i1 == i2);
-
-} // end of SameInt
-
-/**
- check whether a field option have changed
- */
-bool ha_connect::NoFieldOptionChange(TABLE *tab)
-{
- bool rc= true;
- ha_field_option_struct *fop1, *fop2;
- Field* *fld1= table->s->field;
- Field* *fld2= tab->s->field;
-
- for (; rc && *fld1 && *fld2; fld1++, fld2++) {
- fop1= (*fld1)->option_struct;
- fop2= (*fld2)->option_struct;
-
- rc= (fop1->offset == fop2->offset &&
- fop1->fldlen == fop2->fldlen &&
- CheckString(fop1->dateformat, fop2->dateformat) &&
- CheckString(fop1->fieldformat, fop2->fieldformat) &&
- CheckString(fop1->special, fop2->special));
- } // endfor fld
-
- return rc;
-} // end of NoFieldOptionChange
-
- /**
- Check if a storage engine supports a particular alter table in-place
-
- @param altered_table TABLE object for new version of table.
- @param ha_alter_info Structure describing changes to be done
- by ALTER TABLE and holding data used
- during in-place alter.
-
- @retval HA_ALTER_ERROR Unexpected error.
- @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy.
- @retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
- @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
- Supported, but requires SNW lock
- during main phase. Prepare phase
- requires X lock.
- @retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock.
- @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
- Supported, concurrent reads/writes
- allowed. However, prepare phase
- requires X lock.
- @retval HA_ALTER_INPLACE_NO_LOCK Supported, concurrent
- reads/writes allowed.
-
- @note The default implementation uses the old in-place ALTER API
- to determine if the storage engine supports in-place ALTER or not.
-
- @note Called without holding thr_lock.c lock.
- */
-enum_alter_inplace_result
-ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info)
-{
- DBUG_ENTER("check_if_supported_alter");
-
- bool idx= false, outward= false;
- THD *thd= ha_thd();
- int sqlcom= thd_sql_command(thd);
- TABTYPE newtyp, type= TAB_UNDEF;
- HA_CREATE_INFO *create_info= ha_alter_info->create_info;
-//PTOS pos= GetTableOptionStruct(table);
- PTOS newopt, oldopt;
- xp= GetUser(thd, xp);
- PGLOBAL g= xp->g;
-
- if (!g || !table) {
- my_message(ER_UNKNOWN_ERROR, "Cannot check ALTER operations", MYF(0));
- DBUG_RETURN(HA_ALTER_ERROR);
- } // endif Xchk
-
- newopt= altered_table->s->option_struct;
- oldopt= table->s->option_struct;
-
- // If this is the start of a new query, cleanup the previous one
- if (xp->CheckCleanup()) {
- tdbp= NULL;
- valid_info= false;
- } // endif CheckCleanup
-
- g->Alchecked= 1; // Tested in create
- g->Xchk= NULL;
- type= GetRealType(oldopt);
- newtyp= GetRealType(newopt);
-
- // No copy algorithm for outward tables
- outward= (!IsFileType(type) || (oldopt->filename && *oldopt->filename));
-
- // Index operations
- Alter_inplace_info::HA_ALTER_FLAGS index_operations=
- Alter_inplace_info::ADD_INDEX |
- Alter_inplace_info::DROP_INDEX |
- Alter_inplace_info::ADD_UNIQUE_INDEX |
- Alter_inplace_info::DROP_UNIQUE_INDEX |
- Alter_inplace_info::ADD_PK_INDEX |
- Alter_inplace_info::DROP_PK_INDEX;
-
- Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
- Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
- Alter_inplace_info::ALTER_COLUMN_NAME |
- Alter_inplace_info::ALTER_COLUMN_DEFAULT |
- Alter_inplace_info::CHANGE_CREATE_OPTION |
- Alter_inplace_info::ALTER_RENAME | index_operations;
-
- if (ha_alter_info->handler_flags & index_operations ||
- !SameString(altered_table, "optname") ||
- !SameBool(altered_table, "sepindex")) {
- if (!IsTypeIndexable(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 operation
-
- if (!SameString(altered_table, "filename")) {
- if (!outward) {
- // Conversion to outward table is only allowed for file based
- // tables whose file does not exist.
- tshp= altered_table->s;
- char *fn= GetStringOption("filename");
- tshp= NULL;
-
- if (FileExists(fn)) {
- strcpy(g->Message, "Operation denied. Table data would be lost.");
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- DBUG_RETURN(HA_ALTER_ERROR);
- } else
- goto fin;
-
- } else
- goto fin;
-
- } // endif filename
-
- /* Is there at least one operation that requires copy algorithm? */
- if (ha_alter_info->handler_flags & ~inplace_offline_operations)
- goto fin;
-
- /*
- ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
- ALTER TABLE table_name DEFAULT CHARSET = .. most likely
- change column charsets and so not supported in-place through
- old API.
-
- Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
- not supported as in-place operations in old API either.
- */
- if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
- HA_CREATE_USED_DEFAULT_CHARSET |
- HA_CREATE_USED_PACK_KEYS |
- HA_CREATE_USED_MAX_ROWS) ||
- (table->s->row_type != create_info->row_type))
- goto fin;
-
-#if 0
- uint table_changes= (ha_alter_info->handler_flags &
- Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
- IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
-
- if (table->file->check_if_incompatible_data(create_info, table_changes)
- == COMPATIBLE_DATA_YES)
- DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
-#endif // 0
-
- // This was in check_if_incompatible_data
- if (NoFieldOptionChange(altered_table) &&
- type == newtyp &&
- SameInt(altered_table, "lrecl") &&
- SameInt(altered_table, "elements") &&
- SameInt(altered_table, "header") &&
- SameInt(altered_table, "quoted") &&
- SameInt(altered_table, "ending") &&
- SameInt(altered_table, "compressed"))
- DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
-
-fin:
- if (idx) {
- // Indexing is only supported inplace
- my_message(ER_ALTER_OPERATION_NOT_SUPPORTED,
- "Alter operations not supported together by CONNECT", MYF(0));
- DBUG_RETURN(HA_ALTER_ERROR);
- } else if (outward) {
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
- "This is an outward table, table data were not modified.");
- DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
- } else
- DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
-
-} // end of check_if_supported_inplace_alter
-
-
-/**
- check_if_incompatible_data() called if ALTER TABLE can't detect otherwise
- if new and old definition are compatible
-
- @details If there are no other explicit signs like changed number of
- fields this function will be called by compare_tables()
- (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm
- file.
-
- @note: This function is no more called by check_if_supported_inplace_alter
-*/
-
-bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *info,
- uint table_changes)
-{
- DBUG_ENTER("ha_connect::check_if_incompatible_data");
- // TO DO: really implement and check it.
- push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, 0,
- "Unexpected call to check_if_incompatible_data.");
- DBUG_RETURN(COMPATIBLE_DATA_NO);
-} // end of check_if_incompatible_data
-
-
-#if defined(MRRBKA_SUPPORT)
-//#error This is not implemented yet
-/****************************************************************************
- * CONNECT MRR implementation: use DS-MRR
- This is just copied from myisam
- ***************************************************************************/
-
-int ha_connect::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
- uint n_ranges, uint mode,
- HANDLER_BUFFER *buf)
-{
- return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
-} // end of multi_range_read_init
-
-int ha_connect::multi_range_read_next(range_id_t *range_info)
-{
- return ds_mrr.dsmrr_next(range_info);
-} // end of multi_range_read_next
-
-ha_rows ha_connect::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
- void *seq_init_param,
- uint n_ranges, uint *bufsz,
- uint *flags, Cost_estimate *cost)
-{
- /*
- This call is here because there is no location where this->table would
- already be known.
- TODO: consider moving it into some per-query initialization call.
- */
- ds_mrr.init(this, table);
-
- // MMR is implemented for "local" file based tables only
- if (!IsFileType(GetRealType(GetTableOptionStruct(table))))
- *flags|= HA_MRR_USE_DEFAULT_IMPL;
-
- ha_rows rows= ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
- bufsz, flags, cost);
- xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
- return rows;
-} // end of multi_range_read_info_const
-
-ha_rows ha_connect::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
- uint key_parts, uint *bufsz,
- uint *flags, Cost_estimate *cost)
-{
- ds_mrr.init(this, table);
-
- // MMR is implemented for "local" file based tables only
- if (!IsFileType(GetRealType(GetTableOptionStruct(table))))
- *flags|= HA_MRR_USE_DEFAULT_IMPL;
-
- ha_rows rows= ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz,
- flags, cost);
- xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
- return rows;
-} // end of multi_range_read_info
-
-
-int ha_connect::multi_range_read_explain_info(uint mrr_mode, char *str,
- size_t size)
-{
- return ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
-} // end of multi_range_read_explain_info
-
-/* CONNECT MRR implementation ends */
-
-#if 0
-// Does this make sens for CONNECT?
-Item *ha_connect::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
-{
- pushed_idx_cond_keyno= keyno_arg;
- pushed_idx_cond= idx_cond_arg;
- in_range_check_pushed_down= TRUE;
- if (active_index == pushed_idx_cond_keyno)
- mi_set_index_cond_func(file, handler_index_cond_check, this);
- return NULL;
-}
-#endif // 0
-#endif // MRRBKA_SUPPORT
-
-struct st_mysql_storage_engine connect_storage_engine=
-{ MYSQL_HANDLERTON_INTERFACE_VERSION };
-
-maria_declare_plugin(connect)
-{
- MYSQL_STORAGE_ENGINE_PLUGIN,
- &connect_storage_engine,
- "CONNECT",
- "Olivier Bertrand",
- "Management of External Data (SQL/MED), including many file formats",
- PLUGIN_LICENSE_GPL,
- connect_init_func, /* Plugin Init */
- connect_done_func, /* Plugin Deinit */
- 0x0102, /* version number (1.02) */
- NULL, /* status variables */
- NULL, /* system variables */
- "1.02", /* string version */
- MariaDB_PLUGIN_MATURITY_BETA /* maturity */
-}
-maria_declare_plugin_end;
+/* Copyright (C) Olivier Bertrand 2004 - 2014
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file ha_connect.cc
+
+ @brief
+ The ha_connect engine is a stubbed storage engine that enables to create tables
+ based on external data. Principally they are based on plain files of many
+ different types, but also on collections of such files, collection of tables,
+ ODBC tables retrieving data from other DBMS having an ODBC server, and even
+ virtual tables.
+
+ @details
+ ha_connect will let you create/open/delete tables, the created table can be
+ done specifying an already existing file, the drop table command will just
+ suppress the table definition but not the eventual data file.
+ Indexes are not supported for all table types but data can be inserted,
+ updated or deleted.
+
+ You can enable the CONNECT storage engine in your build by doing the
+ following during your build process:<br> ./configure
+ --with-connect-storage-engine
+
+ You can install the CONNECT handler as all other storage handlers.
+
+ Once this is done, MySQL will let you create tables with:<br>
+ CREATE TABLE <table name> (...) ENGINE=CONNECT;
+
+ The example storage engine does not use table locks. It
+ implements an example "SHARE" that is inserted into a hash by table
+ name. This is not used yet.
+
+ Please read the object definition in ha_connect.h before reading the rest
+ of this file.
+
+ @note
+ This MariaDB CONNECT handler is currently an adaptation of the XDB handler
+ that was written for MySQL version 4.1.2-alpha. Its overall design should
+ be enhanced in the future to meet MariaDB requirements.
+
+ @note
+ It was written also from the Brian's ha_example handler and contains parts
+ of it that are there but not currently used, such as table variables.
+
+ @note
+ When you create an CONNECT table, the MySQL Server creates a table .frm
+ (format) file in the database directory, using the table name as the file
+ name as is customary with MySQL. No other files are created. To get an idea
+ of what occurs, here is an example select that would do a scan of an entire
+ table:
+
+ @code
+ ha-connect::open
+ ha_connect::store_lock
+ ha_connect::external_lock
+ ha_connect::info
+ ha_connect::rnd_init
+ ha_connect::extra
+ ENUM HA_EXTRA_CACHE Cache record in HA_rrnd()
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::rnd_next
+ ha_connect::extra
+ ENUM HA_EXTRA_NO_CACHE End caching of records (def)
+ ha_connect::external_lock
+ ha_connect::extra
+ ENUM HA_EXTRA_RESET Reset database to after open
+ @endcode
+
+ Here you see that the connect storage engine has 9 rows called before
+ rnd_next signals that it has reached the end of its data. Calls to
+ ha_connect::extra() are hints as to what will be occuring to the request.
+
+ Happy use!<br>
+ -Olivier
+*/
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#define MYSQL_SERVER 1
+#define DONT_DEFINE_VOID
+//#include "sql_partition.h"
+#include "sql_class.h"
+#include "create_options.h"
+#include "mysql_com.h"
+#include "field.h"
+#include "sql_parse.h"
+#include "sql_base.h"
+#include <sys/stat.h>
+#if defined(NEW_WAY)
+#include "sql_table.h"
+#endif // NEW_WAY
+#undef OFFSET
+
+#define NOPARSE
+#if defined(UNIX)
+#include "osutil.h"
+#endif // UNIX
+#include "global.h"
+#include "plgdbsem.h"
+#if defined(ODBC_SUPPORT)
+#include "odbccat.h"
+#endif // ODBC_SUPPORT
+#if defined(MYSQL_SUPPORT)
+#include "xtable.h"
+#include "tabmysql.h"
+#endif // MYSQL_SUPPORT
+#include "filamdbf.h"
+#include "tabxcl.h"
+#include "tabfmt.h"
+#include "reldef.h"
+#include "tabcol.h"
+#include "xindex.h"
+#if defined(WIN32)
+#include <io.h>
+#include "tabwmi.h"
+#endif // WIN32
+#include "connect.h"
+#include "user_connect.h"
+#include "ha_connect.h"
+#include "mycat.h"
+#include "myutil.h"
+#include "preparse.h"
+#include "inihandl.h"
+
+#define PLGXINI "plgcnx.ini" /* Configuration settings file */
+#define my_strupr(p) my_caseup_str(default_charset_info, (p));
+#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
+#define my_stricmp(a,b) my_strcasecmp(default_charset_info, (a), (b))
+
+#ifdef LIBXML2_SUPPORT
+#include "libdoc.h"
+#endif // LIBXML2_SUPPORT
+
+#include "taboccur.h"
+#include "tabpivot.h"
+
+
+/***********************************************************************/
+/* DB static variables. */
+/***********************************************************************/
+extern "C" char plgxini[];
+extern "C" char plgini[];
+extern "C" char nmfile[];
+extern "C" char pdebug[];
+
+/***********************************************************************/
+/* Initialize the ha_connect static members. */
+/***********************************************************************/
+#define CONNECT_INI "connect.ini"
+extern "C" {
+ char connectini[_MAX_PATH]= CONNECT_INI;
+ char version[]= "Version 1.02.0001 February 03, 2014";
+
+#if defined(XMSG)
+ char msglang[]; // Default message language
+#endif
+ int trace= 0; // The general trace value
+} // extern "C"
+
+int xtrace= 0;
+ulong ha_connect::num= 0;
+//int DTVAL::Shift= 0;
+
+/***********************************************************************/
+/* Utility functions. */
+/***********************************************************************/
+PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
+
+static PCONNECT GetUser(THD *thd, PCONNECT xp);
+static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp);
+
+static handler *connect_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root);
+
+static int connect_assisted_discovery(handlerton *hton, THD* thd,
+ TABLE_SHARE *table_s,
+ HA_CREATE_INFO *info);
+
+handlerton *connect_hton;
+
+/**
+ CREATE TABLE option list (table options)
+
+ These can be specified in the CREATE TABLE:
+ CREATE TABLE ( ... ) {...here...}
+*/
+ha_create_table_option connect_table_option_list[]=
+{
+ HA_TOPTION_STRING("TABLE_TYPE", type),
+ HA_TOPTION_STRING("FILE_NAME", filename),
+ HA_TOPTION_STRING("XFILE_NAME", optname),
+//HA_TOPTION_STRING("CONNECT_STRING", connect),
+ HA_TOPTION_STRING("TABNAME", tabname),
+ HA_TOPTION_STRING("TABLE_LIST", tablist),
+ HA_TOPTION_STRING("DBNAME", dbname),
+ HA_TOPTION_STRING("SEP_CHAR", separator),
+ HA_TOPTION_STRING("QCHAR", qchar),
+ HA_TOPTION_STRING("MODULE", module),
+ HA_TOPTION_STRING("SUBTYPE", subtype),
+ HA_TOPTION_STRING("CATFUNC", catfunc),
+ HA_TOPTION_STRING("SRCDEF", srcdef),
+ HA_TOPTION_STRING("COLIST", colist),
+ HA_TOPTION_STRING("OPTION_LIST", oplist),
+ HA_TOPTION_STRING("DATA_CHARSET", data_charset),
+ HA_TOPTION_NUMBER("LRECL", lrecl, 0, 0, INT_MAX32, 1),
+ HA_TOPTION_NUMBER("BLOCK_SIZE", elements, 0, 0, INT_MAX32, 1),
+//HA_TOPTION_NUMBER("ESTIMATE", estimate, 0, 0, INT_MAX32, 1),
+ HA_TOPTION_NUMBER("MULTIPLE", multiple, 0, 0, 2, 1),
+ HA_TOPTION_NUMBER("HEADER", header, 0, 0, 3, 1),
+ HA_TOPTION_NUMBER("QUOTED", quoted, (ulonglong) -1, 0, 3, 1),
+ HA_TOPTION_NUMBER("ENDING", ending, (ulonglong) -1, 0, INT_MAX32, 1),
+ HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1),
+//HA_TOPTION_BOOL("COMPRESS", compressed, 0),
+ HA_TOPTION_BOOL("MAPPED", mapped, 0),
+ HA_TOPTION_BOOL("HUGE", huge, 0),
+ HA_TOPTION_BOOL("SPLIT", split, 0),
+ HA_TOPTION_BOOL("READONLY", readonly, 0),
+ HA_TOPTION_BOOL("SEPINDEX", sepindex, 0),
+ HA_TOPTION_END
+};
+
+
+/**
+ CREATE TABLE option list (field options)
+
+ These can be specified in the CREATE TABLE per field:
+ CREATE TABLE ( field ... {...here...}, ... )
+*/
+ha_create_table_option connect_field_option_list[]=
+{
+ HA_FOPTION_NUMBER("FLAG", offset, (ulonglong) -1, 0, INT_MAX32, 1),
+ HA_FOPTION_NUMBER("MAX_DIST", freq, 0, 0, INT_MAX32, 1), // BLK_INDX
+ HA_FOPTION_NUMBER("DISTRIB", opt, 0, 0, 2, 1), // used for BLK_INDX
+ HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1),
+ HA_FOPTION_STRING("DATE_FORMAT", dateformat),
+ HA_FOPTION_STRING("FIELD_FORMAT", fieldformat),
+ HA_FOPTION_STRING("SPECIAL", special),
+ HA_FOPTION_END
+};
+
+/***********************************************************************/
+/* Push G->Message as a MySQL warning. */
+/***********************************************************************/
+bool PushWarning(PGLOBAL g, PTDBASE tdbp, int level)
+ {
+ PHC phc;
+ THD *thd;
+ MYCAT *cat= (MYCAT*)tdbp->GetDef()->GetCat();
+ Sql_condition::enum_warning_level wlvl;
+
+
+ if (!cat || !(phc= cat->GetHandler()) || !phc->GetTable() ||
+ !(thd= (phc->GetTable())->in_use))
+ return true;
+
+//push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ wlvl= (Sql_condition::enum_warning_level)level;
+ push_warning(thd, wlvl, 0, g->Message);
+ return false;
+ } // end of PushWarning
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key con_key_mutex_CONNECT_SHARE_mutex;
+
+static PSI_mutex_info all_connect_mutexes[]=
+{
+ { &con_key_mutex_CONNECT_SHARE_mutex, "CONNECT_SHARE::mutex", 0}
+};
+
+static void init_connect_psi_keys()
+{
+ const char* category= "connect";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_connect_mutexes);
+ PSI_server->register_mutex(category, all_connect_mutexes, count);
+}
+#else
+static void init_connect_psi_keys() {}
+#endif
+
+
+DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir)
+{
+ const char *res= PlugSetPath(to, mysql_data_home, name, dir);
+ return res;
+}
+
+
+/**
+ @brief
+ If frm_error() is called then we will use this to determine
+ the file extensions that exist for the storage engine. This is also
+ used by the default rename_table and delete_table method in
+ handler.cc.
+
+ For engines that have two file name extentions (separate meta/index file
+ and data file), the order of elements is relevant. First element of engine
+ file name extentions array should be meta/index file extention. Second
+ element - data file extention. This order is assumed by
+ prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
+
+ @see
+ rename_table method in handler.cc and
+ delete_table method in handler.cc
+*/
+static const char *ha_connect_exts[]= {
+ ".dos", ".fix", ".csv", ".bin", ".fmt", ".dbf", ".xml", ".ini", ".vec",
+ ".dnx", ".fnx", ".bnx", ".vnx", ".dbx", ".dop", ".fop", ".bop", ".vop",
+ NULL};
+
+/**
+ @brief
+ Plugin initialization
+*/
+static int connect_init_func(void *p)
+{
+ DBUG_ENTER("connect_init_func");
+ char dir[_MAX_PATH - sizeof(CONNECT_INI) - 1];
+
+#ifdef LIBXML2_SUPPORT
+ XmlInitParserLib();
+#endif // LIBXML2_SUPPORT
+
+ /* Build connect.ini file name */
+ my_getwd(dir, sizeof(dir) - 1, MYF(0));
+ snprintf(connectini, sizeof(connectini), "%s%s", dir, CONNECT_INI);
+ sql_print_information("CONNECT: %s=%s", CONNECT_INI, connectini);
+
+ if ((xtrace= GetPrivateProfileInt("CONNECT", "Trace", 0, connectini)))
+ {
+ sql_print_information("CONNECT: xtrace=%d", xtrace);
+ sql_print_information("CONNECT: plgini=%s", plgini);
+ sql_print_information("CONNECT: plgxini=%s", plgxini);
+ sql_print_information("CONNECT: nmfile=%s", nmfile);
+ sql_print_information("CONNECT: pdebug=%s", pdebug);
+ sql_print_information("CONNECT: version=%s", version);
+ trace= xtrace;
+ } // endif xtrace
+
+#if !defined(WIN32)
+ PROFILE_Close(connectini);
+#endif // !WIN32
+
+ init_connect_psi_keys();
+
+ connect_hton= (handlerton *)p;
+ connect_hton->state= SHOW_OPTION_YES;
+ connect_hton->create= connect_create_handler;
+ connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_NO_PARTITION;
+ connect_hton->table_options= connect_table_option_list;
+ connect_hton->field_options= connect_field_option_list;
+ connect_hton->tablefile_extensions= ha_connect_exts;
+ connect_hton->discover_table_structure= connect_assisted_discovery;
+
+ if (xtrace)
+ sql_print_information("connect_init: hton=%p", p);
+
+ DTVAL::SetTimeShift(); // Initialize time zone shift once for all
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Plugin clean up
+*/
+static int connect_done_func(void *p)
+{
+ int error= 0;
+ PCONNECT pc, pn;
+ DBUG_ENTER("connect_done_func");
+
+#ifdef LIBXML2_SUPPORT
+ XmlCleanupParserLib();
+#endif // LIBXML2_SUPPORT
+
+#if !defined(WIN32)
+ PROFILE_End();
+#endif // !WIN32
+
+ for (pc= user_connect::to_users; pc; pc= pn) {
+ if (pc->g)
+ PlugCleanup(pc->g, true);
+
+ pn= pc->next;
+ delete pc;
+ } // endfor pc
+
+ DBUG_RETURN(error);
+}
+
+
+/**
+ @brief
+ Example of simple lock controls. The "share" it creates is a
+ structure we will pass to each example handler. Do you have to have
+ one of these? Well, you have pieces that are used for locking, and
+ they are needed to function.
+*/
+
+CONNECT_SHARE *ha_connect::get_share()
+{
+ CONNECT_SHARE *tmp_share;
+ lock_shared_ha_data();
+ if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr())))
+ {
+ tmp_share= new CONNECT_SHARE;
+ if (!tmp_share)
+ goto err;
+ mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
+ &tmp_share->mutex, MY_MUTEX_INIT_FAST);
+ set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
+ }
+err:
+ unlock_shared_ha_data();
+ return tmp_share;
+}
+
+
+static handler* connect_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root)
+{
+ handler *h= new (mem_root) ha_connect(hton, table);
+
+ if (xtrace)
+ htrc("New CONNECT %p, table: %s\n",
+ h, table ? table->table_name.str : "<null>");
+
+ return h;
+} // end of connect_create_handler
+
+/****************************************************************************/
+/* ha_connect constructor. */
+/****************************************************************************/
+ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
+ :handler(hton, table_arg)
+{
+ hnum= ++num;
+ xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
+ if (xp)
+ xp->SetHandler(this);
+ tdbp= NULL;
+ sdvalin= NULL;
+ sdvalout= NULL;
+ xmod= MODE_ANY;
+ istable= false;
+//*tname= '\0';
+ bzero((char*) &xinfo, sizeof(XINFO));
+ valid_info= false;
+ valid_query_id= 0;
+ creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0;
+ stop= false;
+ alter= false;
+ mrr= false;
+ indexing= -1;
+ locked= 0;
+ data_file_name= NULL;
+ index_file_name= NULL;
+ enable_activate_all_index= 0;
+ int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS);
+ ref_length= sizeof(int);
+ share= NULL;
+ tshp= NULL;
+} // end of ha_connect constructor
+
+
+/****************************************************************************/
+/* ha_connect destructor. */
+/****************************************************************************/
+ha_connect::~ha_connect(void)
+{
+ if (xtrace)
+ htrc("Delete CONNECT %p, table: %s, xp=%p count=%d\n", this,
+ table ? table->s->table_name.str : "<null>",
+ xp, xp ? xp->count : 0);
+
+ if (xp) {
+ PCONNECT p;
+
+ xp->count--;
+
+ for (p= user_connect::to_users; p; p= p->next)
+ if (p == xp)
+ break;
+
+ if (p && !p->count) {
+ if (p->next)
+ p->next->previous= p->previous;
+
+ if (p->previous)
+ p->previous->next= p->next;
+ else
+ user_connect::to_users= p->next;
+
+ } // endif p
+
+ if (!xp->count) {
+ PlugCleanup(xp->g, true);
+ delete xp;
+ } // endif count
+
+ } // endif xp
+
+} // end of ha_connect destructor
+
+
+/****************************************************************************/
+/* Get a pointer to the user of this handler. */
+/****************************************************************************/
+static PCONNECT GetUser(THD *thd, PCONNECT xp)
+{
+ const char *dbn= NULL;
+
+ if (!thd)
+ return NULL;
+
+ if (xp && thd == xp->thdp)
+ return xp;
+
+ for (xp= user_connect::to_users; xp; xp= xp->next)
+ if (thd == xp->thdp)
+ break;
+
+ if (!xp) {
+ xp= new user_connect(thd, dbn);
+
+ if (xp->user_init()) {
+ delete xp;
+ xp= NULL;
+ } // endif user_init
+
+ } else
+ xp->count++;
+
+ return xp;
+} // end of GetUser
+
+
+/****************************************************************************/
+/* Get the global pointer of the user of this handler. */
+/****************************************************************************/
+static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp)
+{
+ lxp= GetUser(thd, lxp);
+ return (lxp) ? lxp->g : NULL;
+} // end of GetPlug
+
+/****************************************************************************/
+/* Get the implied table type. */
+/****************************************************************************/
+TABTYPE ha_connect::GetRealType(PTOS pos)
+{
+ TABTYPE type= GetTypeID(pos->type);
+
+ if (type == TAB_UNDEF)
+ type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS;
+
+ return type;
+} // end of GetRealType
+
+/** @brief
+ This is a list of flags that indicate what functionality the storage
+ engine implements. The current table flags are documented in handler.h
+*/
+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_NULL_IN_KEY | not implemented yet
+// HA_FAST_KEY_READ | causes error when sorting (???)
+ HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
+ HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
+ ha_connect *hp= (ha_connect*)this;
+ PTOS pos= hp->GetTableOptionStruct(table);
+
+ if (pos) {
+ TABTYPE type= hp->GetRealType(pos);
+
+ if (IsFileType(type))
+ flags|= HA_FILE_BASED;
+
+ if (IsExactType(type))
+ flags|= (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
+
+ // No data change on ALTER for outward tables
+ if (!IsFileType(type) || hp->FileExists(pos->filename))
+ flags|= HA_NO_COPY_ON_ALTER;
+
+ } // endif pos
+
+ return flags;
+} // end of table_flags
+
+/****************************************************************************/
+/* Return the value of an option specified in the option list. */
+/****************************************************************************/
+char *GetListOption(PGLOBAL g, const char *opname,
+ const char *oplist, const char *def)
+{
+ char key[16], val[256];
+ char *pk, *pv, *pn;
+ char *opval= (char*) def;
+ int n;
+
+ for (pk= (char*)oplist; pk; pk= ++pn) {
+ pn= strchr(pk, ',');
+ pv= strchr(pk, '=');
+
+ if (pv && (!pn || pv < pn)) {
+ n= pv - pk;
+ memcpy(key, pk, n);
+ key[n]= 0;
+ pv++;
+
+ if (pn) {
+ n= pn - pv;
+ memcpy(val, pv, n);
+ val[n]= 0;
+ } else
+ strcpy(val, pv);
+
+ } else {
+ if (pn) {
+ n= min(pn - pk, 15);
+ memcpy(key, pk, n);
+ key[n]= 0;
+ } else
+ strcpy(key, pk);
+
+ val[0]= 0;
+ } // endif pv
+
+ if (!stricmp(opname, key)) {
+ opval= (char*)PlugSubAlloc(g, NULL, strlen(val) + 1);
+ strcpy(opval, val);
+ break;
+ } else if (!pn)
+ break;
+
+ } // endfor pk
+
+ return opval;
+} // end of GetListOption
+
+/****************************************************************************/
+/* Return the table option structure. */
+/****************************************************************************/
+PTOS ha_connect::GetTableOptionStruct(TABLE *tab)
+{
+ return (tshp) ? tshp->option_struct :
+ (tab) ? tab->s->option_struct : NULL;
+} // end of GetTableOptionStruct
+
+/****************************************************************************/
+/* Return the value of a string option or NULL if not specified. */
+/****************************************************************************/
+char *ha_connect::GetStringOption(char *opname, char *sdef)
+{
+ char *opval= NULL;
+ PTOS options= GetTableOptionStruct(table);
+
+ if (!options)
+ ;
+ else if (!stricmp(opname, "Type"))
+ opval= (char*)options->type;
+ else if (!stricmp(opname, "Filename"))
+ opval= (char*)options->filename;
+ else if (!stricmp(opname, "Optname"))
+ opval= (char*)options->optname;
+ else if (!stricmp(opname, "Tabname"))
+ opval= (char*)options->tabname;
+ else if (!stricmp(opname, "Tablist"))
+ opval= (char*)options->tablist;
+ else if (!stricmp(opname, "Database") ||
+ !stricmp(opname, "DBname"))
+ opval= (char*)options->dbname;
+ else if (!stricmp(opname, "Separator"))
+ opval= (char*)options->separator;
+ else if (!stricmp(opname, "Connect"))
+ opval= (tshp) ? tshp->connect_string.str : table->s->connect_string.str;
+ else if (!stricmp(opname, "Qchar"))
+ opval= (char*)options->qchar;
+ else if (!stricmp(opname, "Module"))
+ opval= (char*)options->module;
+ else if (!stricmp(opname, "Subtype"))
+ opval= (char*)options->subtype;
+ else if (!stricmp(opname, "Catfunc"))
+ opval= (char*)options->catfunc;
+ else if (!stricmp(opname, "Srcdef"))
+ opval= (char*)options->srcdef;
+ else if (!stricmp(opname, "Colist"))
+ opval= (char*)options->colist;
+ else if (!stricmp(opname, "Data_charset"))
+ opval= (char*)options->data_charset;
+ else if (!stricmp(opname, "Query_String"))
+ opval= thd_query_string(table->in_use)->str;
+
+ if (!opval && options && options->oplist)
+ opval= GetListOption(xp->g, opname, options->oplist);
+
+ if (!opval) {
+ if (sdef && !strcmp(sdef, "*")) {
+ // Return the handler default value
+ if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database"))
+ opval= (char*)GetDBName(NULL); // Current database
+ else if (!stricmp(opname, "Type")) // Default type
+ opval= (!options) ? NULL :
+ (options->srcdef) ? (char*)"MYSQL" :
+ (options->tabname) ? (char*)"PROXY" : (char*)"DOS";
+ else if (!stricmp(opname, "User")) // Connected user
+ opval= (char *) "root";
+ else if (!stricmp(opname, "Host")) // Connected user host
+ opval= (char *) "localhost";
+ else
+ opval= sdef; // Caller default
+
+ } else
+ opval= sdef; // Caller default
+
+ } // endif !opval
+
+ return opval;
+} // end of GetStringOption
+
+/****************************************************************************/
+/* Return the value of a Boolean option or bdef if not specified. */
+/****************************************************************************/
+bool ha_connect::GetBooleanOption(char *opname, bool bdef)
+{
+ bool opval= bdef;
+ char *pv;
+ PTOS options= GetTableOptionStruct(table);
+
+ if (!stricmp(opname, "View"))
+ opval= (tshp) ? tshp->is_view : table->s->is_view;
+ else if (!options)
+ ;
+ else if (!stricmp(opname, "Mapped"))
+ opval= options->mapped;
+ else if (!stricmp(opname, "Huge"))
+ opval= options->huge;
+//else if (!stricmp(opname, "Compressed"))
+// opval= options->compressed;
+ else if (!stricmp(opname, "Split"))
+ opval= options->split;
+ else if (!stricmp(opname, "Readonly"))
+ opval= options->readonly;
+ else if (!stricmp(opname, "SepIndex"))
+ opval= options->sepindex;
+ else if (options->oplist)
+ if ((pv= GetListOption(xp->g, opname, options->oplist)))
+ opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
+
+ return opval;
+} // end of GetBooleanOption
+
+/****************************************************************************/
+/* Set the value of the opname option (does not work for oplist options) */
+/* Currently used only to set the Sepindex value. */
+/****************************************************************************/
+bool ha_connect::SetBooleanOption(char *opname, bool b)
+{
+ PTOS options= GetTableOptionStruct(table);
+
+ if (!options)
+ return true;
+
+ if (!stricmp(opname, "SepIndex"))
+ options->sepindex= b;
+ else
+ return true;
+
+ return false;
+} // end of SetBooleanOption
+
+/****************************************************************************/
+/* Return the value of an integer option or NO_IVAL if not specified. */
+/****************************************************************************/
+int ha_connect::GetIntegerOption(char *opname)
+{
+ ulonglong opval= NO_IVAL;
+ char *pv;
+ PTOS options= GetTableOptionStruct(table);
+
+ if (!options)
+ ;
+ else if (!stricmp(opname, "Lrecl"))
+ opval= options->lrecl;
+ else if (!stricmp(opname, "Elements"))
+ opval= options->elements;
+ else if (!stricmp(opname, "Estimate"))
+// opval= options->estimate;
+ opval= (int)table->s->max_rows;
+ else if (!stricmp(opname, "Avglen"))
+ opval= (int)table->s->avg_row_length;
+ else if (!stricmp(opname, "Multiple"))
+ opval= options->multiple;
+ else if (!stricmp(opname, "Header"))
+ opval= options->header;
+ else if (!stricmp(opname, "Quoted"))
+ opval= options->quoted;
+ else if (!stricmp(opname, "Ending"))
+ opval= options->ending;
+ else if (!stricmp(opname, "Compressed"))
+ opval= (options->compressed);
+
+ if (opval == (ulonglong)NO_IVAL && options && options->oplist)
+ if ((pv= GetListOption(xp->g, opname, options->oplist)))
+ opval= CharToNumber(pv, strlen(pv), ULONGLONG_MAX, true);
+
+ return (int)opval;
+} // end of GetIntegerOption
+
+/****************************************************************************/
+/* Set the value of the opname option (does not work for oplist options) */
+/* Currently used only to set the Lrecl value. */
+/****************************************************************************/
+bool ha_connect::SetIntegerOption(char *opname, int n)
+{
+ PTOS options= GetTableOptionStruct(table);
+
+ if (!options)
+ return true;
+
+ if (!stricmp(opname, "Lrecl"))
+ options->lrecl= n;
+ else if (!stricmp(opname, "Elements"))
+ options->elements= n;
+//else if (!stricmp(opname, "Estimate"))
+// options->estimate= n;
+ else if (!stricmp(opname, "Multiple"))
+ options->multiple= n;
+ else if (!stricmp(opname, "Header"))
+ options->header= n;
+ else if (!stricmp(opname, "Quoted"))
+ options->quoted= n;
+ else if (!stricmp(opname, "Ending"))
+ options->ending= n;
+ else if (!stricmp(opname, "Compressed"))
+ options->compressed= n;
+ else
+ return true;
+//else if (options->oplist)
+// SetListOption(opname, options->oplist, n);
+
+ return false;
+} // end of SetIntegerOption
+
+/****************************************************************************/
+/* Return a field option structure. */
+/****************************************************************************/
+PFOS ha_connect::GetFieldOptionStruct(Field *fdp)
+{
+ return fdp->option_struct;
+} // end of GetFildOptionStruct
+
+/****************************************************************************/
+/* Returns the column description structure used to make the column. */
+/****************************************************************************/
+void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
+{
+ const char *cp;
+ ha_field_option_struct *fop;
+ Field* fp;
+ Field* *fldp;
+
+ // Double test to be on the safe side
+ if (!table)
+ return NULL;
+
+ // Find the column to describe
+ if (field) {
+ fldp= (Field**)field;
+ fldp++;
+ } else
+ fldp= (tshp) ? tshp->field : table->field;
+
+ if (!fldp || !(fp= *fldp))
+ return NULL;
+
+ // Get the CONNECT field options structure
+ fop= GetFieldOptionStruct(fp);
+ pcf->Flags= 0;
+
+ // Now get column information
+ pcf->Name= (char*)fp->field_name;
+
+ if (fop && fop->special) {
+ pcf->Fieldfmt= (char*)fop->special;
+ pcf->Flags= U_SPECIAL;
+ return fldp;
+ } // endif special
+
+ pcf->Scale= 0;
+ pcf->Opt= (fop) ? (int)fop->opt : 0;
+
+ if ((pcf->Length= fp->field_length) < 0)
+ pcf->Length= 256; // BLOB?
+
+ pcf->Precision= pcf->Length;
+
+ if (fop) {
+ pcf->Offset= (int)fop->offset;
+ pcf->Freq= (int)fop->freq;
+ pcf->Datefmt= (char*)fop->dateformat;
+ pcf->Fieldfmt= (char*)fop->fieldformat;
+ } else {
+ pcf->Offset= -1;
+ pcf->Freq= 0;
+ pcf->Datefmt= NULL;
+ pcf->Fieldfmt= NULL;
+ } // endif fop
+
+ switch (fp->type()) {
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ pcf->Flags |= U_VAR;
+ /* no break */
+ default:
+ pcf->Type= MYSQLtoPLG(fp->type());
+ break;
+ } // endswitch SQL type
+
+ switch (pcf->Type) {
+ case TYPE_STRING:
+ // Do something for case
+ cp= fp->charset()->name;
+
+ // Find if collation name ends by _ci
+ if (!strcmp(cp + strlen(cp) - 3, "_ci")) {
+ pcf->Scale= 1; // Case insensitive
+ pcf->Opt= 0; // Prevent index opt until it is safe
+ } // endif ci
+
+ break;
+ case TYPE_DOUBLE:
+ pcf->Scale= max(min(fp->decimals(), ((unsigned)pcf->Length - 2)), 0);
+ break;
+ case TYPE_DECIM:
+ pcf->Precision= ((Field_new_decimal*)fp)->precision;
+ pcf->Scale= fp->decimals();
+ break;
+ case TYPE_DATE:
+ // Field_length is only used for DATE columns
+ if (fop->fldlen)
+ pcf->Length= (int)fop->fldlen;
+ else {
+ int len;
+
+ if (pcf->Datefmt) {
+ // Find the (max) length produced by the date format
+ char buf[256];
+ PGLOBAL g= GetPlug(table->in_use, xp);
+ PDTP pdtp= MakeDateFormat(g, pcf->Datefmt, false, true, 0);
+ struct tm datm;
+ bzero(&datm, sizeof(datm));
+ datm.tm_mday= 12;
+ datm.tm_mon= 11;
+ datm.tm_year= 112;
+ len= strftime(buf, 256, pdtp->OutFmt, &datm);
+ } else
+ len= 0;
+
+ // 11 is for signed numeric representation of the date
+ pcf->Length= (len) ? len : 11;
+ } // endelse
+
+ break;
+ default:
+ break;
+ } // endswitch type
+
+ if (fp->flags & UNSIGNED_FLAG)
+ pcf->Flags |= U_UNSIGNED;
+
+ if (fp->flags & ZEROFILL_FLAG)
+ pcf->Flags |= U_ZEROFILL;
+
+ // This is used to skip null bit
+ if (fp->real_maybe_null())
+ pcf->Flags |= U_NULLS;
+
+ // Mark virtual columns as such
+ if (fp->vcol_info && !fp->stored_in_db)
+ pcf->Flags |= U_VIRTUAL;
+
+ pcf->Key= 0; // Not used when called from MySQL
+
+ // Get the comment if any
+ if (fp->comment.str && fp->comment.length) {
+ pcf->Remark= (char*)PlugSubAlloc(g, NULL, fp->comment.length + 1);
+ memcpy(pcf->Remark, fp->comment.str, fp->comment.length);
+ pcf->Remark[fp->comment.length]= 0;
+ } else
+ pcf->Remark= NULL;
+
+ return fldp;
+} // end of GetColumnOption
+
+/****************************************************************************/
+/* Returns the index description structure used to make the index. */
+/****************************************************************************/
+PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
+{
+ char *name, *pn;
+ bool unique;
+ PIXDEF xdp, pxd=NULL, toidx= NULL;
+ PKPDEF kpp, pkp;
+ KEY kp;
+ PGLOBAL& g= xp->g;
+
+ if (!s)
+ s= table->s;
+
+ for (int n= 0; (unsigned)n < s->keynames.count; n++) {
+ if (xtrace)
+ htrc("Getting created index %d info\n", n + 1);
+
+ // Find the index to describe
+ kp= s->key_info[n];
+
+ // Now get index information
+ pn= (char*)s->keynames.type_names[n];
+ name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
+ strcpy(name, pn); // This is probably unuseful
+ unique= (kp.flags & 1) != 0;
+ pkp= NULL;
+
+ // Allocate the index description block
+ xdp= new(g) INDEXDEF(name, unique, n);
+
+ // Get the the key parts info
+ for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
+ pn= (char*)kp.key_part[k].field->field_name;
+ name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
+ strcpy(name, pn); // This is probably unuseful
+
+ // Allocate the key part description block
+ kpp= new(g) KPARTDEF(name, k + 1);
+ kpp->SetKlen(kp.key_part[k].length);
+
+#if 0 // NIY
+ // Index on auto increment column can be an XXROW index
+ if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
+ kp.uder_defined_key_parts == 1) {
+ char *type= GetStringOption("Type", "DOS");
+ TABTYPE typ= GetTypeID(type);
+
+ xdp->SetAuto(IsTypeFixed(typ));
+ } // endif AUTO_INCREMENT
+#endif // 0
+
+ if (pkp)
+ pkp->SetNext(kpp);
+ else
+ xdp->SetToKeyParts(kpp);
+
+ pkp= kpp;
+ } // endfor k
+
+ xdp->SetNParts(kp.user_defined_key_parts);
+
+ if (pxd)
+ pxd->SetNext(xdp);
+ else
+ toidx= xdp;
+
+ pxd= xdp;
+ } // endfor n
+
+ return toidx;
+} // end of GetIndexInfo
+
+const char *ha_connect::GetDBName(const char* name)
+{
+ return (name) ? name : table->s->db.str;
+} // end of GetDBName
+
+const char *ha_connect::GetTableName(void)
+{
+ return (tshp) ? tshp->table_name.str : table->s->table_name.str;
+} // end of GetTableName
+
+#if 0
+/****************************************************************************/
+/* Returns the column real or special name length of a field. */
+/****************************************************************************/
+int ha_connect::GetColNameLen(Field *fp)
+{
+ int n;
+ PFOS fop= GetFieldOptionStruct(fp);
+
+ // Now get the column name length
+ if (fop && fop->special)
+ n= strlen(fop->special) + 1;
+ else
+ n= strlen(fp->field_name);
+
+ return n;
+} // end of GetColNameLen
+
+/****************************************************************************/
+/* Returns the column real or special name of a field. */
+/****************************************************************************/
+char *ha_connect::GetColName(Field *fp)
+{
+ PFOS fop= GetFieldOptionStruct(fp);
+
+ return (fop && fop->special) ? fop->special : (char*)fp->field_name;
+} // end of GetColName
+
+/****************************************************************************/
+/* Adds the column real or special name of a field to a string. */
+/****************************************************************************/
+void ha_connect::AddColName(char *cp, Field *fp)
+{
+ PFOS fop= GetFieldOptionStruct(fp);
+
+ // Now add the column name
+ if (fop && fop->special)
+ // The prefix * mark the column as "special"
+ strcat(strcpy(cp, "*"), strupr(fop->special));
+ else
+ strcpy(cp, (char*)fp->field_name);
+
+} // end of AddColName
+#endif // 0
+
+/****************************************************************************/
+/* Get the table description block of a CONNECT table. */
+/****************************************************************************/
+PTDB ha_connect::GetTDB(PGLOBAL g)
+{
+ const char *table_name;
+ PTDB tp;
+
+ // Double test to be on the safe side
+ if (!g || !table)
+ return NULL;
+
+ table_name= GetTableName();
+
+ if (!xp->CheckQuery(valid_query_id) && tdbp
+ && !stricmp(tdbp->GetName(), table_name)
+ && (tdbp->GetMode() == xmod
+ || tdbp->GetAmType() == TYPE_AM_XML)) {
+ tp= tdbp;
+// tp->SetMode(xmod);
+ } else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
+ valid_query_id= xp->last_query_id;
+ tp->SetMode(xmod);
+ } else
+ htrc("GetTDB: %s\n", g->Message);
+
+ return tp;
+} // end of GetTDB
+
+/****************************************************************************/
+/* Open a CONNECT table, restricting column list if cols is true. */
+/****************************************************************************/
+int ha_connect::OpenTable(PGLOBAL g, bool del)
+{
+ bool rc= false;
+ char *c1= NULL, *c2=NULL;
+
+ // Double test to be on the safe side
+ if (!g || !table) {
+ htrc("OpenTable logical error; g=%p table=%p\n", g, table);
+ return HA_ERR_INITIALIZATION;
+ } // endif g
+
+ if (!(tdbp= GetTDB(g)))
+ return RC_FX;
+ else if (tdbp->IsReadOnly())
+ switch (xmod) {
+ case MODE_WRITE:
+ case MODE_INSERT:
+ case MODE_UPDATE:
+ case MODE_DELETE:
+ strcpy(g->Message, MSG(READ_ONLY));
+ return HA_ERR_TABLE_READONLY;
+ default:
+ break;
+ } // endswitch xmode
+
+ if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC
+ || tdbp->GetAmType() == TYPE_AM_MYSQL) {
+ // Get the list of used fields (columns)
+ char *p;
+ unsigned int k1, k2, n1, n2;
+ Field* *field;
+ Field* fp;
+ MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
+ MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
+
+ k1= k2= 0;
+ n1= n2= 1; // 1 is space for final null character
+
+ for (field= table->field; fp= *field; field++) {
+ if (bitmap_is_set(map, fp->field_index)) {
+ n1+= (strlen(fp->field_name) + 1);
+ k1++;
+ } // endif
+
+ if (ump && bitmap_is_set(ump, fp->field_index)) {
+ n2+= (strlen(fp->field_name) + 1);
+ k2++;
+ } // endif
+
+ } // endfor field
+
+ if (k1) {
+ p= c1= (char*)PlugSubAlloc(g, NULL, n1);
+
+ for (field= table->field; fp= *field; field++)
+ if (bitmap_is_set(map, fp->field_index)) {
+ strcpy(p, (char*)fp->field_name);
+ p+= (strlen(p) + 1);
+ } // endif used field
+
+ *p= '\0'; // mark end of list
+ } // endif k1
+
+ if (k2) {
+ p= c2= (char*)PlugSubAlloc(g, NULL, n2);
+
+ for (field= table->field; fp= *field; field++)
+ if (bitmap_is_set(ump, fp->field_index)) {
+ strcpy(p, (char*)fp->field_name);
+ p+= (strlen(p) + 1);
+ } // endif used field
+
+ *p= '\0'; // mark end of list
+ } // endif k2
+
+ } // endif xmod
+
+ // Open the table
+ if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) {
+ istable= true;
+// strmake(tname, table_name, sizeof(tname)-1);
+
+ // We may be in a create index query
+ if (xmod == MODE_ANY && *tdbp->GetName() != '#') {
+ // The current indexes
+ PIXDEF oldpix= GetIndexInfo();
+ } // endif xmod
+
+ } else
+ htrc("OpenTable: %s\n", g->Message);
+
+ if (rc) {
+ tdbp= NULL;
+ valid_info= false;
+ } // endif rc
+
+ return (rc) ? HA_ERR_INITIALIZATION : 0;
+} // end of OpenTable
+
+
+/****************************************************************************/
+/* IsOpened: returns true if the table is already opened. */
+/****************************************************************************/
+bool ha_connect::IsOpened(void)
+{
+ return (!xp->CheckQuery(valid_query_id) && tdbp
+ && tdbp->GetUse() == USE_OPEN);
+} // end of IsOpened
+
+
+/****************************************************************************/
+/* Close a CONNECT table. */
+/****************************************************************************/
+int ha_connect::CloseTable(PGLOBAL g)
+{
+ int rc= CntCloseTable(g, tdbp);
+ tdbp= NULL;
+ sdvalin=NULL;
+ sdvalout=NULL;
+ valid_info= false;
+ indexing= -1;
+ return rc;
+} // end of CloseTable
+
+
+/***********************************************************************/
+/* Make a pseudo record from current row values. Specific to MySQL. */
+/***********************************************************************/
+int ha_connect::MakeRecord(char *buf)
+{
+ char *p, *fmt, val[32];
+ int rc= 0;
+ Field* *field;
+ Field *fp;
+ my_bitmap_map *org_bitmap;
+ CHARSET_INFO *charset= tdbp->data_charset();
+//MY_BITMAP readmap;
+ MY_BITMAP *map;
+ PVAL value;
+ PCOL colp= NULL;
+ DBUG_ENTER("ha_connect::MakeRecord");
+
+ if (xtrace > 1)
+ htrc("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n",
+ *table->read_set->bitmap, *table->write_set->bitmap,
+ *table->vcol_set->bitmap,
+ *table->def_read_set.bitmap, *table->def_write_set.bitmap);
+
+ // Avoid asserts in field::store() for columns that are not updated
+ org_bitmap= dbug_tmp_use_all_columns(table, table->write_set);
+
+ // This is for variable_length rows
+ memset(buf, 0, table->s->null_bytes);
+
+ // When sorting read_set selects all columns, so we use def_read_set
+ map= (MY_BITMAP *)&table->def_read_set;
+
+ // Make the pseudo record from field values
+ for (field= table->field; *field && !rc; field++) {
+ fp= *field;
+
+ if (fp->vcol_info && !fp->stored_in_db)
+ continue; // This is a virtual column
+
+ if (bitmap_is_set(map, fp->field_index) || alter) {
+ // This is a used field, fill the buffer with value
+ for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
+ if ((!mrr || colp->GetKcol()) &&
+ !stricmp(colp->GetName(), (char*)fp->field_name))
+ break;
+
+ if (!colp) {
+ if (mrr)
+ continue;
+
+ htrc("Column %s not found\n", fp->field_name);
+ dbug_tmp_restore_column_map(table->write_set, org_bitmap);
+ DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
+ } // endif colp
+
+ value= colp->GetValue();
+
+ // All this could be better optimized
+ if (!value->IsNull()) {
+ switch (value->GetType()) {
+ case TYPE_DATE:
+ if (!sdvalout)
+ sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
+
+ switch (fp->type()) {
+ case MYSQL_TYPE_DATE:
+ fmt= "%Y-%m-%d";
+ break;
+ case MYSQL_TYPE_TIME:
+ fmt= "%H:%M:%S";
+ break;
+ case MYSQL_TYPE_YEAR:
+ fmt= "%Y";
+ break;
+ default:
+ fmt= "%Y-%m-%d %H:%M:%S";
+ break;
+ } // endswitch type
+
+ // Get date in the format required by MySQL fields
+ value->FormatValue(sdvalout, fmt);
+ p= sdvalout->GetCharValue();
+ break;
+ case TYPE_DOUBLE:
+ p= NULL;
+ break;
+ case TYPE_STRING:
+ // Passthru
+ default:
+ p= value->GetCharString(val);
+ 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;
+
+ DBUG_PRINT("MakeRecord", ("%s", p));
+ } // endif store
+
+ } 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
+
+ fp->set_notnull();
+ } else
+ fp->set_null();
+
+ } // endif bitmap
+
+ } // endfor field
+
+ // This is copied from ha_tina and is necessary to avoid asserts
+ dbug_tmp_restore_column_map(table->write_set, org_bitmap);
+ DBUG_RETURN(rc);
+} // end of MakeRecord
+
+
+/***********************************************************************/
+/* Set row values from a MySQL pseudo record. Specific to MySQL. */
+/***********************************************************************/
+int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
+{
+ char attr_buffer[1024];
+ char data_buffer[1024];
+ char *fmt;
+ int rc= 0;
+ PCOL colp;
+ PVAL value;
+ Field *fp;
+ PTDBASE tp= (PTDBASE)tdbp;
+ String attribute(attr_buffer, sizeof(attr_buffer),
+ table->s->table_charset);
+ my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set);
+ const CHARSET_INFO *charset= tdbp->data_charset();
+ String data_charset_value(data_buffer, sizeof(data_buffer), charset);
+
+ // Scan the pseudo record for field values and set column values
+ for (Field **field=table->field ; *field ; field++) {
+ fp= *field;
+
+ if ((fp->vcol_info && !fp->stored_in_db) ||
+ fp->option_struct->special)
+ continue; // Is a virtual column possible here ???
+
+ if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
+ && tdbp->GetAmType() != TYPE_AM_ODBC) ||
+ bitmap_is_set(table->write_set, fp->field_index)) {
+ for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
+ if (!stricmp(colp->GetName(), fp->field_name))
+ break;
+
+ if (!colp) {
+ htrc("Column %s not found\n", fp->field_name);
+ rc= HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ } else
+ value= colp->GetValue();
+
+ // This is a used field, fill the value from the row buffer
+ // All this could be better optimized
+ if (fp->is_null()) {
+ if (colp->IsNullable())
+ value->SetNull(true);
+
+ value->Reset();
+ } else switch (value->GetType()) {
+ case TYPE_DOUBLE:
+ value->SetValue(fp->val_real());
+ break;
+ case TYPE_DATE:
+ if (!sdvalin)
+ sdvalin= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
+
+ // Get date in the format produced by MySQL fields
+ switch (fp->type()) {
+ case MYSQL_TYPE_DATE:
+ fmt= "YYYY-MM-DD";
+ break;
+ case MYSQL_TYPE_TIME:
+ fmt= "hh:mm:ss";
+ break;
+ case MYSQL_TYPE_YEAR:
+ fmt= "YYYY";
+ break;
+ default:
+ fmt= "YYYY-MM-DD hh:mm:ss";
+ } // endswitch type
+
+ ((DTVAL*)sdvalin)->SetFormat(g, fmt, strlen(fmt));
+ fp->val_str(&attribute);
+ sdvalin->SetValue_psz(attribute.c_ptr_safe());
+ value->SetValue_pval(sdvalin);
+ break;
+ default:
+ fp->val_str(&attribute);
+
+ if (charset != &my_charset_bin) {
+ // Convert from SQL field charset to DATA_CHARSET
+ uint cnv_errors;
+
+ data_charset_value.copy(attribute.ptr(), attribute.length(),
+ attribute.charset(), charset, &cnv_errors);
+ value->SetValue_psz(data_charset_value.c_ptr_safe());
+ } else
+ value->SetValue_psz(attribute.c_ptr_safe());
+
+ break;
+ } // endswitch Type
+
+#ifdef NEWCHANGE
+ } else if (xmod == MODE_UPDATE) {
+ PCOL cp;
+
+ for (cp= tp->GetColumns(); cp; cp= cp->GetNext())
+ if (!stricmp(colp->GetName(), cp->GetName()))
+ break;
+
+ if (!cp) {
+ rc= HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ } // endif cp
+
+ value->SetValue_pval(cp->GetValue());
+ } else // mode Insert
+ value->Reset();
+#else
+ } // endif bitmap_is_set
+#endif
+
+ } // endfor field
+
+ err:
+ dbug_tmp_restore_column_map(table->read_set, bmap);
+ return rc;
+} // end of ScanRecord
+
+
+/***********************************************************************/
+/* Check change in index column. Specific to MySQL. */
+/* Should be elaborated to check for real changes. */
+/***********************************************************************/
+int ha_connect::CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf)
+{
+ return ScanRecord(g, newbuf);
+} // end of dummy CheckRecord
+
+
+/***********************************************************************/
+/* Return the string representing an operator. */
+/***********************************************************************/
+const char *ha_connect::GetValStr(OPVAL vop, bool neg)
+{
+ const char *val;
+
+ switch (vop) {
+ case OP_EQ:
+ val= " = ";
+ break;
+ case OP_NE:
+ val= " <> ";
+ break;
+ case OP_GT:
+ val= " > ";
+ break;
+ case OP_GE:
+ val= " >= ";
+ break;
+ case OP_LT:
+ val= " < ";
+ break;
+ case OP_LE:
+ val= " <= ";
+ break;
+ case OP_IN:
+ val= (neg) ? " NOT IN (" : " IN (";
+ break;
+ case OP_NULL:
+ val= (neg) ? " IS NOT NULL" : " IS NULL";
+ break;
+ case OP_LIKE:
+ val= " LIKE ";
+ break;
+ case OP_XX:
+ val= (neg) ? " NOT BETWEEN " : " BETWEEN ";
+ break;
+ case OP_EXIST:
+ val= (neg) ? " NOT EXISTS " : " EXISTS ";
+ break;
+ case OP_AND:
+ val= " AND ";
+ break;
+ case OP_OR:
+ val= " OR ";
+ break;
+ case OP_NOT:
+ val= " NOT ";
+ break;
+ case OP_CNC:
+ val= " || ";
+ break;
+ case OP_ADD:
+ val= " + ";
+ break;
+ case OP_SUB:
+ val= " - ";
+ break;
+ case OP_MULT:
+ val= " * ";
+ break;
+ case OP_DIV:
+ val= " / ";
+ break;
+ default:
+ val= " ? ";
+ break;
+ } /* endswitch */
+
+ return val;
+} // end of GetValStr
+
+
+/***********************************************************************/
+/* Check the WHERE condition and return a CONNECT filter. */
+/***********************************************************************/
+PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
+{
+ unsigned int i;
+ bool ismul= false;
+ OPVAL vop= OP_XX;
+ PFIL filp= NULL;
+
+ if (!cond)
+ return NULL;
+
+ if (xtrace)
+ htrc("Cond type=%d\n", cond->type());
+
+ if (cond->type() == COND::COND_ITEM) {
+ PFIL fp;
+ Item_cond *cond_item= (Item_cond *)cond;
+
+ if (xtrace)
+ htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
+ cond_item->func_name());
+
+ switch (cond_item->functype()) {
+ case Item_func::COND_AND_FUNC: vop= OP_AND; break;
+ case Item_func::COND_OR_FUNC: vop= OP_OR; break;
+ default: return NULL;
+ } // endswitch functype
+
+ List<Item>* arglist= cond_item->argument_list();
+ List_iterator<Item> li(*arglist);
+ Item *subitem;
+
+ for (i= 0; i < arglist->elements; i++)
+ if ((subitem= li++)) {
+ if (!(fp= CondFilter(g, subitem))) {
+ if (vop == OP_OR)
+ return NULL;
+ } else
+ filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
+
+ } else
+ return NULL;
+
+ } else if (cond->type() == COND::FUNC_ITEM) {
+ unsigned int i;
+ bool iscol, neg= FALSE;
+ PCOL colp[2]= {NULL,NULL};
+ PPARM pfirst= NULL, pprec= NULL;
+ POPER pop;
+ Item_func *condf= (Item_func *)cond;
+ Item* *args= condf->arguments();
+
+ if (xtrace)
+ htrc("Func type=%d argnum=%d\n", condf->functype(),
+ condf->argument_count());
+
+ switch (condf->functype()) {
+ case Item_func::EQUAL_FUNC:
+ case Item_func::EQ_FUNC: vop= OP_EQ; break;
+ case Item_func::NE_FUNC: vop= OP_NE; break;
+ case Item_func::LT_FUNC: vop= OP_LT; break;
+ case Item_func::LE_FUNC: vop= OP_LE; break;
+ case Item_func::GE_FUNC: vop= OP_GE; break;
+ case Item_func::GT_FUNC: vop= OP_GT; break;
+ case Item_func::IN_FUNC: vop= OP_IN;
+ case Item_func::BETWEEN:
+ ismul= true;
+ neg= ((Item_func_opt_neg *)condf)->negated;
+ break;
+ default: return NULL;
+ } // endswitch functype
+
+ pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
+ pop->Name= NULL;
+ pop->Val=vop;
+ pop->Mod= 0;
+
+ if (condf->argument_count() < 2)
+ return NULL;
+
+ for (i= 0; i < condf->argument_count(); i++) {
+ if (xtrace)
+ htrc("Argtype(%d)=%d\n", i, args[i]->type());
+
+ if (i >= 2 && !ismul) {
+ if (xtrace)
+ htrc("Unexpected arg for vop=%d\n", vop);
+
+ continue;
+ } // endif i
+
+ if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
+ Item_field *pField= (Item_field *)args[i];
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (i && ismul)
+ return NULL;
+
+ if (pField->field->table != table ||
+ !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name, 0)))
+ return NULL; // Column does not belong to this table
+
+ if (xtrace) {
+ htrc("Field index=%d\n", pField->field->field_index);
+ htrc("Field name=%s\n", pField->field->field_name);
+ } // endif xtrace
+
+ } else {
+ char buff[256];
+ String *res, tmp(buff, sizeof(buff), &my_charset_bin);
+ Item_basic_constant *pval= (Item_basic_constant *)args[i];
+ PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (!i && (ismul))
+ return NULL;
+
+ if ((res= pval->val_str(&tmp)) == NULL)
+ return NULL; // To be clarified
+
+ switch (args[i]->real_type()) {
+ case COND::STRING_ITEM:
+ pp->Type= TYPE_STRING;
+ pp->Value= PlugSubAlloc(g, NULL, res->length() + 1);
+ strncpy((char*)pp->Value, res->ptr(), res->length() + 1);
+ break;
+ case COND::INT_ITEM:
+ pp->Type= TYPE_INT;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
+ *((int*)pp->Value)= (int)pval->val_int();
+ break;
+ case COND::DATE_ITEM:
+ pp->Type= TYPE_DATE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
+ *((int*)pp->Value)= (int)pval->val_int_from_date();
+ break;
+ case COND::REAL_ITEM:
+ pp->Type= TYPE_DOUBLE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
+ *((double*)pp->Value)= pval->val_real();
+ break;
+ case COND::DECIMAL_ITEM:
+ pp->Type= TYPE_DOUBLE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
+ *((double*)pp->Value)= pval->val_real_from_decimal();
+ break;
+ case COND::CACHE_ITEM: // Possible ???
+ case COND::NULL_ITEM: // TODO: handle this
+ default:
+ return NULL;
+ } // endswitch type
+
+ if (xtrace)
+ htrc("Value=%.*s\n", res->length(), res->ptr());
+
+ // Append the value to the argument list
+ if (pprec)
+ pprec->Next= pp;
+ else
+ pfirst= pp;
+
+ pp->Domain= i;
+ pp->Next= NULL;
+ pprec= pp;
+ } // endif type
+
+ } // endfor i
+
+ filp= MakeFilter(g, colp, pop, pfirst, neg);
+ } else {
+ if (xtrace)
+ htrc("Unsupported condition\n");
+
+ return NULL;
+ } // endif's type
+
+ return filp;
+} // end of CondFilter
+
+/***********************************************************************/
+/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
+/***********************************************************************/
+PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
+{
+ char *body= filp->Body;
+ unsigned int i;
+ bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
+ OPVAL vop= OP_XX;
+
+ if (!cond)
+ return NULL;
+
+ if (xtrace)
+ htrc("Cond type=%d\n", cond->type());
+
+ if (cond->type() == COND::COND_ITEM) {
+ char *p1, *p2;
+ Item_cond *cond_item= (Item_cond *)cond;
+
+ if (x)
+ return NULL;
+
+ if (xtrace)
+ htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
+ cond_item->func_name());
+
+ switch (cond_item->functype()) {
+ case Item_func::COND_AND_FUNC: vop= OP_AND; break;
+ case Item_func::COND_OR_FUNC: vop= OP_OR; break;
+ default: return NULL;
+ } // endswitch functype
+
+ List<Item>* arglist= cond_item->argument_list();
+ List_iterator<Item> li(*arglist);
+ Item *subitem;
+
+ p1= body + strlen(body);
+ strcpy(p1, "(");
+ p2= p1 + 1;
+
+ for (i= 0; i < arglist->elements; i++)
+ if ((subitem= li++)) {
+ if (!CheckCond(g, filp, tty, subitem)) {
+ if (vop == OP_OR)
+ return NULL;
+ else
+ *p2= 0;
+
+ } else {
+ p1= p2 + strlen(p2);
+ strcpy(p1, GetValStr(vop, FALSE));
+ p2= p1 + strlen(p1);
+ } // endif CheckCond
+
+ } else
+ return NULL;
+
+ if (*p1 != '(')
+ strcpy(p1, ")");
+ else
+ return NULL;
+
+ } else if (cond->type() == COND::FUNC_ITEM) {
+ unsigned int i;
+// int n;
+ bool iscol, neg= FALSE;
+ Item_func *condf= (Item_func *)cond;
+ Item* *args= condf->arguments();
+
+ if (xtrace)
+ htrc("Func type=%d argnum=%d\n", condf->functype(),
+ condf->argument_count());
+
+// neg= condf->
+
+ switch (condf->functype()) {
+ case Item_func::EQUAL_FUNC:
+ case Item_func::EQ_FUNC: vop= OP_EQ; break;
+ case Item_func::NE_FUNC: vop= OP_NE; break;
+ case Item_func::LT_FUNC: vop= OP_LT; break;
+ case Item_func::LE_FUNC: vop= OP_LE; break;
+ case Item_func::GE_FUNC: vop= OP_GE; break;
+ case Item_func::GT_FUNC: vop= OP_GT; break;
+ case Item_func::IN_FUNC: vop= OP_IN;
+ case Item_func::BETWEEN:
+ ismul= true;
+ neg= ((Item_func_opt_neg *)condf)->negated;
+ break;
+ default: return NULL;
+ } // endswitch functype
+
+ if (condf->argument_count() < 2)
+ return NULL;
+ else if (ismul && tty == TYPE_AM_WMI)
+ return NULL; // Not supported by WQL
+
+ if (x && (neg || !(vop == OP_EQ || vop == OP_IN)))
+ return NULL;
+
+ for (i= 0; i < condf->argument_count(); i++) {
+ if (xtrace)
+ htrc("Argtype(%d)=%d\n", i, args[i]->type());
+
+ if (i >= 2 && !ismul) {
+ if (xtrace)
+ htrc("Unexpected arg for vop=%d\n", vop);
+
+ continue;
+ } // endif i
+
+ if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
+ const char *fnm;
+ ha_field_option_struct *fop;
+ Item_field *pField= (Item_field *)args[i];
+
+ if (x && i)
+ return NULL;
+
+ if (pField->field->table != table)
+ return NULL; // Field does not belong to this table
+ else
+ fop= GetFieldOptionStruct(pField->field);
+
+ if (fop && fop->special) {
+ if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID"))
+ fnm= "TABID";
+ else if (tty == TYPE_AM_PLG)
+ fnm= fop->special;
+ else
+ return NULL;
+
+ } else if (tty == TYPE_AM_TBL)
+ return NULL;
+ else
+ fnm= pField->field->field_name;
+
+ if (xtrace) {
+ htrc("Field index=%d\n", pField->field->field_index);
+ htrc("Field name=%s\n", pField->field->field_name);
+ } // endif xtrace
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (i && ismul)
+ return NULL;
+
+ strcat(body, fnm);
+ } else if (args[i]->type() == COND::FUNC_ITEM) {
+ if (tty == TYPE_AM_MYSQL) {
+ if (!CheckCond(g, filp, tty, args[i]))
+ return NULL;
+
+ } else
+ return NULL;
+
+ } else {
+ char buff[256];
+ String *res, tmp(buff, sizeof(buff), &my_charset_bin);
+ Item_basic_constant *pval= (Item_basic_constant *)args[i];
+
+ switch (args[i]->real_type()) {
+ case COND::STRING_ITEM:
+ case COND::INT_ITEM:
+ case COND::REAL_ITEM:
+ case COND::NULL_ITEM:
+ case COND::DECIMAL_ITEM:
+ case COND::DATE_ITEM:
+ case COND::CACHE_ITEM:
+ break;
+ default:
+ return NULL;
+ } // endswitch type
+
+ if ((res= pval->val_str(&tmp)) == NULL)
+ return NULL; // To be clarified
+
+ if (xtrace)
+ htrc("Value=%.*s\n", res->length(), res->ptr());
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (!i && (x || ismul))
+ return NULL;
+
+ if (!x) {
+ // Append the value to the filter
+ if (args[i]->field_type() == MYSQL_TYPE_VARCHAR)
+ strcat(strcat(strcat(body, "'"), res->ptr()), "'");
+ else
+ strncat(body, res->ptr(), res->length());
+
+ } else {
+ if (args[i]->field_type() == MYSQL_TYPE_VARCHAR) {
+ // Add the command to the list
+ PCMD *ncp, cmdp= new(g) CMD(g, (char*)res->ptr());
+
+ for (ncp= &filp->Cmds; *ncp; ncp= &(*ncp)->Next) ;
+
+ *ncp= cmdp;
+ } else
+ return NULL;
+
+ } // endif x
+
+ } // endif
+
+ if (!x) {
+ if (!i)
+ strcat(body, GetValStr(vop, neg));
+ else if (vop == OP_XX && i == 1)
+ strcat(body, " AND ");
+ else if (vop == OP_IN)
+ strcat(body, (i == condf->argument_count() - 1) ? ")" : ",");
+
+ } // endif x
+
+ } // endfor i
+
+ if (x)
+ filp->Op= vop;
+
+ } else {
+ if (xtrace)
+ htrc("Unsupported condition\n");
+
+ return NULL;
+ } // endif's type
+
+ return filp;
+} // end of CheckCond
+
+
+ /**
+ Push condition down to the table handler.
+
+ @param cond Condition to be pushed. The condition tree must not be
+ modified by the caller.
+
+ @return
+ The 'remainder' condition that caller must use to filter out records.
+ NULL means the handler will not return rows that do not match the
+ passed condition.
+
+ @note
+ CONNECT handles the filtering only for table types that construct
+ an SQL or WQL query, but still leaves it to MySQL because only some
+ parts of the filter may be relevant.
+ The first suballocate finds the position where the string will be
+ constructed in the sarea. The second one does make the suballocation
+ with the proper length.
+ */
+const COND *ha_connect::cond_push(const COND *cond)
+{
+ DBUG_ENTER("ha_connect::cond_push");
+
+ if (tdbp) {
+ PGLOBAL& g= xp->g;
+ AMT tty= tdbp->GetAmType();
+ bool x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
+ bool b= (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC ||
+ tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL ||
+ tty == TYPE_AM_PLG || x);
+
+ if (b) {
+ PCFIL filp= (PCFIL)PlugSubAlloc(g, NULL, sizeof(CONDFIL));
+
+ filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
+ *filp->Body= 0;
+ filp->Op= OP_XX;
+ filp->Cmds= NULL;
+
+ if (CheckCond(g, filp, tty, (Item *)cond)) {
+ if (xtrace)
+ htrc("cond_push: %s\n", filp->Body);
+
+ if (!x)
+ PlugSubAlloc(g, NULL, strlen(filp->Body) + 1);
+ else
+ cond= NULL; // Does this work?
+
+ tdbp->SetCondFil(filp);
+ } else if (x && cond)
+ tdbp->SetCondFil(filp); // Wrong filter
+
+ } else
+ tdbp->SetFilter(CondFilter(g, (Item *)cond));
+
+ } // endif tdbp
+
+ // Let MySQL do the filtering
+ DBUG_RETURN(cond);
+} // end of cond_push
+
+/**
+ Number of rows in table. It will only be called if
+ (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
+*/
+ha_rows ha_connect::records()
+{
+ if (!valid_info)
+ info(HA_STATUS_VARIABLE);
+
+ if (tdbp && tdbp->Cardinality(NULL))
+ return stats.records;
+ else
+ return HA_POS_ERROR;
+
+} // end of records
+
+
+/**
+ Return an error message specific to this handler.
+
+ @param error error code previously returned by handler
+ @param buf pointer to String where to add error message
+
+ @return
+ Returns true if this is a temporary error
+*/
+bool ha_connect::get_error_message(int error, String* buf)
+{
+ DBUG_ENTER("ha_connect::get_error_message");
+
+ if (xp && xp->g) {
+ PGLOBAL g= xp->g;
+ char *msg= (char*)PlugSubAlloc(g, NULL, strlen(g->Message) * 3);
+ uint dummy_errors;
+ uint32 len= copy_and_convert(msg, strlen(g->Message) * 3,
+ system_charset_info,
+ g->Message, strlen(g->Message),
+ &my_charset_latin1,
+ &dummy_errors);
+ msg[len]= '\0';
+ buf->copy(msg, (uint)strlen(msg), system_charset_info);
+ } else
+ buf->copy("Cannot retrieve msg", 19, system_charset_info);
+
+ DBUG_RETURN(false);
+} // end of get_error_message
+
+
+/**
+ @brief
+ Used for opening tables. The name will be the name of the file.
+
+ @details
+ A table is opened when it needs to be opened; e.g. when a request comes in
+ for a SELECT on the table (tables are not open and closed for each request,
+ they are cached).
+
+ Called from handler.cc by handler::ha_open(). The server opens all tables by
+ calling ha_open() which then calls the handler specific open().
+
+ @note
+ For CONNECT no open can be done here because field information is not yet
+ updated. >>>>> TO BE CHECKED <<<<<
+ (Thread information could be get by using 'ha_thd')
+
+ @see
+ handler::ha_open() in handler.cc
+*/
+int ha_connect::open(const char *name, int mode, uint test_if_locked)
+{
+ int rc= 0;
+ DBUG_ENTER("ha_connect::open");
+
+ if (xtrace)
+ htrc("open: name=%s mode=%d test=%u\n", name, mode, test_if_locked);
+
+ if (!(share= get_share()))
+ DBUG_RETURN(1);
+
+ thr_lock_data_init(&share->lock,&lock,NULL);
+
+ // Try to get the user if possible
+ xp= GetUser(ha_thd(), xp);
+ PGLOBAL g= (xp) ? xp->g : NULL;
+
+ // Try to set the database environment
+ if (g) {
+ rc= (CntCheckDB(g, this, name)) ? (-2) : 0;
+
+ if (g->Mrr) {
+ // This should only happen for the mrr secondary handler
+ mrr= true;
+ g->Mrr= false;
+ } else
+ mrr= false;
+
+ } else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ DBUG_RETURN(rc);
+} // end of open
+
+/**
+ @brief
+ Make the indexes for this table
+*/
+int ha_connect::optimize(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ int rc= 0;
+ PGLOBAL& g= xp->g;
+ PDBUSER dup= PlgGetUser(g);
+
+ // Ignore error on the opt file
+ dup->Check &= ~CHK_OPT;
+ tdbp= GetTDB(g);
+ dup->Check |= CHK_OPT;
+
+ if (tdbp) {
+ bool b= ((PTDBASE)tdbp)->GetDef()->Indexable();
+
+ if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, true, b))) {
+ if (rc == RC_INFO) {
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ rc= 0;
+ } else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ } // endif rc
+
+ } else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ return rc;
+} // end of optimize
+
+/**
+ @brief
+ Closes a table.
+
+ @details
+ Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is
+ only used to close up temporary tables or during the process where a
+ temporary table is converted over to being a myisam table.
+
+ For sql_base.cc look at close_data_tables().
+
+ @see
+ sql_base.cc, sql_select.cc and table.cc
+*/
+int ha_connect::close(void)
+{
+ int rc= 0;
+ DBUG_ENTER("ha_connect::close");
+
+ // If this is called by a later query, the table may have
+ // been already closed and the tdbp is not valid anymore.
+ if (tdbp && xp->last_query_id == valid_query_id)
+ rc= CloseTable(xp->g);
+
+ DBUG_RETURN(rc);
+} // end of close
+
+
+/**
+ @brief
+ write_row() inserts a row. No extra() hint is given currently if a bulk load
+ is happening. buf() is a byte array of data. You can use the field
+ information to extract the data from the native byte array type.
+
+ @details
+ Example of this would be:
+ @code
+ for (Field **field=table->field ; *field ; field++)
+ {
+ ...
+ }
+ @endcode
+
+ See ha_tina.cc for an example of extracting all of the data as strings.
+ ha_berekly.cc has an example of how to store it intact by "packing" it
+ for ha_berkeley's own native storage type.
+
+ See the note for update_row() on auto_increments and timestamps. This
+ case also applies to write_row().
+
+ Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
+ sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
+
+ @see
+ item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
+ sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
+*/
+int ha_connect::write_row(uchar *buf)
+{
+ int rc= 0;
+ PGLOBAL& g= xp->g;
+ DBUG_ENTER("ha_connect::write_row");
+
+ // This is not tested yet
+ if (xmod == MODE_ALTER)
+ xmod= MODE_INSERT;
+
+ // Open the table if it was not opened yet (locked)
+ if (!IsOpened() || xmod != tdbp->GetMode()) {
+ if (IsOpened())
+ CloseTable(g);
+
+ if ((rc= OpenTable(g)))
+ DBUG_RETURN(rc);
+
+ } // endif isopened
+
+ if (tdbp->GetMode() == MODE_ANY)
+ DBUG_RETURN(0);
+
+#if 0 // AUTO_INCREMENT NIY
+ if (table->next_number_field && buf == table->record[0]) {
+ int error;
+
+ if ((error= update_auto_increment()))
+ return error;
+
+ } // endif nex_number_field
+#endif // 0
+
+ // Set column values from the passed pseudo record
+ if ((rc= ScanRecord(g, buf)))
+ DBUG_RETURN(rc);
+
+ // Return result code from write operation
+ if (CntWriteRow(g, tdbp)) {
+ DBUG_PRINT("write_row", ("%s", g->Message));
+ htrc("write_row: %s\n", g->Message);
+ rc= HA_ERR_INTERNAL_ERROR;
+ } // endif RC
+
+ DBUG_RETURN(rc);
+} // end of write_row
+
+
+/**
+ @brief
+ Yes, update_row() does what you expect, it updates a row. old_data will have
+ the previous row record in it, while new_data will have the newest data in it.
+ Keep in mind that the server can do updates based on ordering if an ORDER BY
+ clause was used. Consecutive ordering is not guaranteed.
+
+ @details
+ Currently new_data will not have an updated auto_increament record, or
+ and updated timestamp field. You can do these for example by doing:
+ @code
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+ table->timestamp_field->set_time();
+ if (table->next_number_field && record == table->record[0])
+ update_auto_increment();
+ @endcode
+
+ Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
+
+ @see
+ sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
+*/
+int ha_connect::update_row(const uchar *old_data, uchar *new_data)
+{
+ int rc= 0;
+ PGLOBAL& g= xp->g;
+ DBUG_ENTER("ha_connect::update_row");
+
+ if (xtrace > 1)
+ htrc("update_row: old=%s new=%s\n", old_data, new_data);
+
+ // Check values for possible change in indexed column
+ if ((rc= CheckRecord(g, old_data, new_data)))
+ return rc;
+
+ if (CntUpdateRow(g, tdbp)) {
+ DBUG_PRINT("update_row", ("%s", g->Message));
+ htrc("update_row CONNECT: %s\n", g->Message);
+ rc= HA_ERR_INTERNAL_ERROR;
+ } // endif RC
+
+ DBUG_RETURN(rc);
+} // end of update_row
+
+
+/**
+ @brief
+ This will delete a row. buf will contain a copy of the row to be deleted.
+ The server will call this right after the current row has been called (from
+ either a previous rnd_nexT() or index call).
+
+ @details
+ If you keep a pointer to the last row or can access a primary key it will
+ make doing the deletion quite a bit easier. Keep in mind that the server does
+ not guarantee consecutive deletions. ORDER BY clauses can be used.
+
+ Called in sql_acl.cc and sql_udf.cc to manage internal table
+ information. Called in sql_delete.cc, sql_insert.cc, and
+ sql_select.cc. In sql_select it is used for removing duplicates
+ while in insert it is used for REPLACE calls.
+
+ @see
+ sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc
+*/
+int ha_connect::delete_row(const uchar *buf)
+{
+ int rc= 0;
+ DBUG_ENTER("ha_connect::delete_row");
+
+ if (CntDeleteRow(xp->g, tdbp, false)) {
+ rc= HA_ERR_INTERNAL_ERROR;
+ htrc("delete_row CONNECT: %s\n", xp->g->Message);
+ } // endif DeleteRow
+
+ DBUG_RETURN(rc);
+} // end of delete_row
+
+
+/****************************************************************************/
+/* We seem to come here at the begining of an index use. */
+/****************************************************************************/
+int ha_connect::index_init(uint idx, bool sorted)
+{
+ int rc;
+ PGLOBAL& g= xp->g;
+ DBUG_ENTER("index_init");
+
+ if (xtrace)
+ htrc("index_init: this=%p idx=%u sorted=%d\n", this, idx, sorted);
+
+ if ((rc= rnd_init(0)))
+ return rc;
+
+ if (locked == 2) {
+ // Indexes are not updated in lock write mode
+ active_index= MAX_KEY;
+ indexing= 0;
+ DBUG_RETURN(0);
+ } // endif locked
+
+ indexing= CntIndexInit(g, tdbp, (signed)idx);
+
+ if (indexing <= 0) {
+ DBUG_PRINT("index_init", ("%s", g->Message));
+ htrc("index_init CONNECT: %s\n", g->Message);
+ active_index= MAX_KEY;
+ rc= HA_ERR_INTERNAL_ERROR;
+ } else {
+ if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) {
+ if (((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
+ ((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g);
+
+ active_index= idx;
+ } else // Void table
+ indexing= 0;
+
+ rc= 0;
+ } // endif indexing
+
+ if (xtrace)
+ htrc("index_init: rc=%d indexing=%d active_index=%d\n",
+ rc, indexing, active_index);
+
+ DBUG_RETURN(rc);
+} // end of index_init
+
+/****************************************************************************/
+/* We seem to come here at the end of an index use. */
+/****************************************************************************/
+int ha_connect::index_end()
+{
+ DBUG_ENTER("index_end");
+ active_index= MAX_KEY;
+ ds_mrr.dsmrr_close();
+ DBUG_RETURN(rnd_end());
+} // end of index_end
+
+
+/****************************************************************************/
+/* This is internally called by all indexed reading functions. */
+/****************************************************************************/
+int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len)
+{
+ int rc;
+
+//statistic_increment(ha_read_key_count, &LOCK_status);
+
+ switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) {
+ case RC_OK:
+ xp->fnd++;
+ rc= MakeRecord((char*)buf);
+ break;
+ case RC_EF: // End of file
+ rc= HA_ERR_END_OF_FILE;
+ break;
+ case RC_NF: // Not found
+ xp->nfd++;
+ rc= (op == OP_SAME) ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
+ break;
+ default: // Read error
+ DBUG_PRINT("ReadIndexed", ("%s", xp->g->Message));
+ htrc("ReadIndexed: %s\n", xp->g->Message);
+ rc= HA_ERR_INTERNAL_ERROR;
+ break;
+ } // endswitch RC
+
+ if (xtrace > 1)
+ htrc("ReadIndexed: op=%d rc=%d\n", op, rc);
+
+ table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND;
+ return rc;
+} // end of ReadIndexed
+
+
+#ifdef NOT_USED
+/**
+ @brief
+ Positions an index cursor to the index specified in the handle. Fetches the
+ row if available. If the key value is null, begin at the first key of the
+ index.
+*/
+int ha_connect::index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map __attribute__((unused)),
+ enum ha_rkey_function find_flag
+ __attribute__((unused)))
+{
+ DBUG_ENTER("ha_connect::index_read");
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+}
+#endif // NOT_USED
+
+
+/****************************************************************************/
+/* This is called by handler::index_read_map. */
+/****************************************************************************/
+int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
+ enum ha_rkey_function find_flag)
+{
+ int rc;
+ OPVAL op= OP_XX;
+ DBUG_ENTER("ha_connect::index_read");
+
+ switch(find_flag) {
+ case HA_READ_KEY_EXACT: op= OP_EQ; break;
+ case HA_READ_AFTER_KEY: op= OP_GT; break;
+ case HA_READ_KEY_OR_NEXT: op= OP_GE; break;
+ default: DBUG_RETURN(-1); break;
+ } // endswitch find_flag
+
+ if (xtrace > 1)
+ htrc("%p index_read: op=%d\n", this, op);
+
+ if (indexing > 0)
+ rc= ReadIndexed(buf, op, key, key_len);
+ else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ DBUG_RETURN(rc);
+} // end of index_read
+
+
+/**
+ @brief
+ Used to read forward through the index.
+*/
+int ha_connect::index_next(uchar *buf)
+{
+ int rc;
+ DBUG_ENTER("ha_connect::index_next");
+ //statistic_increment(ha_read_next_count, &LOCK_status);
+
+ if (indexing > 0)
+ rc= ReadIndexed(buf, OP_NEXT);
+ else if (!indexing)
+ rc= rnd_next(buf);
+ else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ DBUG_RETURN(rc);
+} // end of index_next
+
+
+#ifdef NOT_USED
+/**
+ @brief
+ Used to read backwards through the index.
+*/
+int ha_connect::index_prev(uchar *buf)
+{
+ DBUG_ENTER("ha_connect::index_prev");
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+}
+#endif // NOT_USED
+
+
+/**
+ @brief
+ index_first() asks for the first key in the index.
+
+ @details
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
+
+ @see
+ opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
+*/
+int ha_connect::index_first(uchar *buf)
+{
+ int rc;
+ DBUG_ENTER("ha_connect::index_first");
+
+ if (indexing > 0)
+ rc= ReadIndexed(buf, OP_FIRST);
+ else if (indexing < 0)
+ rc= HA_ERR_INTERNAL_ERROR;
+ else if (CntRewindTable(xp->g, tdbp)) {
+ table->status= STATUS_NOT_FOUND;
+ rc= HA_ERR_INTERNAL_ERROR;
+ } else
+ rc= rnd_next(buf);
+
+ DBUG_RETURN(rc);
+} // end of index_first
+
+
+#ifdef NOT_USED
+/**
+ @brief
+ index_last() asks for the last key in the index.
+
+ @details
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
+
+ @see
+ opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
+*/
+int ha_connect::index_last(uchar *buf)
+{
+ DBUG_ENTER("ha_connect::index_last");
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+}
+#endif // NOT_USED
+
+
+/****************************************************************************/
+/* This is called to get more rows having the same index value. */
+/****************************************************************************/
+int ha_connect::index_next_same(uchar *buf, const uchar *key, uint keylen)
+{
+ int rc;
+ DBUG_ENTER("ha_connect::index_next_same");
+//statistic_increment(ha_read_next_count, &LOCK_status);
+
+ if (!indexing)
+ rc= rnd_next(buf);
+ else if (indexing > 0)
+ rc= ReadIndexed(buf, OP_SAME);
+ else
+ rc= HA_ERR_INTERNAL_ERROR;
+
+ DBUG_RETURN(rc);
+} // end of index_next_same
+
+
+/**
+ @brief
+ rnd_init() is called when the system wants the storage engine to do a table
+ scan. See the example in the introduction at the top of this file to see when
+ rnd_init() is called.
+
+ @details
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
+ and sql_update.cc.
+
+ @note
+ We always call open and extern_lock/start_stmt before comming here.
+
+ @see
+ filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
+*/
+int ha_connect::rnd_init(bool scan)
+{
+ int rc;
+ PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use, xp) :
+ (xp) ? xp->g : NULL);
+ DBUG_ENTER("ha_connect::rnd_init");
+
+ // This is not tested yet
+ if (xmod == MODE_ALTER) {
+ xmod= MODE_READ;
+ alter= 1;
+ } // endif xmod
+
+ if (xtrace)
+ htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
+ this, scan, xmod, alter);
+
+ if (!g || !table || xmod == MODE_INSERT)
+ DBUG_RETURN(HA_ERR_INITIALIZATION);
+
+ // Do not close the table if it was opened yet (locked?)
+ if (IsOpened())
+ DBUG_RETURN(0);
+// CloseTable(g); Was done before making things done twice
+ else if (xp->CheckQuery(valid_query_id))
+ tdbp= NULL; // Not valid anymore
+
+ // When updating, to avoid skipped update, force the table
+ // handler to retrieve write-only fields to be able to compare
+ // records and detect data change.
+ if (xmod == MODE_UPDATE)
+ bitmap_union(table->read_set, table->write_set);
+
+ if ((rc= OpenTable(g, xmod == MODE_DELETE)))
+ DBUG_RETURN(rc);
+
+ xp->nrd= xp->fnd= xp->nfd= 0;
+ xp->tb1= my_interval_timer();
+ DBUG_RETURN(0);
+} // end of rnd_init
+
+/**
+ @brief
+ Not described.
+
+ @note
+ The previous version said:
+ Stop scanning of table. Note that this may be called several times during
+ execution of a sub select.
+ =====> This has been moved to external lock to avoid closing subselect tables.
+*/
+int ha_connect::rnd_end()
+{
+ int rc= 0;
+ DBUG_ENTER("ha_connect::rnd_end");
+
+ // If this is called by a later query, the table may have
+ // been already closed and the tdbp is not valid anymore.
+// if (tdbp && xp->last_query_id == valid_query_id)
+// rc= CloseTable(xp->g);
+
+ ds_mrr.dsmrr_close();
+ DBUG_RETURN(rc);
+} // end of rnd_end
+
+
+/**
+ @brief
+ This is called for each row of the table scan. When you run out of records
+ you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
+ The Field structure for the table is the key to getting data into buf
+ in a manner that will allow the server to understand it.
+
+ @details
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
+ and sql_update.cc.
+
+ @see
+ filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
+*/
+int ha_connect::rnd_next(uchar *buf)
+{
+ int rc;
+ DBUG_ENTER("ha_connect::rnd_next");
+//statistic_increment(ha_read_rnd_next_count, &LOCK_status);
+
+ if (tdbp->GetMode() == MODE_ANY) {
+ // We will stop on next read
+ if (!stop) {
+ stop= true;
+ DBUG_RETURN(RC_OK);
+ } else
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
+ } // endif Mode
+
+ switch (CntReadNext(xp->g, tdbp)) {
+ case RC_OK:
+ rc= MakeRecord((char*)buf);
+ break;
+ case RC_EF: // End of file
+ rc= HA_ERR_END_OF_FILE;
+ break;
+ case RC_NF: // Not found
+ rc= HA_ERR_RECORD_DELETED;
+ break;
+ default: // Read error
+ htrc("rnd_next CONNECT: %s\n", xp->g->Message);
+ rc= (records()) ? HA_ERR_INTERNAL_ERROR : HA_ERR_END_OF_FILE;
+ break;
+ } // endswitch RC
+
+ if (xtrace > 1 && (rc || !(xp->nrd++ % 16384))) {
+ ulonglong tb2= my_interval_timer();
+ double elapsed= (double) (tb2 - xp->tb1) / 1000000000ULL;
+ DBUG_PRINT("rnd_next", ("rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
+ rc, (uint)xp->nrd, (uint)xp->fnd,
+ (uint)xp->nfd, elapsed));
+ htrc("rnd_next: rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
+ rc, (uint)xp->nrd, (uint)xp->fnd,
+ (uint)xp->nfd, elapsed);
+ xp->tb1= tb2;
+ xp->fnd= xp->nfd= 0;
+ } // endif nrd
+
+ table->status= (!rc) ? 0 : STATUS_NOT_FOUND;
+ DBUG_RETURN(rc);
+} // end of rnd_next
+
+
+/**
+ @brief
+ position() is called after each call to rnd_next() if the data needs
+ to be ordered. You can do something like the following to store
+ the position:
+ @code
+ my_store_ptr(ref, ref_length, current_position);
+ @endcode
+
+ @details
+ The server uses ref to store data. ref_length in the above case is
+ the size needed to store current_position. ref is just a byte array
+ that the server will maintain. If you are using offsets to mark rows, then
+ current_position should be the offset. If it is a primary key like in
+ BDB, then it needs to be a primary key.
+
+ Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
+
+ @see
+ filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
+*/
+void ha_connect::position(const uchar *record)
+{
+ DBUG_ENTER("ha_connect::position");
+//if (((PTDBASE)tdbp)->GetDef()->Indexable())
+ my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos());
+ DBUG_VOID_RETURN;
+} // end of position
+
+
+/**
+ @brief
+ This is like rnd_next, but you are given a position to use
+ to determine the row. The position will be of the type that you stored in
+ ref. You can use my_get_ptr(pos,ref_length) to retrieve whatever key
+ or position you saved when position() was called.
+
+ @details
+ Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc.
+
+ @note
+ Is this really useful? It was never called even when sorting.
+
+ @see
+ filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
+*/
+int ha_connect::rnd_pos(uchar *buf, uchar *pos)
+{
+ int rc;
+ PTDBASE tp= (PTDBASE)tdbp;
+ DBUG_ENTER("ha_connect::rnd_pos");
+
+ if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length)))
+ rc= rnd_next(buf);
+ else
+ rc= HA_ERR_KEY_NOT_FOUND;
+
+ DBUG_RETURN(rc);
+} // end of rnd_pos
+
+
+/**
+ @brief
+ ::info() is used to return information to the optimizer. See my_base.h for
+ the complete description.
+
+ @details
+ Currently this table handler doesn't implement most of the fields really needed.
+ SHOW also makes use of this data.
+
+ You will probably want to have the following in your code:
+ @code
+ if (records < 2)
+ records= 2;
+ @endcode
+ The reason is that the server will optimize for cases of only a single
+ record. If, in a table scan, you don't know the number of records, it
+ will probably be better to set records to two so you can return as many
+ records as you need. Along with records, a few more variables you may wish
+ to set are:
+ records
+ deleted
+ data_file_length
+ index_file_length
+ delete_length
+ check_time
+ Take a look at the public variables in handler.h for more information.
+
+ Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
+ sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
+ sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc,
+ sql_table.cc, sql_union.cc, and sql_update.cc.
+
+ @see
+ filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc,
+ sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc,
+ sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc,
+ sql_union.cc and sql_update.cc
+*/
+int ha_connect::info(uint flag)
+{
+ bool pure= false;
+ PGLOBAL g= GetPlug((table) ? table->in_use : NULL, xp);
+
+ DBUG_ENTER("ha_connect::info");
+
+ if (xtrace)
+ htrc("%p In info: flag=%u valid_info=%d\n", this, flag, valid_info);
+
+ if (!valid_info) {
+ // tdbp must be available to get updated info
+ if (xp->CheckQuery(valid_query_id) || !tdbp) {
+ if (xmod == MODE_ANY || xmod == MODE_ALTER) {
+ // Pure info, not a query
+ pure= true;
+ xp->CheckCleanup();
+ } // endif xmod
+
+ tdbp= GetTDB(g);
+ } // endif tdbp
+
+ valid_info= CntInfo(g, tdbp, &xinfo);
+ } // endif valid_info
+
+ if (flag & HA_STATUS_VARIABLE) {
+ stats.records= xinfo.records;
+ stats.deleted= 0;
+ stats.data_file_length= xinfo.data_file_length;
+ stats.index_file_length= 0;
+ stats.delete_length= 0;
+ stats.check_time= 0;
+ stats.mean_rec_length= xinfo.mean_rec_length;
+ } // endif HA_STATUS_VARIABLE
+
+ if (flag & HA_STATUS_CONST) {
+ // This is imported from the previous handler and must be reconsidered
+ stats.max_data_file_length= 4294967295;
+ stats.max_index_file_length= 4398046510080;
+ stats.create_time= 0;
+ data_file_name= xinfo.data_file_name;
+ index_file_name= NULL;
+// sortkey= (uint) - 1; // Table is not sorted
+ ref_length= sizeof(int); // Pointer size to row
+ table->s->db_options_in_use= 03;
+ stats.block_size= 1024;
+ table->s->keys_in_use.set_prefix(table->s->keys);
+ table->s->keys_for_keyread= table->s->keys_in_use;
+// table->s->keys_for_keyread.subtract(table->s->read_only_keys);
+ table->s->db_record_offset= 0;
+ } // endif HA_STATUS_CONST
+
+ if (flag & HA_STATUS_ERRKEY) {
+ errkey= 0;
+ } // endif HA_STATUS_ERRKEY
+
+ if (flag & HA_STATUS_TIME)
+ stats.update_time= 0;
+
+ if (flag & HA_STATUS_AUTO)
+ stats.auto_increment_value= 1;
+
+ if (tdbp && pure)
+ CloseTable(g); // Not used anymore
+
+ DBUG_RETURN(0);
+} // end of info
+
+
+/**
+ @brief
+ extra() is called whenever the server wishes to send a hint to
+ the storage engine. The myisam engine implements the most hints.
+ ha_innodb.cc has the most exhaustive list of these hints.
+
+ @note
+ This is not yet implemented for CONNECT.
+
+ @see
+ ha_innodb.cc
+*/
+int ha_connect::extra(enum ha_extra_function operation)
+{
+ DBUG_ENTER("ha_connect::extra");
+ DBUG_RETURN(0);
+} // end of extra
+
+
+/**
+ @brief
+ Used to delete all rows in a table, including cases of truncate and cases where
+ the optimizer realizes that all rows will be removed as a result of an SQL statement.
+
+ @details
+ Called from item_sum.cc by Item_func_group_concat::clear(),
+ Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
+ Called from sql_delete.cc by mysql_delete().
+ Called from sql_select.cc by JOIN::reinit().
+ Called from sql_union.cc by st_select_lex_unit::exec().
+
+ @see
+ Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
+ Item_func_group_concat::clear() in item_sum.cc;
+ mysql_delete() in sql_delete.cc;
+ JOIN::reinit() in sql_select.cc and
+ st_select_lex_unit::exec() in sql_union.cc.
+*/
+int ha_connect::delete_all_rows()
+{
+ int rc= 0;
+ PGLOBAL g= xp->g;
+ DBUG_ENTER("ha_connect::delete_all_rows");
+
+ if (tdbp && tdbp->GetUse() == USE_OPEN &&
+ tdbp->GetAmType() != TYPE_AM_XML &&
+ ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
+ // Close and reopen the table so it will be deleted
+ rc= CloseTable(g);
+
+ if (!(rc= OpenTable(g))) {
+ if (CntDeleteRow(g, tdbp, true)) {
+ htrc("%s\n", g->Message);
+ rc= HA_ERR_INTERNAL_ERROR;
+ } // endif
+
+ } // endif rc
+
+ DBUG_RETURN(rc);
+} // end of delete_all_rows
+
+
+bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
+{
+ const char *db= (dbn && *dbn) ? dbn : NULL;
+ TABTYPE type=GetRealType(options);
+
+ switch (type) {
+ case TAB_UNDEF:
+// case TAB_CATLG:
+ case TAB_PLG:
+ case TAB_JCT:
+ case TAB_DMY:
+ case TAB_NIY:
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Unsupported table type %s", MYF(0), options->type);
+ return true;
+
+ case TAB_DOS:
+ case TAB_FIX:
+ case TAB_BIN:
+ case TAB_CSV:
+ case TAB_FMT:
+ case TAB_DBF:
+ case TAB_XML:
+ case TAB_INI:
+ case TAB_VEC:
+ if (options->filename && *options->filename) {
+ char *s, path[FN_REFLEN], dbpath[FN_REFLEN];
+#if defined(WIN32)
+ s= "\\";
+#else // !WIN32
+ s= "/";
+#endif // !WIN32
+ strcpy(dbpath, mysql_real_data_home);
+
+ if (db)
+ strcat(strcat(dbpath, db), s);
+
+ (void) fn_format(path, options->filename, dbpath, "",
+ MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
+
+ if (!is_secure_file_path(path)) {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
+ return true;
+ } // endif path
+
+ } else
+ return false;
+
+ /* Fall through to check FILE_ACL */
+ case TAB_ODBC:
+ case TAB_MYSQL:
+ case TAB_DIR:
+ case TAB_MAC:
+ case TAB_WMI:
+ case TAB_OEM:
+ return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
+
+ // This is temporary until a solution is found
+ case TAB_TBL:
+ case TAB_XCL:
+ case TAB_PRX:
+ case TAB_OCCUR:
+ case TAB_PIVOT:
+ return false;
+ } // endswitch type
+
+ my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
+ return true;
+} // end of check_privileges
+
+// Check that two indexes are equivalent
+bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
+{
+ bool b= true;
+ PKPDEF kp1, kp2;
+
+ if (stricmp(xp1->Name, xp2->Name))
+ b= false;
+ else if (xp1->Nparts != xp2->Nparts ||
+ xp1->MaxSame != xp2->MaxSame ||
+ xp1->Unique != xp2->Unique)
+ b= false;
+ else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts;
+ b && (kp1 || kp2);
+ kp1= kp1->Next, kp2= kp2->Next)
+ if (!kp1 || !kp2)
+ b= false;
+ else if (stricmp(kp1->Name, kp2->Name))
+ b= false;
+ else if (kp1->Klen != kp2->Klen)
+ b= false;
+
+ return b;
+} // end of IsSameIndex
+
+MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
+ MODE newmode, bool *chk, bool *cras)
+{
+ if (xtrace) {
+ LEX_STRING *query_string= thd_query_string(thd);
+ htrc("%p check_mode: cmdtype=%d\n", this, thd_sql_command(thd));
+ htrc("Cmd=%.*s\n", (int) query_string->length, query_string->str);
+ } // endif xtrace
+
+ // Next code is temporarily replaced until sql_command is set
+ stop= false;
+
+ if (newmode == MODE_WRITE) {
+ switch (thd_sql_command(thd)) {
+ case SQLCOM_LOCK_TABLES:
+ locked= 2;
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_INSERT:
+ case SQLCOM_LOAD:
+ case SQLCOM_INSERT_SELECT:
+ newmode= MODE_INSERT;
+ break;
+// case SQLCOM_REPLACE:
+// case SQLCOM_REPLACE_SELECT:
+// newmode= MODE_UPDATE; // To be checked
+// break;
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_TRUNCATE:
+ newmode= MODE_DELETE;
+ break;
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ newmode= MODE_UPDATE;
+ break;
+ case SQLCOM_SELECT:
+ case SQLCOM_OPTIMIZE:
+ newmode= MODE_READ;
+ break;
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_RENAME_TABLE:
+ newmode= MODE_ANY;
+ break;
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_CREATE_INDEX:
+ newmode= MODE_ANY;
+// stop= true;
+ break;
+ case SQLCOM_CREATE_VIEW:
+ case SQLCOM_DROP_VIEW:
+ newmode= MODE_ANY;
+ break;
+ case SQLCOM_ALTER_TABLE:
+ newmode= MODE_ALTER;
+ break;
+ default:
+ htrc("Unsupported sql_command=%d", thd_sql_command(thd));
+ strcpy(g->Message, "CONNECT Unsupported command");
+ my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
+ newmode= MODE_ERROR;
+ break;
+ } // endswitch newmode
+
+ } else if (newmode == MODE_READ) {
+ switch (thd_sql_command(thd)) {
+ case SQLCOM_CREATE_TABLE:
+ *chk= true;
+ *cras= true;
+ case SQLCOM_INSERT:
+ case SQLCOM_LOAD:
+ case SQLCOM_INSERT_SELECT:
+// case SQLCOM_REPLACE:
+// case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_TRUNCATE:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_SELECT:
+ case SQLCOM_OPTIMIZE:
+ break;
+ case SQLCOM_LOCK_TABLES:
+ locked= 1;
+ break;
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_CREATE_INDEX:
+ *chk= true;
+// stop= true;
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_RENAME_TABLE:
+ newmode= MODE_ANY;
+ break;
+ case SQLCOM_CREATE_VIEW:
+ case SQLCOM_DROP_VIEW:
+ newmode= MODE_ANY;
+ break;
+ case SQLCOM_ALTER_TABLE:
+ *chk= true;
+ newmode= MODE_ALTER;
+ break;
+ default:
+ htrc("Unsupported sql_command=%d", thd_sql_command(thd));
+ strcpy(g->Message, "CONNECT Unsupported command");
+ my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
+ newmode= MODE_ERROR;
+ break;
+ } // endswitch newmode
+
+ } // endif's newmode
+
+ if (xtrace)
+ htrc("New mode=%d\n", newmode);
+
+ return newmode;
+} // end of check_mode
+
+int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
+{
+ int rc= 0;
+ bool chk=false, cras= false;
+ MODE newmode;
+ PGLOBAL g= GetPlug(thd, xp);
+ DBUG_ENTER("ha_connect::start_stmt");
+
+ // Action will depend on lock_type
+ switch (lock_type) {
+ case TL_WRITE_ALLOW_WRITE:
+ case TL_WRITE_CONCURRENT_INSERT:
+ case TL_WRITE_DELAYED:
+ case TL_WRITE_DEFAULT:
+ case TL_WRITE_LOW_PRIORITY:
+ case TL_WRITE:
+ case TL_WRITE_ONLY:
+ newmode= MODE_WRITE;
+ break;
+ case TL_READ:
+ case TL_READ_WITH_SHARED_LOCKS:
+ case TL_READ_HIGH_PRIORITY:
+ case TL_READ_NO_INSERT:
+ case TL_READ_DEFAULT:
+ newmode= MODE_READ;
+ break;
+ case TL_UNLOCK:
+ default:
+ newmode= MODE_ANY;
+ break;
+ } // endswitch mode
+
+ xmod= CheckMode(g, thd, newmode, &chk, &cras);
+ DBUG_RETURN((xmod == MODE_ERROR) ? HA_ERR_INTERNAL_ERROR : 0);
+} // end of start_stmt
+
+/**
+ @brief
+ This create a lock on the table. If you are implementing a storage engine
+ that can handle transacations look at ha_berkely.cc to see how you will
+ want to go about doing this. Otherwise you should consider calling flock()
+ here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
+ this.
+
+ @details
+ Called from lock.cc by lock_external() and unlock_external(). Also called
+ from sql_table.cc by copy_data_between_tables().
+
+ @note
+ Following what we did in the MySQL XDB handler, we use this call to actually
+ physically open the table. This could be reconsider when finalizing this handler
+ design, which means we have a better understanding of what MariaDB does.
+
+ @see
+ lock.cc by lock_external() and unlock_external() in lock.cc;
+ the section "locking functions for mysql" in lock.cc;
+ copy_data_between_tables() in sql_table.cc.
+*/
+int ha_connect::external_lock(THD *thd, int lock_type)
+{
+ int rc= 0;
+ bool xcheck=false, cras= false;
+ MODE newmode;
+ PTOS options= GetTableOptionStruct(table);
+ PGLOBAL g= GetPlug(thd, xp);
+ DBUG_ENTER("ha_connect::external_lock");
+
+ DBUG_ASSERT(thd == current_thd);
+
+ if (xtrace)
+ htrc("external_lock: this=%p thd=%p xp=%p g=%p lock_type=%d\n",
+ this, thd, xp, g, lock_type);
+
+ if (!g)
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+
+ // Action will depend on lock_type
+ switch (lock_type) {
+ case F_WRLCK:
+ newmode= MODE_WRITE;
+ break;
+ case F_RDLCK:
+ newmode= MODE_READ;
+ break;
+ case F_UNLCK:
+ default:
+ newmode= MODE_ANY;
+ break;
+ } // endswitch mode
+
+ if (newmode == MODE_ANY) {
+ int sqlcom= thd_sql_command(thd);
+
+ // This is unlocking, do it by closing the table
+ if (xp->CheckQueryID() && sqlcom != SQLCOM_UNLOCK_TABLES
+ && sqlcom != SQLCOM_LOCK_TABLES)
+ rc= 2; // Logical error ???
+// else if (g->Xchk && (sqlcom == SQLCOM_CREATE_INDEX ||
+// sqlcom == SQLCOM_DROP_INDEX)) {
+ else if (g->Xchk) {
+ if (!tdbp) {
+ if (!(tdbp= GetTDB(g)))
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ else if (!((PTDBASE)tdbp)->GetDef()->Indexable()) {
+ sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
+// DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ DBUG_RETURN(0);
+ } // endif Indexable
+
+ bool oldsep= ((PCHK)g->Xchk)->oldsep;
+ bool newsep= ((PCHK)g->Xchk)->newsep;
+ PTDBDOS tdp= (PTDBDOS)tdbp;
+
+ PDOSDEF ddp= (PDOSDEF)tdp->GetDef();
+ PIXDEF xp, xp1, xp2, drp=NULL, adp= NULL;
+ PIXDEF oldpix= ((PCHK)g->Xchk)->oldpix;
+ PIXDEF newpix= ((PCHK)g->Xchk)->newpix;
+ PIXDEF *xlst, *xprc;
+
+ ddp->SetIndx(oldpix);
+
+ if (oldsep != newsep) {
+ // All indexes have to be remade
+ ddp->DeleteIndexFile(g, NULL);
+ oldpix= NULL;
+ ddp->SetIndx(NULL);
+ SetBooleanOption("Sepindex", newsep);
+ } else if (newsep) {
+ // Make the list of dropped indexes
+ xlst= &drp; xprc= &oldpix;
+
+ for (xp2= oldpix; xp2; xp2= xp) {
+ for (xp1= newpix; xp1; xp1= xp1->Next)
+ if (IsSameIndex(xp1, xp2))
+ break; // Index not to drop
+
+ xp= xp2->GetNext();
+
+ if (!xp1) {
+ *xlst= xp2;
+ *xprc= xp;
+ *(xlst= &xp2->Next)= NULL;
+ } else
+ xprc= &xp2->Next;
+
+ } // endfor xp2
+
+ if (drp) {
+ // Here we erase the index files
+ ddp->DeleteIndexFile(g, drp);
+ } // endif xp1
+
+ } else if (oldpix) {
+ // TODO: optimize the case of just adding new indexes
+ if (!newpix)
+ ddp->DeleteIndexFile(g, NULL);
+
+ oldpix= NULL; // To remake all indexes
+ ddp->SetIndx(NULL);
+ } // endif sepindex
+
+ // Make the list of new created indexes
+ xlst= &adp; xprc= &newpix;
+
+ for (xp1= newpix; xp1; xp1= xp) {
+ for (xp2= oldpix; xp2; xp2= xp2->Next)
+ if (IsSameIndex(xp1, xp2))
+ break; // Index already made
+
+ xp= xp1->Next;
+
+ if (!xp2) {
+ *xlst= xp1;
+ *xprc= xp;
+ *(xlst= &xp1->Next)= NULL;
+ } else
+ xprc= &xp1->Next;
+
+ } // endfor xp1
+
+ if (adp)
+ // Here we do make the new indexes
+ if (tdp->MakeIndex(g, adp, true) == RC_FX) {
+ // Make it a warning to avoid crash
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ 0, g->Message);
+ rc= 0;
+ } // endif MakeIndex
+
+ } // endif Tdbp
+
+ } // endelse Xchk
+
+ if (CloseTable(g)) {
+ // This is an error while builing index
+ // Make it a warning to avoid crash
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ rc= 0;
+ } // endif Close
+
+ locked= 0;
+ DBUG_RETURN(rc);
+ } // endif MODE_ANY
+
+ DBUG_ASSERT(table && table->s);
+
+ if (check_privileges(thd, options, table->s->db.str)) {
+ strcpy(g->Message, "This operation requires the FILE privilege");
+ htrc("%s\n", g->Message);
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif check_privileges
+
+ // Table mode depends on the query type
+ newmode= CheckMode(g, thd, newmode, &xcheck, &cras);
+
+ if (newmode == MODE_ERROR)
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+
+ // If this is the start of a new query, cleanup the previous one
+ if (xp->CheckCleanup()) {
+ tdbp= NULL;
+ valid_info= false;
+ } // endif CheckCleanup
+
+#if 0
+ if (xcheck) {
+ // This must occur after CheckCleanup
+ if (!g->Xchk) {
+ g->Xchk= new(g) XCHK;
+ ((PCHK)g->Xchk)->oldsep= GetBooleanOption("Sepindex", false);
+ ((PCHK)g->Xchk)->oldpix= GetIndexInfo();
+ } // endif Xchk
+
+ } else
+ g->Xchk= NULL;
+#endif // 0
+
+ if (cras)
+ g->Createas= 1; // To tell created table to ignore FLAG
+
+ if (xtrace) {
+#if 0
+ htrc("xcheck=%d cras=%d\n", xcheck, cras);
+
+ if (xcheck)
+ htrc("oldsep=%d oldpix=%p\n",
+ ((PCHK)g->Xchk)->oldsep, ((PCHK)g->Xchk)->oldpix);
+#endif // 0
+ htrc("Calling CntCheckDB db=%s cras=%d\n", GetDBName(NULL), cras);
+ } // endif xtrace
+
+ // Set or reset the good database environment
+ if (CntCheckDB(g, this, GetDBName(NULL))) {
+ htrc("%p external_lock: %s\n", this, g->Message);
+ rc= HA_ERR_INTERNAL_ERROR;
+ // This can NOT be called without open called first, but
+ // the table can have been closed since then
+ } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) {
+ if (tdbp) {
+ // If this is called by a later query, the table may have
+ // been already closed and the tdbp is not valid anymore.
+ if (xp->last_query_id == valid_query_id)
+ rc= CloseTable(g);
+ else
+ tdbp= NULL;
+
+ } // endif tdbp
+
+ xmod= newmode;
+
+ // Delay open until used fields are known
+ } // endif tdbp
+
+ if (xtrace)
+ htrc("external_lock: rc=%d\n", rc);
+
+ DBUG_RETURN(rc);
+} // end of external_lock
+
+
+/**
+ @brief
+ The idea with handler::store_lock() is: The statement decides which locks
+ should be needed for the table. For updates/deletes/inserts we get WRITE
+ locks, for SELECT... we get read locks.
+
+ @details
+ Before adding the lock into the table lock handler (see thr_lock.c),
+ mysqld calls store lock with the requested locks. Store lock can now
+ modify a write lock to a read lock (or some other lock), ignore the
+ lock (if we don't want to use MySQL table locks at all), or add locks
+ for many tables (like we do when we are using a MERGE handler).
+
+ Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE
+ (which signals that we are doing WRITES, but are still allowing other
+ readers and writers).
+
+ When releasing locks, store_lock() is also called. In this case one
+ usually doesn't have to do anything.
+
+ In some exceptional cases MySQL may send a request for a TL_IGNORE;
+ This means that we are requesting the same lock as last time and this
+ should also be ignored. (This may happen when someone does a flush
+ table when we have opened a part of the tables, in which case mysqld
+ closes and reopens the tables and tries to get the same locks at last
+ time). In the future we will probably try to remove this.
+
+ Called from lock.cc by get_lock_data().
+
+ @note
+ In this method one should NEVER rely on table->in_use, it may, in fact,
+ refer to a different thread! (this happens if get_lock_data() is called
+ from mysql_lock_abort_for_thread() function)
+
+ @see
+ get_lock_data() in lock.cc
+*/
+THR_LOCK_DATA **ha_connect::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
+ lock.type=lock_type;
+ *to++ = &lock;
+ return to;
+}
+
+
+/**
+ Searches for a pointer to the last occurrence of the
+ character c in the string src.
+ Returns true on failure, false on success.
+*/
+static bool
+strnrchr(LEX_CSTRING *ls, const char *src, size_t length, int c)
+{
+ const char *srcend, *s;
+ for (s= srcend= src + length; s > src; s--)
+ {
+ if (s[-1] == c)
+ {
+ ls->str= s;
+ ls->length= srcend - s;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/**
+ Split filename into database and table name.
+*/
+static bool
+filename_to_dbname_and_tablename(const char *filename,
+ char *database, size_t database_size,
+ char *table, size_t table_size)
+{
+#if defined(WIN32)
+ char slash= '\\';
+#else // !WIN32
+ char slash= '/';
+#endif // !WIN32
+ LEX_CSTRING d, t;
+ size_t length= strlen(filename);
+
+ /* Find filename - the rightmost directory part */
+ if (strnrchr(&t, filename, length, slash) || t.length + 1 > table_size)
+ return true;
+ memcpy(table, t.str, t.length);
+ table[t.length]= '\0';
+ if (!(length-= t.length))
+ return true;
+
+ length--; /* Skip slash */
+
+ /* Find database name - the second rightmost directory part */
+ if (strnrchr(&d, filename, length, slash) || d.length + 1 > database_size)
+ return true;
+ memcpy(database, d.str, d.length);
+ database[d.length]= '\0';
+ return false;
+} // end of filename_to_dbname_and_tablename
+
+/**
+ @brief
+ Used to delete or rename a table. By the time delete_table() has been
+ called all opened references to this table will have been closed
+ (and your globally shared references released) ===> too bad!!!
+ The variable name will just be the name of the table.
+ You will need to remove or rename any files you have created at
+ this point.
+
+ @details
+ If you do not implement this, the default delete_table() is called from
+ handler.cc and it will delete all files with the file extensions returned
+ by bas_ext().
+
+ Called from handler.cc by delete_table and ha_create_table(). Only used
+ during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
+ the storage engine.
+
+ @see
+ delete_table and ha_create_table() in handler.cc
+*/
+int ha_connect::delete_or_rename_table(const char *name, const char *to)
+{
+ DBUG_ENTER("ha_connect::delete_or_rename_table");
+ char db[128], tabname[128];
+ int rc= 0;
+ bool ok= false;
+ THD *thd= current_thd;
+ int sqlcom= thd_sql_command(thd);
+
+ if (xtrace) {
+ if (to)
+ htrc("rename_table: this=%p thd=%p sqlcom=%d from=%s to=%s\n",
+ this, thd, sqlcom, name, to);
+ else
+ htrc("delete_table: this=%p thd=%p sqlcom=%d name=%s\n",
+ this, thd, sqlcom, name);
+
+ } // endif xtrace
+
+ if (to && (filename_to_dbname_and_tablename(to, db, sizeof(db),
+ tabname, sizeof(tabname))
+ || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX)))
+ DBUG_RETURN(0);
+
+ if (filename_to_dbname_and_tablename(name, db, sizeof(db),
+ tabname, sizeof(tabname))
+ || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX))
+ DBUG_RETURN(0);
+
+ // If a temporary file exists, all the tests below were passed
+ // successfully when making it, so they are not needed anymore
+ // in particular because they sometimes cause DBUG_ASSERT crash.
+ if (*tabname != '#') {
+ // We have to retrieve the information about this table options.
+ ha_table_option_struct *pos;
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ TABLE_SHARE *share;
+
+ key_length= tdc_create_key(key, db, tabname);
+
+ // share contains the option struct that we need
+ if (!(share= alloc_table_share(db, tabname, key, key_length)))
+ DBUG_RETURN(rc);
+
+#if 0
+ if (*tabname == '#') {
+ // These are in ???? charset after renaming
+ char *p= strchr(share->path.str, '@');
+ strcpy(p, share->table_name.str);
+ share->path.length= strlen(share->path.str);
+ share->normalized_path.length= share->path.length;
+ } // endif tabname
+#endif // 0
+
+ // Get the share info from the .frm file
+ if (!open_table_def(thd, share)) {
+ // Now we can work
+ if ((pos= share->option_struct)) {
+ if (check_privileges(thd, pos, db))
+ rc= HA_ERR_INTERNAL_ERROR; // ???
+ else
+ if (IsFileType(GetRealType(pos)) && !pos->filename)
+ ok= true;
+
+ } // endif pos
+
+ } else // Avoid infamous DBUG_ASSERT
+ thd->get_stmt_da()->reset_diagnostics_area();
+
+ free_table_share(share);
+ } else // Temporary file
+ ok= true;
+
+ if (ok) {
+ // Let the base handler do the job
+ if (to)
+ rc= handler::rename_table(name, to);
+ else if ((rc= handler::delete_table(name)) == ENOENT)
+ rc= 0; // No files is not an error for CONNECT
+
+ } // endif ok
+
+ DBUG_RETURN(rc);
+} // end of delete_or_rename_table
+
+int ha_connect::delete_table(const char *name)
+{
+ return delete_or_rename_table(name, NULL);
+} // end of delete_table
+
+int ha_connect::rename_table(const char *from, const char *to)
+{
+ return delete_or_rename_table(from, to);
+} // end of rename_table
+
+/**
+ @brief
+ Given a starting key and an ending key, estimate the number of rows that
+ will exist between the two keys.
+
+ @details
+ end_key may be empty, in which case determine if start_key matches any rows.
+
+ Called from opt_range.cc by check_quick_keys().
+
+ @see
+ check_quick_keys() in opt_range.cc
+*/
+ha_rows ha_connect::records_in_range(uint inx, key_range *min_key,
+ key_range *max_key)
+{
+ ha_rows rows;
+ DBUG_ENTER("ha_connect::records_in_range");
+
+ if (indexing < 0 || inx != active_index)
+ index_init(inx, false);
+
+ if (xtrace)
+ htrc("records_in_range: inx=%d indexing=%d\n", inx, indexing);
+
+ if (indexing > 0) {
+ int nval;
+ uint len[2];
+ const uchar *key[2];
+ bool incl[2];
+ key_part_map kmap[2];
+
+ key[0]= (min_key) ? min_key->key : NULL;
+ key[1]= (max_key) ? max_key->key : NULL;
+ len[0]= (min_key) ? min_key->length : 0;
+ len[1]= (max_key) ? max_key->length : 0;
+ incl[0]= (min_key) ? (min_key->flag == HA_READ_KEY_EXACT) : false;
+ incl[1]= (max_key) ? (max_key->flag == HA_READ_AFTER_KEY) : false;
+ kmap[0]= (min_key) ? min_key->keypart_map : 0;
+ kmap[1]= (max_key) ? max_key->keypart_map : 0;
+
+ if ((nval= CntIndexRange(xp->g, tdbp, key, len, incl, kmap)) < 0)
+ rows= HA_POS_ERROR;
+ else
+ rows= (ha_rows)nval;
+
+ } else if (indexing < 0)
+ rows= HA_POS_ERROR;
+ else
+ rows= 100000000; // Don't use missing index
+
+ DBUG_RETURN(rows);
+} // end of records_in_range
+
+/**
+ Convert an ISO-8859-1 column name to UTF-8
+*/
+static char *encode(PGLOBAL g, char *cnm)
+ {
+ char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3);
+ uint dummy_errors;
+ uint32 len= copy_and_convert(buf, strlen(cnm) * 3,
+ &my_charset_utf8_general_ci,
+ cnm, strlen(cnm),
+ &my_charset_latin1,
+ &dummy_errors);
+ buf[len]= '\0';
+ return buf;
+ } // end of Encode
+
+/**
+ Store field definition for create.
+
+ @return
+ Return 0 if ok
+*/
+#if defined(NEW_WAY)
+static bool add_fields(PGLOBAL g,
+ THD *thd,
+ Alter_info *alter_info,
+ char *name,
+ int typ, int len, int dec,
+ uint type_modifier,
+ char *rem,
+// CHARSET_INFO *cs,
+// void *vcolinfo,
+// engine_option_value *create_options,
+ int flg,
+ bool dbf,
+ char v)
+{
+ register Create_field *new_field;
+ char *length, *decimals= NULL;
+ enum_field_types type;
+//Virtual_column_info *vcol_info= (Virtual_column_info *)vcolinfo;
+ engine_option_value *crop;
+ LEX_STRING *comment;
+ LEX_STRING *field_name;
+
+ DBUG_ENTER("ha_connect::add_fields");
+
+ if (len) {
+ if (!v && typ == TYPE_STRING && len > 255)
+ v= 'V'; // Change CHAR to VARCHAR
+
+ length= (char*)PlugSubAlloc(g, NULL, 8);
+ sprintf(length, "%d", len);
+
+ if (typ == TYPE_DOUBLE) {
+ decimals= (char*)PlugSubAlloc(g, NULL, 8);
+ sprintf(decimals, "%d", min(dec, (min(len, 31) - 1)));
+ } // endif dec
+
+ } else
+ length= NULL;
+
+ if (!rem)
+ rem= "";
+
+ type= PLGtoMYSQL(typ, dbf, v);
+ comment= thd->make_lex_string(rem, strlen(rem));
+ field_name= thd->make_lex_string(name, strlen(name));
+
+ switch (v) {
+ case 'Z': type_modifier|= ZEROFILL_FLAG;
+ case 'U': type_modifier|= UNSIGNED_FLAG; break;
+ } // endswitch v
+
+ if (flg) {
+ engine_option_value *start= NULL, *end= NULL;
+ LEX_STRING *flag= thd->make_lex_string("flag", 4);
+
+ crop= new(thd->mem_root) engine_option_value(*flag, (ulonglong)flg,
+ &start, &end, thd->mem_root);
+ } else
+ crop= NULL;
+
+ if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
+ system_charset_info, 1)) {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ } // endif field_name
+
+ if (!(new_field= new Create_field()) ||
+ new_field->init(thd, field_name->str, type, length, decimals,
+ type_modifier, NULL, NULL, comment, NULL,
+ NULL, NULL, 0, NULL, crop, true))
+ DBUG_RETURN(1);
+
+ alter_info->create_list.push_back(new_field);
+ DBUG_RETURN(0);
+} // end of add_fields
+#else // !NEW_WAY
+static bool add_field(String *sql, const char *field_name, int typ,
+ int len, int dec, uint tm, const char *rem,
+ char *dft, char *xtra, int flag, bool dbf, char v)
+{
+ char var = (len > 255) ? 'V' : v;
+ bool error= false;
+ const char *type= PLGtoMYSQLtype(typ, dbf, var);
+
+ error|= sql->append('`');
+ error|= sql->append(field_name);
+ error|= sql->append("` ");
+ error|= sql->append(type);
+
+ if (len) {
+ error|= sql->append('(');
+ error|= sql->append_ulonglong(len);
+
+ if (!strcmp(type, "DOUBLE")) {
+ error|= sql->append(',');
+ // dec must be < len and < 31
+ error|= sql->append_ulonglong(min(dec, (min(len, 31) - 1)));
+ } else if (dec > 0 && !strcmp(type, "DECIMAL")) {
+ error|= sql->append(',');
+ // dec must be < len
+ error|= sql->append_ulonglong(min(dec, len - 1));
+ } // endif dec
+
+ error|= sql->append(')');
+ } // endif len
+
+ if (v == 'U')
+ error|= sql->append(" UNSIGNED");
+ else if (v == 'Z')
+ error|= sql->append(" ZEROFILL");
+
+ if (tm)
+ error|= sql->append(STRING_WITH_LEN(" NOT NULL"), system_charset_info);
+
+ if (dft && *dft) {
+ error|= sql->append(" DEFAULT ");
+
+ if (!IsTypeNum(typ)) {
+ error|= sql->append("'");
+ error|= sql->append_for_single_quote(dft, strlen(dft));
+ error|= sql->append("'");
+ } else
+ error|= sql->append(dft);
+
+ } // endif dft
+
+ if (xtra && *xtra) {
+ error|= sql->append(" ");
+ error|= sql->append(xtra);
+ } // endif rem
+
+ if (rem && *rem) {
+ error|= sql->append(" COMMENT '");
+ error|= sql->append_for_single_quote(rem, strlen(rem));
+ error|= sql->append("'");
+ } // endif rem
+
+ if (flag) {
+ error|= sql->append(" FLAG=");
+ error|= sql->append_ulonglong(flag);
+ } // endif flag
+
+ error|= sql->append(',');
+ return error;
+} // end of add_field
+#endif // !NEW_WAY
+
+/**
+ Initialise the table share with the new columns.
+
+ @return
+ Return 0 if ok
+*/
+#if defined(NEW_WAY)
+//static bool sql_unusable_for_discovery(THD *thd, const char *sql);
+
+static int init_table_share(THD *thd,
+ TABLE_SHARE *table_s,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
+{
+ KEY *not_used_1;
+ uint not_used_2;
+ int rc= 0;
+ handler *file;
+ LEX_CUSTRING frm= {0,0};
+
+ DBUG_ENTER("init_table_share");
+
+#if 0
+ ulonglong saved_mode= thd->variables.sql_mode;
+ CHARSET_INFO *old_cs= thd->variables.character_set_client;
+ Parser_state parser_state;
+ char *sql_copy;
+ LEX *old_lex;
+ Query_arena *arena, backup;
+ LEX tmp_lex;
+
+ /*
+ Ouch. Parser may *change* the string it's working on.
+ Currently (2013-02-26) it is used to permanently disable
+ conditional comments.
+ Anyway, let's copy the caller's string...
+ */
+ if (!(sql_copy= thd->strmake(sql, sql_length)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ if (parser_state.init(thd, sql_copy, sql_length))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE;
+ thd->variables.character_set_client= system_charset_info;
+ old_lex= thd->lex;
+ thd->lex= &tmp_lex;
+
+ arena= thd->stmt_arena;
+
+ if (arena->is_conventional())
+ arena= 0;
+ else
+ thd->set_n_backup_active_arena(arena, &backup);
+
+ lex_start(thd);
+
+ if ((error= parse_sql(thd, & parser_state, NULL)))
+ goto ret;
+
+ if (table_s->sql_unusable_for_discovery(thd, NULL)) {
+ my_error(ER_SQL_DISCOVER_ERROR, MYF(0), plugin_name(db_plugin)->str,
+ db.str, table_name.str, sql_copy);
+ goto ret;
+ } // endif unusable
+
+ thd->lex->create_info.db_type= plugin_data(db_plugin, handlerton *);
+
+ if (tabledef_version.str)
+ thd->lex->create_info.tabledef_version= tabledef_version;
+#endif // 0
+
+ tmp_disable_binlog(thd);
+
+ file= mysql_create_frm_image(thd, table_s->db.str, table_s->table_name.str,
+ create_info, alter_info, C_ORDINARY_CREATE,
+ &not_used_1, &not_used_2, &frm);
+ if (file)
+ delete file;
+ else
+ rc= OPEN_FRM_CORRUPTED;
+
+ if (!rc && frm.str) {
+ table_s->option_list= 0; // cleanup existing options ...
+ table_s->option_struct= 0; // ... if it's an assisted discovery
+ rc= table_s->init_from_binary_frm_image(thd, true, frm.str, frm.length);
+ } // endif frm
+
+//ret:
+ my_free(const_cast<uchar*>(frm.str));
+ reenable_binlog(thd);
+#if 0
+ lex_end(thd->lex);
+ thd->lex= old_lex;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->variables.sql_mode= saved_mode;
+ thd->variables.character_set_client= old_cs;
+#endif // 0
+
+ if (thd->is_error() || rc) {
+ thd->clear_error();
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_s->db.str,
+ table_s->table_name.str);
+ DBUG_RETURN(HA_ERR_NOT_A_TABLE);
+ } else
+ DBUG_RETURN(0);
+
+} // end of init_table_share
+#else // !NEW_WAY
+static int init_table_share(THD* thd,
+ TABLE_SHARE *table_s,
+ HA_CREATE_INFO *create_info,
+// char *dsn,
+ String *sql)
+{
+ bool oom= false;
+ PTOS topt= table_s->option_struct;
+
+ sql->length(sql->length()-1); // remove the trailing comma
+ sql->append(')');
+
+ for (ha_create_table_option *opt= connect_table_option_list;
+ opt->name; opt++) {
+ ulonglong vull;
+ const char *vstr;
+
+ switch (opt->type) {
+ case HA_OPTION_TYPE_ULL:
+ vull= *(ulonglong*)(((char*)topt) + opt->offset);
+
+ if (vull != opt->def_value) {
+ oom|= sql->append(' ');
+ oom|= sql->append(opt->name);
+ oom|= sql->append('=');
+ oom|= sql->append_ulonglong(vull);
+ } // endif vull
+
+ break;
+ case HA_OPTION_TYPE_STRING:
+ vstr= *(char**)(((char*)topt) + opt->offset);
+
+ if (vstr) {
+ oom|= sql->append(' ');
+ oom|= sql->append(opt->name);
+ oom|= sql->append("='");
+ oom|= sql->append_for_single_quote(vstr, strlen(vstr));
+ oom|= sql->append('\'');
+ } // endif vstr
+
+ break;
+ case HA_OPTION_TYPE_BOOL:
+ vull= *(bool*)(((char*)topt) + opt->offset);
+
+ if (vull != opt->def_value) {
+ oom|= sql->append(' ');
+ oom|= sql->append(opt->name);
+ oom|= sql->append('=');
+ oom|= sql->append(vull ? "ON" : "OFF");
+ } // endif vull
+
+ break;
+ default: // no enums here, good :)
+ break;
+ } // endswitch type
+
+ if (oom)
+ return HA_ERR_OUT_OF_MEM;
+
+ } // endfor opt
+
+ if (create_info->connect_string.length) {
+//if (dsn) {
+ oom|= sql->append(' ');
+ oom|= sql->append("CONNECTION='");
+ oom|= sql->append_for_single_quote(create_info->connect_string.str,
+ create_info->connect_string.length);
+// oom|= sql->append_for_single_quote(dsn, strlen(dsn));
+ oom|= sql->append('\'');
+
+ if (oom)
+ return HA_ERR_OUT_OF_MEM;
+
+ } // endif string
+
+ if (create_info->default_table_charset) {
+ oom|= sql->append(' ');
+ oom|= sql->append("CHARSET=");
+ oom|= sql->append(create_info->default_table_charset->csname);
+
+ if (oom)
+ return HA_ERR_OUT_OF_MEM;
+
+ } // endif charset
+
+ if (xtrace)
+ htrc("s_init: %.*s\n", sql->length(), sql->ptr());
+
+ return table_s->init_from_sql_statement_string(thd, true,
+ sql->ptr(), sql->length());
+} // end of init_table_share
+#endif // !NEW_WAY
+
+// Add an option to the create_info option list
+static void add_option(THD* thd, HA_CREATE_INFO *create_info,
+ const char *opname, const char *opval)
+{
+#if defined(NEW_WAY)
+ LEX_STRING *opn= thd->make_lex_string(opname, strlen(opname));
+ LEX_STRING *val= thd->make_lex_string(opval, strlen(opval));
+ engine_option_value *pov, **start= &create_info->option_list, *end= NULL;
+
+ for (pov= *start; pov; pov= pov->next)
+ end= pov;
+
+ pov= new(thd->mem_root) engine_option_value(*opn, *val, false, start, &end);
+#endif // NEW_WAY
+} // end of add_option
+
+// Used to check whether a MYSQL table is created on itself
+static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
+ const char *db, char *tab, const char *src, int port)
+{
+ if (src)
+ return false;
+ else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
+ return false;
+ else if (db && stricmp(db, s->db.str))
+ return false;
+ else if (tab && stricmp(tab, s->table_name.str))
+ return false;
+ else if (port && port != (signed)GetDefaultPort())
+ return false;
+
+ strcpy(g->Message, "This MySQL table is defined on itself");
+ return true;
+} // end of CheckSelf
+
+/**
+ @brief
+ connect_assisted_discovery() is called when creating a table with no columns.
+
+ @details
+ When assisted discovery is used the .frm file have not already been
+ created. You can overwrite some definitions at this point but the
+ main purpose of it is to define the columns for some table types.
+
+ @note
+ this function is no more called in case of CREATE .. SELECT
+*/
+static int connect_assisted_discovery(handlerton *hton, THD* thd,
+ TABLE_SHARE *table_s,
+ HA_CREATE_INFO *create_info)
+{
+ 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;
+#if defined(WIN32)
+ char *nsp= NULL, *cls= NULL;
+#endif // WIN32
+ int port= 0, hdr= 0, mxr __attribute__((unused))= 0, mxe= 0, rc= 0;
+ int cop __attribute__((unused)) = 0;
+ uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
+ bool bif, ok= false, dbf= false;
+ TABTYPE ttp= TAB_UNDEF;
+ PQRYRES qrp= NULL;
+ PCOLRES crp;
+ PCONNECT xp= NULL;
+ PGLOBAL g= GetPlug(thd, xp);
+ PTOS topt= table_s->option_struct;
+#if defined(NEW_WAY)
+//CHARSET_INFO *cs;
+ Alter_info alter_info;
+#else // !NEW_WAY
+ char buf[1024];
+ String sql(buf, sizeof(buf), system_charset_info);
+
+ sql.copy(STRING_WITH_LEN("CREATE TABLE whatever ("), system_charset_info);
+#endif // !NEW_WAY
+
+ if (!g)
+ return HA_ERR_INTERNAL_ERROR;
+
+ user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
+
+ // Get the useful create options
+ ttp= GetTypeID(topt->type);
+ fn= topt->filename;
+ tab= (char*)topt->tabname;
+ src= topt->srcdef;
+ db= topt->dbname;
+ fncn= topt->catfunc;
+ fnc= GetFuncID(fncn);
+ sep= topt->separator;
+ spc= (!sep || !strcmp(sep, "\\t")) ? '\t' : *sep;
+ qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0;
+ hdr= (int)topt->header;
+ tbl= topt->tablist;
+ col= topt->colist;
+
+ if (topt->oplist) {
+ host= GetListOption(g, "host", topt->oplist, "localhost");
+ user= GetListOption(g, "user", topt->oplist, "root");
+ // Default value db can come from the DBNAME=xxx option.
+ db= GetListOption(g, "database", topt->oplist, db);
+ col= GetListOption(g, "colist", topt->oplist, col);
+ ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
+ pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
+ fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
+ rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
+ pwd= GetListOption(g, "password", topt->oplist);
+#if defined(WIN32)
+ nsp= GetListOption(g, "namespace", topt->oplist);
+ cls= GetListOption(g, "class", topt->oplist);
+#endif // WIN32
+ port= atoi(GetListOption(g, "port", topt->oplist, "0"));
+ mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
+ mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
+#if defined(PROMPT_OK)
+ cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
+#endif // PROMPT_OK
+ } else {
+ host= "localhost";
+ user= "root";
+ } // endif option_list
+
+ if (!(shm= (char*)db))
+ db= table_s->db.str; // Default value
+
+ // Check table type
+ if (ttp == TAB_UNDEF) {
+ topt->type= (src) ? "MYSQL" : (tab) ? "PROXY" : "DOS";
+ ttp= GetTypeID(topt->type);
+ sprintf(g->Message, "No table_type. Was set to %s", topt->type);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ add_option(thd, create_info, "table_type", topt->type);
+ } else if (ttp == TAB_NIY) {
+ sprintf(g->Message, "Unsupported table type %s", topt->type);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif ttp
+
+ if (!tab) {
+ if (ttp == TAB_TBL) {
+ // Make tab the first table of the list
+ char *p;
+
+ if (!tbl) {
+ strcpy(g->Message, "Missing table list");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif tbl
+
+ tab= (char*)PlugSubAlloc(g, NULL, strlen(tbl) + 1);
+ strcpy(tab, tbl);
+
+ if ((p= strchr(tab, ',')))
+ *p= 0;
+
+ if ((p=strchr(tab, '.'))) {
+ *p= 0;
+ db= tab;
+ tab= p + 1;
+ } // endif p
+
+ } else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
+ tab= table_s->table_name.str; // Default value
+
+#if defined(NEW_WAY)
+// add_option(thd, create_info, "tabname", tab);
+#endif // NEW_WAY
+ } // endif tab
+
+ switch (ttp) {
+#if defined(ODBC_SUPPORT)
+ case TAB_ODBC:
+ dsn= create_info->connect_string.str;
+
+ if (fnc & (FNC_DSN | FNC_DRIVER)) {
+ ok= true;
+#if defined(PROMPT_OK)
+ } else if (!stricmp(thd->main_security_ctx.host, "localhost")
+ && cop == 1) {
+ if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
+ thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
+ ok= true;
+ } // endif dsn
+#endif // PROMPT_OK
+
+ } else if (!dsn)
+ sprintf(g->Message, "Missing %s connection string", topt->type);
+ else
+ ok= true;
+
+ supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
+ break;
+#endif // ODBC_SUPPORT
+ case TAB_DBF:
+ dbf= true;
+ // Passthru
+ case TAB_CSV:
+ if (!fn && fnc != FNC_NO)
+ sprintf(g->Message, "Missing %s file name", topt->type);
+ else
+ ok= true;
+
+ break;
+#if defined(MYSQL_SUPPORT)
+ case TAB_MYSQL:
+ ok= true;
+
+ if (create_info->connect_string.str) {
+ int len= create_info->connect_string.length;
+ PMYDEF mydef= new(g) MYSQLDEF();
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
+
+ dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
+ strncpy(dsn, create_info->connect_string.str, len);
+ dsn[len]= 0;
+ mydef->SetName(create_info->alias);
+ mydef->SetCat(cat);
+
+ if (!mydef->ParseURL(g, dsn, false)) {
+ if (mydef->GetHostname())
+ host= mydef->GetHostname();
+
+ if (mydef->GetUsername())
+ user= mydef->GetUsername();
+
+ if (mydef->GetPassword())
+ pwd= mydef->GetPassword();
+
+ if (mydef->GetDatabase())
+ db= mydef->GetDatabase();
+
+ if (mydef->GetTabname())
+ tab= mydef->GetTabname();
+
+ if (mydef->GetPortnumber())
+ port= mydef->GetPortnumber();
+
+ } else
+ ok= false;
+
+ } else if (!user)
+ user= "root";
+
+ if (CheckSelf(g, table_s, host, db, tab, src, port))
+ ok= false;
+
+ break;
+#endif // MYSQL_SUPPORT
+#if defined(WIN32)
+ case TAB_WMI:
+ ok= true;
+ break;
+#endif // WIN32
+ case TAB_PIVOT:
+ supfnc= FNC_NO;
+ case TAB_PRX:
+ case TAB_TBL:
+ case TAB_XCL:
+ case TAB_OCCUR:
+ if (!src && !stricmp(tab, create_info->alias) &&
+ (!db || !stricmp(db, table_s->db.str)))
+ sprintf(g->Message, "A %s table cannot refer to itself", topt->type);
+ else
+ ok= true;
+
+ break;
+ case TAB_OEM:
+ if (topt->module && topt->subtype)
+ ok= true;
+ else
+ strcpy(g->Message, "Missing OEM module or subtype");
+
+ break;
+ default:
+ sprintf(g->Message, "Cannot get column info for table type %s", topt->type);
+ break;
+ } // endif ttp
+
+ // Check for supported catalog function
+ if (ok && !(supfnc & fnc)) {
+ sprintf(g->Message, "Unsupported catalog function %s for table type %s",
+ fncn, topt->type);
+ ok= false;
+ } // endif supfnc
+
+ if (src && fnc != FNC_NO) {
+ strcpy(g->Message, "Cannot make catalog table from srcdef");
+ ok= false;
+ } // endif src
+
+ if (ok) {
+ char *cnm, *rem, *dft, *xtra;
+ int i, len, prec, dec, typ, flg;
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
+
+ if (cat)
+ cat->SetDataPath(g, table_s->db.str);
+ else
+ return HA_ERR_INTERNAL_ERROR; // Should never happen
+
+ if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
+ qrp= SrcColumns(g, host, db, user, pwd, src, port);
+
+ if (qrp && ttp == TAB_OCCUR)
+ if (OcrSrcCols(g, qrp, col, ocl, rnk)) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif OcrSrcCols
+
+ } else switch (ttp) {
+ case TAB_DBF:
+ qrp= DBFColumns(g, fn, fnc == FNC_COL);
+ break;
+#if defined(ODBC_SUPPORT)
+ case TAB_ODBC:
+ switch (fnc) {
+ case FNC_NO:
+ case FNC_COL:
+ if (src) {
+ qrp= ODBCSrcCols(g, dsn, (char*)src);
+ src= NULL; // for next tests
+ } else
+ qrp= ODBCColumns(g, dsn, shm, tab, NULL, mxr, fnc == FNC_COL);
+
+ break;
+ case FNC_TABLE:
+ qrp= ODBCTables(g, dsn, shm, tab, mxr, true);
+ break;
+ case FNC_DSN:
+ qrp= ODBCDataSources(g, mxr, true);
+ break;
+ case FNC_DRIVER:
+ qrp= ODBCDrivers(g, mxr, true);
+ break;
+ default:
+ sprintf(g->Message, "invalid catfunc %s", fncn);
+ break;
+ } // endswitch info
+
+ break;
+#endif // ODBC_SUPPORT
+#if defined(MYSQL_SUPPORT)
+ case TAB_MYSQL:
+ qrp= MyColumns(g, host, db, user, pwd, tab,
+ NULL, port, fnc == FNC_COL);
+ break;
+#endif // MYSQL_SUPPORT
+ case TAB_CSV:
+ qrp= CSVColumns(g, fn, spc, qch, hdr, mxe, fnc == FNC_COL);
+ break;
+#if defined(WIN32)
+ case TAB_WMI:
+ qrp= WMIColumns(g, nsp, cls, fnc == FNC_COL);
+ break;
+#endif // WIN32
+ case TAB_PRX:
+ case TAB_TBL:
+ case TAB_XCL:
+ case TAB_OCCUR:
+ bif= fnc == FNC_COL;
+ qrp= TabColumns(g, thd, db, tab, bif);
+
+ if (!qrp && bif && fnc != FNC_COL) // tab is a view
+ qrp= MyColumns(g, host, db, user, pwd, tab, NULL, port, false);
+
+ if (qrp && ttp == TAB_OCCUR && fnc != FNC_COL)
+ if (OcrColumns(g, qrp, col, ocl, rnk)) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif OcrColumns
+
+ break;
+ case TAB_PIVOT:
+ qrp= PivotColumns(g, tab, src, pic, fcl, host, db, user, pwd, port);
+ break;
+ case TAB_OEM:
+ qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
+ break;
+ default:
+ strcpy(g->Message, "System error during assisted discovery");
+ break;
+ } // endswitch ttp
+
+ if (!qrp) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif qrp
+
+ if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
+ // Catalog like table
+ for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
+ cnm= encode(g, crp->Name);
+ typ= crp->Type;
+ len= crp->Length;
+ dec= crp->Prec;
+ flg= crp->Flag;
+
+ if (!len && typ == TYPE_STRING)
+ len= 256; // STRBLK's have 0 length
+
+#if defined(NEW_WAY)
+ // Now add the field
+ rc= add_fields(g, thd, &alter_info, cnm, typ, len, dec,
+ NOT_NULL_FLAG, "", flg, dbf, 0);
+#else // !NEW_WAY
+ // Now add the field
+ if (add_field(&sql, cnm, typ, len, dec, NOT_NULL_FLAG,
+ NULL, NULL, NULL, flg, dbf, 0))
+ rc= HA_ERR_OUT_OF_MEM;
+#endif // !NEW_WAY
+ } // endfor crp
+
+ } else // Not a catalog table
+ for (i= 0; !rc && i < qrp->Nblin; i++) {
+ typ= len= prec= dec= 0;
+ tm= NOT_NULL_FLAG;
+ cnm= (char*)"noname";
+ dft= xtra= NULL;
+#if defined(NEW_WAY)
+ rem= "";
+// cs= NULL;
+#else // !NEW_WAY
+ rem= NULL;
+#endif // !NEW_WAY
+
+ for (crp= qrp->Colresp; crp; crp= crp->Next)
+ switch (crp->Fld) {
+ case FLD_NAME:
+ cnm= encode(g, crp->Kdata->GetCharValue(i));
+ break;
+ case FLD_TYPE:
+ typ= crp->Kdata->GetIntValue(i);
+ v = (crp->Nulls) ? crp->Nulls[i] : 0;
+ break;
+ case FLD_PREC:
+ // PREC must be always before LENGTH
+ len= prec= crp->Kdata->GetIntValue(i);
+ break;
+ case FLD_LENGTH:
+ len= crp->Kdata->GetIntValue(i);
+ break;
+ case FLD_SCALE:
+ dec= crp->Kdata->GetIntValue(i);
+ break;
+ case FLD_NULL:
+ if (crp->Kdata->GetIntValue(i))
+ tm= 0; // Nullable
+
+ break;
+ case FLD_REM:
+ rem= crp->Kdata->GetCharValue(i);
+ break;
+// case FLD_CHARSET:
+ // No good because remote table is already translated
+// if (*(csn= crp->Kdata->GetCharValue(i)))
+// cs= get_charset_by_name(csn, 0);
+
+// break;
+ case FLD_DEFAULT:
+ dft= crp->Kdata->GetCharValue(i);
+ break;
+ case FLD_EXTRA:
+ xtra= crp->Kdata->GetCharValue(i);
+ break;
+ default:
+ break; // Ignore
+ } // endswitch Fld
+
+#if defined(ODBC_SUPPORT)
+ if (ttp == TAB_ODBC) {
+ int plgtyp;
+
+ // typ must be PLG type, not SQL type
+ if (!(plgtyp= TranslateSQLType(typ, dec, prec, v))) {
+ sprintf(g->Message, "Unsupported SQL type %d", typ);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+ } else
+ typ= plgtyp;
+
+ switch (typ) {
+ case TYPE_DOUBLE:
+ // Some data sources do not count dec in length (prec)
+ prec += (dec + 2); // To be safe
+ case TYPE_DECIM:
+ break;
+ default:
+ dec= 0;
+ } // endswitch typ
+
+ } // endif ttp
+#endif // ODBC_SUPPORT
+
+ // Make the arguments as required by add_fields
+ if (typ == TYPE_DATE)
+ prec= 0;
+ else if (typ == TYPE_DOUBLE)
+ prec= len;
+
+ // Now add the field
+#if defined(NEW_WAY)
+ rc= add_fields(g, thd, &alter_info, cnm, typ, prec, dec,
+ tm, rem, 0, dbf, v);
+#else // !NEW_WAY
+ if (add_field(&sql, cnm, typ, prec, dec, tm, rem, dft, xtra,
+ 0, dbf, v))
+ rc= HA_ERR_OUT_OF_MEM;
+#endif // !NEW_WAY
+ } // endfor i
+
+#if defined(NEW_WAY)
+ rc= init_table_share(thd, table_s, create_info, &alter_info);
+#else // !NEW_WAY
+ if (!rc)
+ rc= init_table_share(thd, table_s, create_info, &sql);
+// rc= init_table_share(thd, table_s, create_info, dsn, &sql);
+#endif // !NEW_WAY
+
+ return rc;
+ } // endif ok
+
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ return HA_ERR_INTERNAL_ERROR;
+} // end of connect_assisted_discovery
+
+/**
+ Get the database name from a qualified table name.
+*/
+char *ha_connect::GetDBfromName(const char *name)
+{
+ char *db, dbname[128], tbname[128];
+
+ if (filename_to_dbname_and_tablename(name, dbname, sizeof(dbname),
+ tbname, sizeof(tbname)))
+ *dbname= 0;
+
+ if (*dbname) {
+ assert(xp && xp->g);
+ db= (char*)PlugSubAlloc(xp->g, NULL, strlen(dbname + 1));
+ strcpy(db, dbname);
+ } else
+ db= NULL;
+
+ return db;
+} // end of GetDBfromName
+
+
+/**
+ @brief
+ create() is called to create a database. The variable name will have the name
+ of the table.
+
+ @details
+ When create() is called you do not need to worry about
+ opening the table. Also, the .frm file will have already been
+ created so adjusting create_info is not necessary. You can overwrite
+ the .frm file at this point if you wish to change the table
+ definition, but there are no methods currently provided for doing
+ so.
+
+ Called from handle.cc by ha_create_table().
+
+ @note
+ Currently we do some checking on the create definitions and stop
+ creating if an error is found. We wish we could change the table
+ definition such as providing a default table type. However, as said
+ above, there are no method to do so.
+
+ @see
+ ha_create_table() in handle.cc
+*/
+
+int ha_connect::create(const char *name, TABLE *table_arg,
+ HA_CREATE_INFO *create_info)
+{
+ int rc= RC_OK;
+ bool dbf;
+ Field* *field;
+ Field *fp;
+ TABTYPE type;
+ TABLE *st= table; // Probably unuseful
+ THD *thd= ha_thd();
+ xp= GetUser(thd, xp);
+ PGLOBAL g= xp->g;
+
+ DBUG_ENTER("ha_connect::create");
+ int sqlcom= thd_sql_command(table_arg->in_use);
+ PTOS options= GetTableOptionStruct(table_arg);
+
+ table= table_arg; // Used by called functions
+
+ if (xtrace)
+ htrc("create: this=%p thd=%p xp=%p g=%p sqlcom=%d name=%s\n",
+ this, thd, xp, g, sqlcom, GetTableName());
+
+ // CONNECT engine specific table options:
+ DBUG_ASSERT(options);
+ type= GetTypeID(options->type);
+
+ // Check table type
+ if (type == TAB_UNDEF) {
+ options->type= (options->srcdef) ? "MYSQL" :
+ (options->tabname) ? "PROXY" : "DOS";
+ type= GetTypeID(options->type);
+ sprintf(g->Message, "No table_type. Will be set to %s", options->type);
+
+ if (sqlcom == SQLCOM_CREATE_TABLE)
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+
+ } else if (type == TAB_NIY) {
+ sprintf(g->Message, "Unsupported table type %s", options->type);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif ttp
+
+ if (check_privileges(thd, options, GetDBfromName(name)))
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+
+ if (options->data_charset) {
+ const CHARSET_INFO *data_charset;
+
+ if (!(data_charset= get_charset_by_csname(options->data_charset,
+ MY_CS_PRIMARY, MYF(0)))) {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), options->data_charset);
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif charset
+
+ if (type == TAB_XML && data_charset != &my_charset_utf8_general_ci) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "DATA_CHARSET='%s' is not supported for TABLE_TYPE=XML",
+ MYF(0), options->data_charset);
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif utf8
+
+ } // endif charset
+
+ if (!g) {
+ rc= HA_ERR_INTERNAL_ERROR;
+ DBUG_RETURN(rc);
+ } else
+ dbf= (GetTypeID(options->type) == TAB_DBF && !options->catfunc);
+
+ // Can be null in ALTER TABLE
+ if (create_info->alias)
+ // Check whether a table is defined on itself
+ switch (type) {
+ case TAB_PRX:
+ case TAB_XCL:
+ case TAB_PIVOT:
+ case TAB_OCCUR:
+ if (options->srcdef) {
+ strcpy(g->Message, "Cannot check looping reference");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ } else if (options->tabname) {
+ if (!stricmp(options->tabname, create_info->alias) &&
+ (!options->dbname || !stricmp(options->dbname, table_arg->s->db.str))) {
+ sprintf(g->Message, "A %s table cannot refer to itself",
+ options->type);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif tab
+
+ } else {
+ strcpy(g->Message, "Missing object table name or definition");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif tabname
+
+ case TAB_MYSQL:
+ {const char *src= options->srcdef;
+ char *host, *db, *tab= (char*)options->tabname;
+ int port;
+
+ host= GetListOption(g, "host", options->oplist, NULL);
+ db= GetListOption(g, "database", options->oplist, NULL);
+ port= atoi(GetListOption(g, "port", options->oplist, "0"));
+
+ if (create_info->connect_string.str) {
+ char *dsn;
+ int len= create_info->connect_string.length;
+ PMYDEF mydef= new(g) MYSQLDEF();
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
+
+ dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
+ strncpy(dsn, create_info->connect_string.str, len);
+ dsn[len]= 0;
+ mydef->SetName(create_info->alias);
+ mydef->SetCat(cat);
+
+ if (!mydef->ParseURL(g, dsn, false)) {
+ if (mydef->GetHostname())
+ host= mydef->GetHostname();
+
+ if (mydef->GetDatabase())
+ db= mydef->GetDatabase();
+
+ if (mydef->GetTabname())
+ tab= mydef->GetTabname();
+
+ if (mydef->GetPortnumber())
+ port= mydef->GetPortnumber();
+
+ } else {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif ParseURL
+
+ } // endif connect_string
+
+ if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif CheckSelf
+
+ }break;
+ default: /* do nothing */;
+ break;
+ } // endswitch ttp
+
+ if (type == TAB_XML) {
+ bool dom; // True: MS-DOM, False libxml2
+ char *xsup= GetListOption(g, "Xmlsup", options->oplist, "*");
+
+ // Note that if no support is specified, the default is MS-DOM
+ // on Windows and libxml2 otherwise
+ switch (*xsup) {
+ case '*':
+#if defined(WIN32)
+ dom= true;
+#else // !WIN32
+ dom= false;
+#endif // !WIN32
+ break;
+ case 'M':
+ case 'D':
+ dom= true;
+ break;
+ default:
+ dom= false;
+ break;
+ } // endswitch xsup
+
+#if !defined(DOMDOC_SUPPORT)
+ if (dom) {
+ strcpy(g->Message, "MS-DOM not supported by this version");
+ xsup= NULL;
+ } // endif DomDoc
+#endif // !DOMDOC_SUPPORT
+
+#if !defined(LIBXML2_SUPPORT)
+ if (!dom) {
+ strcpy(g->Message, "libxml2 not supported by this version");
+ xsup= NULL;
+ } // endif Libxml2
+#endif // !LIBXML2_SUPPORT
+
+ if (!xsup) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ DBUG_RETURN(rc);
+ } // endif xsup
+
+ } // endif type
+
+ // Check column types
+ for (field= table_arg->field; *field; field++) {
+ fp= *field;
+
+ if (fp->vcol_info && !fp->stored_in_db)
+ continue; // This is a virtual column
+
+ if (fp->flags & AUTO_INCREMENT_FLAG) {
+ strcpy(g->Message, "Auto_increment is not supported yet");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ DBUG_RETURN(rc);
+ } // endif flags
+
+ if (fp->flags & (BLOB_FLAG | ENUM_FLAG | SET_FLAG)) {
+ sprintf(g->Message, "Unsupported type for column %s",
+ fp->field_name);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ DBUG_RETURN(rc);
+ } // endif flags
+
+ switch (fp->type()) {
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_TINY:
+ break; // Ok
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_INT24:
+ break; // To be checked
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ default:
+// fprintf(stderr, "Unsupported type column %s\n", fp->field_name);
+ sprintf(g->Message, "Unsupported type for column %s",
+ fp->field_name);
+ rc= HA_ERR_INTERNAL_ERROR;
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Unsupported type for column '%s'",
+ MYF(0), fp->field_name);
+ DBUG_RETURN(rc);
+ break;
+ } // endswitch type
+
+ if ((fp)->real_maybe_null() && !IsTypeNullable(type)) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Table type %s does not support nullable columns",
+ MYF(0), options->type);
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } // endif !nullable
+
+ if (dbf) {
+ bool b= false;
+
+ if ((b= strlen(fp->field_name) > 10))
+ sprintf(g->Message, "DBF: Column name '%s' is too long (max=10)",
+ fp->field_name);
+ else if ((b= fp->field_length > 255))
+ sprintf(g->Message, "DBF: Column length too big for '%s' (max=255)",
+ fp->field_name);
+
+ if (b) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ DBUG_RETURN(rc);
+ } // endif b
+
+ } // endif dbf
+
+ } // endfor field
+
+ if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#')
+ && IsFileType(type) && !options->filename) {
+ // The file name is not specified, create a default file in
+ // the database directory named table_name.table_type.
+ // (temporarily not done for XML because a void file causes
+ // the XML parsers to report an error on the first Insert)
+ char buf[256], fn[_MAX_PATH], dbpath[128], lwt[12];
+ int h;
+
+ strcpy(buf, GetTableName());
+
+ // Check for incompatible options
+ if (options->sepindex) {
+ my_message(ER_UNKNOWN_ERROR,
+ "SEPINDEX is incompatible with unspecified file name",
+ MYF(0));
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } else if (GetTypeID(options->type) == TAB_VEC)
+ if (!table->s->max_rows || options->split) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "%s tables whose file name is unspecified cannot be split",
+ MYF(0), options->type);
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } else if (options->header == 2) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "header=2 is not allowed for %s tables whose file name is unspecified",
+ MYF(0), options->type);
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } // endif's
+
+ // Fold type to lower case
+ for (int i= 0; i < 12; i++)
+ if (!options->type[i]) {
+ lwt[i]= 0;
+ break;
+ } else
+ lwt[i]= tolower(options->type[i]);
+
+ strcat(strcat(buf, "."), lwt);
+ sprintf(g->Message, "No file name. Table will use %s", buf);
+
+ if (sqlcom == SQLCOM_CREATE_TABLE)
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+
+ strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
+ PlugSetPath(fn, buf, dbpath);
+
+ if ((h= ::open(fn, O_CREAT | O_EXCL, 0666)) == -1) {
+ if (errno == EEXIST)
+ sprintf(g->Message, "Default file %s already exists", fn);
+ else
+ sprintf(g->Message, "Error %d creating file %s", errno, fn);
+
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ } else
+ ::close(h);
+
+ if (type == TAB_FMT || options->readonly)
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ "Congratulation, you just created a read-only void table!");
+
+ } // endif
+
+ if (xtrace)
+ htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
+
+ // To check whether indices have to be made or remade
+ if (!g->Xchk) {
+ PIXDEF xdp;
+
+ // We should be in CREATE TABLE or ALTER_TABLE
+ if (sqlcom != SQLCOM_CREATE_TABLE && sqlcom != SQLCOM_ALTER_TABLE)
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ "Wrong command in create, please contact CONNECT team");
+
+ if (sqlcom == SQLCOM_ALTER_TABLE && g->Alchecked == 0 &&
+ (!IsFileType(type) || FileExists(options->filename))) {
+ // This is an ALTER to CONNECT from another engine.
+ // It cannot be accepted because the table data would be lost
+ // except when the target file does not exist.
+ strcpy(g->Message, "Operation denied. Table data would be lost.");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif outward
+
+ // Get the index definitions
+ if (xdp= GetIndexInfo()) {
+ if (IsTypeIndexable(type)) {
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
+
+ if (cat) {
+ cat->SetDataPath(g, table_arg->s->db.str);
+
+ if ((rc= optimize(table->in_use, NULL))) {
+ htrc("Create rc=%d %s\n", rc, g->Message);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ } else
+ CloseTable(g);
+
+ } // endif cat
+
+ } else {
+ sprintf(g->Message, "Table type %s is not indexable", options->type);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_UNSUPPORTED;
+ } // endif Indexable
+
+ } // endif xdp
+
+ } else {
+ // This should not happen anymore with indexing new way
+ my_message(ER_UNKNOWN_ERROR,
+ "CONNECT index modification should be in-place", MYF(0));
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+#if 0
+ PIXDEF xdp= GetIndexInfo();
+ PCHK xcp= (PCHK)g->Xchk;
+
+ if (xdp) {
+ if (!IsTypeIndexable(type)) {
+ g->Xchk= NULL;
+ sprintf(g->Message, "Table type %s is not indexable", options->type);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ rc= HA_ERR_INTERNAL_ERROR;
+ } else {
+ xcp->newpix= xdp;
+ xcp->newsep= GetBooleanOption("Sepindex", false);
+ } // endif Indexable
+
+ } else if (!xcp->oldpix)
+ g->Xchk= NULL;
+
+ if (xtrace && g->Xchk)
+ htrc("oldsep=%d newsep=%d oldpix=%p newpix=%p\n",
+ xcp->oldsep, xcp->newsep, xcp->oldpix, xcp->newpix);
+
+// if (g->Xchk &&
+// (sqlcom != SQLCOM_CREATE_INDEX && sqlcom != SQLCOM_DROP_INDEX)) {
+ if (g->Xchk) {
+ PIXDEF xp1, xp2;
+ bool b= false; // true if index changes
+
+ if (xcp->oldsep == xcp->newsep) {
+ for (xp1= xcp->newpix, xp2= xcp->oldpix;
+ xp1 || xp2;
+ xp1= xp1->Next, xp2= xp2->Next)
+ if (!xp1 || !xp2 || !IsSameIndex(xp1, xp2)) {
+ b= true;
+ break;
+ } // endif xp1
+
+ } else
+ b= true;
+
+ if (!b)
+ g->Xchk= NULL;
+
+#if 0
+ if (b) {
+ // CONNECT does not support indexing via ALTER TABLE
+ my_message(ER_UNKNOWN_ERROR,
+ "CONNECT does not support index modification via ALTER TABLE",
+ MYF(0));
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } // endif b
+#endif // 0
+
+ } // endif Xchk
+
+#endif // 0
+ } // endif Xchk
+
+ table= st;
+ DBUG_RETURN(rc);
+} // end of create
+
+/**
+ Used to check whether a file based outward table can be populated by
+ an ALTER TABLE command. The conditions are:
+ - file does not exist or is void
+ - user has file privilege
+*/
+bool ha_connect::FileExists(const char *fn)
+{
+ if (!fn || !*fn)
+ return false;
+
+ if (table) {
+ char *s, filename[_MAX_PATH], path[128];
+ int n;
+ struct stat info;
+
+ if (check_access(ha_thd(), FILE_ACL, table->s->db.str,
+ NULL, NULL, 0, 0))
+ return true;
+
+#if defined(WIN32)
+ s= "\\";
+#else // !WIN32
+ s= "/";
+#endif // !WIN32
+
+ strcat(strcat(strcat(strcpy(path, "."), s), table->s->db.str), s);
+ PlugSetPath(filename, fn, path);
+ n= stat(filename, &info);
+
+ if (n < 0) {
+ if (errno != ENOENT) {
+ char buf[_MAX_PATH + 20];
+
+ sprintf(buf, "Error %d for file %s", errno, filename);
+ push_warning(table->in_use, Sql_condition::WARN_LEVEL_WARN, 0, buf);
+ return true;
+ } else
+ return false;
+
+ } else
+ return (info.st_size) ? true : false;
+
+ } // endif table
+
+ return true;
+} // end of FileExists
+
+// Called by SameString and NoFieldOptionChange
+bool ha_connect::CheckString(const char *str1, const char *str2)
+{
+ bool b1= (!str1 || !*str1), b2= (!str2 || !*str2);
+
+ if (b1 && b2)
+ return true;
+ else if ((b1 && !b2) || (!b1 && b2) || stricmp(str1, str2))
+ return false;
+
+ return true;
+} // end of CheckString
+
+/**
+ check whether a string option have changed
+ */
+bool ha_connect::SameString(TABLE *tab, char *opn)
+{
+ char *str1, *str2;
+
+ tshp= tab->s; // The altered table
+ str1= GetStringOption(opn);
+ tshp= NULL;
+ str2= GetStringOption(opn);
+ return CheckString(str1, str2);
+} // end of SameString
+
+/**
+ check whether a Boolean option have changed
+ */
+bool ha_connect::SameBool(TABLE *tab, char *opn)
+{
+ bool b1, b2;
+
+ tshp= tab->s; // The altered table
+ b1= GetBooleanOption(opn, false);
+ tshp= NULL;
+ b2= GetBooleanOption(opn, false);
+ return (b1 == b2);
+} // end of SameBool
+
+/**
+ check whether an integer option have changed
+ */
+bool ha_connect::SameInt(TABLE *tab, char *opn)
+{
+ int i1, i2;
+
+ tshp= tab->s; // The altered table
+ i1= GetIntegerOption(opn);
+ tshp= NULL;
+ i2= GetIntegerOption(opn);
+
+ if (!stricmp(opn, "lrecl"))
+ return (i1 == i2 || !i1 || !i2);
+ else if (!stricmp(opn, "ending"))
+ return (i1 == i2 || i1 <= 0 || i2 <= 0);
+ else
+ return (i1 == i2);
+
+} // end of SameInt
+
+/**
+ check whether a field option have changed
+ */
+bool ha_connect::NoFieldOptionChange(TABLE *tab)
+{
+ bool rc= true;
+ ha_field_option_struct *fop1, *fop2;
+ Field* *fld1= table->s->field;
+ Field* *fld2= tab->s->field;
+
+ for (; rc && *fld1 && *fld2; fld1++, fld2++) {
+ fop1= (*fld1)->option_struct;
+ fop2= (*fld2)->option_struct;
+
+ rc= (fop1->offset == fop2->offset &&
+ fop1->fldlen == fop2->fldlen &&
+ CheckString(fop1->dateformat, fop2->dateformat) &&
+ CheckString(fop1->fieldformat, fop2->fieldformat) &&
+ CheckString(fop1->special, fop2->special));
+ } // endfor fld
+
+ return rc;
+} // end of NoFieldOptionChange
+
+ /**
+ Check if a storage engine supports a particular alter table in-place
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+
+ @retval HA_ALTER_ERROR Unexpected error.
+ @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy.
+ @retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ Supported, but requires SNW lock
+ during main phase. Prepare phase
+ requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
+ Supported, concurrent reads/writes
+ allowed. However, prepare phase
+ requires X lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK Supported, concurrent
+ reads/writes allowed.
+
+ @note The default implementation uses the old in-place ALTER API
+ to determine if the storage engine supports in-place ALTER or not.
+
+ @note Called without holding thr_lock.c lock.
+ */
+enum_alter_inplace_result
+ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ DBUG_ENTER("check_if_supported_alter");
+
+ bool idx= false, outward= false;
+ THD *thd= ha_thd();
+ int sqlcom= thd_sql_command(thd);
+ TABTYPE newtyp, type= TAB_UNDEF;
+ HA_CREATE_INFO *create_info= ha_alter_info->create_info;
+//PTOS pos= GetTableOptionStruct(table);
+ PTOS newopt, oldopt;
+ xp= GetUser(thd, xp);
+ PGLOBAL g= xp->g;
+
+ if (!g || !table) {
+ my_message(ER_UNKNOWN_ERROR, "Cannot check ALTER operations", MYF(0));
+ DBUG_RETURN(HA_ALTER_ERROR);
+ } // endif Xchk
+
+ newopt= altered_table->s->option_struct;
+ oldopt= table->s->option_struct;
+
+ // If this is the start of a new query, cleanup the previous one
+ if (xp->CheckCleanup()) {
+ tdbp= NULL;
+ valid_info= false;
+ } // endif CheckCleanup
+
+ g->Alchecked= 1; // Tested in create
+ g->Xchk= NULL;
+ type= GetRealType(oldopt);
+ newtyp= GetRealType(newopt);
+
+ // No copy algorithm for outward tables
+ outward= (!IsFileType(type) || (oldopt->filename && *oldopt->filename));
+
+ // Index operations
+ Alter_inplace_info::HA_ALTER_FLAGS index_operations=
+ Alter_inplace_info::ADD_INDEX |
+ Alter_inplace_info::DROP_INDEX |
+ Alter_inplace_info::ADD_UNIQUE_INDEX |
+ Alter_inplace_info::DROP_UNIQUE_INDEX |
+ Alter_inplace_info::ADD_PK_INDEX |
+ Alter_inplace_info::DROP_PK_INDEX;
+
+ Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
+ Alter_inplace_info::ALTER_COLUMN_NAME |
+ Alter_inplace_info::ALTER_COLUMN_DEFAULT |
+ Alter_inplace_info::CHANGE_CREATE_OPTION |
+ Alter_inplace_info::ALTER_RENAME | index_operations;
+
+ if (ha_alter_info->handler_flags & index_operations ||
+ !SameString(altered_table, "optname") ||
+ !SameBool(altered_table, "sepindex")) {
+ if (!IsTypeIndexable(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 operation
+
+ if (!SameString(altered_table, "filename")) {
+ if (!outward) {
+ // Conversion to outward table is only allowed for file based
+ // tables whose file does not exist.
+ tshp= altered_table->s;
+ char *fn= GetStringOption("filename");
+ tshp= NULL;
+
+ if (FileExists(fn)) {
+ strcpy(g->Message, "Operation denied. Table data would be lost.");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ALTER_ERROR);
+ } else
+ goto fin;
+
+ } else
+ goto fin;
+
+ } // endif filename
+
+ /* Is there at least one operation that requires copy algorithm? */
+ if (ha_alter_info->handler_flags & ~inplace_offline_operations)
+ goto fin;
+
+ /*
+ ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
+ ALTER TABLE table_name DEFAULT CHARSET = .. most likely
+ change column charsets and so not supported in-place through
+ old API.
+
+ Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
+ not supported as in-place operations in old API either.
+ */
+ if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
+ HA_CREATE_USED_DEFAULT_CHARSET |
+ HA_CREATE_USED_PACK_KEYS |
+ HA_CREATE_USED_MAX_ROWS) ||
+ (table->s->row_type != create_info->row_type))
+ goto fin;
+
+#if 0
+ uint table_changes= (ha_alter_info->handler_flags &
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
+ IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
+
+ if (table->file->check_if_incompatible_data(create_info, table_changes)
+ == COMPATIBLE_DATA_YES)
+ DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+#endif // 0
+
+ // This was in check_if_incompatible_data
+ if (NoFieldOptionChange(altered_table) &&
+ type == newtyp &&
+ SameInt(altered_table, "lrecl") &&
+ SameInt(altered_table, "elements") &&
+ SameInt(altered_table, "header") &&
+ SameInt(altered_table, "quoted") &&
+ SameInt(altered_table, "ending") &&
+ SameInt(altered_table, "compressed"))
+ DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+
+fin:
+ if (idx) {
+ // Indexing is only supported inplace
+ my_message(ER_ALTER_OPERATION_NOT_SUPPORTED,
+ "Alter operations not supported together by CONNECT", MYF(0));
+ DBUG_RETURN(HA_ALTER_ERROR);
+ } else if (outward) {
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ "This is an outward table, table data were not modified.");
+ DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+ } else
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+} // end of check_if_supported_inplace_alter
+
+
+/**
+ check_if_incompatible_data() called if ALTER TABLE can't detect otherwise
+ if new and old definition are compatible
+
+ @details If there are no other explicit signs like changed number of
+ fields this function will be called by compare_tables()
+ (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm
+ file.
+
+ @note: This function is no more called by check_if_supported_inplace_alter
+*/
+
+bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *info,
+ uint table_changes)
+{
+ DBUG_ENTER("ha_connect::check_if_incompatible_data");
+ // TO DO: really implement and check it.
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, 0,
+ "Unexpected call to check_if_incompatible_data.");
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+} // end of check_if_incompatible_data
+
+/****************************************************************************
+ * CONNECT MRR implementation: use DS-MRR
+ This is just copied from myisam
+ ***************************************************************************/
+
+int ha_connect::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf)
+{
+ return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
+} // end of multi_range_read_init
+
+int ha_connect::multi_range_read_next(range_id_t *range_info)
+{
+ return ds_mrr.dsmrr_next(range_info);
+} // end of multi_range_read_next
+
+ha_rows ha_connect::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, Cost_estimate *cost)
+{
+ /*
+ This call is here because there is no location where this->table would
+ already be known.
+ TODO: consider moving it into some per-query initialization call.
+ */
+ ds_mrr.init(this, table);
+
+ // MMR is implemented for "local" file based tables only
+ if (!IsFileType(GetRealType(GetTableOptionStruct(table))))
+ *flags|= HA_MRR_USE_DEFAULT_IMPL;
+
+ ha_rows rows= ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
+ bufsz, flags, cost);
+ xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
+ return rows;
+} // end of multi_range_read_info_const
+
+ha_rows ha_connect::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, Cost_estimate *cost)
+{
+ ds_mrr.init(this, table);
+
+ // MMR is implemented for "local" file based tables only
+ if (!IsFileType(GetRealType(GetTableOptionStruct(table))))
+ *flags|= HA_MRR_USE_DEFAULT_IMPL;
+
+ ha_rows rows= ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz,
+ flags, cost);
+ xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
+ return rows;
+} // end of multi_range_read_info
+
+
+int ha_connect::multi_range_read_explain_info(uint mrr_mode, char *str,
+ size_t size)
+{
+ return ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
+} // end of multi_range_read_explain_info
+
+/* CONNECT MRR implementation ends */
+
+#if 0
+// Does this make sens for CONNECT?
+Item *ha_connect::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ pushed_idx_cond_keyno= keyno_arg;
+ pushed_idx_cond= idx_cond_arg;
+ in_range_check_pushed_down= TRUE;
+ if (active_index == pushed_idx_cond_keyno)
+ mi_set_index_cond_func(file, handler_index_cond_check, this);
+ return NULL;
+}
+#endif // 0
+
+
+struct st_mysql_storage_engine connect_storage_engine=
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+maria_declare_plugin(connect)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &connect_storage_engine,
+ "CONNECT",
+ "Olivier Bertrand",
+ "Management of External Data (SQL/MED), including many file formats",
+ PLUGIN_LICENSE_GPL,
+ connect_init_func, /* Plugin Init */
+ connect_done_func, /* Plugin Deinit */
+ 0x0103, /* version number (1.03) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ "1.03", /* string version */
+ MariaDB_PLUGIN_MATURITY_BETA /* maturity */
+}
+maria_declare_plugin_end;
diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h
index a2d3c5d6801..1924d8de185 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -1,529 +1,524 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2013
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/** @file ha_connect.h
-
- @brief
- The ha_connect engine is a prototype storage engine to access external data.
-
- @see
- /sql/handler.h and /storage/connect/ha_connect.cc
-*/
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/****************************************************************************/
-/* Structures used to pass info between CONNECT and ha_connect. */
-/****************************************************************************/
-typedef struct _create_xinfo {
- char *Type; /* Retrieved from table comment */
- char *Filename; /* Set if not standard */
- char *IndexFN; /* Set if not standard */
- ulonglong Maxrows; /* Estimated max nb of rows */
- ulong Lrecl; /* Set if not default */
- ulong Elements; /* Number of lines in blocks */
- bool Fixed; /* False for DOS type */
- void *Pcf; /* To list of columns */
- void *Pxdf; /* To list of indexes */
-} CRXINFO, *PCXF;
-
-typedef struct _xinfo {
- ulonglong data_file_length; /* Length of data file */
- ha_rows records; /* Records in table */
- ulong mean_rec_length; /* Physical record length */
- char *data_file_name; /* Physical file name */
-} XINFO, *PXF;
-
-class XCHK : public BLOCK {
-public:
- XCHK(void) {oldsep= newsep= false;
- oldopn= newopn= NULL;
- oldpix= newpix= NULL;}
-
- inline char *SetName(PGLOBAL g, char *name) {
- char *nm= NULL;
- if (name) {nm= (char*)PlugSubAlloc(g, NULL, strlen(name) + 1);
- strcpy(nm, name);}
- return nm;}
-
- bool oldsep; // Sepindex before create/alter
- bool newsep; // Sepindex after create/alter
- char *oldopn; // Optname before create/alter
- char *newopn; // Optname after create/alter
- PIXDEF oldpix; // The indexes before create/alter
- PIXDEF newpix; // The indexes after create/alter
-}; // end of class XCHK
-
-typedef class XCHK *PCHK;
-typedef class user_connect *PCONNECT;
-typedef struct ha_table_option_struct TOS, *PTOS;
-typedef struct ha_field_option_struct FOS, *PFOS;
-
-extern handlerton *connect_hton;
-
-/**
- structure for CREATE TABLE options (table options)
-
- These can be specified in the CREATE TABLE:
- CREATE TABLE ( ... ) {...here...}
-*/
-struct ha_table_option_struct {
- const char *type;
- const char *filename;
- const char *optname;
- const char *tabname;
- const char *tablist;
- const char *dbname;
- const char *separator;
-//const char *connect;
- const char *qchar;
- const char *module;
- const char *subtype;
- const char *catfunc;
- const char *srcdef;
- const char *colist;
- const char *oplist;
- const char *data_charset;
- ulonglong lrecl;
- ulonglong elements;
-//ulonglong estimate;
- ulonglong multiple;
- ulonglong header;
- ulonglong quoted;
- ulonglong ending;
- ulonglong compressed;
- bool mapped;
- bool huge;
- bool split;
- bool readonly;
- bool sepindex;
- };
-
-/**
- structure for CREATE TABLE options (field options)
-
- These can be specified in the CREATE TABLE per field:
- CREATE TABLE ( field ... {...here...}, ... )
-*/
-struct ha_field_option_struct
-{
- ulonglong offset;
- ulonglong freq; // Not used by this version
- ulonglong opt; // Not used by this version
- ulonglong fldlen;
- const char *dateformat;
- const char *fieldformat;
- char *special;
-};
-
-/** @brief
- CONNECT_SHARE is a structure that will be shared among all open handlers.
- This example implements the minimum of what you will probably need.
-*/
-class CONNECT_SHARE : public Handler_share {
-public:
- mysql_mutex_t mutex;
- THR_LOCK lock;
- CONNECT_SHARE()
- {
- thr_lock_init(&lock);
- }
- ~CONNECT_SHARE()
- {
- thr_lock_delete(&lock);
- mysql_mutex_destroy(&mutex);
- }
-};
-
-typedef class ha_connect *PHC;
-
-/** @brief
- Class definition for the storage engine
-*/
-class ha_connect: public handler
-{
- THR_LOCK_DATA lock; ///< MySQL lock
- CONNECT_SHARE *share; ///< Shared lock info
- CONNECT_SHARE *get_share();
-
-public:
- ha_connect(handlerton *hton, TABLE_SHARE *table_arg);
- ~ha_connect();
-
- // CONNECT Implementation
- static bool connect_init(void);
- static bool connect_end(void);
- TABTYPE GetRealType(PTOS pos);
- char *GetStringOption(char *opname, char *sdef= NULL);
- PTOS GetTableOptionStruct(TABLE *table_arg);
- bool GetBooleanOption(char *opname, bool bdef);
- bool SetBooleanOption(char *opname, bool b);
- int GetIntegerOption(char *opname);
- bool CheckString(const char *str1, const char *str2);
- bool SameString(TABLE *tab, char *opn);
- bool SetIntegerOption(char *opname, int n);
- bool SameInt(TABLE *tab, char *opn);
- bool SameBool(TABLE *tab, char *opn);
- bool FileExists(const char *fn);
- bool NoFieldOptionChange(TABLE *tab);
- PFOS GetFieldOptionStruct(Field *fp);
- void *GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf);
- PIXDEF GetIndexInfo(TABLE_SHARE *s= NULL);
- const char *GetDBName(const char *name);
- const char *GetTableName(void);
-//int GetColNameLen(Field *fp);
-//char *GetColName(Field *fp);
-//void AddColName(char *cp, Field *fp);
- TABLE *GetTable(void) {return table;}
- bool IsSameIndex(PIXDEF xp1, PIXDEF xp2);
-
- PTDB GetTDB(PGLOBAL g);
- int OpenTable(PGLOBAL g, bool del= false);
- bool IsOpened(void);
- int CloseTable(PGLOBAL g);
- int MakeRecord(char *buf);
- int ScanRecord(PGLOBAL g, uchar *buf);
- int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
- int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
- uint key_len= 0);
-
- /** @brief
- The name that will be used for display purposes.
- */
- const char *table_type() const {return "CONNECT";}
-
- /** @brief
- The name of the index type that will be used for display.
- Don't implement this method unless you really have indexes.
- */
- const char *index_type(uint inx) { return "XPLUG"; }
-
- /** @brief
- The file extensions.
- */
- const char **bas_ext() const;
-
- /**
- Check if a storage engine supports a particular alter table in-place
- @note Called without holding thr_lock.c lock.
- */
- virtual enum_alter_inplace_result
- check_if_supported_inplace_alter(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info);
-
- /** @brief
- This is a list of flags that indicate what functionality the storage engine
- implements. The current table flags are documented in handler.h
- */
- ulonglong table_flags() const;
-
- /** @brief
- This is a bitmap of flags that indicates how the storage engine
- implements indexes. The current index flags are documented in
- handler.h. If you do not implement indexes, just return zero here.
-
- @details
- part is the key part to check. First key part is 0.
- If all_parts is set, MySQL wants to know the flags for the combined
- index, up to and including 'part'.
- */
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return HA_READ_NEXT | HA_READ_RANGE | HA_READ_ORDER
-#if defined(MRRBKA_SUPPORT)
- | HA_KEYREAD_ONLY
-#endif // MRRBKA_SUPPORT
- ;
- } // end of index_flags
-
- /** @brief
- unireg.cc will call max_supported_record_length(), max_supported_keys(),
- max_supported_key_parts(), uint max_supported_key_length()
- to make sure that the storage engine can handle the data it is about to
- send. Return *real* limits of your storage engine here; MySQL will do
- min(your_limits, MySQL_limits) automatically.
- */
- uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
-
- /** @brief
- unireg.cc will call this to make sure that the storage engine can handle
- the data it is about to send. Return *real* limits of your storage engine
- here; MySQL will do min(your_limits, MySQL_limits) automatically.
-
- @details
- There is no need to implement ..._key_... methods if your engine doesn't
- support indexes.
- */
- uint max_supported_keys() const { return 10; }
-
- /** @brief
- unireg.cc will call this to make sure that the storage engine can handle
- the data it is about to send. Return *real* limits of your storage engine
- here; MySQL will do min(your_limits, MySQL_limits) automatically.
-
- @details
- There is no need to implement ..._key_... methods if your engine doesn't
- support indexes.
- */
- uint max_supported_key_parts() const { return 10; }
-
- /** @brief
- unireg.cc will call this to make sure that the storage engine can handle
- the data it is about to send. Return *real* limits of your storage engine
- here; MySQL will do min(your_limits, MySQL_limits) automatically.
-
- @details
- There is no need to implement ..._key_... methods if your engine doesn't
- support indexes.
- */
- uint max_supported_key_length() const { return 255; }
-
- /** @brief
- Called in test_quick_select to determine if indexes should be used.
- */
- virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; }
-
- /** @brief
- This method will never be called if you do not implement indexes.
- */
- virtual double read_time(uint, uint, ha_rows rows)
- { return (double) rows / 20.0+1; }
-
- /*
- Everything below are methods that we implement in ha_connect.cc.
-
- Most of these methods are not obligatory, skip them and
- MySQL will treat them as not implemented
- */
- virtual bool get_error_message(int error, String *buf);
-
- /**
- Push condition down to the table handler.
-
- @param cond Condition to be pushed. The condition tree must not be
- modified by the by the caller.
-
- @return
- The 'remainder' condition that caller must use to filter out records.
- NULL means the handler will not return rows that do not match the
- passed condition.
-
- @note
- The pushed conditions form a stack (from which one can remove the
- last pushed condition using cond_pop).
- The table handler filters out rows using (pushed_cond1 AND pushed_cond2
- AND ... AND pushed_condN)
- or less restrictive condition, depending on handler's capabilities.
-
- handler->ha_reset() call empties the condition stack.
- Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
- condition stack.
- */
-virtual const COND *cond_push(const COND *cond);
-PCFIL CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond);
-const char *GetValStr(OPVAL vop, bool neg);
-#if defined(BLK_INDX)
-PFIL CondFilter(PGLOBAL g, Item *cond);
-#endif // BLK_INDX
-
- /**
- Number of rows in table. It will only be called if
- (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
- */
- virtual ha_rows records();
-
- /**
- Type of table for caching query
- CONNECT should not use caching because its tables are external
- data prone to me modified out of MariaDB
- */
- virtual uint8 table_cache_type(void)
- {
-#if defined(MEMORY_TRACE)
- // Temporary until bug MDEV-4771 is fixed
- return HA_CACHE_TBL_NONTRANSACT;
-#else
- return HA_CACHE_TBL_NOCACHE;
-#endif
- }
-
- /** @brief
- We implement this in ha_connect.cc; it's a required method.
- */
- int open(const char *name, int mode, uint test_if_locked); // required
-
- /** @brief
- We implement this in ha_connect.cc; it's a required method.
- */
- int close(void); // required
-
- /** @brief
- 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 write_row(uchar *buf);
-
- /** @brief
- 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 update_row(const uchar *old_data, uchar *new_data);
-
- /** @brief
- 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 delete_row(const uchar *buf);
-
- // Added to the connect handler
- int index_init(uint idx, bool sorted);
- int index_end();
- int index_read(uchar * buf, const uchar * key, uint key_len,
- enum ha_rkey_function find_flag);
- int index_next_same(uchar *buf, const uchar *key, uint keylen);
-
- /** @brief
- 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_read_map(uchar *buf, const uchar *key,
-// key_part_map keypart_map, enum ha_rkey_function find_flag);
-
- /** @brief
- 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_next(uchar *buf);
-
- /** @brief
- 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);
-
- /** @brief
- 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_first(uchar *buf);
-
- /** @brief
- 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_last(uchar *buf);
-
- /** @brief
- Unlike index_init(), rnd_init() can be called two consecutive times
- without rnd_end() in between (it only makes sense if scan=1). In this
- case, the second call should prepare for the new table scan (e.g if
- rnd_init() allocates the cursor, the second call should position the
- cursor to the start of the table; no need to deallocate and allocate
- it again. This is a required method.
- */
- int rnd_init(bool scan); //required
- int rnd_end();
- int rnd_next(uchar *buf); ///< required
- int rnd_pos(uchar *buf, uchar *pos); ///< required
- void position(const uchar *record); ///< required
- int info(uint); ///< required
- int extra(enum ha_extra_function operation);
- int start_stmt(THD *thd, thr_lock_type lock_type);
- int external_lock(THD *thd, int lock_type); ///< required
- int delete_all_rows(void);
- ha_rows records_in_range(uint inx, key_range *min_key,
- key_range *max_key);
- /**
- These methods can be overridden, but their default implementation
- provide useful functionality.
- */
- int rename_table(const char *from, const char *to);
- /**
- Delete a table in the engine. Called for base as well as temporary
- tables.
- */
- int delete_table(const char *name);
- /**
- Called by delete_table and rename_table
- */
- int delete_or_rename_table(const char *from, const char *to);
- int create(const char *name, TABLE *form,
- HA_CREATE_INFO *create_info); ///< required
- bool check_if_incompatible_data(HA_CREATE_INFO *info,
- uint table_changes);
-
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type); ///< required
- int optimize(THD* thd, HA_CHECK_OPT* check_opt);
-
-protected:
- bool check_privileges(THD *thd, PTOS options, char *dbn);
- MODE CheckMode(PGLOBAL g, THD *thd, MODE newmode, bool *chk, bool *cras);
- char *GetDBfromName(const char *name);
-
- // Members
- static ulong num; // Tracable handler number
- PCONNECT xp; // To user_connect associated class
- ulong hnum; // The number of this handler
- query_id_t valid_query_id; // The one when tdbp was allocated
- query_id_t creat_query_id; // The one when handler was allocated
- PTDB tdbp; // To table class object
- PVAL sdvalin; // Used to convert date values
- PVAL sdvalout; // Used to convert date values
- bool istable; // True for table handler
-//char tname[64]; // The table name
- MODE xmod; // Table mode
- XINFO xinfo; // The table info structure
- bool valid_info; // True if xinfo is valid
- bool stop; // Used when creating index
- bool alter; // True when converting to other engine
- bool mrr; // True when getting index positions
- int indexing; // Type of indexing for CONNECT
- int locked; // Table lock
- THR_LOCK_DATA lock_data;
-
-public:
- TABLE_SHARE *tshp; // Used by called tables
- char *data_file_name;
- char *index_file_name;
- uint int_table_flags; // Inherited from MyISAM
- bool enable_activate_all_index; // Inherited from MyISAM
-
-#if defined(MRRBKA_SUPPORT)
- /**
- * Multi Range Read interface
- */
- int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
- uint n_ranges, uint mode, HANDLER_BUFFER *buf);
- int multi_range_read_next(range_id_t *range_info);
- ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
- void *seq_init_param,
- uint n_ranges, uint *bufsz,
- uint *flags, Cost_estimate *cost);
- ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
- uint key_parts, uint *bufsz,
- uint *flags, Cost_estimate *cost);
- int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size);
-
- int reset(void) {ds_mrr.dsmrr_close(); return 0;}
-
- /* Index condition pushdown implementation */
-// Item *idx_cond_push(uint keyno, Item* idx_cond);
-private:
- DsMrr_impl ds_mrr;
-#endif // MRRBKA_SUPPORT
-}; // end of ha_connect class definition
+/* Copyright (C) Olivier Bertrand 2004 - 2014
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/** @file ha_connect.h
+
+ @brief
+ The ha_connect engine is a prototype storage engine to access external data.
+
+ @see
+ /sql/handler.h and /storage/connect/ha_connect.cc
+*/
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/****************************************************************************/
+/* Structures used to pass info between CONNECT and ha_connect. */
+/****************************************************************************/
+typedef struct _create_xinfo {
+ char *Type; /* Retrieved from table comment */
+ char *Filename; /* Set if not standard */
+ char *IndexFN; /* Set if not standard */
+ ulonglong Maxrows; /* Estimated max nb of rows */
+ ulong Lrecl; /* Set if not default */
+ ulong Elements; /* Number of lines in blocks */
+ bool Fixed; /* False for DOS type */
+ void *Pcf; /* To list of columns */
+ void *Pxdf; /* To list of indexes */
+} CRXINFO, *PCXF;
+
+typedef struct _xinfo {
+ ulonglong data_file_length; /* Length of data file */
+ ha_rows records; /* Records in table */
+ ulong mean_rec_length; /* Physical record length */
+ char *data_file_name; /* Physical file name */
+} XINFO, *PXF;
+
+class XCHK : public BLOCK {
+public:
+ XCHK(void) {oldsep= newsep= false;
+ oldopn= newopn= NULL;
+ oldpix= newpix= NULL;}
+
+ inline char *SetName(PGLOBAL g, char *name) {
+ char *nm= NULL;
+ if (name) {nm= (char*)PlugSubAlloc(g, NULL, strlen(name) + 1);
+ strcpy(nm, name);}
+ return nm;}
+
+ bool oldsep; // Sepindex before create/alter
+ bool newsep; // Sepindex after create/alter
+ char *oldopn; // Optname before create/alter
+ char *newopn; // Optname after create/alter
+ PIXDEF oldpix; // The indexes before create/alter
+ PIXDEF newpix; // The indexes after create/alter
+}; // end of class XCHK
+
+typedef class XCHK *PCHK;
+typedef class user_connect *PCONNECT;
+typedef struct ha_table_option_struct TOS, *PTOS;
+typedef struct ha_field_option_struct FOS, *PFOS;
+
+extern handlerton *connect_hton;
+
+/**
+ structure for CREATE TABLE options (table options)
+
+ These can be specified in the CREATE TABLE:
+ CREATE TABLE ( ... ) {...here...}
+*/
+struct ha_table_option_struct {
+ const char *type;
+ const char *filename;
+ const char *optname;
+ const char *tabname;
+ const char *tablist;
+ const char *dbname;
+ const char *separator;
+//const char *connect;
+ const char *qchar;
+ const char *module;
+ const char *subtype;
+ const char *catfunc;
+ const char *srcdef;
+ const char *colist;
+ const char *oplist;
+ const char *data_charset;
+ ulonglong lrecl;
+ ulonglong elements;
+//ulonglong estimate;
+ ulonglong multiple;
+ ulonglong header;
+ ulonglong quoted;
+ ulonglong ending;
+ ulonglong compressed;
+ bool mapped;
+ bool huge;
+ bool split;
+ bool readonly;
+ bool sepindex;
+ };
+
+/**
+ structure for CREATE TABLE options (field options)
+
+ These can be specified in the CREATE TABLE per field:
+ CREATE TABLE ( field ... {...here...}, ... )
+*/
+struct ha_field_option_struct
+{
+ ulonglong offset;
+ ulonglong freq;
+ ulonglong opt;
+ ulonglong fldlen;
+ const char *dateformat;
+ const char *fieldformat;
+ char *special;
+};
+
+/** @brief
+ CONNECT_SHARE is a structure that will be shared among all open handlers.
+ This example implements the minimum of what you will probably need.
+*/
+class CONNECT_SHARE : public Handler_share {
+public:
+ mysql_mutex_t mutex;
+ THR_LOCK lock;
+ CONNECT_SHARE()
+ {
+ thr_lock_init(&lock);
+ }
+ ~CONNECT_SHARE()
+ {
+ thr_lock_delete(&lock);
+ mysql_mutex_destroy(&mutex);
+ }
+};
+
+typedef class ha_connect *PHC;
+
+/** @brief
+ Class definition for the storage engine
+*/
+class ha_connect: public handler
+{
+ THR_LOCK_DATA lock; ///< MySQL lock
+ CONNECT_SHARE *share; ///< Shared lock info
+ CONNECT_SHARE *get_share();
+
+public:
+ ha_connect(handlerton *hton, TABLE_SHARE *table_arg);
+ ~ha_connect();
+
+ // CONNECT Implementation
+ static bool connect_init(void);
+ static bool connect_end(void);
+ TABTYPE GetRealType(PTOS pos);
+ char *GetStringOption(char *opname, char *sdef= NULL);
+ PTOS GetTableOptionStruct(TABLE *table_arg);
+ bool GetBooleanOption(char *opname, bool bdef);
+ bool SetBooleanOption(char *opname, bool b);
+ int GetIntegerOption(char *opname);
+ bool CheckString(const char *str1, const char *str2);
+ bool SameString(TABLE *tab, char *opn);
+ bool SetIntegerOption(char *opname, int n);
+ bool SameInt(TABLE *tab, char *opn);
+ bool SameBool(TABLE *tab, char *opn);
+ bool FileExists(const char *fn);
+ bool NoFieldOptionChange(TABLE *tab);
+ PFOS GetFieldOptionStruct(Field *fp);
+ void *GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf);
+ PIXDEF GetIndexInfo(TABLE_SHARE *s= NULL);
+ const char *GetDBName(const char *name);
+ const char *GetTableName(void);
+//int GetColNameLen(Field *fp);
+//char *GetColName(Field *fp);
+//void AddColName(char *cp, Field *fp);
+ TABLE *GetTable(void) {return table;}
+ bool IsSameIndex(PIXDEF xp1, PIXDEF xp2);
+
+ PTDB GetTDB(PGLOBAL g);
+ int OpenTable(PGLOBAL g, bool del= false);
+ bool IsOpened(void);
+ int CloseTable(PGLOBAL g);
+ int MakeRecord(char *buf);
+ int ScanRecord(PGLOBAL g, uchar *buf);
+ int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
+ int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
+ uint key_len= 0);
+
+ /** @brief
+ The name that will be used for display purposes.
+ */
+ const char *table_type() const {return "CONNECT";}
+
+ /** @brief
+ The name of the index type that will be used for display.
+ Don't implement this method unless you really have indexes.
+ */
+ const char *index_type(uint inx) { return "XPLUG"; }
+
+ /** @brief
+ The file extensions.
+ */
+ const char **bas_ext() const;
+
+ /**
+ Check if a storage engine supports a particular alter table in-place
+ @note Called without holding thr_lock.c lock.
+ */
+ virtual enum_alter_inplace_result
+ check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+
+ /** @brief
+ This is a list of flags that indicate what functionality the storage engine
+ implements. The current table flags are documented in handler.h
+ */
+ ulonglong table_flags() const;
+
+ /** @brief
+ This is a bitmap of flags that indicates how the storage engine
+ implements indexes. The current index flags are documented in
+ handler.h. If you do not implement indexes, just return zero here.
+
+ @details
+ part is the key part to check. First key part is 0.
+ If all_parts is set, MySQL wants to know the flags for the combined
+ index, up to and including 'part'.
+ */
+ ulong index_flags(uint inx, uint part, bool all_parts) const
+ {
+ return HA_READ_NEXT | HA_READ_RANGE | HA_READ_ORDER | HA_KEYREAD_ONLY;
+ } // end of index_flags
+
+ /** @brief
+ unireg.cc will call max_supported_record_length(), max_supported_keys(),
+ max_supported_key_parts(), uint max_supported_key_length()
+ to make sure that the storage engine can handle the data it is about to
+ send. Return *real* limits of your storage engine here; MySQL will do
+ min(your_limits, MySQL_limits) automatically.
+ */
+ uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
+
+ /** @brief
+ unireg.cc will call this to make sure that the storage engine can handle
+ the data it is about to send. Return *real* limits of your storage engine
+ here; MySQL will do min(your_limits, MySQL_limits) automatically.
+
+ @details
+ There is no need to implement ..._key_... methods if your engine doesn't
+ support indexes.
+ */
+ uint max_supported_keys() const { return 10; }
+
+ /** @brief
+ unireg.cc will call this to make sure that the storage engine can handle
+ the data it is about to send. Return *real* limits of your storage engine
+ here; MySQL will do min(your_limits, MySQL_limits) automatically.
+
+ @details
+ There is no need to implement ..._key_... methods if your engine doesn't
+ support indexes.
+ */
+ uint max_supported_key_parts() const { return 10; }
+
+ /** @brief
+ unireg.cc will call this to make sure that the storage engine can handle
+ the data it is about to send. Return *real* limits of your storage engine
+ here; MySQL will do min(your_limits, MySQL_limits) automatically.
+
+ @details
+ There is no need to implement ..._key_... methods if your engine doesn't
+ support indexes.
+ */
+ uint max_supported_key_length() const { return 255; }
+
+ /** @brief
+ Called in test_quick_select to determine if indexes should be used.
+ */
+ virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; }
+
+ /** @brief
+ This method will never be called if you do not implement indexes.
+ */
+ virtual double read_time(uint, uint, ha_rows rows)
+ { return (double) rows / 20.0+1; }
+
+ /*
+ Everything below are methods that we implement in ha_connect.cc.
+
+ Most of these methods are not obligatory, skip them and
+ MySQL will treat them as not implemented
+ */
+ virtual bool get_error_message(int error, String *buf);
+
+ /**
+ Push condition down to the table handler.
+
+ @param cond Condition to be pushed. The condition tree must not be
+ modified by the by the caller.
+
+ @return
+ The 'remainder' condition that caller must use to filter out records.
+ NULL means the handler will not return rows that do not match the
+ passed condition.
+
+ @note
+ The pushed conditions form a stack (from which one can remove the
+ last pushed condition using cond_pop).
+ The table handler filters out rows using (pushed_cond1 AND pushed_cond2
+ AND ... AND pushed_condN)
+ or less restrictive condition, depending on handler's capabilities.
+
+ handler->ha_reset() call empties the condition stack.
+ Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
+ condition stack.
+ */
+virtual const COND *cond_push(const COND *cond);
+PCFIL CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond);
+const char *GetValStr(OPVAL vop, bool neg);
+PFIL CondFilter(PGLOBAL g, Item *cond);
+
+ /**
+ Number of rows in table. It will only be called if
+ (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
+ */
+ virtual ha_rows records();
+
+ /**
+ Type of table for caching query
+ CONNECT should not use caching because its tables are external
+ data prone to me modified out of MariaDB
+ */
+ virtual uint8 table_cache_type(void)
+ {
+#if defined(MEMORY_TRACE)
+ // Temporary until bug MDEV-4771 is fixed
+ return HA_CACHE_TBL_NONTRANSACT;
+#else
+ return HA_CACHE_TBL_NOCACHE;
+#endif
+ }
+
+ /** @brief
+ We implement this in ha_connect.cc; it's a required method.
+ */
+ int open(const char *name, int mode, uint test_if_locked); // required
+
+ /** @brief
+ We implement this in ha_connect.cc; it's a required method.
+ */
+ int close(void); // required
+
+ /** @brief
+ 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 write_row(uchar *buf);
+
+ /** @brief
+ 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 update_row(const uchar *old_data, uchar *new_data);
+
+ /** @brief
+ 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 delete_row(const uchar *buf);
+
+ // Added to the connect handler
+ int index_init(uint idx, bool sorted);
+ int index_end();
+ int index_read(uchar * buf, const uchar * key, uint key_len,
+ enum ha_rkey_function find_flag);
+ int index_next_same(uchar *buf, const uchar *key, uint keylen);
+
+ /** @brief
+ 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_read_map(uchar *buf, const uchar *key,
+// key_part_map keypart_map, enum ha_rkey_function find_flag);
+
+ /** @brief
+ 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_next(uchar *buf);
+
+ /** @brief
+ 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);
+
+ /** @brief
+ 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_first(uchar *buf);
+
+ /** @brief
+ 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_last(uchar *buf);
+
+ /* Index condition pushdown implementation */
+//Item *idx_cond_push(uint keyno, Item* idx_cond);
+
+ /** @brief
+ Unlike index_init(), rnd_init() can be called two consecutive times
+ without rnd_end() in between (it only makes sense if scan=1). In this
+ case, the second call should prepare for the new table scan (e.g if
+ rnd_init() allocates the cursor, the second call should position the
+ cursor to the start of the table; no need to deallocate and allocate
+ it again. This is a required method.
+ */
+ int rnd_init(bool scan); //required
+ int rnd_end();
+ int rnd_next(uchar *buf); ///< required
+ int rnd_pos(uchar *buf, uchar *pos); ///< required
+ void position(const uchar *record); ///< required
+ int info(uint); ///< required
+ int extra(enum ha_extra_function operation);
+ int start_stmt(THD *thd, thr_lock_type lock_type);
+ int external_lock(THD *thd, int lock_type); ///< required
+ int delete_all_rows(void);
+ ha_rows records_in_range(uint inx, key_range *min_key,
+ key_range *max_key);
+ /**
+ These methods can be overridden, but their default implementation
+ provide useful functionality.
+ */
+ int rename_table(const char *from, const char *to);
+ /**
+ Delete a table in the engine. Called for base as well as temporary
+ tables.
+ */
+ int delete_table(const char *name);
+ /**
+ Called by delete_table and rename_table
+ */
+ int delete_or_rename_table(const char *from, const char *to);
+ int create(const char *name, TABLE *form,
+ HA_CREATE_INFO *create_info); ///< required
+ bool check_if_incompatible_data(HA_CREATE_INFO *info,
+ uint table_changes);
+
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type); ///< required
+ int optimize(THD* thd, HA_CHECK_OPT* check_opt);
+
+ /**
+ * Multi Range Read interface
+ */
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf);
+ int multi_range_read_next(range_id_t *range_info);
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, Cost_estimate *cost);
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, Cost_estimate *cost);
+ int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size);
+
+ int reset(void) {ds_mrr.dsmrr_close(); return 0;}
+
+ /* Index condition pushdown implementation */
+// Item *idx_cond_push(uint keyno, Item* idx_cond);
+private:
+ DsMrr_impl ds_mrr;
+
+protected:
+ bool check_privileges(THD *thd, PTOS options, char *dbn);
+ MODE CheckMode(PGLOBAL g, THD *thd, MODE newmode, bool *chk, bool *cras);
+ char *GetDBfromName(const char *name);
+
+ // Members
+ static ulong num; // Tracable handler number
+ PCONNECT xp; // To user_connect associated class
+ ulong hnum; // The number of this handler
+ query_id_t valid_query_id; // The one when tdbp was allocated
+ query_id_t creat_query_id; // The one when handler was allocated
+ PTDB tdbp; // To table class object
+ PVAL sdvalin; // Used to convert date values
+ PVAL sdvalout; // Used to convert date values
+ bool istable; // True for table handler
+//char tname[64]; // The table name
+ MODE xmod; // Table mode
+ XINFO xinfo; // The table info structure
+ bool valid_info; // True if xinfo is valid
+ bool stop; // Used when creating index
+ bool alter; // True when converting to other engine
+ bool mrr; // True when getting index positions
+ int indexing; // Type of indexing for CONNECT
+ int locked; // Table lock
+ THR_LOCK_DATA lock_data;
+
+public:
+ TABLE_SHARE *tshp; // Used by called tables
+ char *data_file_name;
+ char *index_file_name;
+ uint int_table_flags; // Inherited from MyISAM
+ bool enable_activate_all_index; // Inherited from MyISAM
+}; // end of ha_connect class definition
diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h
index 8a49239ec7a..856b6c73ef3 100644
--- a/storage/connect/myconn.h
+++ b/storage/connect/myconn.h
@@ -63,15 +63,10 @@ class DllItem MYSQLC {
bool Connected(void);
// Methods
-// int GetCurPos(void) {return (m_Res) ? N : 0;}
-// int GetProgCur(void) {return N;}
int GetResultSize(PGLOBAL g, PSZ sql);
int Open(PGLOBAL g, const char *host, const char *db,
const char *user= "root", const char *pwd= "*",
int pt= 0);
-//ulong GetThreadID(void);
-//ulong ServerVersion(void);
-//const char *ServerInfo(void);
int KillQuery(ulong id);
int ExecSQL(PGLOBAL g, const char *query, int *w = NULL);
int ExecSQLcmd(PGLOBAL g, const char *query, int *w);
@@ -87,7 +82,6 @@ class DllItem MYSQLC {
void Rewind(void);
void FreeResult(void);
void Close(void);
-//void DiscardResults(void);
protected:
MYSQL_FIELD *GetNextField(void);
diff --git a/storage/connect/plgcnx.h b/storage/connect/plgcnx.h
index fe3997d1986..a1208f9b885 100644
--- a/storage/connect/plgcnx.h
+++ b/storage/connect/plgcnx.h
@@ -62,146 +62,6 @@ enum INFO {INDX_RC, /* Index of PlugDB return code field */
INDX_SIZE, /* Index of returned data size field */
INDX_MAX}; /* Size of info array */
-#ifdef NOT_USED
-/**************************************************************************/
-/* Internal message types. */
-/**************************************************************************/
-enum MSGTYP {MST_OPEN = 10, /* Code for old connecting message */
- MST_COMMAND = 11, /* Code for send command message */
- MST_RESULT = 12, /* Code for get result message */
- MST_CLOSE = 13, /* Code for disconnecting message */
- MST_PROGRESS = 14, /* Code for progress message */
- MST_CANCEL = 15, /* Code for cancel message */
- MST_PROCESSED = 16, /* Code for already processed msg */
- MST_ERROR = 17, /* Code for get error message */
- MST_CHAR = 18, /* Code for get char value message */
- MST_LONG = 19, /* Code for get int value message */
- MST_COLUMN = 20, /* Code for get col value message */
- MST_MESSAGE = 21, /* Code for get message message */
- MST_HEADER = 22, /* Code for get header message */
- MST_SOCKET = 23, /* Code for socket error message */
- MST_SHUTDOWN = 24, /* Code for shutdown message */
- MST_SOCKPROG = 25, /* Code for socket progress message */
- MST_POST = 26, /* Code for post command message */
- MST_NEW_OPEN = 27, /* Code for new connecting message */
- MST_PROG_NUM = 5}; /* Num of integers in progress msg */
-
-/**************************************************************************/
-/* Vendors. */
-/**************************************************************************/
-enum VENDOR {VDR_UNKNOWN = -2, /* Not known or not connected */
- VDR_PlugDB = -1, /* PlugDB */
- VDR_OTHER = 0}; /* OEM */
-
-/**************************************************************************/
-/* Attribute keys of Result Description structure (arranged by type). */
-/**************************************************************************/
-enum CKEYS {K_ProgMsg, K_Lang, K_ActiveDB, K_Cmax};
-enum LKEYS {K_NBcol, K_NBlin, K_CurPos, K_RC, K_Result, K_Elapsed,
- K_Continued, K_Maxsize, K_Affrows, K_Lmax, K_Maxcol,
- K_Maxres, K_Maxlin, K_NBparm};
-enum NKEYS {K_Type, K_Length, K_Prec, K_DataLen, K_Unsigned, K_Nmax};
-
-/**************************************************************************/
-/* Result description structures. */
-/**************************************************************************/
-typedef struct _MsgTagAttr {
- bool fSupplied;
- char Attr[MAXMESSAGE];
- } MTAG, *PMTAG;
-
-typedef struct _CharTagAttr {
- bool fSupplied;
- char Attr[MAXDBNAME];
- } CTAG, *PCTAG;
-
-typedef struct _LongTagAttr {
- bool fSupplied;
- int Attr;
- } LTAG, *PLTAG;
-
-typedef struct _ColVar {
- LTAG Lat[K_Nmax];
- CTAG Cat;
- } COLVAR, *LPCOLVAR;
-
-typedef struct _ResDesc {
- int Maxcol; /* Max number of columns */
- int Colnum; /* Number of columns */
- MTAG Mat; /* Message */
- CTAG Cat[K_Cmax]; /* Character attributes */
- LTAG Lat[K_Lmax]; /* Long int attributes */
- COLVAR Col[1]; /* Column attributes */
- } RDESC, *PRDESC;
-
-/**************************************************************************/
-/* Exported PlugDB client functions in Plgcnx DLL. */
-/**************************************************************************/
-#if !defined(CNXFUNC)
-#if defined(UNIX) || defined(UNIV_LINUX)
-#undef __stdcall
-#define __stdcall
-#endif
-
-#if defined(NOLIB) /* Dynamic link of plgcnx.dll */
-#define CNXFUNC(f) (__stdcall *f)
-#else /* LIB */ /* Static link with plgcnx.lib */
-#define CNXFUNC(f) __stdcall f
-#endif
-#endif
-
-#if !defined(CNXKEY)
-#define CNXKEY uint
-#endif
-
-#if !defined(XTRN)
-#define XTRN
-#endif
-
-//#if !defined(NO_FUNC)
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-XTRN int CNXFUNC(PLGConnect) (CNXKEY *, const char *, bool);
-XTRN int CNXFUNC(PLGSendCommand) (CNXKEY, const char *, void *, int, int *);
-XTRN int CNXFUNC(PLGGetResult) (CNXKEY, void *, int, int *, bool);
-XTRN int CNXFUNC(PLGDisconnect) (CNXKEY);
-XTRN int CNXFUNC(PLGGetErrorMsg) (CNXKEY, char *, int, int *);
-XTRN bool CNXFUNC(PLGGetCharValue)(CNXKEY, char *, int, int);
-XTRN bool CNXFUNC(PLGGetIntValue)(CNXKEY, int *, int);
-XTRN bool CNXFUNC(PLGGetColValue) (CNXKEY, int *, int, int);
-XTRN bool CNXFUNC(PLGGetMessage) (CNXKEY, char *, int);
-XTRN bool CNXFUNC(PLGGetHeader) (CNXKEY, char *, int, int, int);
-
-#ifdef __cplusplus
-}
-#endif
-//#endif /* !NO_FUNC */
-
-/**************************************************************************/
-/* Convenience function Definitions */
-/**************************************************************************/
-#define PLGPostCommand(T,C) PLGSendCommand(T,C,NULL,0,NULL)
-#if defined(FNCMAC)
-#define PLGGetProgMsg(T,C,S) PLGGetCharValue(T,C,S,K_ProgMsg)
-#define PLGGetLangID(T,C,S) PLGGetCharValue(T,C,S,K_Lang)
-#define PLGGetActiveDB(T,C,S) PLGGetCharValue(T,C,S,K_ActiveDB)
-#define PLGGetCursorPos(T,L) PLGGetIntValue(T,L,K_CurPos)
-#define PLGGetResultType(T,L) PLGGetIntValue(T,L,K_Result)
-#define PLGGetNBcol(T,L) PLGGetIntValue(T,L,K_NBcol)
-#define PLGGetNBlin(T,L) PLGGetIntValue(T,L,K_NBlin)
-#define PLGGetRetCode(T,L) PLGGetIntValue(T,L,K_RC)
-#define PLGGetElapsed(T,L) PLGGetIntValue(T,L,K_Elapsed)
-#define PLGGetContinued(T,L) PLGGetIntValue(T,L,K_Continued)
-#define PLGGetMaxSize(T,L) PLGGetIntValue(T,L,K_Maxsize)
-#define PLGGetLength(T,L,C) PLGGetColValue(T,L,K_Length,C)
-#define PLGGetDataSize(T,L,C) PLGGetColValue(T,L,K_DataLen,C)
-#define PLGGetDecimal(T,L,C) PLGGetColValue(T,L,K_Prec,C)
-#define PLGGetType(T,L,C) PLGGetColValue(T,L,K_Type,C)
-#endif /* FNCMAC */
-#endif // NOT_USED
-
#endif /* !_PLGCNX_H */
/* ------------------------- End of Plgcnx.h ---------------------------- */
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index 99e9fb8fb08..e7c824aa164 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -1,643 +1,597 @@
-/************** PlgDBSem H Declares Source Code File (.H) **************/
-/* Name: PLGDBSEM.H Version 3.6 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
-/* */
-/* This file contains the PlugDB++ application type definitions. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include required application header files */
-/***********************************************************************/
-#include "checklvl.h"
-
-/***********************************************************************/
-/* DB Constant definitions. */
-/***********************************************************************/
-#if defined(FRENCH)
-#define DEFAULT_LOCALE "French"
-#else // !FRENCH
-#define DEFAULT_LOCALE "English"
-#endif // !FRENCH
-
-#define DOS_MAX_PATH 144 /* Must be the same across systems */
-#define DOS_BUFF_LEN 100 /* Number of lines in binary file buffer */
-#undef DOMAIN /* For Unix version */
-
-enum BLKTYP {TYPE_TABLE = 50, /* Table Name/Srcdef/... Block */
- TYPE_COLUMN = 51, /* Column Name/Qualifier Block */
-// TYPE_OPVAL = 52, /* Operator value (OPVAL) */
- TYPE_TDB = 53, /* Table Description Block */
- TYPE_COLBLK = 54, /* Column Description Block */
-#if defined(BLK_INDX)
- TYPE_FILTER = 55, /* Filter Description Block */
- TYPE_ARRAY = 63, /* General array type */
-#endif // BLK_INDX
- TYPE_PSZ = 64, /* Pointer to String ended by 0 */
- TYPE_SQL = 65, /* Pointer to SQL block */
- TYPE_XOBJECT = 69, /* Extended DB object */
- TYPE_COLCRT = 71, /* Column creation block */
- TYPE_CONST = 72, /* Constant */
-// TYPE_INDEXDEF = 73, /* Index definition block */
-// TYPE_OPER = 74, /* Operator block (OPER) */
-
-/*-------------------- type tokenized string --------------------------*/
- TYPE_DATE = 8, /* Timestamp */
-/*-------------------- additional values used by LNA ------------------*/
- TYPE_COLIST = 14, /* Column list */
- TYPE_COL = 41, /* Column */
-/*-------------------- types used by scalar functions -----------------*/
- TYPE_NUM = 12,
- TYPE_UNDEF = 13,
-/*-------------------- file blocks used when closing ------------------*/
- TYPE_FB_FILE = 22, /* File block (stream) */
- TYPE_FB_MAP = 23, /* Mapped file block (storage) */
- TYPE_FB_HANDLE = 24, /* File block (handle) */
- TYPE_FB_XML = 21, /* DOM XML file block */
- TYPE_FB_XML2 = 27}; /* libxml2 XML file block */
-
-enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */
- TAB_DOS = 1, /* Fixed column offset, variable LRECL */
- TAB_FIX = 2, /* Fixed column offset, fixed LRECL */
- TAB_BIN = 3, /* Like FIX but can have binary fields */
- TAB_CSV = 4, /* DOS files with CSV records */
- TAB_FMT = 5, /* DOS files with formatted recordss */
- TAB_DBF = 6, /* DBF Dbase or Foxpro files */
- TAB_XML = 7, /* XML or HTML files */
- TAB_INI = 8, /* INI or CFG files */
- TAB_VEC = 9, /* Vector column arrangement */
- TAB_ODBC = 10, /* Table accessed via (unix)ODBC */
- TAB_MYSQL = 11, /* MySQL table accessed via MySQL API */
- TAB_DIR = 12, /* Returns a list of files */
- TAB_MAC = 13, /* MAC address (Windows only) */
- TAB_WMI = 14, /* WMI tables (Windows only) */
- TAB_TBL = 15, /* Collection of CONNECT tables */
- TAB_OEM = 16, /* OEM implemented table */
- TAB_XCL = 17, /* XCL table */
- TAB_OCCUR = 18, /* OCCUR table */
- TAB_PRX = 19, /* Proxy (catalog) table */
- TAB_PLG = 20, /* PLG NIY */
- TAB_PIVOT = 21, /* PIVOT NIY */
- TAB_JCT = 22, /* Junction tables NIY */
- TAB_DMY = 23, /* DMY Dummy tables NIY */
- TAB_NIY = 24}; /* Table not implemented yet */
-
-enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
- TYPE_AM_ROWID = 1, /* ROWID type (special column) */
- TYPE_AM_FILID = 2, /* FILEID type (special column) */
- TYPE_AM_TAB = 3, /* Table (any type) */
- TYPE_AM_VIEW = 4, /* VIEW (any type) */
- TYPE_AM_SRVID = 5, /* SERVID type (special column) */
- TYPE_AM_TABID = 6, /* TABID type (special column) */
- TYPE_AM_CNSID = 7, /* CONSTID type (special column) */
- TYPE_AM_COUNT = 10, /* CPT AM type no (count table) */
- TYPE_AM_DCD = 20, /* Decode access method type no */
- TYPE_AM_CMS = 30, /* CMS access method type no */
- TYPE_AM_MAP = 32, /* MAP access method type no */
- TYPE_AM_FMT = 33, /* DOS files with formatted recs */
- TYPE_AM_CSV = 34, /* DOS files with CSV records */
- TYPE_AM_MCV = 35, /* MAP files with CSV records */
- TYPE_AM_DOS = 36, /* DOS am with Lrecl = V */
- TYPE_AM_FIX = 38, /* DOS am with Lrecl = F */
- TYPE_AM_BIN = 39, /* DOS am with Lrecl = B */
- TYPE_AM_VCT = 40, /* VCT access method type no */
- TYPE_AM_VMP = 43, /* VMP access method type no */
- TYPE_AM_QRY = 50, /* QRY access method type no */
- TYPE_AM_QRS = 51, /* QRYRES access method type no */
- TYPE_AM_SQL = 60, /* SQL VIEW access method type */
- TYPE_AM_PLG = 70, /* PLG access method type no */
- TYPE_AM_PLM = 71, /* PDM access method type no */
- TYPE_AM_DOM = 80, /* DOM access method type no */
- TYPE_AM_DIR = 90, /* DIR access method type no */
- TYPE_AM_ODBC = 100, /* ODBC access method type no */
- TYPE_AM_XDBC = 101, /* XDBC access method type no */
- TYPE_AM_OEM = 110, /* OEM access method type no */
- TYPE_AM_TBL = 115, /* TBL access method type no */
- TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
- TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
- TYPE_AM_FNC = 122, /* PIVOT source column type no */
- TYPE_AM_XCOL = 124, /* XCOL access method type no */
- TYPE_AM_XML = 127, /* XML access method type no */
- TYPE_AM_OCCUR = 128, /* OCCUR access method type no */
- TYPE_AM_PRX = 129, /* PROXY access method type no */
- TYPE_AM_XTB = 130, /* SYS table access method type */
- TYPE_AM_BLK = 131, /* BLK access method type no */
- TYPE_AM_ZIP = 132, /* ZIP access method type no */
- TYPE_AM_ZLIB = 133, /* ZLIB access method type no */
- TYPE_AM_MAC = 137, /* MAC table access method type */
- TYPE_AM_WMI = 139, /* WMI table access method type */
- TYPE_AM_XCL = 140, /* SYS column access method type */
- TYPE_AM_INI = 150, /* INI files access method */
- TYPE_AM_TFC = 155, /* TFC (Circa) (Fuzzy compare) */
- TYPE_AM_DBF = 160, /* DBF Dbase files am type no */
- TYPE_AM_JCT = 170, /* Junction tables am type no */
- TYPE_AM_DMY = 172, /* DMY Dummy tables am type no */
- TYPE_AM_SET = 180, /* SET Set tables am type no */
- TYPE_AM_MYSQL = 192, /* MYSQL access method type no */
- TYPE_AM_MYX = 193, /* MYSQL EXEC access method type */
- TYPE_AM_CAT = 195, /* Catalog access method type no */
- TYPE_AM_OUT = 200}; /* Output relations (storage) */
-
-enum RECFM {RECFM_NAF = -2, /* Not a file */
- RECFM_OEM = -1, /* OEM file access method */
- RECFM_VAR = 0, /* Varying length DOS files */
- RECFM_FIX = 1, /* Fixed length DOS files */
- RECFM_BIN = 2, /* Binary DOS files (also fixed) */
- RECFM_VCT = 3, /* VCT formatted files */
- RECFM_ODBC = 4, /* Table accessed via ODBC */
- RECFM_PLG = 5, /* Table accessed via PLGconn */
- RECFM_DBF = 6}; /* DBase formatted file */
-
-enum MISC {DB_TABNO = 1, /* DB routines in Utility Table */
- MAX_MULT_KEY = 10, /* Max multiple key number */
- NAM_LEN = 128, /* Length of col and tab names */
- ARRAY_SIZE = 50, /* Default array block size */
-// MAXRES = 500, /* Default maximum result lines */
-// MAXLIN = 10000, /* Default maximum data lines */
- MAXBMP = 32}; /* Default XDB2 max bitmap size */
-
-#if 0
-enum ALGMOD {AMOD_AUTO = 0, /* PLG chooses best algorithm */
- AMOD_SQL = 1, /* Use SQL algorithm */
- AMOD_QRY = 2}; /* Use QUERY algorithm */
-#endif // 0
-
-enum MODE {MODE_ERROR = -1, /* Invalid mode */
- MODE_ANY = 0, /* Unspecified mode */
- MODE_READ = 10, /* Input/Output mode */
- MODE_WRITE = 20, /* Input/Output mode */
- MODE_UPDATE = 30, /* Input/Output mode */
- MODE_INSERT = 40, /* Input/Output mode */
- MODE_DELETE = 50, /* Input/Output mode */
- MODE_ALTER = 60}; /* alter mode */
-
-#if !defined(RC_OK_DEFINED)
-#define RC_OK_DEFINED
-enum RCODE {RC_OK = 0, /* No error return code */
- RC_NF = 1, /* Not found return code */
- RC_EF = 2, /* End of file return code */
- RC_FX = 3, /* Error return code */
- RC_INFO = 4}; /* Success with info */
-#endif // !RC_OK_DEFINED
-
-enum OPVAL {OP_EQ = 1, /* Filtering operator = */
- OP_NE = 2, /* Filtering operator != */
- OP_GT = 3, /* Filtering operator > */
- OP_GE = 4, /* Filtering operator >= */
- OP_LT = 5, /* Filtering operator < */
- OP_LE = 6, /* Filtering operator <= */
- OP_IN = 7, /* Filtering operator IN */
- OP_NULL = 8, /* Filtering operator IS NULL */
- OP_EXIST = 9, /* Filtering operator EXISTS */
- OP_LIKE = 10, /* Filtering operator LIKE */
- OP_LOJ = -1, /* Filter op LEFT OUTER JOIN */
- OP_ROJ = -2, /* Filter op RIGHT OUTER JOIN */
- OP_DTJ = -3, /* Filter op DISTINCT JOIN */
- OP_XX = 11, /* Filtering operator unknown */
- OP_AND = 12, /* Filtering operator AND */
- OP_OR = 13, /* Filtering operator OR */
- OP_CNC = 14, /* Expression Concat operator */
- OP_NOT = 15, /* Filtering operator NOT */
- OP_SEP = 20, /* Filtering separator */
- OP_ADD = 16, /* Expression Add operator */
- OP_SUB = 17, /* Expression Substract operator */
- OP_MULT = 18, /* Expression Multiply operator */
- OP_DIV = 19, /* Expression Divide operator */
- OP_NOP = 21, /* Scalar function is nopped */
- OP_NUM = 22, /* Scalar function Op Num */
- OP_ABS = 23, /* Scalar function Op Abs */
- OP_MAX = 24, /* Scalar function Op Max */
- OP_MIN = 25, /* Scalar function Op Min */
- OP_CEIL = 26, /* Scalar function Op Ceil */
- OP_FLOOR = 27, /* Scalar function Op Floor */
- OP_MOD = 28, /* Scalar function Op Mod */
- OP_ROUND = 29, /* Scalar function Op Round */
- OP_SIGN = 30, /* Scalar function Op Sign */
- OP_LEN = 31, /* Scalar function Op Len */
- OP_INSTR = 32, /* Scalar function Op Instr */
- OP_LEFT = 33, /* Scalar function Op Left */
- OP_RIGHT = 34, /* Scalar function Op Right */
- OP_ASCII = 35, /* Scalar function Op Ascii */
- OP_EXP = 36, /* Scalar function Op Exp */
- OP_LN = 37, /* Scalar function Op Ln */
- OP_LOG = 38, /* Scalar function Op Log */
- OP_POWER = 39, /* Scalar function Op Power */
- OP_SQRT = 40, /* Scalar function Op Sqrt */
- OP_COS = 41, /* Scalar function Op Cos */
- OP_COSH = 42, /* Scalar function Op Cosh */
- OP_SIN = 43, /* Scalar function Op Sin */
- OP_SINH = 44, /* Scalar function Op Sinh */
- OP_TAN = 45, /* Scalar function Op Tan */
- OP_TANH = 46, /* Scalar function Op Tanh */
- OP_USER = 47, /* Scalar function Op User */
- OP_CHAR = 48, /* Scalar function Op Char */
- OP_UPPER = 49, /* Scalar function Op Upper */
- OP_LOWER = 50, /* Scalar function Op Lower */
- OP_RPAD = 51, /* Scalar function Op Rpad */
- OP_LPAD = 52, /* Scalar function Op Lpad */
- OP_LTRIM = 53, /* Scalar function Op Ltrim */
- OP_RTRIM = 54, /* Scalar function Op Rtrim */
- OP_REPL = 55, /* Scalar function Op Replace */
- OP_SUBST = 56, /* Scalar function Op Substr */
- OP_LJUST = 57, /* Scalar function Op Ljustify */
- OP_RJUST = 58, /* Scalar function Op Rjustify */
- OP_CJUST = 59, /* Scalar function Op Cjustify */
- OP_ENCODE = 60, /* Scalar function Op Encode */
- OP_DECODE = 61, /* Scalar function Op Decode */
- OP_SEQU = 62, /* Scalar function Op Sequence */
- OP_IF = 63, /* Scalar function Op If */
- OP_STRING = 64, /* Scalar function Op String */
- OP_TOKEN = 65, /* Scalar function Op Token */
- OP_SNDX = 66, /* Scalar function Op Soundex */
- OP_DATE = 67, /* Scalar function Op Date */
- OP_MDAY = 68, /* Scalar function Op Month Day */
- OP_MONTH = 69, /* Scalar function Op Month of */
- OP_YEAR = 70, /* Scalar function Op Year of */
- OP_WDAY = 71, /* Scalar function Op Week Day */
- OP_YDAY = 72, /* Scalar function Op Year Day */
- OP_DBTWN = 73, /* Scalar function Op Days betwn */
- OP_MBTWN = 74, /* Scalar function Op Months btw */
- OP_YBTWN = 75, /* Scalar function Op Years btwn */
- OP_ADDAY = 76, /* Scalar function Op Add Days */
- OP_ADDMTH = 77, /* Scalar function Op Add Months */
- OP_ADDYR = 78, /* Scalar function Op Add Years */
- OP_NXTDAY = 79, /* Scalar function Op Next Day */
- OP_SYSDT = 80, /* Scalar function Op SysDate */
- OP_DELTA = 81, /* Scalar function Op Delta */
- OP_LAST = 82, /* Scalar function Op Last */
- OP_IFF = 83, /* Scalar function Op Iff */
- OP_MAVG = 84, /* Scalar function Op Moving Avg */
- OP_VWAP = 85, /* Scalar function Op VWAP */
- OP_TIME = 86, /* Scalar function Op TIME */
- OP_SETLEN = 87, /* Scalar function Op Set Length */
- OP_TRANSL = 88, /* Scalar function Op Translate */
- OP_BITAND = 89, /* Expression BitAnd operator */
- OP_BITOR = 90, /* Expression BitOr operator */
- OP_BITXOR = 91, /* Expression XOR operator */
- OP_BITNOT = 92, /* Expression Complement operator*/
- OP_CNTIN = 93, /* Scalar function Count In */
- OP_FDISK = 94, /* Scalar function Disk of fileid*/
- OP_FPATH = 95, /* Scalar function Path of fileid*/
- OP_FNAME = 96, /* Scalar function Name of fileid*/
- OP_FTYPE = 97, /* Scalar function Type of fileid*/
- OP_XDATE = 98, /* Scalar function Op Fmt Date */
- OP_SWITCH = 99, /* Scalar function Op Switch */
- OP_EXIT = 100, /* Scalar function Op Exit */
- OP_LIT = 101, /* Scalar function Op Literal */
- OP_LOCALE = 102, /* Scalar function Op Locale */
- OP_FRNCH = 103, /* Scalar function Op French */
- OP_ENGLSH = 104, /* Scalar function Op English */
- OP_RAND = 105, /* Scalar function Op Rand(om) */
- OP_FIRST = 106, /* Index operator Find First */
- OP_NEXT = 107, /* Index operator Find Next */
- OP_SAME = 108, /* Index operator Find Next Same */
- OP_FSTDIF = 109, /* Index operator Find First dif */
- OP_NXTDIF = 110, /* Index operator Find Next dif */
- OP_VAL = 111, /* Scalar function Op Valist */
- OP_QUART = 112, /* Scalar function Op QUARTER */
- OP_CURDT = 113, /* Scalar function Op CurDate */
- OP_NWEEK = 114, /* Scalar function Op Week number*/
- OP_ROW = 115, /* Scalar function Op Row */
- OP_SYSTEM = 200, /* Scalar function Op System */
- OP_REMOVE = 201, /* Scalar function Op Remove */
- OP_RENAME = 202, /* Scalar function Op Rename */
- OP_FCOMP = 203}; /* Scalar function Op Compare */
-
-enum TUSE {USE_NO = 0, /* Table is not yet linearized */
- USE_LIN = 1, /* Table is linearized */
- USE_READY = 2, /* Column buffers are allocated */
- USE_OPEN = 3, /* Table is open */
- USE_CNT = 4, /* Specific to LNA */
- USE_NOKEY = 5}; /* Specific to SqlToHql */
-
-/***********************************************************************/
-/* Following definitions are used to indicate the status of a column. */
-/***********************************************************************/
-enum STATUS {BUF_NO = 0x00, /* Column buffer not allocated */
- BUF_EMPTY = 0x01, /* Column buffer is empty */
- BUF_READY = 0x02, /* Column buffer is ready */
- BUF_READ = 0x04, /* Column buffer has read value */
- BUF_MAPPED = 0x08}; /* Used by the VMPFAM class */
-
-/***********************************************************************/
-/* Following definitions are used to indicate how a column is used. */
-/* Corresponding bits are ON if the column is used in: */
-/***********************************************************************/
-enum COLUSE {U_P = 0x01, /* the projection list. */
- U_J_EXT = 0x02, /* a join filter. */
- U_J_INT = 0x04, /* a join after linearisation. */
-/*-- Such a column have a constant value throughout a subquery eval. --*/
- U_CORREL = 0x08, /* a correlated sub-query */
-/*-------------------- additional values used by CONNECT --------------*/
- U_VAR = 0x10, /* a VARCHAR column */
- U_VIRTUAL = 0x20, /* a VIRTUAL column */
- U_NULLS = 0x40, /* The column may have nulls */
- U_IS_NULL = 0x80, /* The column has a null value */
- U_SPECIAL = 0x100, /* The column is special */
- U_UNSIGNED = 0x200, /* The column type is unsigned */
- U_ZEROFILL = 0x400}; /* The column is zero filled */
-
-/***********************************************************************/
-/* DB description class and block pointer definitions. */
-/***********************************************************************/
-typedef class XTAB *PTABLE;
-typedef class COLUMN *PCOLUMN;
-typedef class XOBJECT *PXOB;
-typedef class COLBLK *PCOL;
-//pedef class TBX *PTBX;
-typedef class TDB *PTDB;
-typedef void *PSQL; // Not used
-typedef class TDBASE *PTDBASE;
-typedef class TDBDOS *PTDBDOS;
-typedef class TDBFIX *PTDBFIX;
-typedef class TDBFMT *PTDBFMT;
-typedef class TDBCSV *PTDBCSV;
-typedef class TDBDOM *PTDBDOM;
-typedef class TDBDIR *PTDBDIR;
-typedef class DOSCOL *PDOSCOL;
-typedef class CSVCOL *PCSVCOL;
-typedef class MAPCOL *PMAPCOL;
-typedef class TDBMFT *PTDBMFT;
-typedef class TDBMCV *PTDBMCV;
-typedef class MCVCOL *PMCVCOL;
-typedef class RESCOL *PRESCOL;
-typedef class XXBASE *PKXBASE;
-typedef class KXYCOL *PXCOL;
-typedef class CATALOG *PCATLG;
-typedef class RELDEF *PRELDEF;
-typedef class TABDEF *PTABDEF;
-typedef class DOSDEF *PDOSDEF;
-typedef class CSVDEF *PCSVDEF;
-typedef class VCTDEF *PVCTDEF;
-typedef class PIVOTDEF *PPIVOTDEF;
-typedef class DOMDEF *PDOMDEF;
-typedef class DIRDEF *PDIRDEF;
-typedef class OEMDEF *POEMDEF;
-typedef class COLCRT *PCOLCRT;
-typedef class COLDEF *PCOLDEF;
-typedef class CONSTANT *PCONST;
-typedef class VALUE *PVAL;
-typedef class VALBLK *PVBLK;
-#if defined(BLK_INDX)
-typedef class FILTER *PFIL;
-#endif // BLK_INDX
-
-typedef struct _fblock *PFBLOCK;
-typedef struct _mblock *PMBLOCK;
-typedef struct _cblock *PCBLOCK;
-typedef struct _tabs *PTABS;
-typedef struct _qryres *PQRYRES;
-typedef struct _colres *PCOLRES;
-typedef struct _datpar *PDTP;
-typedef struct indx_used *PXUSED;
-
-/***********************************************************************/
-/* Utility blocks for file and storage. */
-/***********************************************************************/
-typedef struct _fblock { /* Opened (mapped) file block */
- struct _fblock *Next;
- LPCSTR Fname; /* Point on file name */
- size_t Length; /* File length (<4GB) */
- short Count; /* Nb of times map is used */
- short Type; /* TYPE_FB_FILE or TYPE_FB_MAP */
- MODE Mode; /* Open mode */
- char *Memory; /* Pointer to file mapping view */
- void *File; /* FILE pointer */
- HANDLE Handle; /* File handle */
- } FBLOCK;
-
-typedef struct _mblock { /* Memory block */
- PMBLOCK Next;
- bool Inlist; /* True if in mblock list */
- size_t Size; /* Size of allocation */
- bool Sub; /* True if suballocated */
- void *Memp; /* Memory pointer */
- } MBLOCK;
-
-/***********************************************************************/
-/* The QUERY application User Block. */
-/***********************************************************************/
-typedef struct { /* User application block */
-//void *Act2; /* RePoint to activity block */
-//short LineLen; /* Current output line len */
- NAME Name; /* User application name */
-//NAME Password; /* User application password */
-//PSZ UserFile; /* User application filename */
- char Server[17]; /* Server name */
- char DBName[17]; /* Current database name */
-//char Host[65]; /* Caller's host name */
-//char User[17]; /* Caller's user name */
-//uint Granted; /* Grant bitmap */
- PCATLG Catalog; /* To CATALOG class */
- PQRYRES Result; /* To query result blocks */
- PFBLOCK Openlist; /* To file/map open list */
- PMBLOCK Memlist; /* To memory block list */
- PXUSED Xlist; /* To used index list */
-//int Maxres; /* Result Max nb of lines */
-//int Maxtmp; /* Intermediate tables Maxres */
-//int Maxlin; /* Query Max nb of data lines */
-#if defined(BLK_INDX)
- int Maxbmp; /* Maximum XDB2 bitmap size */
-#endif // BLK_INDX
- int Check; /* General level of checking */
- int Numlines; /* Number of lines involved */
-//ALGMOD AlgChoice; /* Choice of algorithm mode */
-//AREADEF DescArea; /* Table desc. area size */
- USETEMP UseTemp; /* Use temporary file */
-//int Curtype; /* 0: static else: dynamic */
- int Vtdbno; /* Used for TDB number setting */
- bool Remote; /* true: if remotely called */
-//bool NotFinal; /* true: for intermediate table */
- bool Proginfo; /* true: return progress info */
- bool Subcor; /* Used for Progress info */
- size_t ProgMax; /* Used for Progress info */
- size_t ProgCur; /* Used for Progress info */
- size_t ProgSav; /* Used for Progress info */
- LPCSTR Step; /* Execution step name */
-//char Work[_MAX_PATH]; /* Local work path */
- } DBUSERBLK, *PDBUSER;
-
-/***********************************************************************/
-/* Column output format. */
-/***********************************************************************/
-typedef struct _format { /* Format descriptor block */
- char Type[2]; /* C:char, F:double, N:int, Dx: date */
- ushort Length; /* Output length */
- short Prec; /* Output precision */
- } FORMAT, *PFORMAT;
-
-/***********************************************************************/
-/* Definition of blocks used in type and copy routines. */
-/***********************************************************************/
-typedef struct _tabptr { /* start=P1 */
- struct _tabptr *Next;
- int Num; /* alignement */
- void *Old[50];
- void *New[50]; /* old and new values of copied ptrs */
- } TABPTR, *PTABPTR;
-
-typedef struct _tabadr { /* start=P3 */
- struct _tabadr *Next;
- int Num;
- void *Adx[50]; /* addr of pointers to be reset */
- } TABADR, *PTABADR;
-
-typedef struct _tabs {
- PGLOBAL G;
- PTABPTR P1;
- PTABADR P3;
- } TABS;
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Argument of expression, function, filter etc. (Xobject) */
-/***********************************************************************/
-typedef struct _arg { /* Argument */
- PXOB To_Obj; /* To the argument object */
- PVAL Value; /* Argument value */
- bool Conv; /* TRUE if conversion is required */
- } ARGBLK, *PARG;
-
-typedef struct _oper { /* Operator */
- PSZ Name; /* The input/output operator name */
- OPVAL Val; /* Operator numeric value */
- int Mod; /* The modificator */
- } OPER, *POPER;
-
-#if 0
-/***********************************************************************/
-/* Definitions and table of Scalar Functions. */
-/***********************************************************************/
-typedef struct _sfdsc { /* Scalar function description block*/
- char Name[16]; /* Scalar function name */
- EVAL EvalType; /* Type of Init and Eval functions */
- OPVAL Op; /* Equivalent operator number */
- int R_Type; /* Result Type */
- int R_Length; /* Result Length */
- int R_Prec; /* Result Precision */
- int Numarg; /* Number of arguments */
- } SFDSC, *PSFDSC;
-#endif // 0
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* Following definitions are used to define table fields (columns). */
-/***********************************************************************/
-enum XFLD {FLD_NO = 0, /* Not a field definition item */
- FLD_NAME = 1, /* Item name */
- FLD_TYPE = 2, /* Field type */
- FLD_TYPENAME = 3, /* Field type name */
- FLD_PREC = 4, /* Field precision (length?) */
- FLD_LENGTH = 5, /* Field length (?) */
- FLD_SCALE = 6, /* Field scale (precision) */
- FLD_RADIX = 7, /* Field radix */
- FLD_NULL = 8, /* Field nullable property */
- FLD_REM = 9, /* Field comment (remark) */
- FLD_CHARSET = 10, /* Field collation */
- FLD_KEY = 11, /* Field key property */
- FLD_DEFAULT = 12, /* Field default value */
- FLD_EXTRA = 13, /* Field extra info */
- FLD_PRIV = 14, /* Field priviledges */
- FLD_DATEFMT = 15, /* Field date format */
- FLD_CAT = 16, /* Table catalog */
- FLD_SCHEM = 17, /* Table schema */
- FLD_TABNAME = 18}; /* Column Table name */
-
-/***********************************************************************/
-/* Result of last SQL noconv query. */
-/***********************************************************************/
-typedef struct _qryres {
- PCOLRES Colresp; /* Points to columns of result */
- bool Continued; /* true when more rows to fetch */
- bool Truncated; /* true when truncated by maxres */
- bool Suball; /* true when entirely suballocated */
- bool Info; /* true when info msg generated */
- int Maxsize; /* Max query number of lines */
- int Maxres; /* Allocation size */
- int Nblin; /* Number of rows in result set */
- int Nbcol; /* Number of columns in result set */
- int Cursor; /* Starting position to get data */
- int BadLines; /* Skipped bad lines in table file */
- } QRYRES, *PQRYRES;
-
-typedef struct _colres {
- PCOLRES Next; /* To next result column */
- PCOL Colp; /* To matching column block */
- PSZ Name; /* Column header */
- PVBLK Kdata; /* Column block of values */
- char *Nulls; /* Column null value array */
- int Type; /* Internal type */
- int Datasize; /* Overall data size */
- int Ncol; /* Column number */
- int Clen; /* Data individual internal size */
- int Length; /* Data individual print length */
- int Prec; /* Precision */
- int Flag; /* Flag option value */
- XFLD Fld; /* Type of field info */
- } COLRES;
-
-#if defined(WIN32) && !defined(NOEX)
-#define DllExport __declspec( dllexport )
-#else // !WIN32
-#define DllExport
-#endif // !WIN32
-
-/***********************************************************************/
-/* Utility routines. */
-/***********************************************************************/
-PPARM Vcolist(PGLOBAL, PTDB, PSZ, bool);
-void PlugPutOut(PGLOBAL, FILE *, short, void *, uint);
-void PlugLineDB(PGLOBAL, PSZ, short, void *, uint);
-char *PlgGetDataPath(PGLOBAL g);
-void AddPointer(PTABS, void *);
-PDTP MakeDateFormat(PGLOBAL, PSZ, bool, bool, int);
-int ExtractDate(char *, PDTP, int, int val[6]);
-
-/**************************************************************************/
-/* Allocate the result structure that will contain result data. */
-/**************************************************************************/
-DllExport PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
- int *buftyp, XFLD *fldtyp,
- unsigned int *length,
- bool blank, bool nonull);
-
-/***********************************************************************/
-/* Exported utility routines. */
-/***********************************************************************/
-DllExport FILE *PlugOpenFile(PGLOBAL, LPCSTR, LPCSTR);
-DllExport int PlugCloseFile(PGLOBAL, PFBLOCK, bool all = false);
-DllExport void PlugCleanup(PGLOBAL, bool);
-DllExport bool GetPromptAnswer(PGLOBAL, char *);
-DllExport char *GetAmName(PGLOBAL g, AMT am, void *memp = NULL);
-DllExport PDBUSER PlgMakeUser(PGLOBAL g);
-DllExport PDBUSER PlgGetUser(PGLOBAL g);
-DllExport PCATLG PlgGetCatalog(PGLOBAL g, bool jump = true);
-DllExport bool PlgSetXdbPath(PGLOBAL g, PSZ, PSZ, char *, int, char *, int);
-DllExport void PlgDBfree(MBLOCK&);
-DllExport void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size);
-DllExport void *PlgDBalloc(PGLOBAL, void *, MBLOCK&);
-DllExport void *PlgDBrealloc(PGLOBAL, void *, MBLOCK&, size_t);
-//lExport PSZ GetIniString(PGLOBAL, void *, LPCSTR, LPCSTR, LPCSTR, LPCSTR);
-//lExport int GetIniSize(char *, char *, char *, char *);
-//lExport bool WritePrivateProfileInt(LPCSTR, LPCSTR, int, LPCSTR);
-DllExport void NewPointer(PTABS, void *, void *);
-DllExport char *GetIni(int n= 0);
-DllExport void SetTrc(void);
-DllExport char *GetListOption(PGLOBAL, const char *, const char *,
- const char *def=NULL);
-
-#define MSGID_NONE 0
-#define MSGID_CANNOT_OPEN 1
-#define MSGID_OPEN_MODE_ERROR 2
-#define MSGID_OPEN_STRERROR 3
-#define MSGID_OPEN_ERROR_AND_STRERROR 4
-#define MSGID_OPEN_MODE_STRERROR 5
-#define MSGID_OPEN_EMPTY_FILE 6
-
-FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode);
-int global_open(GLOBAL *g, int msgid, const char *filename, int flags);
-int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode);
-DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
-char *MakeEscape(PGLOBAL g, char* str, char q);
-
-DllExport bool PushWarning(PGLOBAL, PTDBASE, int level = 1);
+/************** PlgDBSem H Declares Source Code File (.H) **************/
+/* Name: PLGDBSEM.H Version 3.6 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* */
+/* This file contains the PlugDB++ application type definitions. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include required application header files */
+/***********************************************************************/
+#include "checklvl.h"
+
+/***********************************************************************/
+/* DB Constant definitions. */
+/***********************************************************************/
+#if defined(FRENCH)
+#define DEFAULT_LOCALE "French"
+#else // !FRENCH
+#define DEFAULT_LOCALE "English"
+#endif // !FRENCH
+
+#define DOS_MAX_PATH 144 /* Must be the same across systems */
+#define DOS_BUFF_LEN 100 /* Number of lines in binary file buffer */
+#undef DOMAIN /* For Unix version */
+
+enum BLKTYP {TYPE_TABLE = 50, /* Table Name/Srcdef/... Block */
+ TYPE_COLUMN = 51, /* Column Name/Qualifier Block */
+ TYPE_TDB = 53, /* Table Description Block */
+ TYPE_COLBLK = 54, /* Column Description Block */
+ TYPE_FILTER = 55, /* Filter Description Block */
+ TYPE_ARRAY = 63, /* General array type */
+ TYPE_PSZ = 64, /* Pointer to String ended by 0 */
+ TYPE_SQL = 65, /* Pointer to SQL block */
+ TYPE_XOBJECT = 69, /* Extended DB object */
+ TYPE_COLCRT = 71, /* Column creation block */
+ TYPE_CONST = 72, /* Constant */
+
+/*-------------------- type tokenized string --------------------------*/
+ TYPE_DATE = 8, /* Timestamp */
+/*-------------------- additional values used by LNA ------------------*/
+ TYPE_COLIST = 14, /* Column list */
+ TYPE_COL = 41, /* Column */
+/*-------------------- types used by scalar functions -----------------*/
+ TYPE_NUM = 12,
+ TYPE_UNDEF = 13,
+/*-------------------- file blocks used when closing ------------------*/
+ TYPE_FB_FILE = 22, /* File block (stream) */
+ TYPE_FB_MAP = 23, /* Mapped file block (storage) */
+ TYPE_FB_HANDLE = 24, /* File block (handle) */
+ TYPE_FB_XML = 21, /* DOM XML file block */
+ TYPE_FB_XML2 = 27}; /* libxml2 XML file block */
+
+enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */
+ TAB_DOS = 1, /* Fixed column offset, variable LRECL */
+ TAB_FIX = 2, /* Fixed column offset, fixed LRECL */
+ TAB_BIN = 3, /* Like FIX but can have binary fields */
+ TAB_CSV = 4, /* DOS files with CSV records */
+ TAB_FMT = 5, /* DOS files with formatted recordss */
+ TAB_DBF = 6, /* DBF Dbase or Foxpro files */
+ TAB_XML = 7, /* XML or HTML files */
+ TAB_INI = 8, /* INI or CFG files */
+ TAB_VEC = 9, /* Vector column arrangement */
+ TAB_ODBC = 10, /* Table accessed via (unix)ODBC */
+ TAB_MYSQL = 11, /* MySQL table accessed via MySQL API */
+ TAB_DIR = 12, /* Returns a list of files */
+ TAB_MAC = 13, /* MAC address (Windows only) */
+ TAB_WMI = 14, /* WMI tables (Windows only) */
+ TAB_TBL = 15, /* Collection of CONNECT tables */
+ TAB_OEM = 16, /* OEM implemented table */
+ TAB_XCL = 17, /* XCL table */
+ TAB_OCCUR = 18, /* OCCUR table */
+ TAB_PRX = 19, /* Proxy (catalog) table */
+ TAB_PLG = 20, /* PLG NIY */
+ TAB_PIVOT = 21, /* PIVOT NIY */
+ TAB_JCT = 22, /* Junction tables NIY */
+ TAB_DMY = 23, /* DMY Dummy tables NIY */
+ TAB_NIY = 24}; /* Table not implemented yet */
+
+enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
+ TYPE_AM_ROWID = 1, /* ROWID type (special column) */
+ TYPE_AM_FILID = 2, /* FILEID type (special column) */
+ TYPE_AM_TAB = 3, /* Table (any type) */
+ TYPE_AM_VIEW = 4, /* VIEW (any type) */
+ TYPE_AM_SRVID = 5, /* SERVID type (special column) */
+ TYPE_AM_TABID = 6, /* TABID type (special column) */
+ TYPE_AM_CNSID = 7, /* CONSTID type (special column) */
+ TYPE_AM_COUNT = 10, /* CPT AM type no (count table) */
+ TYPE_AM_DCD = 20, /* Decode access method type no */
+ TYPE_AM_CMS = 30, /* CMS access method type no */
+ TYPE_AM_MAP = 32, /* MAP access method type no */
+ TYPE_AM_FMT = 33, /* DOS files with formatted recs */
+ TYPE_AM_CSV = 34, /* DOS files with CSV records */
+ TYPE_AM_MCV = 35, /* MAP files with CSV records */
+ TYPE_AM_DOS = 36, /* DOS am with Lrecl = V */
+ TYPE_AM_FIX = 38, /* DOS am with Lrecl = F */
+ TYPE_AM_BIN = 39, /* DOS am with Lrecl = B */
+ TYPE_AM_VCT = 40, /* VCT access method type no */
+ TYPE_AM_VMP = 43, /* VMP access method type no */
+ TYPE_AM_QRY = 50, /* QRY access method type no */
+ TYPE_AM_QRS = 51, /* QRYRES access method type no */
+ TYPE_AM_SQL = 60, /* SQL VIEW access method type */
+ TYPE_AM_PLG = 70, /* PLG access method type no */
+ TYPE_AM_PLM = 71, /* PDM access method type no */
+ TYPE_AM_DOM = 80, /* DOM access method type no */
+ TYPE_AM_DIR = 90, /* DIR access method type no */
+ TYPE_AM_ODBC = 100, /* ODBC access method type no */
+ TYPE_AM_XDBC = 101, /* XDBC access method type no */
+ TYPE_AM_OEM = 110, /* OEM access method type no */
+ TYPE_AM_TBL = 115, /* TBL access method type no */
+ TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
+ TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
+ TYPE_AM_FNC = 122, /* PIVOT source column type no */
+ TYPE_AM_XCOL = 124, /* XCOL access method type no */
+ TYPE_AM_XML = 127, /* XML access method type no */
+ TYPE_AM_OCCUR = 128, /* OCCUR access method type no */
+ TYPE_AM_PRX = 129, /* PROXY access method type no */
+ TYPE_AM_XTB = 130, /* SYS table access method type */
+ TYPE_AM_BLK = 131, /* BLK access method type no */
+ TYPE_AM_ZIP = 132, /* ZIP access method type no */
+ TYPE_AM_ZLIB = 133, /* ZLIB access method type no */
+ TYPE_AM_MAC = 137, /* MAC table access method type */
+ TYPE_AM_WMI = 139, /* WMI table access method type */
+ TYPE_AM_XCL = 140, /* SYS column access method type */
+ TYPE_AM_INI = 150, /* INI files access method */
+ TYPE_AM_TFC = 155, /* TFC (Circa) (Fuzzy compare) */
+ TYPE_AM_DBF = 160, /* DBF Dbase files am type no */
+ TYPE_AM_JCT = 170, /* Junction tables am type no */
+ TYPE_AM_DMY = 172, /* DMY Dummy tables am type no */
+ TYPE_AM_SET = 180, /* SET Set tables am type no */
+ TYPE_AM_MYSQL = 192, /* MYSQL access method type no */
+ TYPE_AM_MYX = 193, /* MYSQL EXEC access method type */
+ TYPE_AM_CAT = 195, /* Catalog access method type no */
+ TYPE_AM_OUT = 200}; /* Output relations (storage) */
+
+enum RECFM {RECFM_NAF = -2, /* Not a file */
+ RECFM_OEM = -1, /* OEM file access method */
+ RECFM_VAR = 0, /* Varying length DOS files */
+ RECFM_FIX = 1, /* Fixed length DOS files */
+ RECFM_BIN = 2, /* Binary DOS files (also fixed) */
+ RECFM_VCT = 3, /* VCT formatted files */
+ RECFM_ODBC = 4, /* Table accessed via ODBC */
+ RECFM_PLG = 5, /* Table accessed via PLGconn */
+ RECFM_DBF = 6}; /* DBase formatted file */
+
+enum MISC {DB_TABNO = 1, /* DB routines in Utility Table */
+ MAX_MULT_KEY = 10, /* Max multiple key number */
+ NAM_LEN = 128, /* Length of col and tab names */
+ ARRAY_SIZE = 50, /* Default array block size */
+// MAXRES = 500, /* Default maximum result lines */
+// MAXLIN = 10000, /* Default maximum data lines */
+ MAXBMP = 32}; /* Default XDB2 max bitmap size */
+
+#if 0
+enum ALGMOD {AMOD_AUTO = 0, /* PLG chooses best algorithm */
+ AMOD_SQL = 1, /* Use SQL algorithm */
+ AMOD_QRY = 2}; /* Use QUERY algorithm */
+#endif // 0
+
+enum MODE {MODE_ERROR = -1, /* Invalid mode */
+ MODE_ANY = 0, /* Unspecified mode */
+ MODE_READ = 10, /* Input/Output mode */
+ MODE_WRITE = 20, /* Input/Output mode */
+ MODE_UPDATE = 30, /* Input/Output mode */
+ MODE_INSERT = 40, /* Input/Output mode */
+ MODE_DELETE = 50, /* Input/Output mode */
+ MODE_ALTER = 60}; /* alter mode */
+
+#if !defined(RC_OK_DEFINED)
+#define RC_OK_DEFINED
+enum RCODE {RC_OK = 0, /* No error return code */
+ RC_NF = 1, /* Not found return code */
+ RC_EF = 2, /* End of file return code */
+ RC_FX = 3, /* Error return code */
+ RC_INFO = 4}; /* Success with info */
+#endif // !RC_OK_DEFINED
+
+enum OPVAL {OP_EQ = 1, /* Filtering operator = */
+ OP_NE = 2, /* Filtering operator != */
+ OP_GT = 3, /* Filtering operator > */
+ OP_GE = 4, /* Filtering operator >= */
+ OP_LT = 5, /* Filtering operator < */
+ OP_LE = 6, /* Filtering operator <= */
+ OP_IN = 7, /* Filtering operator IN */
+ OP_NULL = 8, /* Filtering operator IS NULL */
+ OP_EXIST = 9, /* Filtering operator EXISTS */
+ OP_LIKE = 10, /* Filtering operator LIKE */
+ OP_LOJ = -1, /* Filter op LEFT OUTER JOIN */
+ OP_ROJ = -2, /* Filter op RIGHT OUTER JOIN */
+ OP_DTJ = -3, /* Filter op DISTINCT JOIN */
+ OP_XX = 11, /* Filtering operator unknown */
+ OP_AND = 12, /* Filtering operator AND */
+ OP_OR = 13, /* Filtering operator OR */
+ OP_CNC = 14, /* Expression Concat operator */
+ OP_NOT = 15, /* Filtering operator NOT */
+ OP_SEP = 20, /* Filtering separator */
+ OP_ADD = 16, /* Expression Add operator */
+ OP_SUB = 17, /* Expression Substract operator */
+ OP_MULT = 18, /* Expression Multiply operator */
+ OP_DIV = 19, /* Expression Divide operator */
+ OP_NOP = 21, /* Scalar function is nopped */
+ OP_NUM = 22, /* Scalar function Op Num */
+ OP_ABS = 23, /* Scalar function Op Abs */
+ OP_MAX = 24, /* Scalar function Op Max */
+ OP_MIN = 25, /* Scalar function Op Min */
+ OP_CEIL = 26, /* Scalar function Op Ceil */
+ OP_FLOOR = 27, /* Scalar function Op Floor */
+ OP_MOD = 28, /* Scalar function Op Mod */
+ OP_ROUND = 29, /* Scalar function Op Round */
+ OP_SIGN = 30, /* Scalar function Op Sign */
+ OP_LEN = 31, /* Scalar function Op Len */
+ OP_INSTR = 32, /* Scalar function Op Instr */
+ OP_LEFT = 33, /* Scalar function Op Left */
+ OP_RIGHT = 34, /* Scalar function Op Right */
+ OP_ASCII = 35, /* Scalar function Op Ascii */
+ OP_EXP = 36, /* Scalar function Op Exp */
+ OP_LN = 37, /* Scalar function Op Ln */
+ OP_LOG = 38, /* Scalar function Op Log */
+ OP_POWER = 39, /* Scalar function Op Power */
+ OP_SQRT = 40, /* Scalar function Op Sqrt */
+ OP_COS = 41, /* Scalar function Op Cos */
+ OP_COSH = 42, /* Scalar function Op Cosh */
+ OP_SIN = 43, /* Scalar function Op Sin */
+ OP_SINH = 44, /* Scalar function Op Sinh */
+ OP_TAN = 45, /* Scalar function Op Tan */
+ OP_TANH = 46, /* Scalar function Op Tanh */
+ OP_USER = 47, /* Scalar function Op User */
+ OP_CHAR = 48, /* Scalar function Op Char */
+ OP_UPPER = 49, /* Scalar function Op Upper */
+ OP_LOWER = 50, /* Scalar function Op Lower */
+ OP_RPAD = 51, /* Scalar function Op Rpad */
+ OP_LPAD = 52, /* Scalar function Op Lpad */
+ OP_LTRIM = 53, /* Scalar function Op Ltrim */
+ OP_RTRIM = 54, /* Scalar function Op Rtrim */
+ OP_REPL = 55, /* Scalar function Op Replace */
+ OP_SUBST = 56, /* Scalar function Op Substr */
+ OP_LJUST = 57, /* Scalar function Op Ljustify */
+ OP_RJUST = 58, /* Scalar function Op Rjustify */
+ OP_CJUST = 59, /* Scalar function Op Cjustify */
+ OP_ENCODE = 60, /* Scalar function Op Encode */
+ OP_DECODE = 61, /* Scalar function Op Decode */
+ OP_SEQU = 62, /* Scalar function Op Sequence */
+ OP_IF = 63, /* Scalar function Op If */
+ OP_STRING = 64, /* Scalar function Op String */
+ OP_TOKEN = 65, /* Scalar function Op Token */
+ OP_SNDX = 66, /* Scalar function Op Soundex */
+ OP_DATE = 67, /* Scalar function Op Date */
+ OP_MDAY = 68, /* Scalar function Op Month Day */
+ OP_MONTH = 69, /* Scalar function Op Month of */
+ OP_YEAR = 70, /* Scalar function Op Year of */
+ OP_WDAY = 71, /* Scalar function Op Week Day */
+ OP_YDAY = 72, /* Scalar function Op Year Day */
+ OP_DBTWN = 73, /* Scalar function Op Days betwn */
+ OP_MBTWN = 74, /* Scalar function Op Months btw */
+ OP_YBTWN = 75, /* Scalar function Op Years btwn */
+ OP_ADDAY = 76, /* Scalar function Op Add Days */
+ OP_ADDMTH = 77, /* Scalar function Op Add Months */
+ OP_ADDYR = 78, /* Scalar function Op Add Years */
+ OP_NXTDAY = 79, /* Scalar function Op Next Day */
+ OP_SYSDT = 80, /* Scalar function Op SysDate */
+ OP_DELTA = 81, /* Scalar function Op Delta */
+ OP_LAST = 82, /* Scalar function Op Last */
+ OP_IFF = 83, /* Scalar function Op Iff */
+ OP_MAVG = 84, /* Scalar function Op Moving Avg */
+ OP_VWAP = 85, /* Scalar function Op VWAP */
+ OP_TIME = 86, /* Scalar function Op TIME */
+ OP_SETLEN = 87, /* Scalar function Op Set Length */
+ OP_TRANSL = 88, /* Scalar function Op Translate */
+ OP_BITAND = 89, /* Expression BitAnd operator */
+ OP_BITOR = 90, /* Expression BitOr operator */
+ OP_BITXOR = 91, /* Expression XOR operator */
+ OP_BITNOT = 92, /* Expression Complement operator*/
+ OP_CNTIN = 93, /* Scalar function Count In */
+ OP_FDISK = 94, /* Scalar function Disk of fileid*/
+ OP_FPATH = 95, /* Scalar function Path of fileid*/
+ OP_FNAME = 96, /* Scalar function Name of fileid*/
+ OP_FTYPE = 97, /* Scalar function Type of fileid*/
+ OP_XDATE = 98, /* Scalar function Op Fmt Date */
+ OP_SWITCH = 99, /* Scalar function Op Switch */
+ OP_EXIT = 100, /* Scalar function Op Exit */
+ OP_LIT = 101, /* Scalar function Op Literal */
+ OP_LOCALE = 102, /* Scalar function Op Locale */
+ OP_FRNCH = 103, /* Scalar function Op French */
+ OP_ENGLSH = 104, /* Scalar function Op English */
+ OP_RAND = 105, /* Scalar function Op Rand(om) */
+ OP_FIRST = 106, /* Index operator Find First */
+ OP_NEXT = 107, /* Index operator Find Next */
+ OP_SAME = 108, /* Index operator Find Next Same */
+ OP_FSTDIF = 109, /* Index operator Find First dif */
+ OP_NXTDIF = 110, /* Index operator Find Next dif */
+ OP_VAL = 111, /* Scalar function Op Valist */
+ OP_QUART = 112, /* Scalar function Op QUARTER */
+ OP_CURDT = 113, /* Scalar function Op CurDate */
+ OP_NWEEK = 114, /* Scalar function Op Week number*/
+ OP_ROW = 115, /* Scalar function Op Row */
+ OP_SYSTEM = 200, /* Scalar function Op System */
+ OP_REMOVE = 201, /* Scalar function Op Remove */
+ OP_RENAME = 202, /* Scalar function Op Rename */
+ OP_FCOMP = 203}; /* Scalar function Op Compare */
+
+enum TUSE {USE_NO = 0, /* Table is not yet linearized */
+ USE_LIN = 1, /* Table is linearized */
+ USE_READY = 2, /* Column buffers are allocated */
+ USE_OPEN = 3, /* Table is open */
+ USE_CNT = 4, /* Specific to LNA */
+ USE_NOKEY = 5}; /* Specific to SqlToHql */
+
+/***********************************************************************/
+/* Following definitions are used to indicate the status of a column. */
+/***********************************************************************/
+enum STATUS {BUF_NO = 0x00, /* Column buffer not allocated */
+ BUF_EMPTY = 0x01, /* Column buffer is empty */
+ BUF_READY = 0x02, /* Column buffer is ready */
+ BUF_READ = 0x04, /* Column buffer has read value */
+ BUF_MAPPED = 0x08}; /* Used by the VMPFAM class */
+
+/***********************************************************************/
+/* Following definitions are used to indicate how a column is used. */
+/* Corresponding bits are ON if the column is used in: */
+/***********************************************************************/
+enum COLUSE {U_P = 0x01, /* the projection list. */
+ U_J_EXT = 0x02, /* a join filter. */
+ U_J_INT = 0x04, /* a join after linearisation. */
+/*-- Such a column have a constant value throughout a subquery eval. --*/
+ U_CORREL = 0x08, /* a correlated sub-query */
+/*-------------------- additional values used by CONNECT --------------*/
+ U_VAR = 0x10, /* a VARCHAR column */
+ U_VIRTUAL = 0x20, /* a VIRTUAL column */
+ U_NULLS = 0x40, /* The column may have nulls */
+ U_IS_NULL = 0x80, /* The column has a null value */
+ U_SPECIAL = 0x100, /* The column is special */
+ U_UNSIGNED = 0x200, /* The column type is unsigned */
+ U_ZEROFILL = 0x400}; /* The column is zero filled */
+
+/***********************************************************************/
+/* DB description class and block pointer definitions. */
+/***********************************************************************/
+typedef class XTAB *PTABLE;
+typedef class COLUMN *PCOLUMN;
+typedef class XOBJECT *PXOB;
+typedef class COLBLK *PCOL;
+typedef class TDB *PTDB;
+typedef class TDBASE *PTDBASE;
+typedef class TDBDOS *PTDBDOS;
+typedef class TDBFIX *PTDBFIX;
+typedef class TDBFMT *PTDBFMT;
+typedef class TDBCSV *PTDBCSV;
+typedef class TDBDOM *PTDBDOM;
+typedef class TDBDIR *PTDBDIR;
+typedef class DOSCOL *PDOSCOL;
+typedef class CSVCOL *PCSVCOL;
+typedef class MAPCOL *PMAPCOL;
+typedef class TDBMFT *PTDBMFT;
+typedef class TDBMCV *PTDBMCV;
+typedef class MCVCOL *PMCVCOL;
+typedef class RESCOL *PRESCOL;
+typedef class XXBASE *PKXBASE;
+typedef class KXYCOL *PXCOL;
+typedef class CATALOG *PCATLG;
+typedef class RELDEF *PRELDEF;
+typedef class TABDEF *PTABDEF;
+typedef class DOSDEF *PDOSDEF;
+typedef class CSVDEF *PCSVDEF;
+typedef class VCTDEF *PVCTDEF;
+typedef class PIVOTDEF *PPIVOTDEF;
+typedef class DOMDEF *PDOMDEF;
+typedef class DIRDEF *PDIRDEF;
+typedef class OEMDEF *POEMDEF;
+typedef class COLCRT *PCOLCRT;
+typedef class COLDEF *PCOLDEF;
+typedef class CONSTANT *PCONST;
+typedef class VALUE *PVAL;
+typedef class VALBLK *PVBLK;
+typedef class FILTER *PFIL;
+
+typedef struct _fblock *PFBLOCK;
+typedef struct _mblock *PMBLOCK;
+typedef struct _cblock *PCBLOCK;
+typedef struct _tabs *PTABS;
+typedef struct _qryres *PQRYRES;
+typedef struct _colres *PCOLRES;
+typedef struct _datpar *PDTP;
+typedef struct indx_used *PXUSED;
+
+/***********************************************************************/
+/* Utility blocks for file and storage. */
+/***********************************************************************/
+typedef struct _fblock { /* Opened (mapped) file block */
+ struct _fblock *Next;
+ LPCSTR Fname; /* Point on file name */
+ size_t Length; /* File length (<4GB) */
+ short Count; /* Nb of times map is used */
+ short Type; /* TYPE_FB_FILE or TYPE_FB_MAP */
+ MODE Mode; /* Open mode */
+ char *Memory; /* Pointer to file mapping view */
+ void *File; /* FILE pointer */
+ HANDLE Handle; /* File handle */
+ } FBLOCK;
+
+typedef struct _mblock { /* Memory block */
+ PMBLOCK Next;
+ bool Inlist; /* True if in mblock list */
+ size_t Size; /* Size of allocation */
+ bool Sub; /* True if suballocated */
+ void *Memp; /* Memory pointer */
+ } MBLOCK;
+
+/***********************************************************************/
+/* The QUERY application User Block. */
+/***********************************************************************/
+typedef struct { /* User application block */
+ NAME Name; /* User application name */
+ char Server[17]; /* Server name */
+ char DBName[17]; /* Current database name */
+ PCATLG Catalog; /* To CATALOG class */
+ PQRYRES Result; /* To query result blocks */
+ PFBLOCK Openlist; /* To file/map open list */
+ PMBLOCK Memlist; /* To memory block list */
+ PXUSED Xlist; /* To used index list */
+ int Maxbmp; /* Maximum XDB2 bitmap size */
+ int Check; /* General level of checking */
+ int Numlines; /* Number of lines involved */
+ USETEMP UseTemp; /* Use temporary file */
+ int Vtdbno; /* Used for TDB number setting */
+ bool Remote; /* true: if remotely called */
+ bool Proginfo; /* true: return progress info */
+ bool Subcor; /* Used for Progress info */
+ size_t ProgMax; /* Used for Progress info */
+ size_t ProgCur; /* Used for Progress info */
+ size_t ProgSav; /* Used for Progress info */
+ LPCSTR Step; /* Execution step name */
+ } DBUSERBLK, *PDBUSER;
+
+/***********************************************************************/
+/* Column output format. */
+/***********************************************************************/
+typedef struct _format { /* Format descriptor block */
+ char Type[2]; /* C:char, F:double, N:int, Dx: date */
+ ushort Length; /* Output length */
+ short Prec; /* Output precision */
+ } FORMAT, *PFORMAT;
+
+/***********************************************************************/
+/* Definition of blocks used in type and copy routines. */
+/***********************************************************************/
+typedef struct _tabptr { /* start=P1 */
+ struct _tabptr *Next;
+ int Num; /* alignement */
+ void *Old[50];
+ void *New[50]; /* old and new values of copied ptrs */
+ } TABPTR, *PTABPTR;
+
+typedef struct _tabadr { /* start=P3 */
+ struct _tabadr *Next;
+ int Num;
+ void *Adx[50]; /* addr of pointers to be reset */
+ } TABADR, *PTABADR;
+
+typedef struct _tabs {
+ PGLOBAL G;
+ PTABPTR P1;
+ PTABADR P3;
+ } TABS;
+
+/***********************************************************************/
+/* Argument of expression, function, filter etc. (Xobject) */
+/***********************************************************************/
+typedef struct _arg { /* Argument */
+ PXOB To_Obj; /* To the argument object */
+ PVAL Value; /* Argument value */
+ bool Conv; /* TRUE if conversion is required */
+ } ARGBLK, *PARG;
+
+typedef struct _oper { /* Operator */
+ PSZ Name; /* The input/output operator name */
+ OPVAL Val; /* Operator numeric value */
+ int Mod; /* The modificator */
+ } OPER, *POPER;
+
+/***********************************************************************/
+/* Following definitions are used to define table fields (columns). */
+/***********************************************************************/
+enum XFLD {FLD_NO = 0, /* Not a field definition item */
+ FLD_NAME = 1, /* Item name */
+ FLD_TYPE = 2, /* Field type */
+ FLD_TYPENAME = 3, /* Field type name */
+ FLD_PREC = 4, /* Field precision (length?) */
+ FLD_LENGTH = 5, /* Field length (?) */
+ FLD_SCALE = 6, /* Field scale (precision) */
+ FLD_RADIX = 7, /* Field radix */
+ FLD_NULL = 8, /* Field nullable property */
+ FLD_REM = 9, /* Field comment (remark) */
+ FLD_CHARSET = 10, /* Field collation */
+ FLD_KEY = 11, /* Field key property */
+ FLD_DEFAULT = 12, /* Field default value */
+ FLD_EXTRA = 13, /* Field extra info */
+ FLD_PRIV = 14, /* Field priviledges */
+ FLD_DATEFMT = 15, /* Field date format */
+ FLD_CAT = 16, /* Table catalog */
+ FLD_SCHEM = 17, /* Table schema */
+ FLD_TABNAME = 18}; /* Column Table name */
+
+/***********************************************************************/
+/* Result of last SQL noconv query. */
+/***********************************************************************/
+typedef struct _qryres {
+ PCOLRES Colresp; /* Points to columns of result */
+ bool Continued; /* true when more rows to fetch */
+ bool Truncated; /* true when truncated by maxres */
+ bool Suball; /* true when entirely suballocated */
+ bool Info; /* true when info msg generated */
+ int Maxsize; /* Max query number of lines */
+ int Maxres; /* Allocation size */
+ int Nblin; /* Number of rows in result set */
+ int Nbcol; /* Number of columns in result set */
+ int Cursor; /* Starting position to get data */
+ int BadLines; /* Skipped bad lines in table file */
+ } QRYRES, *PQRYRES;
+
+typedef struct _colres {
+ PCOLRES Next; /* To next result column */
+ PCOL Colp; /* To matching column block */
+ PSZ Name; /* Column header */
+ PVBLK Kdata; /* Column block of values */
+ char *Nulls; /* Column null value array */
+ int Type; /* Internal type */
+ int Datasize; /* Overall data size */
+ int Ncol; /* Column number */
+ int Clen; /* Data individual internal size */
+ int Length; /* Data individual print length */
+ int Prec; /* Precision */
+ int Flag; /* Flag option value */
+ XFLD Fld; /* Type of field info */
+ } COLRES;
+
+#if defined(WIN32) && !defined(NOEX)
+#define DllExport __declspec( dllexport )
+#else // !WIN32
+#define DllExport
+#endif // !WIN32
+
+/***********************************************************************/
+/* Utility routines. */
+/***********************************************************************/
+PPARM Vcolist(PGLOBAL, PTDB, PSZ, bool);
+void PlugPutOut(PGLOBAL, FILE *, short, void *, uint);
+void PlugLineDB(PGLOBAL, PSZ, short, void *, uint);
+char *PlgGetDataPath(PGLOBAL g);
+void AddPointer(PTABS, void *);
+PDTP MakeDateFormat(PGLOBAL, PSZ, bool, bool, int);
+int ExtractDate(char *, PDTP, int, int val[6]);
+
+/**************************************************************************/
+/* Allocate the result structure that will contain result data. */
+/**************************************************************************/
+DllExport PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
+ int *buftyp, XFLD *fldtyp,
+ unsigned int *length,
+ bool blank, bool nonull);
+
+/***********************************************************************/
+/* Exported utility routines. */
+/***********************************************************************/
+DllExport FILE *PlugOpenFile(PGLOBAL, LPCSTR, LPCSTR);
+DllExport int PlugCloseFile(PGLOBAL, PFBLOCK, bool all = false);
+DllExport void PlugCleanup(PGLOBAL, bool);
+DllExport bool GetPromptAnswer(PGLOBAL, char *);
+DllExport char *GetAmName(PGLOBAL g, AMT am, void *memp = NULL);
+DllExport PDBUSER PlgMakeUser(PGLOBAL g);
+DllExport PDBUSER PlgGetUser(PGLOBAL g);
+DllExport PCATLG PlgGetCatalog(PGLOBAL g, bool jump = true);
+DllExport bool PlgSetXdbPath(PGLOBAL g, PSZ, PSZ, char *, int, char *, int);
+DllExport void PlgDBfree(MBLOCK&);
+DllExport void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size);
+DllExport void *PlgDBalloc(PGLOBAL, void *, MBLOCK&);
+DllExport void *PlgDBrealloc(PGLOBAL, void *, MBLOCK&, size_t);
+DllExport void NewPointer(PTABS, void *, void *);
+DllExport char *GetIni(int n= 0);
+DllExport void SetTrc(void);
+DllExport char *GetListOption(PGLOBAL, const char *, const char *,
+ const char *def=NULL);
+
+#define MSGID_NONE 0
+#define MSGID_CANNOT_OPEN 1
+#define MSGID_OPEN_MODE_ERROR 2
+#define MSGID_OPEN_STRERROR 3
+#define MSGID_OPEN_ERROR_AND_STRERROR 4
+#define MSGID_OPEN_MODE_STRERROR 5
+#define MSGID_OPEN_EMPTY_FILE 6
+
+FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode);
+int global_open(GLOBAL *g, int msgid, const char *filename, int flags);
+int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode);
+DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
+char *MakeEscape(PGLOBAL g, char* str, char q);
+
+DllExport bool PushWarning(PGLOBAL, PTDBASE, int level = 1);
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index f52515e540b..2987ef62e21 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -374,18 +374,7 @@ PDBUSER PlgMakeUser(PGLOBAL g)
} // endif dbuserp
memset(dbuserp, 0, sizeof(DBUSERBLK));
-//dbuserp->Act2 = g->Activityp;
-//#if defined(UNIX)
-// dbuserp->LineLen = 160;
-//#else
-// dbuserp->LineLen = 78;
-//#endif
-//dbuserp->Maxres = MAXRES;
-//dbuserp->Maxlin = MAXLIN;
-#if defined(BLK_INDX)
dbuserp->Maxbmp = MAXBMP;
-#endif // BLK_INDX
-//dbuserp->AlgChoice = AMOD_AUTO;
dbuserp->UseTemp = TMP_AUTO;
dbuserp->Check = CHK_ALL;
strcpy(dbuserp->Server, "CONNECT");
diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c
index e6d452aaf97..6c2a1ed403f 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -1,552 +1,550 @@
-/************** PlugUtil C Program Source Code File (.C) ***************/
-/* */
-/* PROGRAM NAME: PLUGUTIL */
-/* ------------- */
-/* Version 2.7 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1993-2012 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are initialization and utility Plug routines. */
-/* */
-/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
-/* -------------------------------------- */
-/* */
-/* REQUIRED FILES: */
-/* --------------- */
-/* See Readme.C for a list and description of required SYSTEM files. */
-/* */
-/* PLUG.C - Source code */
-/* GLOBAL.H - Global declaration file */
-/* OPTION.H - Option declaration file */
-/* */
-/* REQUIRED LIBRARIES: */
-/* ------------------- */
-/* */
-/* OS2.LIB - OS2 libray */
-/* LLIBCE.LIB - Protect mode/standard combined large model C */
-/* library */
-/* */
-/* REQUIRED PROGRAMS: */
-/* ------------------ */
-/* */
-/* IBM C Compiler */
-/* IBM Linker */
-/* */
-/***********************************************************************/
-//efine DEBTRACE 3
-//efine DEBTRACE2
-
-/***********************************************************************/
-/* */
-/* Include relevant MariaDB header file. */
-/* */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-//#include <windows.h>
-#else
-#if defined(UNIX) || defined(UNIV_LINUX)
-#include <errno.h>
-#include <unistd.h>
-//#define __stdcall
-#else
-#include <dir.h>
-#endif
-#include <stdarg.h>
-#endif
-
-#if defined(WIN)
-#include <alloc.h>
-#endif
-#include <errno.h> /* definitions of ERANGE ENOMEM */
-#if !defined(UNIX) && !defined(UNIV_LINUX)
-#include <direct.h> /* Directory management library */
-#endif
-
-/***********************************************************************/
-/* */
-/* Include application header files */
-/* */
-/* global.h is header containing all global declarations. */
-/* */
-/***********************************************************************/
-#define STORAGE /* Initialize global variables */
-
-#include "osutil.h"
-#include "global.h"
-
-#if defined(WIN32)
-extern HINSTANCE s_hModule; /* Saved module handle */
-#endif // WIN32
-
-extern char plgini[];
-extern int trace;
-
-#if defined(XMSG)
-extern char msglang[];
-#endif // XMSG
-
-/***********************************************************************/
-/* Local Definitions and static variables */
-/***********************************************************************/
-typedef struct {
- ushort Segsize;
- ushort Size;
- } AREASIZE;
-
-ACTIVITY defActivity = { /* Describes activity and language */
- NULL, /* Points to user work area(s) */
- "Unknown"}; /* Application name */
-
-#if defined(XMSG) || defined(NEWMSG)
- static char stmsg[200];
-#endif // XMSG || NEWMSG
-
-#if defined(UNIX) || defined(UNIV_LINUX)
-#include "rcmsg.h"
-#endif // UNIX
-
-/**************************************************************************/
-/* Tracing output function. */
-/**************************************************************************/
-void htrc(char const *fmt, ...)
- {
- va_list ap;
- va_start (ap, fmt);
-
-//if (trace == 0 || (trace == 1 && !debug) || !fmt) {
-// printf("In %s wrong trace=%d debug=%p fmt=%p\n",
-// __FILE__, trace, debug, fmt);
-// trace = 0;
-// } // endif trace
-
-//if (trace == 1)
-// vfprintf(debug, fmt, ap);
-//else
- vfprintf(stderr, fmt, ap);
-
- va_end (ap);
- } // end of htrc
-
-/***********************************************************************/
-/* Plug initialization routine. */
-/* Language points on initial language name and eventual path. */
-/* Return value is the pointer to the Global structure. */
-/***********************************************************************/
-PGLOBAL PlugInit(LPCSTR Language, uint worksize)
- {
- PGLOBAL g;
-
- if (trace > 1)
- htrc("PlugInit: Language='%s'\n",
- ((!Language) ? "Null" : (char*)Language));
-
- if (!(g = malloc(sizeof(GLOBAL)))) {
- fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
- return NULL;
- } else {
- g->Sarea_Size = worksize;
- g->Trace = 0;
- g->Createas = 0;
- g->Alchecked = 0;
-#if defined(MRRBKA_SUPPORT)
- g->Mrr = 0;
-#endif // MRRBKA_SUPPORT
- g->Activityp = g->ActivityStart = NULL;
- g->Xchk = NULL;
- strcpy(g->Message, "");
-
- /*******************************************************************/
- /* Allocate the main work segment. */
- /*******************************************************************/
- if (!(g->Sarea = PlugAllocMem(g, worksize))) {
- char errmsg[256];
- sprintf(errmsg, MSG(WORK_AREA), g->Message);
- strcpy(g->Message, errmsg);
- } /* endif Sarea */
-
- } /* endif g */
-
- g->jump_level = -1; /* New setting to allow recursive call of Plug */
- return(g);
- } /* end of PlugInit */
-
-/***********************************************************************/
-/* PlugExit: Terminate Plug operations. */
-/***********************************************************************/
-int PlugExit(PGLOBAL g)
- {
- int rc = 0;
-
- if (!g)
- return rc;
-
- if (g->Sarea)
- free(g->Sarea);
-
- free(g);
- return rc;
- } /* end of PlugExit */
-
-/***********************************************************************/
-/* Remove the file type from a file name. */
-/* Note: this routine is not really implemented for Unix. */
-/***********************************************************************/
-LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName)
- {
-#if !defined(UNIX) && !defined(UNIV_LINUX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ftype[_MAX_EXT];
-
- _splitpath(FileName, drive, direc, fname, ftype);
-
- if (trace > 1) {
- htrc("after _splitpath: FileName=%s\n", FileName);
- htrc("drive=%s dir=%s fname=%s ext=%s\n",
- SVP(drive), direc, fname, ftype);
- } // endif trace
-
- _makepath(pBuff, drive, direc, fname, "");
-
- if (trace > 1)
- htrc("buff='%s'\n", pBuff);
-
- return pBuff;
- } // end of PlugRemoveType
-
-
-BOOL PlugIsAbsolutePath(LPCSTR path)
-{
-#if defined(WIN32)
- return ((path[0] >= 'a' && path[0] <= 'z') ||
- (path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':';
-#else
- return path[0] == '/';
-#endif
-}
-
-
-/***********************************************************************/
-/* Set the full path of a file relatively to a given path. */
-/* Note: this routine is not really implemented for Unix. */
-/***********************************************************************/
-LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
- {
- char newname[_MAX_PATH];
- char direc[_MAX_DIR], defdir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ftype[_MAX_EXT];
-#if !defined(UNIX) && !defined(UNIV_LINUX)
- char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE];
-#else
- char *drive = NULL, *defdrv = NULL;
-#endif
-
- if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
- strcpy(pBuff, FileName); // Remote file
- return pBuff;
- } // endif
-
- if (PlugIsAbsolutePath(FileName))
- {
- strcpy(pBuff, FileName); // FileName includes absolute path
- return pBuff;
- } // endif
-
- if (strcmp(prefix, ".") && !PlugIsAbsolutePath(defpath))
- {
- char tmp[_MAX_PATH];
- int len= snprintf(tmp, sizeof(tmp) - 1, "%s%s%s",
- prefix, defpath, FileName);
- memcpy(pBuff, tmp, (size_t) len);
- pBuff[len]= '\0';
- return pBuff;
- }
-
- _splitpath(FileName, drive, direc, fname, ftype);
- _splitpath(defpath, defdrv, defdir, NULL, NULL);
-
- if (trace > 1) {
- htrc("after _splitpath: FileName=%s\n", FileName);
-#if defined(UNIX) || defined(UNIV_LINUX)
- htrc("dir=%s fname=%s ext=%s\n", direc, fname, ftype);
-#else
- htrc("drive=%s dir=%s fname=%s ext=%s\n", drive, direc, fname, ftype);
- htrc("defdrv=%s defdir=%s\n", defdrv, defdir);
-#endif
- } // endif trace
-
- if (drive && !*drive)
- strcpy(drive, defdrv);
-
- switch (*direc) {
- case '\0':
- strcpy(direc, defdir);
- break;
- case '\\':
- case '/':
- break;
- default:
- // This supposes that defdir ends with a SLASH
- strcpy(direc, strcat(defdir, direc));
- } // endswitch
-
- _makepath(newname, drive, direc, fname, ftype);
-
- if (trace > 1)
- htrc("newname='%s'\n", newname);
-
- if (_fullpath(pBuff, newname, _MAX_PATH)) {
- if (trace > 1)
- htrc("pbuff='%s'\n", pBuff);
-
- return pBuff;
- } else
- return FileName; // Error, return unchanged name
-
- } // end of PlugSetPath
-
-#if defined(XMSG)
-/***********************************************************************/
-/* PlugGetMessage: get a message from the message file. */
-/***********************************************************************/
-char *PlugReadMessage(PGLOBAL g, int mid, char *m)
- {
- char msgfile[_MAX_PATH], msgid[32], buff[256];
- char *msg;
- FILE *mfile = NULL;
-
- GetPrivateProfileString("Message", msglang, "Message\\english.msg",
- msgfile, _MAX_PATH, plgini);
-
- if (!(mfile = fopen(msgfile, "rt"))) {
- sprintf(stmsg, "Fail to open message file %s for %s", msgfile, msglang);
- goto err;
- } // endif mfile
-
- for (;;)
- if (!fgets(buff, 256, mfile)) {
- sprintf(stmsg, "Cannot get message %d %s", mid, SVP(m));
- goto fin;
- } else
- if (atoi(buff) == mid)
- break;
-
- if (sscanf(buff, " %*d %s \"%[^\"]", msgid, stmsg) < 2) {
- // Old message file
- if (!sscanf(buff, " %*d \"%[^\"]", stmsg)) {
- sprintf(stmsg, "Bad message file for %d %s", mid, SVP(m));
- goto fin;
- } else
- m = NULL;
-
- } // endif sscanf
-
- if (m && strcmp(m, msgid)) {
- // Message file is out of date
- strcpy(stmsg, m);
- goto fin;
- } // endif m
-
- fin:
- fclose(mfile);
-
- err:
- if (g) {
- // Called by STEP
- msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
- strcpy(msg, stmsg);
- } else // Called by MSG or PlgGetErrorMsg
- msg = stmsg;
-
- return msg;
- } // end of PlugReadMessage
-
-#elif defined(NEWMSG)
-/***********************************************************************/
-/* PlugGetMessage: get a message from the resource string table. */
-/***********************************************************************/
-char *PlugGetMessage(PGLOBAL g, int mid)
- {
- char *msg;
-
-#if !defined(UNIX) && !defined(UNIV_LINUX)
- int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200);
-
- if (n == 0) {
- DWORD rc = GetLastError();
- msg = (char*)PlugSubAlloc(g, NULL, 512); // Extend buf allocation
- n = sprintf(msg, "Message %d, rc=%d: ", mid, rc);
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
- (LPTSTR)(msg + n), 512 - n, NULL);
- return msg;
- } // endif n
-
-#else // UNIX
- if (!GetRcString(mid, stmsg, 200))
- sprintf(stmsg, "Message %d not found", mid);
-#endif // UNIX
-
- if (g) {
- // Called by STEP
- msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
- strcpy(msg, stmsg);
- } else // Called by MSG or PlgGetErrorMsg
- msg = stmsg;
-
- return msg;
- } // end of PlugGetMessage
-#endif // NEWMSG
-
-#if defined(WIN32)
-/***********************************************************************/
-/* Return the line length of the console screen buffer. */
-/***********************************************************************/
-short GetLineLength(PGLOBAL g)
- {
- CONSOLE_SCREEN_BUFFER_INFO coninfo;
- HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE);
- BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo);
-
- return (b) ? coninfo.dwSize.X : 0;
- } // end of GetLineLength
-#endif // WIN32
-
-/***********************************************************************/
-/* Program for memory allocation of work and language areas. */
-/***********************************************************************/
-void *PlugAllocMem(PGLOBAL g, uint size)
- {
- void *areap; /* Pointer to allocated area */
-
- /*********************************************************************/
- /* This is the allocation routine for the WIN32/UNIX/AIX version. */
- /*********************************************************************/
- if (!(areap = malloc(size)))
- sprintf(g->Message, MSG(MALLOC_ERROR), "malloc");
-
- if (trace > 1) {
- if (areap)
- htrc("Memory of %u allocated at %p\n", size, areap);
- else
- htrc("PlugAllocMem: %s\n", g->Message);
-
- } // endif trace
-
- return (areap);
- } /* end of PlugAllocMem */
-
-/***********************************************************************/
-/* Program for SubSet initialization of memory pools. */
-/* Here there should be some verification done such as validity of */
-/* the address and size not larger than memory size. */
-/***********************************************************************/
-BOOL PlugSubSet(PGLOBAL g __attribute__((unused)), void *memp, uint size)
- {
- PPOOLHEADER pph = memp;
-
- pph->To_Free = (OFFSET)sizeof(POOLHEADER);
- pph->FreeBlk = size - pph->To_Free;
-
- return FALSE;
- } /* end of PlugSubSet */
-
-/***********************************************************************/
-/* Program for sub-allocating one item in a storage area. */
-/* Note: SubAlloc routines of OS/2 are no more used to increase the */
-/* code portability and avoid problems when a grammar compiled under */
-/* one version of OS/2 is used under another version. */
-/* The simple way things are done here is also based on the fact */
-/* that no freeing of suballocated blocks is permitted in Plug. */
-/***********************************************************************/
-void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
- {
- PPOOLHEADER pph; /* Points on area header. */
-
- if (!memp)
- /*******************************************************************/
- /* Allocation is to be done in the Sarea. */
- /*******************************************************************/
- memp = g->Sarea;
-
-//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
- size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
- pph = (PPOOLHEADER)memp;
-
-#if defined(DEBUG2) || defined(DEBUG3)
- htrc("SubAlloc in %p size=%d used=%d free=%d\n",
- memp, size, pph->To_Free, pph->FreeBlk);
-#endif
-
- if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */
- char *pname = "Work";
-
- sprintf(g->Message,
- "Not enough memory in %s area for request of %u (used=%d free=%d)",
- pname, (uint) size, pph->To_Free, pph->FreeBlk);
-
-#if defined(DEBUG2) || defined(DEBUG3)
- htrc("%s\n", g->Message);
-#endif
-
- longjmp(g->jumper[g->jump_level], 1);
- } /* endif size OS32 code */
-
- /*********************************************************************/
- /* Do the suballocation the simplest way. */
- /*********************************************************************/
- memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */
- pph->To_Free += size; /* New offset of pool free block */
- pph->FreeBlk -= size; /* New size of pool free block */
-#if defined(DEBUG2) || defined(DEBUG3)
- htrc("Done memp=%p used=%d free=%d\n",
- memp, pph->To_Free, pph->FreeBlk);
-#endif
- return (memp);
- } /* end of PlugSubAlloc */
-
-/***********************************************************************/
-/* This routine suballocate a copy of the passed string. */
-/***********************************************************************/
-char *PlugDup(PGLOBAL g, const char *str)
- {
- char *buf;
- size_t len;
-
- if (str && (len = strlen(str))) {
- buf = (char*)PlugSubAlloc(g, NULL, len + 1);
- strcpy(buf, str);
- } else
- buf = NULL;
-
- return(buf);
- } /* end of PlugDup */
-
-/***********************************************************************/
-/* This routine makes a pointer from an offset to a memory pointer. */
-/***********************************************************************/
-void *MakePtr(void *memp, OFFSET offset)
- {
- return ((offset == 0) ? NULL : &((char *)memp)[offset]);
- } /* end of MakePtr */
-
-/***********************************************************************/
-/* This routine makes an offset from a pointer new format. */
-/***********************************************************************/
-#if 0
-OFFSET MakeOff(void *memp, void *ptr)
- {
- return ((!ptr) ? 0 : (OFFSET)((char *)ptr - (char *)memp));
- } /* end of MakeOff */
-#endif
-/*--------------------- End of PLUGUTIL program -----------------------*/
+/************** PlugUtil C Program Source Code File (.C) ***************/
+/* */
+/* PROGRAM NAME: PLUGUTIL */
+/* ------------- */
+/* Version 2.8 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 1993-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are initialization and utility Plug routines. */
+/* */
+/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
+/* -------------------------------------- */
+/* */
+/* REQUIRED FILES: */
+/* --------------- */
+/* See Readme.C for a list and description of required SYSTEM files. */
+/* */
+/* PLUG.C - Source code */
+/* GLOBAL.H - Global declaration file */
+/* OPTION.H - Option declaration file */
+/* */
+/* REQUIRED LIBRARIES: */
+/* ------------------- */
+/* */
+/* OS2.LIB - OS2 libray */
+/* LLIBCE.LIB - Protect mode/standard combined large model C */
+/* library */
+/* */
+/* REQUIRED PROGRAMS: */
+/* ------------------ */
+/* */
+/* IBM C Compiler */
+/* IBM Linker */
+/* */
+/***********************************************************************/
+//efine DEBTRACE 3
+//efine DEBTRACE2
+
+/***********************************************************************/
+/* */
+/* Include relevant MariaDB header file. */
+/* */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+//#include <windows.h>
+#else
+#if defined(UNIX) || defined(UNIV_LINUX)
+#include <errno.h>
+#include <unistd.h>
+//#define __stdcall
+#else
+#include <dir.h>
+#endif
+#include <stdarg.h>
+#endif
+
+#if defined(WIN)
+#include <alloc.h>
+#endif
+#include <errno.h> /* definitions of ERANGE ENOMEM */
+#if !defined(UNIX) && !defined(UNIV_LINUX)
+#include <direct.h> /* Directory management library */
+#endif
+
+/***********************************************************************/
+/* */
+/* Include application header files */
+/* */
+/* global.h is header containing all global declarations. */
+/* */
+/***********************************************************************/
+#define STORAGE /* Initialize global variables */
+
+#include "osutil.h"
+#include "global.h"
+
+#if defined(WIN32)
+extern HINSTANCE s_hModule; /* Saved module handle */
+#endif // WIN32
+
+extern char plgini[];
+extern int trace;
+
+#if defined(XMSG)
+extern char msglang[];
+#endif // XMSG
+
+/***********************************************************************/
+/* Local Definitions and static variables */
+/***********************************************************************/
+typedef struct {
+ ushort Segsize;
+ ushort Size;
+ } AREASIZE;
+
+ACTIVITY defActivity = { /* Describes activity and language */
+ NULL, /* Points to user work area(s) */
+ "Unknown"}; /* Application name */
+
+#if defined(XMSG) || defined(NEWMSG)
+ static char stmsg[200];
+#endif // XMSG || NEWMSG
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+#include "rcmsg.h"
+#endif // UNIX
+
+/**************************************************************************/
+/* Tracing output function. */
+/**************************************************************************/
+void htrc(char const *fmt, ...)
+ {
+ va_list ap;
+ va_start (ap, fmt);
+
+//if (trace == 0 || (trace == 1 && !debug) || !fmt) {
+// printf("In %s wrong trace=%d debug=%p fmt=%p\n",
+// __FILE__, trace, debug, fmt);
+// trace = 0;
+// } // endif trace
+
+//if (trace == 1)
+// vfprintf(debug, fmt, ap);
+//else
+ vfprintf(stderr, fmt, ap);
+
+ va_end (ap);
+ } // end of htrc
+
+/***********************************************************************/
+/* Plug initialization routine. */
+/* Language points on initial language name and eventual path. */
+/* Return value is the pointer to the Global structure. */
+/***********************************************************************/
+PGLOBAL PlugInit(LPCSTR Language, uint worksize)
+ {
+ PGLOBAL g;
+
+ if (trace > 1)
+ htrc("PlugInit: Language='%s'\n",
+ ((!Language) ? "Null" : (char*)Language));
+
+ if (!(g = malloc(sizeof(GLOBAL)))) {
+ fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
+ return NULL;
+ } else {
+ g->Sarea_Size = worksize;
+ g->Trace = 0;
+ g->Createas = 0;
+ g->Alchecked = 0;
+ g->Mrr = 0;
+ g->Activityp = g->ActivityStart = NULL;
+ g->Xchk = NULL;
+ strcpy(g->Message, "");
+
+ /*******************************************************************/
+ /* Allocate the main work segment. */
+ /*******************************************************************/
+ if (!(g->Sarea = PlugAllocMem(g, worksize))) {
+ char errmsg[256];
+ sprintf(errmsg, MSG(WORK_AREA), g->Message);
+ strcpy(g->Message, errmsg);
+ } /* endif Sarea */
+
+ } /* endif g */
+
+ g->jump_level = -1; /* New setting to allow recursive call of Plug */
+ return(g);
+ } /* end of PlugInit */
+
+/***********************************************************************/
+/* PlugExit: Terminate Plug operations. */
+/***********************************************************************/
+int PlugExit(PGLOBAL g)
+ {
+ int rc = 0;
+
+ if (!g)
+ return rc;
+
+ if (g->Sarea)
+ free(g->Sarea);
+
+ free(g);
+ return rc;
+ } /* end of PlugExit */
+
+/***********************************************************************/
+/* Remove the file type from a file name. */
+/* Note: this routine is not really implemented for Unix. */
+/***********************************************************************/
+LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName)
+ {
+#if !defined(UNIX) && !defined(UNIV_LINUX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ftype[_MAX_EXT];
+
+ _splitpath(FileName, drive, direc, fname, ftype);
+
+ if (trace > 1) {
+ htrc("after _splitpath: FileName=%s\n", FileName);
+ htrc("drive=%s dir=%s fname=%s ext=%s\n",
+ SVP(drive), direc, fname, ftype);
+ } // endif trace
+
+ _makepath(pBuff, drive, direc, fname, "");
+
+ if (trace > 1)
+ htrc("buff='%s'\n", pBuff);
+
+ return pBuff;
+ } // end of PlugRemoveType
+
+
+BOOL PlugIsAbsolutePath(LPCSTR path)
+{
+#if defined(WIN32)
+ return ((path[0] >= 'a' && path[0] <= 'z') ||
+ (path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':';
+#else
+ return path[0] == '/';
+#endif
+}
+
+
+/***********************************************************************/
+/* Set the full path of a file relatively to a given path. */
+/* Note: this routine is not really implemented for Unix. */
+/***********************************************************************/
+LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
+ {
+ char newname[_MAX_PATH];
+ char direc[_MAX_DIR], defdir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ftype[_MAX_EXT];
+#if !defined(UNIX) && !defined(UNIV_LINUX)
+ char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE];
+#else
+ char *drive = NULL, *defdrv = NULL;
+#endif
+
+ if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
+ strcpy(pBuff, FileName); // Remote file
+ return pBuff;
+ } // endif
+
+ if (PlugIsAbsolutePath(FileName))
+ {
+ strcpy(pBuff, FileName); // FileName includes absolute path
+ return pBuff;
+ } // endif
+
+ if (strcmp(prefix, ".") && !PlugIsAbsolutePath(defpath))
+ {
+ char tmp[_MAX_PATH];
+ int len= snprintf(tmp, sizeof(tmp) - 1, "%s%s%s",
+ prefix, defpath, FileName);
+ memcpy(pBuff, tmp, (size_t) len);
+ pBuff[len]= '\0';
+ return pBuff;
+ }
+
+ _splitpath(FileName, drive, direc, fname, ftype);
+ _splitpath(defpath, defdrv, defdir, NULL, NULL);
+
+ if (trace > 1) {
+ htrc("after _splitpath: FileName=%s\n", FileName);
+#if defined(UNIX) || defined(UNIV_LINUX)
+ htrc("dir=%s fname=%s ext=%s\n", direc, fname, ftype);
+#else
+ htrc("drive=%s dir=%s fname=%s ext=%s\n", drive, direc, fname, ftype);
+ htrc("defdrv=%s defdir=%s\n", defdrv, defdir);
+#endif
+ } // endif trace
+
+ if (drive && !*drive)
+ strcpy(drive, defdrv);
+
+ switch (*direc) {
+ case '\0':
+ strcpy(direc, defdir);
+ break;
+ case '\\':
+ case '/':
+ break;
+ default:
+ // This supposes that defdir ends with a SLASH
+ strcpy(direc, strcat(defdir, direc));
+ } // endswitch
+
+ _makepath(newname, drive, direc, fname, ftype);
+
+ if (trace > 1)
+ htrc("newname='%s'\n", newname);
+
+ if (_fullpath(pBuff, newname, _MAX_PATH)) {
+ if (trace > 1)
+ htrc("pbuff='%s'\n", pBuff);
+
+ return pBuff;
+ } else
+ return FileName; // Error, return unchanged name
+
+ } // end of PlugSetPath
+
+#if defined(XMSG)
+/***********************************************************************/
+/* PlugGetMessage: get a message from the message file. */
+/***********************************************************************/
+char *PlugReadMessage(PGLOBAL g, int mid, char *m)
+ {
+ char msgfile[_MAX_PATH], msgid[32], buff[256];
+ char *msg;
+ FILE *mfile = NULL;
+
+ GetPrivateProfileString("Message", msglang, "Message\\english.msg",
+ msgfile, _MAX_PATH, plgini);
+
+ if (!(mfile = fopen(msgfile, "rt"))) {
+ sprintf(stmsg, "Fail to open message file %s for %s", msgfile, msglang);
+ goto err;
+ } // endif mfile
+
+ for (;;)
+ if (!fgets(buff, 256, mfile)) {
+ sprintf(stmsg, "Cannot get message %d %s", mid, SVP(m));
+ goto fin;
+ } else
+ if (atoi(buff) == mid)
+ break;
+
+ if (sscanf(buff, " %*d %s \"%[^\"]", msgid, stmsg) < 2) {
+ // Old message file
+ if (!sscanf(buff, " %*d \"%[^\"]", stmsg)) {
+ sprintf(stmsg, "Bad message file for %d %s", mid, SVP(m));
+ goto fin;
+ } else
+ m = NULL;
+
+ } // endif sscanf
+
+ if (m && strcmp(m, msgid)) {
+ // Message file is out of date
+ strcpy(stmsg, m);
+ goto fin;
+ } // endif m
+
+ fin:
+ fclose(mfile);
+
+ err:
+ if (g) {
+ // Called by STEP
+ msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
+ strcpy(msg, stmsg);
+ } else // Called by MSG or PlgGetErrorMsg
+ msg = stmsg;
+
+ return msg;
+ } // end of PlugReadMessage
+
+#elif defined(NEWMSG)
+/***********************************************************************/
+/* PlugGetMessage: get a message from the resource string table. */
+/***********************************************************************/
+char *PlugGetMessage(PGLOBAL g, int mid)
+ {
+ char *msg;
+
+#if !defined(UNIX) && !defined(UNIV_LINUX)
+ int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200);
+
+ if (n == 0) {
+ DWORD rc = GetLastError();
+ msg = (char*)PlugSubAlloc(g, NULL, 512); // Extend buf allocation
+ n = sprintf(msg, "Message %d, rc=%d: ", mid, rc);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)(msg + n), 512 - n, NULL);
+ return msg;
+ } // endif n
+
+#else // UNIX
+ if (!GetRcString(mid, stmsg, 200))
+ sprintf(stmsg, "Message %d not found", mid);
+#endif // UNIX
+
+ if (g) {
+ // Called by STEP
+ msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
+ strcpy(msg, stmsg);
+ } else // Called by MSG or PlgGetErrorMsg
+ msg = stmsg;
+
+ return msg;
+ } // end of PlugGetMessage
+#endif // NEWMSG
+
+#if defined(WIN32)
+/***********************************************************************/
+/* Return the line length of the console screen buffer. */
+/***********************************************************************/
+short GetLineLength(PGLOBAL g)
+ {
+ CONSOLE_SCREEN_BUFFER_INFO coninfo;
+ HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE);
+ BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo);
+
+ return (b) ? coninfo.dwSize.X : 0;
+ } // end of GetLineLength
+#endif // WIN32
+
+/***********************************************************************/
+/* Program for memory allocation of work and language areas. */
+/***********************************************************************/
+void *PlugAllocMem(PGLOBAL g, uint size)
+ {
+ void *areap; /* Pointer to allocated area */
+
+ /*********************************************************************/
+ /* This is the allocation routine for the WIN32/UNIX/AIX version. */
+ /*********************************************************************/
+ if (!(areap = malloc(size)))
+ sprintf(g->Message, MSG(MALLOC_ERROR), "malloc");
+
+ if (trace > 1) {
+ if (areap)
+ htrc("Memory of %u allocated at %p\n", size, areap);
+ else
+ htrc("PlugAllocMem: %s\n", g->Message);
+
+ } // endif trace
+
+ return (areap);
+ } /* end of PlugAllocMem */
+
+/***********************************************************************/
+/* Program for SubSet initialization of memory pools. */
+/* Here there should be some verification done such as validity of */
+/* the address and size not larger than memory size. */
+/***********************************************************************/
+BOOL PlugSubSet(PGLOBAL g __attribute__((unused)), void *memp, uint size)
+ {
+ PPOOLHEADER pph = memp;
+
+ pph->To_Free = (OFFSET)sizeof(POOLHEADER);
+ pph->FreeBlk = size - pph->To_Free;
+
+ return FALSE;
+ } /* end of PlugSubSet */
+
+/***********************************************************************/
+/* Program for sub-allocating one item in a storage area. */
+/* Note: SubAlloc routines of OS/2 are no more used to increase the */
+/* code portability and avoid problems when a grammar compiled under */
+/* one version of OS/2 is used under another version. */
+/* The simple way things are done here is also based on the fact */
+/* that no freeing of suballocated blocks is permitted in Plug. */
+/***********************************************************************/
+void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
+ {
+ PPOOLHEADER pph; /* Points on area header. */
+
+ if (!memp)
+ /*******************************************************************/
+ /* Allocation is to be done in the Sarea. */
+ /*******************************************************************/
+ memp = g->Sarea;
+
+//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
+ size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
+ pph = (PPOOLHEADER)memp;
+
+#if defined(DEBUG2) || defined(DEBUG3)
+ htrc("SubAlloc in %p size=%d used=%d free=%d\n",
+ memp, size, pph->To_Free, pph->FreeBlk);
+#endif
+
+ if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */
+ char *pname = "Work";
+
+ sprintf(g->Message,
+ "Not enough memory in %s area for request of %u (used=%d free=%d)",
+ pname, (uint) size, pph->To_Free, pph->FreeBlk);
+
+#if defined(DEBUG2) || defined(DEBUG3)
+ htrc("%s\n", g->Message);
+#endif
+
+ longjmp(g->jumper[g->jump_level], 1);
+ } /* endif size OS32 code */
+
+ /*********************************************************************/
+ /* Do the suballocation the simplest way. */
+ /*********************************************************************/
+ memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */
+ pph->To_Free += size; /* New offset of pool free block */
+ pph->FreeBlk -= size; /* New size of pool free block */
+#if defined(DEBUG2) || defined(DEBUG3)
+ htrc("Done memp=%p used=%d free=%d\n",
+ memp, pph->To_Free, pph->FreeBlk);
+#endif
+ return (memp);
+ } /* end of PlugSubAlloc */
+
+/***********************************************************************/
+/* This routine suballocate a copy of the passed string. */
+/***********************************************************************/
+char *PlugDup(PGLOBAL g, const char *str)
+ {
+ char *buf;
+ size_t len;
+
+ if (str && (len = strlen(str))) {
+ buf = (char*)PlugSubAlloc(g, NULL, len + 1);
+ strcpy(buf, str);
+ } else
+ buf = NULL;
+
+ return(buf);
+ } /* end of PlugDup */
+
+/***********************************************************************/
+/* This routine makes a pointer from an offset to a memory pointer. */
+/***********************************************************************/
+void *MakePtr(void *memp, OFFSET offset)
+ {
+ return ((offset == 0) ? NULL : &((char *)memp)[offset]);
+ } /* end of MakePtr */
+
+/***********************************************************************/
+/* This routine makes an offset from a pointer new format. */
+/***********************************************************************/
+#if 0
+OFFSET MakeOff(void *memp, void *ptr)
+ {
+ return ((!ptr) ? 0 : (OFFSET)((char *)ptr - (char *)memp));
+ } /* end of MakeOff */
+#endif
+/*--------------------- End of PLUGUTIL program -----------------------*/
diff --git a/storage/connect/preparse.h b/storage/connect/preparse.h
index 8b57d487736..2892a958bdd 100644
--- a/storage/connect/preparse.h
+++ b/storage/connect/preparse.h
@@ -4,31 +4,6 @@
#include "checklvl.h"
/***********************************************************************/
-/* Struct of variables used by the SQL pre-parsers. */
-/***********************************************************************/
-typedef struct _prepar {
- struct _prepar *Next;
- char *Debinp; // Start of input buffer
- char *Endinp; // End of input buffer
- char *Pluginp; // Points on current parsing position
- char *Plugbuf; // Start of output buffer
- char *Plugptr; // Current output position
- char *Debchar; // Next/current start of command
- char *Debselp; // Beginning of selection
- char *Debline; // Start of current line
- char *Plugpar[32]; // Parameters
- int Numparms; // Number of defined parameters
- int Nprms; // Number of ODBC parameters
- int Lines; // Line number
- int Chars; // Index of selection start in line
- int Endchars; // Index of selection end in line
- int Frinp, Frbuf; // 0: no, 1: free, 2: delete Debinp/Plugbuf
- int Outsize; // Size of output buffer
- FILE *Argfile; // File containing arguments
- int Addargs; // 1 if arguments are added to the list
- } PREPAR, *PPREP;
-
-/***********************************************************************/
/* Struct of variables used by the date format pre-parser. */
/***********************************************************************/
typedef struct _datpar {
@@ -49,8 +24,6 @@ typedef struct _datpar {
extern "C" {
#endif
-int sqlflex(PPREP pp);
-int sqpflex(PPREP pp);
int fmdflex(PDTP pp);
#ifdef __cplusplus
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index a36901a5d65..38a6fd2eb11 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -1,444 +1,436 @@
-/************* RelDef CPP Program Source Code File (.CPP) **************/
-/* PROGRAM NAME: REFDEF */
-/* ------------- */
-/* Version 1.3 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the DB definition related routines. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant MariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <sqlext.h>
-#else
-#include <dlfcn.h> // dlopen(), dlclose(), dlsym() ...
-#include "osutil.h"
-//#include "sqlext.h"
-#endif
-
-/***********************************************************************/
-/* Include application header files */
-/* */
-/* global.h is header containing all global declarations. */
-/* plgdbsem.h is header containing DB application declarations. */
-/* catalog.h is header containing DB description declarations. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "mycat.h"
-#include "reldef.h"
-#include "colblk.h"
-#include "filamap.h"
-#include "filamfix.h"
-#include "filamvct.h"
-#if defined(ZIP_SUPPORT)
-#include "filamzip.h"
-#endif // ZIP_SUPPORT
-#include "tabdos.h"
-#include "valblk.h"
-#include "tabmul.h"
-
-/***********************************************************************/
-/* External static variables. */
-/***********************************************************************/
-//extern "C" char plgini[];
-
-/* --------------------------- Class RELDEF -------------------------- */
-
-/***********************************************************************/
-/* RELDEF Constructor. */
-/***********************************************************************/
-RELDEF::RELDEF(void)
- {
- Next = NULL;
- To_Cols = NULL;
- Name = NULL;
- Database = NULL;
- Cat = NULL;
- } // end of RELDEF constructor
-
-/* --------------------------- Class TABDEF -------------------------- */
-
-/***********************************************************************/
-/* TABDEF Constructor. */
-/***********************************************************************/
-TABDEF::TABDEF(void)
- {
- Schema = NULL;
- Desc = NULL;
- Catfunc = FNC_NO;
- Card = 0;
- Elemt = 0;
- Sort = 0;
- Multiple = 0;
- Degree = 0;
- Pseudo = 0;
- Read_Only = false;
- } // end of TABDEF constructor
-
-/***********************************************************************/
-/* Define: initialize the table definition block from XDB file. */
-/***********************************************************************/
-bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
- {
- int poff = 0;
-
- 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);
- 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)
- return true;
-
- // Do the definition of AM specific fields
- return DefineAM(g, am, poff);
- } // end of Define
-
-/* --------------------------- Class OEMDEF -------------------------- */
-
-/***********************************************************************/
-/* GetXdef: get the external TABDEF from OEM module. */
-/***********************************************************************/
-PTABDEF OEMDEF::GetXdef(PGLOBAL g)
- {
- typedef PTABDEF (__stdcall *XGETDEF) (PGLOBAL, void *);
- char c, getname[40] = "Get";
- PTABDEF xdefp;
- XGETDEF getdef = NULL;
- PCATLG cat = Cat;
-
-#if defined(WIN32)
- // Is the DLL already loaded?
- if (!Hdll && !(Hdll = GetModuleHandle(Module)))
- // No, load the Dll implementing the function
- if (!(Hdll = LoadLibrary(Module))) {
- char buf[256];
- DWORD rc = GetLastError();
-
- sprintf(g->Message, MSG(DLL_LOAD_ERROR), rc, Module);
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
- (LPTSTR)buf, sizeof(buf), NULL);
- strcat(strcat(g->Message, ": "), buf);
- return NULL;
- } // endif hDll
-
- // The exported name is always in uppercase
- for (int i = 0; ; i++) {
- c = Subtype[i];
- getname[i + 3] = toupper(c);
- if (!c) break;
- } // endfor i
-
- // Get the function returning an instance of the external DEF class
- if (!(getdef = (XGETDEF)GetProcAddress((HINSTANCE)Hdll, getname))) {
- sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), getname);
- FreeLibrary((HMODULE)Hdll);
- return NULL;
- } // endif getdef
-#else // !WIN32
- const char *error = NULL;
- // Is the library already loaded?
-// if (!Hdll && !(Hdll = ???))
- // Load the desired shared library
- if (!(Hdll = dlopen(Module, RTLD_LAZY))) {
- error = dlerror();
- sprintf(g->Message, MSG(SHARED_LIB_ERR), Module, SVP(error));
- return NULL;
- } // endif Hdll
-
- // The exported name is always in uppercase
- for (int i = 0; ; i++) {
- c = Subtype[i];
- getname[i + 3] = toupper(c);
- if (!c) break;
- } // endfor i
-
- // Get the function returning an instance of the external DEF class
- if (!(getdef = (XGETDEF)dlsym(Hdll, getname))) {
- error = dlerror();
- sprintf(g->Message, MSG(GET_FUNC_ERR), getname, SVP(error));
- dlclose(Hdll);
- return NULL;
- } // endif getdef
-#endif // !WIN32
-
- // Just in case the external Get function does not set error messages
- sprintf(g->Message, MSG(DEF_ALLOC_ERROR), Subtype);
-
- // Get the table definition block
- if (!(xdefp = getdef(g, NULL)))
- return NULL;
-
- // 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->Cbuf = (char*)PlugSubAlloc(g, NULL, cat->Cblen);
- } // endif Cbuf
-
- // Here "OEM" should be replace by a more useful value
- if (xdefp->Define(g, cat, Name, "OEM"))
- return NULL;
-
- // Ok, return external block
- return xdefp;
- } // end of GetXdef
-
-#if 0
-/***********************************************************************/
-/* DeleteTableFile: Delete an OEM table file if applicable. */
-/***********************************************************************/
-bool OEMDEF::DeleteTableFile(PGLOBAL g)
- {
- if (!Pxdef)
- Pxdef = GetXdef(g);
-
- return (Pxdef) ? Pxdef->DeleteTableFile(g) : true;
- } // end of DeleteTableFile
-#endif // 0
-
-/***********************************************************************/
-/* Define: initialize the table definition block from XDB file. */
-/***********************************************************************/
-bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
- {
- Module = Cat->GetStringCatInfo(g, "Module", "");
- Subtype = Cat->GetStringCatInfo(g, "Subtype", Module);
-
- if (!*Module)
- Module = Subtype;
-
- Desc = (char*)PlugSubAlloc(g, NULL, strlen(Module)
- + strlen(Subtype) + 3);
- sprintf(Desc, "%s(%s)", Module, Subtype);
- return false;
- } // end of DefineAM
-
-/***********************************************************************/
-/* GetTable: makes a new Table Description Block. */
-/***********************************************************************/
-PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
- {
- RECFM rfm;
- PTDBASE tdbp = NULL;
-
- // If define block not here yet, get it now
- if (!Pxdef && !(Pxdef = GetXdef(g)))
- return NULL; // Error
-
- /*********************************************************************/
- /* Allocate a TDB of the proper type. */
- /* Column blocks will be allocated only when needed. */
- /*********************************************************************/
- if (!(tdbp = (PTDBASE)Pxdef->GetTable(g, mode)))
- return NULL;
- else
- rfm = tdbp->GetFtype();
-
- if (rfm == RECFM_NAF)
- return tdbp;
- else if (rfm == RECFM_OEM) {
- if (Multiple)
- tdbp = new(g) TDBMUL(tdbp); // No block optimization yet
-
- return tdbp;
- } // endif OEM
-
- /*********************************************************************/
- /* The OEM table is based on a file type (currently DOS+ only) */
- /*********************************************************************/
- assert (rfm == RECFM_VAR || rfm == RECFM_FIX ||
- rfm == RECFM_BIN || rfm == RECFM_VCT);
-
- PTXF txfp = NULL;
- PDOSDEF defp = (PDOSDEF)Pxdef;
- bool map = defp->Mapped && mode != MODE_INSERT &&
- !(PlgGetUser(g)->UseTemp == TMP_FORCE &&
- (mode == MODE_UPDATE || mode == MODE_DELETE));
- int cmpr = defp->Compressed;
-
- /*********************************************************************/
- /* Allocate table and file processing class of the proper type. */
- /* Column blocks will be allocated only when needed. */
- /*********************************************************************/
- if (!((PTDBDOS)tdbp)->GetTxfp()) {
- if (cmpr) {
-#if defined(ZIP_SUPPORT)
- if (cmpr == 1)
- txfp = new(g) ZIPFAM(defp);
- else {
-#if defined(BLK_INDX)
- txfp = new(g) ZLBFAM(defp);
-#else // !BLK_INDX
- strcpy(g->Message, "Compress 2 not supported yet");
-#endif // !BLK_INDX
- return NULL;
- } // endelse
-#else // !ZIP_SUPPORT
- strcpy(g->Message, "Compress not supported");
- return NULL;
-#endif // !ZIP_SUPPORT
- } else if (rfm == RECFM_VAR) {
- if (map)
- txfp = new(g) MAPFAM(defp);
- else
- txfp = new(g) DOSFAM(defp);
-
- } else if (rfm == RECFM_FIX || rfm == RECFM_BIN) {
- if (map)
- txfp = new(g) MPXFAM(defp);
- else
- txfp = new(g) FIXFAM(defp);
-
- } else if (rfm == RECFM_VCT) {
- assert (Pxdef->GetDefType() == TYPE_AM_VCT);
-
- if (map)
- txfp = new(g) VCMFAM((PVCTDEF)defp);
- else
- txfp = new(g) VCTFAM((PVCTDEF)defp);
-
- } // endif's
-
- ((PTDBDOS)tdbp)->SetTxfp(txfp);
- } // endif Txfp
-
- if (Multiple)
- tdbp = new(g) TDBMUL(tdbp);
-
- return tdbp;
- } // end of GetTable
-
-/* --------------------------- Class COLCRT -------------------------- */
-
-/***********************************************************************/
-/* COLCRT Constructors. */
-/***********************************************************************/
-COLCRT::COLCRT(PSZ name)
- {
- Next = NULL;
- Name = name;
- Desc = NULL;
- Decode = NULL;
- Fmt = NULL;
- Offset = -1;
- Long = -1;
- Precision = -1;
- Freq = -1;
- Key = -1;
- Scale = -1;
- Opt = -1;
- DataType = '*';
- } // end of COLCRT constructor for table creation
-
-COLCRT::COLCRT(void)
- {
- Next = NULL;
- Name = NULL;
- Desc = NULL;
- Decode = NULL;
- Fmt = NULL;
- Offset = 0;
- Long = 0;
- Precision = 0;
- Freq = 0;
- Key = 0;
- Scale = 0;
- Opt = 0;
- DataType = '*';
- } // end of COLCRT constructor for table & view definition
-
-/* --------------------------- Class COLDEF -------------------------- */
-
-/***********************************************************************/
-/* COLDEF Constructor. */
-/***********************************************************************/
-COLDEF::COLDEF(void) : COLCRT()
- {
-#if defined(BLK_INDX)
- To_Min = NULL;
- To_Max = NULL;
- To_Pos = NULL;
- Xdb2 = FALSE;
- To_Bmap = NULL;
- To_Dval = NULL;
- Ndv = 0;
- Nbm = 0;
-#endif // BLK_INDX
- Buf_Type = TYPE_ERROR;
- Clen = 0;
- Poff = 0;
- memset(&F, 0, sizeof(FORMAT));
- Flags = 0;
- } // end of COLDEF constructor
-
-/***********************************************************************/
-/* Define: initialize a column definition from a COLINFO structure. */
-/***********************************************************************/
-int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
- {
- Name = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Name) + 1);
- strcpy(Name, cfp->Name);
-
- if (!(cfp->Flags & U_SPECIAL)) {
- Poff = poff;
- Buf_Type = cfp->Type;
-
- if ((Clen = GetTypeSize(Buf_Type, cfp->Length)) <= 0) {
- sprintf(g->Message, MSG(BAD_COL_TYPE), GetTypeName(Buf_Type), Name);
- return -1;
- } // endswitch
-
- strcpy(F.Type, GetFormatType(Buf_Type));
- F.Length = cfp->Length;
- F.Prec = cfp->Scale;
- Offset = (cfp->Offset < 0) ? poff : cfp->Offset;
- Precision = cfp->Precision;
- Scale = cfp->Scale;
- Long = cfp->Length;
- Opt = cfp->Opt;
- Key = cfp->Key;
- Freq = cfp->Freq;
-
- if (cfp->Remark && *cfp->Remark) {
- Desc = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Remark) + 1);
- strcpy(Desc, cfp->Remark);
- } // endif Remark
-
- if (cfp->Datefmt) {
- Decode = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Datefmt) + 1);
- strcpy(Decode, cfp->Datefmt);
- } // endif Datefmt
-
- } // endif special
-
- if (cfp->Fieldfmt) {
- Fmt = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Fieldfmt) + 1);
- strcpy(Fmt, cfp->Fieldfmt);
- } // endif Fieldfmt
-
- Flags = cfp->Flags;
- return (Flags & (U_VIRTUAL|U_SPECIAL)) ? 0 : Long;
- } // end of Define
-
-/* ------------------------- End of RelDef --------------------------- */
+/************* RelDef CPP Program Source Code File (.CPP) **************/
+/* PROGRAM NAME: REFDEF */
+/* ------------- */
+/* Version 1.4 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the DB definition related routines. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <sqlext.h>
+#else
+#include <dlfcn.h> // dlopen(), dlclose(), dlsym() ...
+#include "osutil.h"
+//#include "sqlext.h"
+#endif
+
+/***********************************************************************/
+/* Include application header files */
+/* */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing DB application declarations. */
+/* catalog.h is header containing DB description declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "mycat.h"
+#include "reldef.h"
+#include "colblk.h"
+#include "filamap.h"
+#include "filamfix.h"
+#include "filamvct.h"
+#if defined(ZIP_SUPPORT)
+#include "filamzip.h"
+#endif // ZIP_SUPPORT
+#include "tabdos.h"
+#include "valblk.h"
+#include "tabmul.h"
+
+/***********************************************************************/
+/* External static variables. */
+/***********************************************************************/
+//extern "C" char plgini[];
+
+/* --------------------------- Class RELDEF -------------------------- */
+
+/***********************************************************************/
+/* RELDEF Constructor. */
+/***********************************************************************/
+RELDEF::RELDEF(void)
+ {
+ Next = NULL;
+ To_Cols = NULL;
+ Name = NULL;
+ Database = NULL;
+ Cat = NULL;
+ } // end of RELDEF constructor
+
+/* --------------------------- Class TABDEF -------------------------- */
+
+/***********************************************************************/
+/* TABDEF Constructor. */
+/***********************************************************************/
+TABDEF::TABDEF(void)
+ {
+ Schema = NULL;
+ Desc = NULL;
+ Catfunc = FNC_NO;
+ Card = 0;
+ Elemt = 0;
+ Sort = 0;
+ Multiple = 0;
+ Degree = 0;
+ Pseudo = 0;
+ Read_Only = false;
+ } // end of TABDEF constructor
+
+/***********************************************************************/
+/* Define: initialize the table definition block from XDB file. */
+/***********************************************************************/
+bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
+ {
+ int poff = 0;
+
+ 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);
+ 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)
+ return true;
+
+ // Do the definition of AM specific fields
+ return DefineAM(g, am, poff);
+ } // end of Define
+
+/* --------------------------- Class OEMDEF -------------------------- */
+
+/***********************************************************************/
+/* GetXdef: get the external TABDEF from OEM module. */
+/***********************************************************************/
+PTABDEF OEMDEF::GetXdef(PGLOBAL g)
+ {
+ typedef PTABDEF (__stdcall *XGETDEF) (PGLOBAL, void *);
+ char c, getname[40] = "Get";
+ PTABDEF xdefp;
+ XGETDEF getdef = NULL;
+ PCATLG cat = Cat;
+
+#if defined(WIN32)
+ // Is the DLL already loaded?
+ if (!Hdll && !(Hdll = GetModuleHandle(Module)))
+ // No, load the Dll implementing the function
+ if (!(Hdll = LoadLibrary(Module))) {
+ char buf[256];
+ DWORD rc = GetLastError();
+
+ sprintf(g->Message, MSG(DLL_LOAD_ERROR), rc, Module);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ strcat(strcat(g->Message, ": "), buf);
+ return NULL;
+ } // endif hDll
+
+ // The exported name is always in uppercase
+ for (int i = 0; ; i++) {
+ c = Subtype[i];
+ getname[i + 3] = toupper(c);
+ if (!c) break;
+ } // endfor i
+
+ // Get the function returning an instance of the external DEF class
+ if (!(getdef = (XGETDEF)GetProcAddress((HINSTANCE)Hdll, getname))) {
+ sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), getname);
+ FreeLibrary((HMODULE)Hdll);
+ return NULL;
+ } // endif getdef
+#else // !WIN32
+ const char *error = NULL;
+ // Is the library already loaded?
+// if (!Hdll && !(Hdll = ???))
+ // Load the desired shared library
+ if (!(Hdll = dlopen(Module, RTLD_LAZY))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(SHARED_LIB_ERR), Module, SVP(error));
+ return NULL;
+ } // endif Hdll
+
+ // The exported name is always in uppercase
+ for (int i = 0; ; i++) {
+ c = Subtype[i];
+ getname[i + 3] = toupper(c);
+ if (!c) break;
+ } // endfor i
+
+ // Get the function returning an instance of the external DEF class
+ if (!(getdef = (XGETDEF)dlsym(Hdll, getname))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(GET_FUNC_ERR), getname, SVP(error));
+ dlclose(Hdll);
+ return NULL;
+ } // endif getdef
+#endif // !WIN32
+
+ // Just in case the external Get function does not set error messages
+ sprintf(g->Message, MSG(DEF_ALLOC_ERROR), Subtype);
+
+ // Get the table definition block
+ if (!(xdefp = getdef(g, NULL)))
+ return NULL;
+
+ // 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->Cbuf = (char*)PlugSubAlloc(g, NULL, cat->Cblen);
+ } // endif Cbuf
+
+ // Here "OEM" should be replace by a more useful value
+ if (xdefp->Define(g, cat, Name, "OEM"))
+ return NULL;
+
+ // Ok, return external block
+ return xdefp;
+ } // end of GetXdef
+
+#if 0
+/***********************************************************************/
+/* DeleteTableFile: Delete an OEM table file if applicable. */
+/***********************************************************************/
+bool OEMDEF::DeleteTableFile(PGLOBAL g)
+ {
+ if (!Pxdef)
+ Pxdef = GetXdef(g);
+
+ return (Pxdef) ? Pxdef->DeleteTableFile(g) : true;
+ } // end of DeleteTableFile
+#endif // 0
+
+/***********************************************************************/
+/* Define: initialize the table definition block from XDB file. */
+/***********************************************************************/
+bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+ {
+ Module = Cat->GetStringCatInfo(g, "Module", "");
+ Subtype = Cat->GetStringCatInfo(g, "Subtype", Module);
+
+ if (!*Module)
+ Module = Subtype;
+
+ Desc = (char*)PlugSubAlloc(g, NULL, strlen(Module)
+ + strlen(Subtype) + 3);
+ sprintf(Desc, "%s(%s)", Module, Subtype);
+ return false;
+ } // end of DefineAM
+
+/***********************************************************************/
+/* GetTable: makes a new Table Description Block. */
+/***********************************************************************/
+PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
+ {
+ RECFM rfm;
+ PTDBASE tdbp = NULL;
+
+ // If define block not here yet, get it now
+ if (!Pxdef && !(Pxdef = GetXdef(g)))
+ return NULL; // Error
+
+ /*********************************************************************/
+ /* Allocate a TDB of the proper type. */
+ /* Column blocks will be allocated only when needed. */
+ /*********************************************************************/
+ if (!(tdbp = (PTDBASE)Pxdef->GetTable(g, mode)))
+ return NULL;
+ else
+ rfm = tdbp->GetFtype();
+
+ if (rfm == RECFM_NAF)
+ return tdbp;
+ else if (rfm == RECFM_OEM) {
+ if (Multiple)
+ tdbp = new(g) TDBMUL(tdbp); // No block optimization yet
+
+ return tdbp;
+ } // endif OEM
+
+ /*********************************************************************/
+ /* The OEM table is based on a file type (currently DOS+ only) */
+ /*********************************************************************/
+ assert (rfm == RECFM_VAR || rfm == RECFM_FIX ||
+ rfm == RECFM_BIN || rfm == RECFM_VCT);
+
+ PTXF txfp = NULL;
+ PDOSDEF defp = (PDOSDEF)Pxdef;
+ bool map = defp->Mapped && mode != MODE_INSERT &&
+ !(PlgGetUser(g)->UseTemp == TMP_FORCE &&
+ (mode == MODE_UPDATE || mode == MODE_DELETE));
+ int cmpr = defp->Compressed;
+
+ /*********************************************************************/
+ /* Allocate table and file processing class of the proper type. */
+ /* Column blocks will be allocated only when needed. */
+ /*********************************************************************/
+ if (!((PTDBDOS)tdbp)->GetTxfp()) {
+ if (cmpr) {
+#if defined(ZIP_SUPPORT)
+ if (cmpr == 1)
+ txfp = new(g) ZIPFAM(defp);
+ else
+ txfp = new(g) ZLBFAM(defp);
+#else // !ZIP_SUPPORT
+ strcpy(g->Message, "Compress not supported");
+ return NULL;
+#endif // !ZIP_SUPPORT
+ } else if (rfm == RECFM_VAR) {
+ if (map)
+ txfp = new(g) MAPFAM(defp);
+ else
+ txfp = new(g) DOSFAM(defp);
+
+ } else if (rfm == RECFM_FIX || rfm == RECFM_BIN) {
+ if (map)
+ txfp = new(g) MPXFAM(defp);
+ else
+ txfp = new(g) FIXFAM(defp);
+
+ } else if (rfm == RECFM_VCT) {
+ assert (Pxdef->GetDefType() == TYPE_AM_VCT);
+
+ if (map)
+ txfp = new(g) VCMFAM((PVCTDEF)defp);
+ else
+ txfp = new(g) VCTFAM((PVCTDEF)defp);
+
+ } // endif's
+
+ ((PTDBDOS)tdbp)->SetTxfp(txfp);
+ } // endif Txfp
+
+ if (Multiple)
+ tdbp = new(g) TDBMUL(tdbp);
+
+ return tdbp;
+ } // end of GetTable
+
+/* --------------------------- Class COLCRT -------------------------- */
+
+/***********************************************************************/
+/* COLCRT Constructors. */
+/***********************************************************************/
+COLCRT::COLCRT(PSZ name)
+ {
+ Next = NULL;
+ Name = name;
+ Desc = NULL;
+ Decode = NULL;
+ Fmt = NULL;
+ Offset = -1;
+ Long = -1;
+ Precision = -1;
+ Freq = -1;
+ Key = -1;
+ Scale = -1;
+ Opt = -1;
+ DataType = '*';
+ } // end of COLCRT constructor for table creation
+
+COLCRT::COLCRT(void)
+ {
+ Next = NULL;
+ Name = NULL;
+ Desc = NULL;
+ Decode = NULL;
+ Fmt = NULL;
+ Offset = 0;
+ Long = 0;
+ Precision = 0;
+ Freq = 0;
+ Key = 0;
+ Scale = 0;
+ Opt = 0;
+ DataType = '*';
+ } // end of COLCRT constructor for table & view definition
+
+/* --------------------------- Class COLDEF -------------------------- */
+
+/***********************************************************************/
+/* COLDEF Constructor. */
+/***********************************************************************/
+COLDEF::COLDEF(void) : COLCRT()
+ {
+ To_Min = NULL;
+ To_Max = NULL;
+ To_Pos = NULL;
+ Xdb2 = FALSE;
+ To_Bmap = NULL;
+ To_Dval = NULL;
+ Ndv = 0;
+ Nbm = 0;
+ Buf_Type = TYPE_ERROR;
+ Clen = 0;
+ Poff = 0;
+ memset(&F, 0, sizeof(FORMAT));
+ Flags = 0;
+ } // end of COLDEF constructor
+
+/***********************************************************************/
+/* Define: initialize a column definition from a COLINFO structure. */
+/***********************************************************************/
+int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
+ {
+ Name = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Name) + 1);
+ strcpy(Name, cfp->Name);
+
+ if (!(cfp->Flags & U_SPECIAL)) {
+ Poff = poff;
+ Buf_Type = cfp->Type;
+
+ if ((Clen = GetTypeSize(Buf_Type, cfp->Length)) <= 0) {
+ sprintf(g->Message, MSG(BAD_COL_TYPE), GetTypeName(Buf_Type), Name);
+ return -1;
+ } // endswitch
+
+ strcpy(F.Type, GetFormatType(Buf_Type));
+ F.Length = cfp->Length;
+ F.Prec = cfp->Scale;
+ Offset = (cfp->Offset < 0) ? poff : cfp->Offset;
+ Precision = cfp->Precision;
+ Scale = cfp->Scale;
+ Long = cfp->Length;
+ Opt = cfp->Opt;
+ Key = cfp->Key;
+ Freq = cfp->Freq;
+
+ if (cfp->Remark && *cfp->Remark) {
+ Desc = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Remark) + 1);
+ strcpy(Desc, cfp->Remark);
+ } // endif Remark
+
+ if (cfp->Datefmt) {
+ Decode = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Datefmt) + 1);
+ strcpy(Decode, cfp->Datefmt);
+ } // endif Datefmt
+
+ } // endif special
+
+ if (cfp->Fieldfmt) {
+ Fmt = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Fieldfmt) + 1);
+ strcpy(Fmt, cfp->Fieldfmt);
+ } // endif Fieldfmt
+
+ Flags = cfp->Flags;
+ return (Flags & (U_VIRTUAL|U_SPECIAL)) ? 0 : Long;
+ } // end of Define
+
+/* ------------------------- End of RelDef --------------------------- */
diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h
index c54d81f30cb..b64c21b3a43 100644
--- a/storage/connect/reldef.h
+++ b/storage/connect/reldef.h
@@ -1,231 +1,225 @@
-/*************** RelDef H Declares Source Code File (.H) ***************/
-/* Name: RELDEF.H Version 1.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */
-/* */
-/* 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 DeleteTableFile(PGLOBAL g) {return true;}
- 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 DeleteTableFile(PGLOBAL g);
- 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;}
-#if defined(BLK_INDX)
- 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;}
-#endif // BLK_INDX
- int Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff);
- void Define(PGLOBAL g, PCOL colp);
-
- protected:
-#if defined(BLK_INDX)
- 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) */
-#endif // BLK_INDX
- 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;
+
+/***********************************************************************/
+/* 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
+
diff --git a/storage/connect/tabcol.h b/storage/connect/tabcol.h
index fe643e075c3..fdee653207e 100644
--- a/storage/connect/tabcol.h
+++ b/storage/connect/tabcol.h
@@ -85,8 +85,6 @@ class DllExport COLUMN: public XOBJECT { // Column Name/Qualifier block.
virtual bool Compare(PXOB) {assert(false); return false;}
virtual bool SetFormat(PGLOBAL, FORMAT&);
virtual bool Eval(PGLOBAL) {assert(false); return true;}
- virtual int CheckSpcCol(PTDB, int) {assert(false); return 2;}
- virtual bool CheckSort(PTDB) {assert(false); return false;}
private:
// Members
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index c1da09080cb..9f3dd50ba46 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -1,2710 +1,2579 @@
-/************* 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"
-#if defined(BLK_INDX)
-#include "array.h"
-#include "blkfil.h"
-//nclude "token.h"
-//#include "scalfnc.h"
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* DB static variables. */
-/***********************************************************************/
-int num_read, num_there, num_eq[2]; // Statistics
-extern "C" int trace;
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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);
-#endif // BLK_INDX
-
-/* --------------------------- 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;
-#if defined(BLK_INDX)
- To_Pos = NULL;
- Optimized = 0;
- AllocBlks = 0;
-#endif // BLK_INDX
- Compressed = 0;
- Lrecl = 0;
- AvgLen = 0;
- Block = 0;
- Last = 0;
- Blksize = 0;
- Maxerr = 0;
- ReadMode = 0;
- Ending = 0;
-//Mtime = 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
-
-#if 0
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* DeleteTableFile: Delete DOS/UNIX table files using platform API. */
-/* If the table file is protected (declared as read/only) we still */
-/* erase the the eventual optimize and index files but return TRUE. */
-/***********************************************************************/
-bool DOSDEF::DeleteTableFile(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- bool rc, irc, orc = FALSE;
- PIXDEF pxd;
- PCOLDEF cdp = NULL;
-
- /*********************************************************************/
- /* Check for potential optimization. These tests are done */
- /* because Optimized is set to 1 only after the first use of an */
- /* optimized table and can be 0 if it has not been used yet. */
- /*********************************************************************/
- if (!Optimized)
- for (cdp = To_Cols; cdp; cdp = cdp->GetNext())
- if (cdp->GetOpt())
- break;
-
- if (IsOptimized() || cdp || (Recfm == RECFM_VAR && Elemt > 1 && Block))
- if (!GetOptFileName(g, filename))
-#if defined(WIN32)
- orc = !DeleteFile(filename);
-#else // UNIX
- orc = remove(filename);
-#endif // WIN32
-
- // Now delete the table file itself if not protected
- if (!IsReadOnly()) {
- rc = Erase(filename);
- } else
- rc = true;
-
- // Delete eventual index file(s)
- if ((pxd = To_Indx)) {
- To_Indx = NULL; // So file can be erase
- irc = DeleteIndexFile(g, pxd);
- } else
- irc = false;
-
- return rc || orc || irc; // Return TRUE if error
- } // end of DeleteTableFile
-
-#else // !BLK_INDX
-/***********************************************************************/
-/* DeleteTableFile: Delete DOS/UNIX table files using platform API. */
-/* If the table file is protected (declared as read/only) we still */
-/* erase the the eventual optimize and index files but return true. */
-/***********************************************************************/
-bool DOSDEF::DeleteTableFile(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- bool rc = false;
-
- // Now delete the table file itself if not protected
- if (!IsReadOnly()) {
- rc = Erase(filename);
- } else
- rc =true;
-
- return rc; // Return true if error
- } // end of DeleteTableFile
-#endif // !BLK_INDX
-
-/***********************************************************************/
-/* Erase: This was made a separate routine because a strange thing */
-/* happened when DeleteTablefile was defined for the VCTDEF class: */
-/* when called from Catalog, the DOSDEF routine was still called even */
-/* when the class was VCTDEF. It also minimizes the specific code. */
-/***********************************************************************/
-bool DOSDEF::Erase(char *filename)
- {
- bool rc;
-
- PlugSetPath(filename, Fn, GetPath());
-#if defined(WIN32)
- rc = !DeleteFile(filename);
-#else // UNIX
- rc = remove(filename);
-#endif // UNIX
-
- return rc; // Return true if error
- } // end of Erase
-#endif // 0
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* 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 {
-#if defined(BLK_INDX)
- txfp = new(g) ZLBFAM(this);
-#else // !BLK_INDX
- strcpy(g->Message, "Compress 2 not supported yet");
- return NULL;
-#endif // !BLK_INDX
- } // endelse
-#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);
-#if defined(BLK_INDX)
- 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
-#endif // BLK_INDX
-
- 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;
-#if defined(BLK_INDX)
-//To_BlkIdx = NULL;
- To_BlkFil = NULL;
- SavFil = NULL;
-//Xeval = 0;
- Beval = 0;
-#endif // BLK_INDX
- } // 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;
-#if defined(BLK_INDX)
-//To_BlkIdx = tdbp->To_BlkIdx;
- To_BlkFil = tdbp->To_BlkFil;
- SavFil = tdbp->SavFil;
-//Xeval = tdbp->Xeval;
- Beval = tdbp->Beval;
-#endif // BLK_INDX
- } // 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
-
-#if defined(BLK_INDX)
- 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
-#endif // BLK_INDX
-
- 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);
-
-#if defined(BLK_INDX)
- rc = (rc == RC_INFO) ? prc : rc;
-#endif // BLK_INDX
- } // endif dox
-
- return rc;
- } // end of ResetTableOpt
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* 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();
-
-#if defined(BLK_INDX)
- ResetBlockFilter(g);
-#endif // BLK_INDX
- 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
-
-#if defined(BLK_INDX)
- /*********************************************************************/
- /* Allocate the block filter tree if evaluation is possible. */
- /*********************************************************************/
- To_BlkFil = InitBlockFilter(g, To_Filter);
-#endif // BLK_INDX
-
- /*********************************************************************/
- /* 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;
-
-#if defined(BLK_INDX)
- 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
-#endif // BLK_INDX
-
- 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;
-#if defined(BLK_INDX)
- Clustered = col1->Clustered;
- Sorted = col1->Sorted;
- Min = col1->Min;
- Max = col1->Max;
- Bmap = col1->Bmap;
- Dval = col1->Dval;
- Ndv = col1->Ndv;
- Nbm = col1->Nbm;
-#endif // BLK_INDX
- } // end of DOSCOL copy constructor
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* 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;
- len = (signed)strlen(tdbp->To_Line);
-
- if (tdbp->GetAmType() == TYPE_AM_DOS && len > tdbp->Lrecl) {
- sprintf(g->Message, "Line size %d is bigger than lrecl %d",
- len, tdbp->Lrecl);
- longjmp(g->jumper[g->jump_level], 32);
- } // endif
-
- 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
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* 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 = 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;
+ len = (signed)strlen(tdbp->To_Line);
+
+ if (tdbp->GetAmType() == TYPE_AM_DOS && len > tdbp->Lrecl) {
+ sprintf(g->Message, "Line size %d is bigger than lrecl %d",
+ len, tdbp->Lrecl);
+ longjmp(g->jumper[g->jump_level], 32);
+ } // endif
+
+ 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/tabdos.h b/storage/connect/tabdos.h
index 52bb1450c29..a175cc32ec9 100644
--- a/storage/connect/tabdos.h
+++ b/storage/connect/tabdos.h
@@ -1,286 +1,253 @@
-/*************** TabDos H Declares Source Code File (.H) ***************/
-/* Name: TABDOS.H Version 3.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
-/* */
-/* This file contains the DOS classes declares. */
-/***********************************************************************/
-
-#ifndef __TABDOS_H
-#define __TABDOS_H
-
-#include "xtable.h" // Table base class declares
-#include "colblk.h" // Column base class declares
-#include "xindex.h"
-#if defined(BLK_INDX)
-#include "filter.h"
-#endif // BLK_INDX
-
-//pedef struct _tabdesc *PTABD; // For friend setting
-typedef class TXTFAM *PTXF;
-#if defined(BLK_INDX)
-typedef class BLOCKFILTER *PBF;
-typedef class BLOCKINDEX *PBX;
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* DOS table. */
-/***********************************************************************/
-class DllExport DOSDEF : public TABDEF { /* Logical table description */
- friend class OEMDEF;
- friend class TDBDOS;
- friend class TDBFIX;
- friend class TXTFAM;
- friend class DBFBASE;
- public:
- // Constructor
- DOSDEF(void);
-
- // Implementation
- virtual AMT GetDefType(void) {return TYPE_AM_DOS;}
- virtual const char *GetType(void) {return "DOS";}
- virtual PIXDEF GetIndx(void) {return To_Indx;}
- virtual void SetIndx(PIXDEF xdp) {To_Indx = xdp;}
- virtual bool IsHuge(void) {return Huge;}
- PSZ GetFn(void) {return Fn;}
- PSZ GetOfn(void) {return Ofn;}
- void SetBlock(int block) {Block = block;}
- int GetBlock(void) {return Block;}
- int GetLast(void) {return Last;}
- void SetLast(int last) {Last = last;}
- int GetLrecl(void) {return Lrecl;}
- void SetLrecl(int lrecl) {Lrecl = lrecl;}
- bool GetPadded(void) {return Padded;}
- bool GetEof(void) {return Eof;}
- int GetBlksize(void) {return Blksize;}
- int GetEnding(void) {return Ending;}
-#if defined(BLK_INDX)
- bool IsOptimized(void) {return (Optimized == 1);}
- void SetOptimized(int opt) {Optimized = opt;}
- void SetAllocBlks(int blks) {AllocBlks = blks;}
- int GetAllocBlks(void) {return AllocBlks;}
- int *GetTo_Pos(void) {return To_Pos;}
-#endif // BLK_INDX
-
- // Methods
-//virtual bool DeleteTableFile(PGLOBAL g);
- virtual bool Indexable(void) {return Compressed != 1;}
- virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf);
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE mode);
- bool InvalidateIndex(PGLOBAL g);
-#if defined(BLK_INDX)
- bool GetOptFileName(PGLOBAL g, char *filename);
- void RemoveOptValues(PGLOBAL g);
-#endif // BLK_INDX
-
- protected:
-//virtual bool Erase(char *filename);
-
- // Members
- PSZ Fn; /* Path/Name of corresponding file */
- PSZ Ofn; /* Base Path/Name of matching index files*/
- PIXDEF To_Indx; /* To index definitions blocks */
- RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */
- bool Mapped; /* 0: disk file, 1: memory mapped file */
- bool Padded; /* true for padded table file */
- bool Huge; /* true for files larger than 2GB */
- bool Accept; /* true if wrong lines are accepted (DBF)*/
- bool Eof; /* true if an EOF (0xA) character exists */
-#if defined(BLK_INDX)
- int *To_Pos; /* To array of block starting positions */
- int Optimized; /* 0: No, 1:Yes, 2:Redo optimization */
- int AllocBlks; /* Number of suballocated opt blocks */
-#endif // BLK_INDX
- int Compressed; /* 0: No, 1: gz, 2:zlib compressed file */
- int Lrecl; /* Size of biggest record */
- int AvgLen; /* Average size of records */
- int Block; /* Number de blocks of FIX/VCT tables */
- int Last; /* Number of elements of last block */
- int Blksize; /* Size of padded blocks */
- int Maxerr; /* Maximum number of bad records (DBF) */
- int ReadMode; /* Specific to DBF */
- int Ending; /* Length of end of lines */
- }; // end of DOSDEF
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* that are standard files with columns starting at fixed offset. */
-/* The last column (and record) is of variable length. */
-/***********************************************************************/
-class DllExport TDBDOS : public TDBASE {
-//friend class KINDEX;
- friend class XINDEX;
- friend class DOSCOL;
- friend class MAPCOL;
- friend class TXTFAM;
- friend class DOSFAM;
- friend class VCTCOL;
-//friend class TDBMUL;
- friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool);
- public:
- // Constructors
- TDBDOS(PDOSDEF tdp, PTXF txfp);
- TDBDOS(PGLOBAL g, PTDBDOS tdbp);
-
- // Inline functions
- inline void SetTxfp(PTXF txfp) {Txfp = txfp; Txfp->SetTdbp(this);}
- inline PTXF GetTxfp(void) {return Txfp;}
- inline char *GetLine(void) {return To_Line;}
- inline int GetCurBlk(void) {return Txfp->GetCurBlk();}
- inline void SetLine(char *toline) {To_Line = toline;}
- inline void IncLine(int inc) {To_Line += inc;}
- inline bool IsRead(void) {return Txfp->IsRead;}
- inline PXOB *GetLink(void) {return To_Link;}
-//inline PCOL *GetKeyCol(void) {return To_Key_Col;}
-
- // Implementation
- virtual AMT GetAmType(void) {return Txfp->GetAmType();}
- virtual PSZ GetFile(PGLOBAL g) {return Txfp->To_File;}
- virtual void SetFile(PGLOBAL g, PSZ fn) {Txfp->To_File = fn;}
- virtual RECFM GetFtype(void) {return Ftype;}
- virtual bool SkipHeader(PGLOBAL g) {return false;}
- virtual void RestoreNrec(void) {Txfp->SetNrec(1);}
- virtual PTDB Duplicate(PGLOBAL g)
- {return (PTDB)new(g) TDBDOS(g, this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
- virtual void ResetDB(void) {Txfp->Reset();}
- virtual bool IsUsingTemp(PGLOBAL g);
-//virtual bool NeedIndexing(PGLOBAL g);
- virtual void ResetSize(void) {MaxSize = Cardinal = -1;}
- virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
-#if defined(BLK_INDX)
- virtual int MakeBlockValues(PGLOBAL g);
- virtual bool SaveBlockValues(PGLOBAL g);
- virtual bool GetBlockValues(PGLOBAL g);
- virtual PBF InitBlockFilter(PGLOBAL g, PFIL filp);
-//virtual PBX InitBlockIndex(PGLOBAL g);
- virtual int TestBlock(PGLOBAL g);
-#endif // BLK_INDX
- virtual void PrintAM(FILE *f, char *m);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual char *GetOpenMode(PGLOBAL g, char *opmode) {return NULL;}
- virtual int GetFileLength(PGLOBAL g) {return Txfp->GetFileLength(g);}
- virtual int GetProgMax(PGLOBAL g);
- virtual int GetProgCur(void);
- virtual int GetAffectedRows(void) {return Txfp->GetDelRows();}
- virtual int GetRecpos(void) {return Txfp->GetPos();}
- virtual bool SetRecpos(PGLOBAL g, int recpos)
- {return Txfp->SetPos(g, recpos);}
- virtual int RowNumber(PGLOBAL g, bool b = false);
- virtual int Cardinality(PGLOBAL g);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g) {return Txfp->ReadBuffer(g);}
-
- // Specific routine
- virtual int EstimatedLength(PGLOBAL g);
-
- // Optimization routines
- int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
-#if defined(BLK_INDX)
- void ResetBlockFilter(PGLOBAL g);
- bool GetDistinctColumnValues(PGLOBAL g, int nrec);
-
- protected:
- PBF CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv);
-#endif // BLK_INDX
-
- // Members
- PTXF Txfp; // To the File access method class
-#if defined(BLK_INDX)
-//PBX To_BlkIdx; // To index test block
- PBF To_BlkFil; // To evaluation block filter
- PFIL SavFil; // Saved hidden filter
-#endif // BLK_INDX
- char *To_Line; // Points to current processed line
- int Cardinal; // Table Cardinality
- RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT)
- int Lrecl; // Logical Record Length
- int AvgLen; // Logical Record Average Length
-#if defined(BLK_INDX)
-//int Xeval; // BlockTest return value
- int Beval; // BlockEval return value
-#endif // BLK_INDX
- }; // end of class TDBDOS
-
-/***********************************************************************/
-/* Class DOSCOL: DOS access method column descriptor. */
-/* This A.M. is used for text file tables under operating systems */
-/* DOS, OS2, UNIX, WIN16 and WIN32. */
-/***********************************************************************/
-class DllExport DOSCOL : public COLBLK {
- friend class TDBDOS;
- friend class TDBFIX;
- public:
- // Constructors
- DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am = "DOS");
- DOSCOL(DOSCOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_DOS;}
- virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
-#if defined(BLK_INDX)
- virtual int GetClustered(void) {return Clustered;}
- virtual int IsClustered(void) {return (Clustered &&
- ((PDOSDEF)(((PTDBDOS)To_Tdb)->To_Def))->IsOptimized());}
- virtual int IsSorted(void) {return Sorted;}
- virtual PVBLK GetMin(void) {return Min;}
- virtual PVBLK GetMax(void) {return Max;}
- virtual int GetNdv(void) {return Ndv;}
- virtual int GetNbm(void) {return Nbm;}
- virtual PVBLK GetBmap(void) {return Bmap;}
- virtual PVBLK GetDval(void) {return Dval;}
-#endif // BLK_INDX
-
- // Methods
-#if defined(BLK_INDX)
- virtual bool VarSize(void);
-#endif // BLK_INDX
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- virtual void Print(PGLOBAL g, FILE *, uint);
-
- protected:
-#if defined(BLK_INDX)
- virtual bool SetMinMax(PGLOBAL g);
- virtual bool SetBitMap(PGLOBAL g);
- bool CheckSorted(PGLOBAL g);
- bool AddDistinctValue(PGLOBAL g);
-#endif // BLK_INDX
-
- // Default constructor not to be used
- DOSCOL(void) {}
-
- // Members
-#if defined(BLK_INDX)
- PVBLK Min; // Array of block min values
- PVBLK Max; // Array of block max values
- PVBLK Bmap; // Array of block bitmap values
- PVBLK Dval; // Array of column distinct values
-#endif // BLK_INDX
- PVAL To_Val; // To value used for Update/Insert
- PVAL OldVal; // The previous value of the object.
- char *Buf; // Buffer used in write operations
- bool Ldz; // True if field contains leading zeros
- bool Nod; // True if no decimal point
- int Dcm; // Last Dcm digits are decimals
- int Deplac; // Offset in dos_buf
-#if defined(BLK_INDX)
- int Clustered; // 0:No 1:Yes
- int Sorted; // 0:No 1:Asc (2:Desc - NIY)
- int Ndv; // Number of distinct values
- int Nbm; // Number of uint in bitmap
-#endif // BLK_INDX
- }; // end of class DOSCOL
-
-#endif // __TABDOS_H
+/*************** TabDos H Declares Source Code File (.H) ***************/
+/* Name: TABDOS.H Version 3.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
+/* */
+/* This file contains the DOS classes declares. */
+/***********************************************************************/
+
+#ifndef __TABDOS_H
+#define __TABDOS_H
+
+#include "xtable.h" // Table base class declares
+#include "colblk.h" // Column base class declares
+#include "xindex.h"
+#include "filter.h"
+
+//pedef struct _tabdesc *PTABD; // For friend setting
+typedef class TXTFAM *PTXF;
+typedef class BLOCKFILTER *PBF;
+typedef class BLOCKINDEX *PBX;
+
+/***********************************************************************/
+/* DOS table. */
+/***********************************************************************/
+class DllExport DOSDEF : public TABDEF { /* Logical table description */
+ friend class OEMDEF;
+ friend class TDBDOS;
+ friend class TDBFIX;
+ friend class TXTFAM;
+ friend class DBFBASE;
+ public:
+ // Constructor
+ DOSDEF(void);
+
+ // Implementation
+ virtual AMT GetDefType(void) {return TYPE_AM_DOS;}
+ virtual const char *GetType(void) {return "DOS";}
+ virtual PIXDEF GetIndx(void) {return To_Indx;}
+ virtual void SetIndx(PIXDEF xdp) {To_Indx = xdp;}
+ virtual bool IsHuge(void) {return Huge;}
+ PSZ GetFn(void) {return Fn;}
+ PSZ GetOfn(void) {return Ofn;}
+ void SetBlock(int block) {Block = block;}
+ int GetBlock(void) {return Block;}
+ int GetLast(void) {return Last;}
+ void SetLast(int last) {Last = last;}
+ int GetLrecl(void) {return Lrecl;}
+ void SetLrecl(int lrecl) {Lrecl = lrecl;}
+ bool GetPadded(void) {return Padded;}
+ bool GetEof(void) {return Eof;}
+ int GetBlksize(void) {return Blksize;}
+ int GetEnding(void) {return Ending;}
+ bool IsOptimized(void) {return (Optimized == 1);}
+ void SetOptimized(int opt) {Optimized = opt;}
+ void SetAllocBlks(int blks) {AllocBlks = blks;}
+ int GetAllocBlks(void) {return AllocBlks;}
+ int *GetTo_Pos(void) {return To_Pos;}
+
+ // Methods
+ virtual bool Indexable(void) {return Compressed != 1;}
+ virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf);
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE mode);
+ bool InvalidateIndex(PGLOBAL g);
+ bool GetOptFileName(PGLOBAL g, char *filename);
+ void RemoveOptValues(PGLOBAL g);
+
+ protected:
+//virtual bool Erase(char *filename);
+
+ // Members
+ PSZ Fn; /* Path/Name of corresponding file */
+ PSZ Ofn; /* Base Path/Name of matching index files*/
+ PIXDEF To_Indx; /* To index definitions blocks */
+ RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */
+ bool Mapped; /* 0: disk file, 1: memory mapped file */
+ bool Padded; /* true for padded table file */
+ bool Huge; /* true for files larger than 2GB */
+ bool Accept; /* true if wrong lines are accepted (DBF)*/
+ bool Eof; /* true if an EOF (0xA) character exists */
+ int *To_Pos; /* To array of block starting positions */
+ int Optimized; /* 0: No, 1:Yes, 2:Redo optimization */
+ int AllocBlks; /* Number of suballocated opt blocks */
+ int Compressed; /* 0: No, 1: gz, 2:zlib compressed file */
+ int Lrecl; /* Size of biggest record */
+ int AvgLen; /* Average size of records */
+ int Block; /* Number de blocks of FIX/VCT tables */
+ int Last; /* Number of elements of last block */
+ int Blksize; /* Size of padded blocks */
+ int Maxerr; /* Maximum number of bad records (DBF) */
+ int ReadMode; /* Specific to DBF */
+ int Ending; /* Length of end of lines */
+ }; // end of DOSDEF
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* that are standard files with columns starting at fixed offset. */
+/* The last column (and record) is of variable length. */
+/***********************************************************************/
+class DllExport TDBDOS : public TDBASE {
+ friend class XINDEX;
+ friend class DOSCOL;
+ friend class MAPCOL;
+ friend class TXTFAM;
+ friend class DOSFAM;
+ friend class VCTCOL;
+ friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool);
+ public:
+ // Constructors
+ TDBDOS(PDOSDEF tdp, PTXF txfp);
+ TDBDOS(PGLOBAL g, PTDBDOS tdbp);
+
+ // Inline functions
+ inline void SetTxfp(PTXF txfp) {Txfp = txfp; Txfp->SetTdbp(this);}
+ inline PTXF GetTxfp(void) {return Txfp;}
+ inline char *GetLine(void) {return To_Line;}
+ inline int GetCurBlk(void) {return Txfp->GetCurBlk();}
+ inline void SetLine(char *toline) {To_Line = toline;}
+ inline void IncLine(int inc) {To_Line += inc;}
+ inline bool IsRead(void) {return Txfp->IsRead;}
+ inline PXOB *GetLink(void) {return To_Link;}
+
+ // Implementation
+ virtual AMT GetAmType(void) {return Txfp->GetAmType();}
+ virtual PSZ GetFile(PGLOBAL g) {return Txfp->To_File;}
+ virtual void SetFile(PGLOBAL g, PSZ fn) {Txfp->To_File = fn;}
+ virtual RECFM GetFtype(void) {return Ftype;}
+ virtual bool SkipHeader(PGLOBAL g) {return false;}
+ virtual void RestoreNrec(void) {Txfp->SetNrec(1);}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBDOS(g, this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+ virtual void ResetDB(void) {Txfp->Reset();}
+ virtual bool IsUsingTemp(PGLOBAL g);
+ virtual void ResetSize(void) {MaxSize = Cardinal = -1;}
+ virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
+ virtual int MakeBlockValues(PGLOBAL g);
+ virtual bool SaveBlockValues(PGLOBAL g);
+ virtual bool GetBlockValues(PGLOBAL g);
+ virtual PBF InitBlockFilter(PGLOBAL g, PFIL filp);
+//virtual PBX InitBlockIndex(PGLOBAL g);
+ virtual int TestBlock(PGLOBAL g);
+ virtual void PrintAM(FILE *f, char *m);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual char *GetOpenMode(PGLOBAL g, char *opmode) {return NULL;}
+ virtual int GetFileLength(PGLOBAL g) {return Txfp->GetFileLength(g);}
+ virtual int GetProgMax(PGLOBAL g);
+ virtual int GetProgCur(void);
+ virtual int GetAffectedRows(void) {return Txfp->GetDelRows();}
+ virtual int GetRecpos(void) {return Txfp->GetPos();}
+ virtual bool SetRecpos(PGLOBAL g, int recpos)
+ {return Txfp->SetPos(g, recpos);}
+ virtual int RowNumber(PGLOBAL g, bool b = false);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g) {return Txfp->ReadBuffer(g);}
+
+ // Specific routine
+ virtual int EstimatedLength(PGLOBAL g);
+
+ // Optimization routines
+ int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
+ void ResetBlockFilter(PGLOBAL g);
+ bool GetDistinctColumnValues(PGLOBAL g, int nrec);
+
+ protected:
+ PBF CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv);
+
+ // Members
+ PTXF Txfp; // To the File access method class
+//PBX To_BlkIdx; // To index test block
+ PBF To_BlkFil; // To evaluation block filter
+ PFIL SavFil; // Saved hidden filter
+ char *To_Line; // Points to current processed line
+ int Cardinal; // Table Cardinality
+ RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT)
+ int Lrecl; // Logical Record Length
+ int AvgLen; // Logical Record Average Length
+//int Xeval; // BlockTest return value
+ int Beval; // BlockEval return value
+ }; // end of class TDBDOS
+
+/***********************************************************************/
+/* Class DOSCOL: DOS access method column descriptor. */
+/* This A.M. is used for text file tables under operating systems */
+/* DOS, OS2, UNIX, WIN16 and WIN32. */
+/***********************************************************************/
+class DllExport DOSCOL : public COLBLK {
+ friend class TDBDOS;
+ friend class TDBFIX;
+ public:
+ // Constructors
+ DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am = "DOS");
+ DOSCOL(DOSCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_DOS;}
+ virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
+ virtual int GetClustered(void) {return Clustered;}
+ virtual int IsClustered(void) {return (Clustered &&
+ ((PDOSDEF)(((PTDBDOS)To_Tdb)->To_Def))->IsOptimized());}
+ virtual int IsSorted(void) {return Sorted;}
+ virtual PVBLK GetMin(void) {return Min;}
+ virtual PVBLK GetMax(void) {return Max;}
+ virtual int GetNdv(void) {return Ndv;}
+ virtual int GetNbm(void) {return Nbm;}
+ virtual PVBLK GetBmap(void) {return Bmap;}
+ virtual PVBLK GetDval(void) {return Dval;}
+
+ // Methods
+ virtual bool VarSize(void);
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ virtual void Print(PGLOBAL g, FILE *, uint);
+
+ protected:
+ virtual bool SetMinMax(PGLOBAL g);
+ virtual bool SetBitMap(PGLOBAL g);
+ bool CheckSorted(PGLOBAL g);
+ bool AddDistinctValue(PGLOBAL g);
+
+ // Default constructor not to be used
+ DOSCOL(void) {}
+
+ // Members
+ PVBLK Min; // Array of block min values
+ PVBLK Max; // Array of block max values
+ PVBLK Bmap; // Array of block bitmap values
+ PVBLK Dval; // Array of column distinct values
+ PVAL To_Val; // To value used for Update/Insert
+ PVAL OldVal; // The previous value of the object.
+ char *Buf; // Buffer used in write operations
+ bool Ldz; // True if field contains leading zeros
+ bool Nod; // True if no decimal point
+ int Dcm; // Last Dcm digits are decimals
+ int Deplac; // Offset in dos_buf
+ int Clustered; // 0:No 1:Yes
+ int Sorted; // 0:No 1:Asc (2:Desc - NIY)
+ int Ndv; // Number of distinct values
+ int Nbm; // Number of uint in bitmap
+ }; // end of class DOSCOL
+
+#endif // __TABDOS_H
diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp
index cb95cebe7d1..aa6a44fc791 100644
--- a/storage/connect/tabfix.cpp
+++ b/storage/connect/tabfix.cpp
@@ -45,10 +45,8 @@
#include "filamfix.h"
#include "filamdbf.h"
#include "tabfix.h" // TDBFIX, FIXCOL classes declares
-#if defined(BLK_INDX)
#include "array.h"
#include "blkfil.h"
-#endif // BLK_INDX
/***********************************************************************/
/* DB static variables. */
@@ -129,7 +127,6 @@ PCOL TDBFIX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
/***********************************************************************/
int TDBFIX::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{
-#if defined(BLK_INDX)
int prc, rc = RC_OK;
To_Filter = NULL; // Disable filtering
@@ -169,10 +166,6 @@ int TDBFIX::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
} // endif dox
return rc;
-#else // !BLK_INDX
- RestoreNrec(); // May have been modified
- return TDBDOS::ResetTableOpt(g, dop, dox);
-#endif // !BLK_INDX
} // end of ResetTableOpt
/***********************************************************************/
@@ -211,14 +204,14 @@ int TDBFIX::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
MaxSize = Cardinality(g);
-#if defined(BLK_INDX)
+
if (MaxSize > 0 && (To_BlkFil = InitBlockFilter(g, To_Filter))
&& !To_BlkFil->Correlated()) {
// Use BlockTest to reduce the estimated size
MaxSize = Txfp->MaxBlkSize(g, MaxSize);
ResetBlockFilter(g);
} // endif To_BlkFil
-#endif // BLK_INDX
+
} // endif MaxSize
return MaxSize;
@@ -301,9 +294,7 @@ bool TDBFIX::OpenDB(PGLOBAL g)
else
Txfp->Rewind(); // see comment in Work.log
-#if defined(BLK_INDX)
ResetBlockFilter(g);
-#endif // BLK_INDX
return false;
} // endif use
@@ -335,12 +326,10 @@ bool TDBFIX::OpenDB(PGLOBAL g)
/*********************************************************************/
To_Line = Txfp->GetBuf(); // For WriteDB
-#if defined(BLK_INDX)
/*********************************************************************/
/* Allocate the block filter tree if evaluation is possible. */
/*********************************************************************/
To_BlkFil = InitBlockFilter(g, To_Filter);
-#endif // BLK_INDX
if (trace)
htrc("OpenDos: R%hd mode=%d\n", Tdb_No, Mode);
diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp
index 18ecaae430a..2c62806ff52 100644
--- a/storage/connect/tabfmt.cpp
+++ b/storage/connect/tabfmt.cpp
@@ -1,1436 +1,1429 @@
-/************* TabFmt C++ Program Source Code File (.CPP) **************/
-/* PROGRAM NAME: TABFMT */
-/* ------------- */
-/* Version 3.8 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2001 - 2013 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the TABFMT classes DB execution routines. */
-/* The base class CSV is comma separated files. */
-/* FMT (Formatted) files are those having a complex internal record */
-/* format described in the Format keyword of their definition. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant MariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-
-#if defined(WIN32)
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <locale.h>
-#if defined(__BORLANDC__)
-#define __MFC_COMPAT__ // To define min/max as macro
-#endif
-//#include <windows.h>
-#include "osutil.h"
-#else
-#if defined(UNIX)
-#include <errno.h>
-#include <unistd.h>
-#include "osutil.h"
-#else
-#include <io.h>
-#endif
-#include <fcntl.h>
-#endif
-
-/***********************************************************************/
-/* 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 "plgdbsem.h"
-#include "mycat.h"
-#include "filamap.h"
-#if defined(ZIP_SUPPORT)
-#include "filamzip.h"
-#endif // ZIP_SUPPORT
-#include "tabfmt.h"
-#include "tabmul.h"
-#define NO_FUNC
-#include "plgcnx.h" // For DB types
-#include "resource.h"
-
-/***********************************************************************/
-/* This should be an option. */
-/***********************************************************************/
-#define MAXCOL 200 /* Default max column nb in result */
-#define TYPE_UNKNOWN 10 /* Must be greater than other types */
-
-extern "C" int trace;
-
-/***********************************************************************/
-/* CSVColumns: constructs the result blocks containing the description */
-/* of all the columns of a CSV file that will be retrieved by #GetData.*/
-/* Note: the algorithm to set the type is based on the internal values */
-/* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7). */
-/* If these values are changed, this will have to be revisited. */
-/***********************************************************************/
-PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
- int hdr, int mxr, bool info)
- {
- static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
- TYPE_INT, TYPE_INT, TYPE_SHORT};
- static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME,
- FLD_PREC, FLD_LENGTH, FLD_SCALE};
- static unsigned int length[] = {6, 6, 8, 10, 10, 6};
- char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096];
- int i, imax, hmax, n, nerr, phase, blank, digit, dec, type;
- int ncol = sizeof(buftyp) / sizeof(int);
- int num_read = 0, num_max = 10000000; // Statistics
- int len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
- FILE *infile;
- PQRYRES qrp;
- PCOLRES crp;
-
- if (info) {
- imax = hmax = 0;
- length[0] = 128;
- goto skipit;
- } // endif info
-
-// num_max = atoi(p+1); // Max num of record to test
-#if defined(WIN32)
- if (sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6))
- dechar = '.';
- else
- dechar = ',';
-#else // !WIN32
- dechar = '.';
-#endif // !WIN32
-
- if (trace)
- htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n",
- SVP(fn), sep, q, hdr, mxr);
-
- if (!fn) {
- strcpy(g->Message, MSG(MISSING_FNAME));
- return NULL;
- } // endif fn
-
- imax = hmax = nerr = 0;
- mxr = max(0, mxr);
-
- for (i = 0; i < MAXCOL; i++) {
- colname[i] = NULL;
- len[i] = 0;
- typ[i] = TYPE_UNKNOWN;
- prc[i] = 0;
- } // endfor i
-
- /*********************************************************************/
- /* Open the input file. */
- /*********************************************************************/
- PlugSetPath(filename, fn, PlgGetDataPath(g));
-
- if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
- return NULL;
-
- if (hdr) {
- /*******************************************************************/
- /* Make the column names from the first line. */
- /*******************************************************************/
- phase = 0;
-
- if (fgets(buf, sizeof(buf), infile)) {
- n = strlen(buf) + 1;
- buf[n - 2] = '\0';
-#if defined(UNIX)
- // The file can be imported from Windows
- if (buf[n - 3] == '\r')
- buf[n - 3] = 0;
-#endif // UNIX
- p = (char*)PlugSubAlloc(g, NULL, n);
- memcpy(p, buf, n);
-
- //skip leading blanks
- for (; *p == ' '; p++) ;
-
- if (q && *p == q) {
- // Header is quoted
- p++;
- phase = 1;
- } // endif q
-
- colname[0] = p;
- } else {
- sprintf(g->Message, MSG(FILE_IS_EMPTY), fn);
- goto err;
- } // endif's
-
- for (i = 1; *p; p++)
- if (phase == 1 && *p == q) {
- *p = '\0';
- phase = 0;
- } else if (*p == sep && !phase) {
- *p = '\0';
-
- //skip leading blanks
- for (; *(p+1) == ' '; p++) ;
-
- if (q && *(p+1) == q) {
- // Header is quoted
- p++;
- phase = 1;
- } // endif q
-
- colname[i++] = p + 1;
- } // endif sep
-
- num_read++;
- imax = hmax = i;
-
- for (i = 0; i < hmax; i++)
- length[0] = max(length[0], strlen(colname[i]));
-
- } // endif hdr
-
- for (num_read++; num_read <= num_max; num_read++) {
- /*******************************************************************/
- /* Now start the reading process. Read one line. */
- /*******************************************************************/
- if (fgets(buf, sizeof(buf), infile)) {
- n = strlen(buf);
- buf[n - 1] = '\0';
-#if defined(UNIX)
- // The file can be imported from Windows
- if (buf[n - 2] == '\r')
- buf[n - 2] = 0;
-#endif // UNIX
- } else if (feof(infile)) {
- sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1);
- break;
- } else {
- sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn);
- goto err;
- } // endif's
-
- /*******************************************************************/
- /* Make the test for field lengths. */
- /*******************************************************************/
- i = n = phase = blank = digit = dec = 0;
-
- for (p = buf; *p; p++)
- if (*p == sep) {
- if (phase != 1) {
- if (i == MAXCOL - 1) {
- sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn);
- goto err;
- } // endif i
-
- if (n) {
- len[i] = max(len[i], n);
- type = (digit || (dec && n == 1)) ? TYPE_STRING
- : (dec) ? TYPE_DOUBLE : TYPE_INT;
- typ[i] = min(type, typ[i]);
- prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
- } // endif n
-
- i++;
- n = phase = blank = digit = dec = 0;
- } else // phase == 1
- n++;
-
- } else if (*p == ' ') {
- if (phase < 2)
- n++;
-
- if (blank)
- digit = 1;
-
- } else if (*p == q) {
- if (phase == 0) {
- if (blank)
- if (++nerr > mxr) {
- sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
- goto err;
- } else
- goto skip;
-
- n = 0;
- phase = digit = 1;
- } else if (phase == 1) {
- if (*(p+1) == q) {
- // This is currently not implemented for CSV tables
-// if (++nerr > mxr) {
-// sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read);
-// goto err;
-// } else
-// goto skip;
-
- p++;
- n++;
- } else
- phase = 2;
-
- } else if (++nerr > mxr) { // phase == 2
- sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
- goto err;
- } else
- goto skip;
-
- } else {
- if (phase == 2)
- if (++nerr > mxr) {
- sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
- goto err;
- } else
- goto skip;
-
- // isdigit cannot be used here because of debug assert
- if (!strchr("0123456789", *p)) {
- if (!digit && *p == dechar)
- dec = 1; // Decimal point found
- else if (blank || !(*p == '-' || *p == '+'))
- digit = 1;
-
- } else if (dec)
- dec++; // More decimals
-
- n++;
- blank = 1;
- } // endif's *p
-
- if (phase == 1)
- if (++nerr > mxr) {
- sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read);
- goto err;
- } else
- goto skip;
-
- if (n) {
- len[i] = max(len[i], n);
- type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING
- : (dec) ? TYPE_DOUBLE : TYPE_INT;
- typ[i] = min(type, typ[i]);
- prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
- } // endif n
-
- imax = max(imax, i+1);
- skip: ; // Skip erroneous line
- } // endfor num_read
-
- if (trace) {
- htrc("imax=%d Lengths:", imax);
-
- for (i = 0; i < imax; i++)
- htrc(" %d", len[i]);
-
- htrc("\n");
- } // endif trace
-
- fclose(infile);
-
- skipit:
- if (trace)
- htrc("CSVColumns: imax=%d hmax=%d len=%d\n",
- imax, hmax, length[0]);
-
- /*********************************************************************/
- /* Allocate the structures used to refer to the result set. */
- /*********************************************************************/
- qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3,
- buftyp, fldtyp, length, false, false);
- if (info || !qrp)
- return qrp;
-
- qrp->Nblin = imax;
-
- /*********************************************************************/
- /* Now get the results into blocks. */
- /*********************************************************************/
- for (i = 0; i < imax; i++) {
- if (i >= hmax) {
- sprintf(buf, "COL%.3d", i+1);
- p = buf;
- } else
- p = colname[i];
-
- if (typ[i] == TYPE_UNKNOWN) // Void column
- typ[i] = TYPE_STRING;
-
- crp = qrp->Colresp; // Column Name
- crp->Kdata->SetValue(p, i);
- crp = crp->Next; // Data Type
- crp->Kdata->SetValue(typ[i], i);
- crp = crp->Next; // Type Name
- crp->Kdata->SetValue(GetTypeName(typ[i]), i);
- crp = crp->Next; // Precision
- crp->Kdata->SetValue(len[i], i);
- crp = crp->Next; // Length
- crp->Kdata->SetValue(len[i], i);
- crp = crp->Next; // Scale (precision)
- crp->Kdata->SetValue(prc[i], i);
- } // endfor i
-
- /*********************************************************************/
- /* Return the result pointer for use by GetData routines. */
- /*********************************************************************/
- return qrp;
-
- err:
- fclose(infile);
- return NULL;
- } // end of CSVCColumns
-
-/* --------------------------- Class CSVDEF -------------------------- */
-
-/***********************************************************************/
-/* CSVDEF constructor. */
-/***********************************************************************/
-CSVDEF::CSVDEF(void)
- {
- Fmtd = Accept = Header = false;
- Maxerr = 0;
- Quoted = -1;
- Sep = ',';
- Qot = '\0';
- } // end of CSVDEF constructor
-
-/***********************************************************************/
-/* DefineAM: define specific AM block values from XDB file. */
-/***********************************************************************/
-bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
- {
- char buf[8];
-
- // Double check correctness of offset values
- if (Catfunc == FNC_NO)
- for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
- if (cdp->GetOffset() < 1) {
- strcpy(g->Message, MSG(BAD_OFFSET_VAL));
- return true;
- } // endif Offset
-
- // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX
- if (DOSDEF::DefineAM(g, "CSV", poff))
- return true;
-
- Cat->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));
- Qot = *buf;
-
- if (Qot && Quoted < 0)
- Quoted = 0;
- else if (!Qot && Quoted >= 0)
- 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);
- return false;
- } // end of DefineAM
-
-/***********************************************************************/
-/* GetTable: makes a new Table Description Block. */
-/***********************************************************************/
-PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
- {
- PTDBASE tdbp;
-
- if (Catfunc != FNC_COL) {
- USETEMP tmp = PlgGetUser(g)->UseTemp;
- bool map = Mapped && mode != MODE_INSERT &&
- !(tmp != TMP_NO && mode == MODE_UPDATE) &&
- !(tmp == TMP_FORCE &&
- (mode == MODE_UPDATE || mode == MODE_DELETE));
- PTXF txfp;
-
- /*******************************************************************/
- /* Allocate a file processing class of the proper type. */
- /*******************************************************************/
- if (map) {
- // Should be now compatible with UNIX
- txfp = new(g) MAPFAM(this);
- } else if (Compressed) {
-#if defined(ZIP_SUPPORT)
- if (Compressed == 1)
- txfp = new(g) ZIPFAM(this);
- else {
-#if defined(BLK_INDX)
- txfp = new(g) ZLBFAM(this);
-#else // !BLK_INDX
- strcpy(g->Message, "Compress 2 not supported yet");
- return NULL;
-#endif // !BLK_INDX
- } // endelse
-#else // !ZIP_SUPPORT
- strcpy(g->Message, "Compress not supported");
- return NULL;
-#endif // !ZIP_SUPPORT
- } else
- txfp = new(g) DOSFAM(this);
-
- /*******************************************************************/
- /* Allocate a TDB of the proper type. */
- /* Column blocks will be allocated only when needed. */
- /*******************************************************************/
- if (!Fmtd)
- tdbp = new(g) TDBCSV(this, txfp);
- else
- tdbp = new(g) TDBFMT(this, txfp);
-
- if (Multiple)
- tdbp = new(g) TDBMUL(tdbp);
-
- } else
- tdbp = new(g)TDBCCL(this);
-
- return tdbp;
- } // end of GetTable
-
-/* -------------------------- Class TDBCSV --------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBCSV class. */
-/***********************************************************************/
-TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
- {
-#if defined(_DEBUG)
- assert (tdp);
-#endif
- Field = NULL;
- Offset = NULL;
- Fldlen = NULL;
- Fields = 0;
- Nerr = 0;
- Quoted = tdp->Quoted;
- Maxerr = tdp->Maxerr;
- Accept = tdp->Accept;
- Header = tdp->Header;
- Sep = tdp->GetSep();
- Qot = tdp->GetQot();
- } // end of TDBCSV standard constructor
-
-TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
- {
- Fields = tdbp->Fields;
-
- if (Fields) {
- if (tdbp->Offset)
- Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
-
- if (tdbp->Fldlen)
- Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
-
- Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
-
- for (int i = 0; i < Fields; i++) {
- if (Offset)
- Offset[i] = tdbp->Offset[i];
-
- if (Fldlen)
- Fldlen[i] = tdbp->Fldlen[i];
-
- if (Field) {
- assert (Fldlen);
- Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1);
- Field[i][Fldlen[i]] = '\0';
- } // endif Field
-
- } // endfor i
-
- } else {
- Field = NULL;
- Offset = NULL;
- Fldlen = NULL;
- } // endif Fields
-
- Nerr = tdbp->Nerr;
- Maxerr = tdbp->Maxerr;
- Quoted = tdbp->Quoted;
- Accept = tdbp->Accept;
- Header = tdbp->Header;
- Sep = tdbp->Sep;
- Qot = tdbp->Qot;
- } // end of TDBCSV copy constructor
-
-// Method
-PTDB TDBCSV::CopyOne(PTABS t)
- {
- PTDB tp;
- PCSVCOL cp1, cp2;
- PGLOBAL g = t->G; // Is this really useful ???
-
- tp = new(g) TDBCSV(g, this);
-
- for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
- cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
- NewPointer(t, cp1, cp2);
- } // endfor cp1
-
- return tp;
- } // end of CopyOne
-
-/***********************************************************************/
-/* Allocate CSV column description block. */
-/***********************************************************************/
-PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- return new(g) CSVCOL(g, cdp, this, cprec, n);
- } // end of MakeCol
-
-/***********************************************************************/
-/* Check whether the number of errors is greater than the maximum. */
-/***********************************************************************/
-bool TDBCSV::CheckErr(void)
- {
- return (++Nerr) > Maxerr;
- } // end of CheckErr
-
-/***********************************************************************/
-/* CSV EstimatedLength. Returns an estimated minimum line length. */
-/***********************************************************************/
-int TDBCSV::EstimatedLength(PGLOBAL g)
- {
- if (trace)
- htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns);
-
- if (!Fields) {
- PCSVCOL colp;
-
- for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
- if (!colp->IsSpecial()) // Not a pseudo column
- Fields = max(Fields, (int)colp->Fldnum);
-
- if (Columns)
- Fields++; // Fldnum was 0 based
-
- } // endif Fields
-
- return (int)Fields; // Number of separators if all fields are null
- } // end of Estimated Length
-
-#if 0
-/***********************************************************************/
-/* CSV tables favor the use temporary files for Update. */
-/***********************************************************************/
-bool TDBCSV::IsUsingTemp(PGLOBAL g)
- {
- USETEMP usetemp = PlgGetUser(g)->UseTemp;
-
- return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
- (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
- } // end of IsUsingTemp
-#endif // 0 (Same as TDBDOS one)
-
-/***********************************************************************/
-/* CSV Access Method opening routine. */
-/* First allocate the Offset and Fldlen arrays according to the */
-/* greatest field used in that query. Then call the DOS opening fnc. */
-/***********************************************************************/
-bool TDBCSV::OpenDB(PGLOBAL g)
- {
- bool rc = false;
- PCOLDEF cdp;
- PDOSDEF tdp = (PDOSDEF)To_Def;
-
- if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) {
- // Allocate the storage used to read (or write) records
- int i, len;
- PCSVCOL colp;
-
- if (!Fields) // May have been set in TABFMT::OpenDB
- if (Mode != MODE_UPDATE && Mode != MODE_INSERT) {
- for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
- if (!colp->IsSpecial()) // Not a pseudo column
- Fields = max(Fields, (int)colp->Fldnum);
-
- if (Columns)
- Fields++; // Fldnum was 0 based
-
- } else
- for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- Fields++;
-
- Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
- Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
-
- if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
- Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
- Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields);
- } // endif Mode
-
- for (i = 0; i < Fields; i++) {
- Offset[i] = 0;
- Fldlen[i] = 0;
-
- if (Field) {
- Field[i] = NULL;
- Fldtyp[i] = false;
- } // endif Field
-
- } // endfor i
-
- if (Field)
- // Prepare writing fields
- if (Mode != MODE_UPDATE)
- for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) {
- i = colp->Fldnum;
- len = colp->GetLength();
- Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
- Field[i][len] = '\0';
- Fldlen[i] = len;
- Fldtyp[i] = IsTypeNum(colp->GetResultType());
- } // endfor colp
-
- else // MODE_UPDATE
- for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) {
- i = cdp->GetOffset() - 1;
- len = cdp->GetLength();
- Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
- Field[i][len] = '\0';
- Fldlen[i] = len;
- Fldtyp[i] = IsTypeNum(cdp->GetType());
- } // endfor colp
-
- } // endif Use
-
- if (Header) {
- // Check that the Lrecl is at least equal to the header line length
- int headlen = 0;
- PCOLDEF cdp;
- PDOSDEF tdp = (PDOSDEF)To_Def;
-
- for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- headlen += strlen(cdp->GetName()) + 3; // 3 if names are quoted
-
- if (headlen > Lrecl) {
- Lrecl = headlen;
- Txfp->Lrecl = headlen;
- } // endif headlen
-
- } // endif Header
-
- Nerr = 0;
- rc = TDBDOS::OpenDB(g);
-
- if (!rc && Mode == MODE_UPDATE && To_Kindex)
- // Because KINDEX::Init is executed in mode READ, we must restore
- // the Fldlen array that was modified when reading the table file.
- for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- Fldlen[cdp->GetOffset() - 1] = cdp->GetLength();
-
- return rc;
- } // end of OpenDB
-
-/***********************************************************************/
-/* SkipHeader: Physically skip first header line if applicable. */
-/* This is called from TDBDOS::OpenDB and must be executed before */
-/* Kindex construction if the file is accessed using an index. */
-/***********************************************************************/
-bool TDBCSV::SkipHeader(PGLOBAL g)
- {
- int len = GetFileLength(g);
- bool rc = false;
-
-#if defined(_DEBUG)
- if (len < 0)
- return true;
-#endif // _DEBUG
-
- if (Header) {
- if (Mode == MODE_INSERT) {
- if (!len) {
- // New file, the header line must be constructed and written
- int i, n = 0;
- int hlen = 0;
- bool q = Qot && Quoted > 0;
- PCOLDEF cdp;
-
- // Estimate the length of the header list
- for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
- hlen += (1 + strlen(cdp->GetName()));
- hlen += ((q) ? 2 : 0);
- n++; // Calculate the number of columns
- } // endfor cdp
-
- if (hlen > Lrecl) {
- sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen);
- return true;
- } // endif hlen
-
- // File is empty, write a header record
- memset(To_Line, 0, Lrecl);
-
- // The column order in the file is given by the offset value
- for (i = 1; i <= n; i++)
- for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
- if (cdp->GetOffset() == i) {
- if (q)
- To_Line[strlen(To_Line)] = Qot;
-
- strcat(To_Line, cdp->GetName());
-
- if (q)
- To_Line[strlen(To_Line)] = Qot;
-
- if (i < n)
- To_Line[strlen(To_Line)] = Sep;
-
- } // endif Offset
-
- rc = (Txfp->WriteBuffer(g) == RC_FX);
- } // endif !FileLength
-
- } else if (Mode == MODE_DELETE) {
- if (len)
- rc = (Txfp->SkipRecord(g, true) == RC_FX);
-
- } else if (len) // !Insert && !Delete
- rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
-
- } // endif Header
-
- return rc;
- } // end of SkipHeader
-
-/***********************************************************************/
-/* ReadBuffer: Physical read routine for the CSV access method. */
-/***********************************************************************/
-int TDBCSV::ReadBuffer(PGLOBAL g)
- {
- char *p1, *p2, *p = NULL;
- int i, n, len, rc = Txfp->ReadBuffer(g);
- bool bad = false;
-
- if (trace > 1)
- htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc);
-
- if (rc != RC_OK || !Fields)
- return rc;
- else
- p2 = To_Line;
-
- // Find the offsets and lengths of the columns for this row
- for (i = 0; i < Fields; i++) {
- if (!bad) {
- if (Qot && *p2 == Qot) { // Quoted field
- for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2)
- if (*(p + 1) == Qot)
- n++; // Doubled internal quotes
- else
- break; // Final quote
-
- if (p) {
- len = p++ - p2;
-
-// if (Sep != ' ')
-// for (; *p == ' '; p++) ; // Skip blanks
-
- if (*p != Sep && i != Fields - 1) { // Should be the separator
- if (CheckErr()) {
- sprintf(g->Message, MSG(MISSING_FIELD),
- i+1, Name, RowNumber(g));
- return RC_FX;
- } else if (Accept)
- bad = true;
- else
- return RC_NF;
-
- } // endif p
-
- if (n) {
- int j, k;
-
- // Suppress the double of internal quotes
- for (j = k = 0; j < len; j++, k++) {
- if (p2[j] == Qot)
- j++; // skip first one
-
- p2[k] = p2[j];
- } // endfor i, j
-
- len -= n;
- } // endif n
-
- } else if (CheckErr()) {
- sprintf(g->Message, MSG(BAD_QUOTE_FIELD),
- Name, i+1, RowNumber(g));
- return RC_FX;
- } else if (Accept) {
- len = strlen(p2);
- bad = true;
- } else
- return RC_NF;
-
- } else if ((p = strchr(p2, Sep)))
- len = p - p2;
- else if (i == Fields - 1)
- len = strlen(p2);
- else if (Accept && Maxerr == 0) {
- len = strlen(p2);
- bad = true;
- } else if (CheckErr()) {
- sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g));
- return RC_FX;
- } else if (Accept) {
- len = strlen(p2);
- bad = true;
- } else
- return RC_NF;
-
- } else
- len = 0;
-
- Offset[i] = p2 - To_Line;
-
- if (Mode != MODE_UPDATE)
- Fldlen[i] = len;
- else if (len > Fldlen[i]) {
- sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g));
- return RC_FX;
- } else {
- strncpy(Field[i], p2, len);
- Field[i][len] = '\0';
- } // endif Mode
-
- if (p)
- p2 = p + 1;
-
- } // endfor i
-
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* Data Base write routine CSV file access method. */
-/***********************************************************************/
-int TDBCSV::WriteDB(PGLOBAL g)
- {
- char sep[2], qot[2];
- int i, nlen, oldlen = strlen(To_Line);
-
- if (trace > 1)
- htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n",
- Tdb_No, Mode, To_Key_Col, To_Link);
-
- // Before writing the line we must check its length
- if ((nlen = CheckWrite(g)) < 0)
- return RC_FX;
-
- // Before writing the line we must make it
- sep[0] = Sep;
- sep[1] = '\0';
- qot[0] = Qot;
- qot[1] = '\0';
- *To_Line = '\0';
-
- for (i = 0; i < Fields; i++) {
- if (i)
- strcat(To_Line, sep);
-
- if (Field[i])
- if (!strlen(Field[i])) {
- // Generally null fields are not quoted
- if (Quoted > 2)
- // Except if explicitely required
- strcat(strcat(To_Line, qot), qot);
-
- } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
- || Quoted > 1 || (Quoted == 1 && !Fldtyp[i])))
- if (strchr(Field[i], Qot)) {
- // Field contains quotes that must be doubled
- int j, k = strlen(To_Line), n = strlen(Field[i]);
-
- To_Line[k++] = Qot;
-
- for (j = 0; j < n; j++) {
- if (Field[i][j] == Qot)
- To_Line[k++] = Qot;
-
- To_Line[k++] = Field[i][j];
- } // endfor j
-
- To_Line[k++] = Qot;
- To_Line[k] = '\0';
- } else
- strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
-
- else
- strcat(To_Line, Field[i]);
-
- } // endfor i
-
-#if defined(_DEBUG)
- assert ((unsigned)nlen == strlen(To_Line));
-#endif
-
- if (Mode == MODE_UPDATE && nlen < oldlen
- && !((PDOSFAM)Txfp)->GetUseTemp()) {
- // In Update mode with no temp file, line length must not change
- To_Line[nlen] = Sep;
-
- for (nlen++; nlen < oldlen; nlen++)
- To_Line[nlen] = ' ';
-
- To_Line[nlen] = '\0';
- } // endif
-
- if (trace > 1)
- htrc("Write: line is=%s", To_Line);
-
- /*********************************************************************/
- /* Now start the writing process. */
- /*********************************************************************/
- return Txfp->WriteBuffer(g);
- } // end of WriteDB
-
-/***********************************************************************/
-/* Check whether a new line fit in the file lrecl size. */
-/***********************************************************************/
-int TDBCSV::CheckWrite(PGLOBAL g)
- {
- int maxlen, n, nlen = (Fields - 1);
-
- if (trace > 1)
- htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode);
-
- // Before writing the line we must check its length
- maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp())
- ? strlen(To_Line) : Lrecl;
-
- // Check whether record is too int
- for (int i = 0; i < Fields; i++)
- if (Field[i]) {
- if (!(n = strlen(Field[i])))
- n += (Quoted > 2 ? 2 : 0);
- else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot)
- || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))
- if (!Qot) {
- sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1);
- return -1;
- } else {
- // Quotes inside a quoted field must be doubled
- char *p1, *p2;
-
- for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1)
- n++;
-
- n += 2; // Outside quotes
- } // endif
-
- if ((nlen += n) > maxlen) {
- strcpy(g->Message, MSG(LINE_TOO_LONG));
- return -1;
- } // endif nlen
-
- } // endif Field
-
- return nlen;
- } // end of CheckWrite
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBFMT class. */
-/***********************************************************************/
-TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
- {
- FldFormat = tdbp->FldFormat;
- To_Fld = tdbp->To_Fld;
- FmtTest = tdbp->FmtTest;
- Linenum = tdbp->Linenum;
- } // end of TDBFMT copy constructor
-
-// Method
-PTDB TDBFMT::CopyOne(PTABS t)
- {
- PTDB tp;
- PCSVCOL cp1, cp2;
-//PFMTCOL cp1, cp2;
- PGLOBAL g = t->G; // Is this really useful ???
-
- tp = new(g) TDBFMT(g, this);
-
- for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
-//for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) {
- cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
-// cp2 = new(g) FMTCOL(cp1, tp); // Make a copy
- NewPointer(t, cp1, cp2);
- } // endfor cp1
-
- return tp;
- } // end of CopyOne
-
-/***********************************************************************/
-/* Allocate FMT column description block. */
-/***********************************************************************/
-PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- return new(g) CSVCOL(g, cdp, this, cprec, n);
-//return new(g) FMTCOL(cdp, this, cprec, n);
- } // end of MakeCol
-
-/***********************************************************************/
-/* FMT EstimatedLength. Returns an estimated minimum line length. */
-/* The big problem here is how can we astimated that minimum ? */
-/***********************************************************************/
-int TDBFMT::EstimatedLength(PGLOBAL g)
- {
- // This is rather stupid !!!
- return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
- } // end of EstimatedLength
-
-/***********************************************************************/
-/* FMT Access Method opening routine. */
-/***********************************************************************/
-bool TDBFMT::OpenDB(PGLOBAL g)
- {
- Linenum = 0;
-
- if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
- sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
- return true; // NIY
- } // endif Mode
-
- if (Use != USE_OPEN && Columns) {
- // Make the formats used to read records
- PSZ pfm;
- int i, n;
- PCSVCOL colp;
- PCOLDEF cdp;
- PDOSDEF tdp = (PDOSDEF)To_Def;
-
- for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
- if (!colp->IsSpecial()) // Not a pseudo column
- Fields = max(Fields, (int)colp->Fldnum);
-
- if (Columns)
- Fields++; // Fldnum was 0 based
-
- To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1);
- FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
- memset(FldFormat, 0, sizeof(PSZ) * Fields);
- FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
- memset(FmtTest, 0, sizeof(int) * Fields);
-
- // Get the column formats
- for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- if ((i = cdp->GetOffset() - 1) < Fields) {
- if (!(pfm = cdp->GetFmt())) {
- sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
- return true;
- } // endif pfm
-
- // Roughly check the Fmt format
- if ((n = strlen(pfm) - 2) < 4) {
- sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name);
- return true;
- } // endif n
-
- FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
- strcpy(FldFormat[i], pfm);
-
- if (!strcmp(pfm + n, "%m")) {
- // This is a field that can be missing. Flag it so it can
- // be handled with special processing.
- FldFormat[i][n+1] = 'n'; // To have sscanf normal processing
- FmtTest[i] = 2;
- } else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
- // There are trailing characters after the field contents
- // add a marker for the next field start position.
- strcat(FldFormat[i], "%n");
- FmtTest[i] = 1;
- } // endif's
-
- } // endif i
-
- } // endif Use
-
- return TDBCSV::OpenDB(g);
- } // end of OpenDB
-
-/***********************************************************************/
-/* ReadBuffer: Physical read routine for the FMT access method. */
-/***********************************************************************/
-int TDBFMT::ReadBuffer(PGLOBAL g)
- {
- int i, len, n, deb, fin, nwp, pos = 0, rc;
- bool bad = false;
-
- if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields)
- return rc;
- else
- ++Linenum;
-
- if (trace > 1)
- htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc);
-
- // Find the offsets and lengths of the columns for this row
- for (i = 0; i < Fields; i++) {
- if (!bad) {
- deb = fin = -1;
-
- if (!FldFormat[i]) {
- n = 0;
- } else if (FmtTest[i] == 1) {
- nwp = -1;
- n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp);
- } else {
- n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin);
-
- if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) {
- // Missing optional field, not an error
- n = 1;
-
- if (i == Fields - 1)
- fin = deb = 0;
- else
- fin = deb;
-
- } // endif n
-
- nwp = fin;
- } // endif i
-
- if (n != 1 || deb < 0 || fin < 0 || nwp < 0) {
- // This is to avoid a very strange sscanf bug occuring
- // with fields that ends with a null character.
- // This bug causes subsequent sscanf to return in error,
- // so next lines are not parsed correctly.
- sscanf("a", "%*c"); // Seems to reset things Ok
-
- if (CheckErr()) {
- sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name);
- return RC_FX;
- } else if (Accept)
- bad = true;
- else
- return RC_NF;
-
- } // endif n...
-
- } // endif !bad
-
- if (!bad) {
- Offset[i] = pos + deb;
- len = fin - deb;
- } else {
- nwp = 0;
- Offset[i] = pos;
- len = 0;
- } // endif bad
-
-// if (Mode != MODE_UPDATE)
- Fldlen[i] = len;
-// else if (len > Fldlen[i]) {
-// sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g));
-// return RC_FX;
-// } else {
-// strncpy(Field[i], To_Line + pos, len);
-// Field[i][len] = '\0';
-// } // endif Mode
-
- pos += nwp;
- } // endfor i
-
- if (bad)
- Nerr++;
- else
- sscanf("a", "%*c"); // Seems to reset things Ok
-
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* Data Base write routine FMT file access method. */
-/***********************************************************************/
-int TDBFMT::WriteDB(PGLOBAL g)
- {
- sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
- return RC_FX; // NIY
- } // end of WriteDB
-
-// ------------------------ CSVCOL functions ----------------------------
-
-/***********************************************************************/
-/* CSVCOL public constructor */
-/***********************************************************************/
-CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
- : DOSCOL(g, cdp, tdbp, cprec, i, "CSV")
- {
- Fldnum = Deplac - 1;
- Deplac = 0;
- } // end of CSVCOL constructor
-
-/***********************************************************************/
-/* CSVCOL constructor used for copying columns. */
-/* tdbp is the pointer to the new table descriptor. */
-/***********************************************************************/
-CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
- {
- Fldnum = col1->Fldnum;
- } // end of CSVCOL copy constructor
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* 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 a blocked table, */
-/* because if it is updated using a temporary file, the block size */
-/* may be modified. */
-/***********************************************************************/
-bool CSVCOL::VarSize(void)
- {
- PTXF txfp = ((PTDBCSV)To_Tdb)->Txfp;
-
- if (txfp->IsBlocked() && txfp->GetUseTemp())
- // Blocked table using a temporary file
- return true;
- else
- return false;
-
- } // end VarSize
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* ReadColumn: call DOSCOL::ReadColumn after having set the offet */
-/* and length of the field to read as calculated by TDBCSV::ReadDB. */
-/***********************************************************************/
-void CSVCOL::ReadColumn(PGLOBAL g)
- {
- int rc;
- PTDBCSV tdbp = (PTDBCSV)To_Tdb;
-
- /*********************************************************************/
- /* 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], 34);
- } // endif
-
- if (tdbp->Mode != MODE_UPDATE) {
- int colen = Long; // Column length
-
- // Set the field offset and length for this row
- Deplac = tdbp->Offset[Fldnum]; // Field offset
- Long = tdbp->Fldlen[Fldnum]; // Field length
-
- if (trace > 1)
- htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n",
- Name, Fldnum, Deplac, Long);
-
- if (Long > colen && tdbp->CheckErr()) {
- Long = colen; // Restore column length
- sprintf(g->Message, MSG(FLD_TOO_LNG_FOR),
- Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g));
- longjmp(g->jumper[g->jump_level], 34);
- } // endif Long
-
- // Now do the reading
- DOSCOL::ReadColumn(g);
-
- // Restore column length
- Long = colen;
- } else { // Mode Update
- // Field have been copied in TDB Field array
- PSZ fp = tdbp->Field[Fldnum];
-
- Value->SetValue_psz(fp);
-
- // Set null when applicable
- if (Nullable)
- Value->SetNull(Value->IsZero());
-
- } // endif Mode
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* WriteColumn: The column is written in TDBCSV matching Field. */
-/***********************************************************************/
-void CSVCOL::WriteColumn(PGLOBAL g)
- {
- char *p, buf[32];
- int flen;
- PTDBCSV tdbp = (PTDBCSV)To_Tdb;
-
- if (trace > 1)
- htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
- Name, tdbp->GetTdb_No(), ColUse, Status);
-
- flen = GetLength();
-
- if (trace > 1)
- htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n",
- tdbp->Lrecl, Long, flen, Buf_Type, Value);
-
- /*********************************************************************/
- /* Check whether the new value has to be converted to Buf_Type. */
- /*********************************************************************/
- if (Value != To_Val)
- Value->SetValue_pval(To_Val, false); // Convert the updated value
-
- /*********************************************************************/
- /* Get the string representation of the column value. */
- /*********************************************************************/
- p = Value->ShowValue(buf);
-
- if (trace > 1)
- htrc("new length(%p)=%d\n", p, strlen(p));
-
- if ((signed)strlen(p) > flen) {
- sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, flen,
- tdbp->RowNumber(g), tdbp->GetFile(g));
- longjmp(g->jumper[g->jump_level], 34);
- } // endif
-
- if (trace > 1)
- htrc("buffer=%s\n", p);
-
- /*********************************************************************/
- /* Updating must be done also during the first pass so writing the */
- /* updated record can be checked for acceptable record length. */
- /*********************************************************************/
- if (Fldnum < 0) {
- // This can happen for wrong offset value in XDB files
- sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name);
- longjmp(g->jumper[g->jump_level], 34);
- } else
- strncpy(tdbp->Field[Fldnum], p, flen);
-
- if (trace > 1)
- htrc(" col written: '%s'\n", p);
-
- } // end of WriteColumn
-
-/* ---------------------------TDBCCL class --------------------------- */
-
-/***********************************************************************/
-/* TDBCCL class constructor. */
-/***********************************************************************/
-TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
- {
- Fn = tdp->GetFn();
- Hdr = tdp->Header;
- Mxr = tdp->Maxerr;
- Qtd = tdp->Quoted;
- Sep = tdp->Sep;
- } // end of TDBCCL constructor
-
-/***********************************************************************/
-/* GetResult: Get the list the CSV file columns. */
-/***********************************************************************/
-PQRYRES TDBCCL::GetResult(PGLOBAL g)
- {
- return CSVColumns(g, Fn, Sep, Qtd, Hdr, Mxr, false);
- } // end of GetResult
-
-/* ------------------------ End of TabFmt ---------------------------- */
+/************* TabFmt C++ Program Source Code File (.CPP) **************/
+/* PROGRAM NAME: TABFMT */
+/* ------------- */
+/* Version 3.9 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2001 - 2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the TABFMT classes DB execution routines. */
+/* The base class CSV is comma separated files. */
+/* FMT (Formatted) files are those having a complex internal record */
+/* format described in the Format keyword of their definition. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+
+#if defined(WIN32)
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <locale.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif
+//#include <windows.h>
+#include "osutil.h"
+#else
+#if defined(UNIX)
+#include <errno.h>
+#include <unistd.h>
+#include "osutil.h"
+#else
+#include <io.h>
+#endif
+#include <fcntl.h>
+#endif
+
+/***********************************************************************/
+/* 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 "plgdbsem.h"
+#include "mycat.h"
+#include "filamap.h"
+#if defined(ZIP_SUPPORT)
+#include "filamzip.h"
+#endif // ZIP_SUPPORT
+#include "tabfmt.h"
+#include "tabmul.h"
+#define NO_FUNC
+#include "plgcnx.h" // For DB types
+#include "resource.h"
+
+/***********************************************************************/
+/* This should be an option. */
+/***********************************************************************/
+#define MAXCOL 200 /* Default max column nb in result */
+#define TYPE_UNKNOWN 10 /* Must be greater than other types */
+
+extern "C" int trace;
+
+/***********************************************************************/
+/* CSVColumns: constructs the result blocks containing the description */
+/* of all the columns of a CSV file that will be retrieved by #GetData.*/
+/* Note: the algorithm to set the type is based on the internal values */
+/* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7). */
+/* If these values are changed, this will have to be revisited. */
+/***********************************************************************/
+PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
+ int hdr, int mxr, bool info)
+ {
+ static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
+ TYPE_INT, TYPE_INT, TYPE_SHORT};
+ static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME,
+ FLD_PREC, FLD_LENGTH, FLD_SCALE};
+ static unsigned int length[] = {6, 6, 8, 10, 10, 6};
+ char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096];
+ int i, imax, hmax, n, nerr, phase, blank, digit, dec, type;
+ int ncol = sizeof(buftyp) / sizeof(int);
+ int num_read = 0, num_max = 10000000; // Statistics
+ int len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
+ FILE *infile;
+ PQRYRES qrp;
+ PCOLRES crp;
+
+ if (info) {
+ imax = hmax = 0;
+ length[0] = 128;
+ goto skipit;
+ } // endif info
+
+// num_max = atoi(p+1); // Max num of record to test
+#if defined(WIN32)
+ if (sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6))
+ dechar = '.';
+ else
+ dechar = ',';
+#else // !WIN32
+ dechar = '.';
+#endif // !WIN32
+
+ if (trace)
+ htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n",
+ SVP(fn), sep, q, hdr, mxr);
+
+ if (!fn) {
+ strcpy(g->Message, MSG(MISSING_FNAME));
+ return NULL;
+ } // endif fn
+
+ imax = hmax = nerr = 0;
+ mxr = max(0, mxr);
+
+ for (i = 0; i < MAXCOL; i++) {
+ colname[i] = NULL;
+ len[i] = 0;
+ typ[i] = TYPE_UNKNOWN;
+ prc[i] = 0;
+ } // endfor i
+
+ /*********************************************************************/
+ /* Open the input file. */
+ /*********************************************************************/
+ PlugSetPath(filename, fn, PlgGetDataPath(g));
+
+ if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
+ return NULL;
+
+ if (hdr) {
+ /*******************************************************************/
+ /* Make the column names from the first line. */
+ /*******************************************************************/
+ phase = 0;
+
+ if (fgets(buf, sizeof(buf), infile)) {
+ n = strlen(buf) + 1;
+ buf[n - 2] = '\0';
+#if defined(UNIX)
+ // The file can be imported from Windows
+ if (buf[n - 3] == '\r')
+ buf[n - 3] = 0;
+#endif // UNIX
+ p = (char*)PlugSubAlloc(g, NULL, n);
+ memcpy(p, buf, n);
+
+ //skip leading blanks
+ for (; *p == ' '; p++) ;
+
+ if (q && *p == q) {
+ // Header is quoted
+ p++;
+ phase = 1;
+ } // endif q
+
+ colname[0] = p;
+ } else {
+ sprintf(g->Message, MSG(FILE_IS_EMPTY), fn);
+ goto err;
+ } // endif's
+
+ for (i = 1; *p; p++)
+ if (phase == 1 && *p == q) {
+ *p = '\0';
+ phase = 0;
+ } else if (*p == sep && !phase) {
+ *p = '\0';
+
+ //skip leading blanks
+ for (; *(p+1) == ' '; p++) ;
+
+ if (q && *(p+1) == q) {
+ // Header is quoted
+ p++;
+ phase = 1;
+ } // endif q
+
+ colname[i++] = p + 1;
+ } // endif sep
+
+ num_read++;
+ imax = hmax = i;
+
+ for (i = 0; i < hmax; i++)
+ length[0] = max(length[0], strlen(colname[i]));
+
+ } // endif hdr
+
+ for (num_read++; num_read <= num_max; num_read++) {
+ /*******************************************************************/
+ /* Now start the reading process. Read one line. */
+ /*******************************************************************/
+ if (fgets(buf, sizeof(buf), infile)) {
+ n = strlen(buf);
+ buf[n - 1] = '\0';
+#if defined(UNIX)
+ // The file can be imported from Windows
+ if (buf[n - 2] == '\r')
+ buf[n - 2] = 0;
+#endif // UNIX
+ } else if (feof(infile)) {
+ sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1);
+ break;
+ } else {
+ sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn);
+ goto err;
+ } // endif's
+
+ /*******************************************************************/
+ /* Make the test for field lengths. */
+ /*******************************************************************/
+ i = n = phase = blank = digit = dec = 0;
+
+ for (p = buf; *p; p++)
+ if (*p == sep) {
+ if (phase != 1) {
+ if (i == MAXCOL - 1) {
+ sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn);
+ goto err;
+ } // endif i
+
+ if (n) {
+ len[i] = max(len[i], n);
+ type = (digit || (dec && n == 1)) ? TYPE_STRING
+ : (dec) ? TYPE_DOUBLE : TYPE_INT;
+ typ[i] = min(type, typ[i]);
+ prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
+ } // endif n
+
+ i++;
+ n = phase = blank = digit = dec = 0;
+ } else // phase == 1
+ n++;
+
+ } else if (*p == ' ') {
+ if (phase < 2)
+ n++;
+
+ if (blank)
+ digit = 1;
+
+ } else if (*p == q) {
+ if (phase == 0) {
+ if (blank)
+ if (++nerr > mxr) {
+ sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
+ goto err;
+ } else
+ goto skip;
+
+ n = 0;
+ phase = digit = 1;
+ } else if (phase == 1) {
+ if (*(p+1) == q) {
+ // This is currently not implemented for CSV tables
+// if (++nerr > mxr) {
+// sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read);
+// goto err;
+// } else
+// goto skip;
+
+ p++;
+ n++;
+ } else
+ phase = 2;
+
+ } else if (++nerr > mxr) { // phase == 2
+ sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
+ goto err;
+ } else
+ goto skip;
+
+ } else {
+ if (phase == 2)
+ if (++nerr > mxr) {
+ sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
+ goto err;
+ } else
+ goto skip;
+
+ // isdigit cannot be used here because of debug assert
+ if (!strchr("0123456789", *p)) {
+ if (!digit && *p == dechar)
+ dec = 1; // Decimal point found
+ else if (blank || !(*p == '-' || *p == '+'))
+ digit = 1;
+
+ } else if (dec)
+ dec++; // More decimals
+
+ n++;
+ blank = 1;
+ } // endif's *p
+
+ if (phase == 1)
+ if (++nerr > mxr) {
+ sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read);
+ goto err;
+ } else
+ goto skip;
+
+ if (n) {
+ len[i] = max(len[i], n);
+ type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING
+ : (dec) ? TYPE_DOUBLE : TYPE_INT;
+ typ[i] = min(type, typ[i]);
+ prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
+ } // endif n
+
+ imax = max(imax, i+1);
+ skip: ; // Skip erroneous line
+ } // endfor num_read
+
+ if (trace) {
+ htrc("imax=%d Lengths:", imax);
+
+ for (i = 0; i < imax; i++)
+ htrc(" %d", len[i]);
+
+ htrc("\n");
+ } // endif trace
+
+ fclose(infile);
+
+ skipit:
+ if (trace)
+ htrc("CSVColumns: imax=%d hmax=%d len=%d\n",
+ imax, hmax, length[0]);
+
+ /*********************************************************************/
+ /* Allocate the structures used to refer to the result set. */
+ /*********************************************************************/
+ qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3,
+ buftyp, fldtyp, length, false, false);
+ if (info || !qrp)
+ return qrp;
+
+ qrp->Nblin = imax;
+
+ /*********************************************************************/
+ /* Now get the results into blocks. */
+ /*********************************************************************/
+ for (i = 0; i < imax; i++) {
+ if (i >= hmax) {
+ sprintf(buf, "COL%.3d", i+1);
+ p = buf;
+ } else
+ p = colname[i];
+
+ if (typ[i] == TYPE_UNKNOWN) // Void column
+ typ[i] = TYPE_STRING;
+
+ crp = qrp->Colresp; // Column Name
+ crp->Kdata->SetValue(p, i);
+ crp = crp->Next; // Data Type
+ crp->Kdata->SetValue(typ[i], i);
+ crp = crp->Next; // Type Name
+ crp->Kdata->SetValue(GetTypeName(typ[i]), i);
+ crp = crp->Next; // Precision
+ crp->Kdata->SetValue(len[i], i);
+ crp = crp->Next; // Length
+ crp->Kdata->SetValue(len[i], i);
+ crp = crp->Next; // Scale (precision)
+ crp->Kdata->SetValue(prc[i], i);
+ } // endfor i
+
+ /*********************************************************************/
+ /* Return the result pointer for use by GetData routines. */
+ /*********************************************************************/
+ return qrp;
+
+ err:
+ fclose(infile);
+ return NULL;
+ } // end of CSVCColumns
+
+/* --------------------------- Class CSVDEF -------------------------- */
+
+/***********************************************************************/
+/* CSVDEF constructor. */
+/***********************************************************************/
+CSVDEF::CSVDEF(void)
+ {
+ Fmtd = Accept = Header = false;
+ Maxerr = 0;
+ Quoted = -1;
+ Sep = ',';
+ Qot = '\0';
+ } // end of CSVDEF constructor
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from XDB file. */
+/***********************************************************************/
+bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+ {
+ char buf[8];
+
+ // Double check correctness of offset values
+ if (Catfunc == FNC_NO)
+ for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
+ if (cdp->GetOffset() < 1) {
+ strcpy(g->Message, MSG(BAD_OFFSET_VAL));
+ return true;
+ } // endif Offset
+
+ // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX
+ if (DOSDEF::DefineAM(g, "CSV", poff))
+ return true;
+
+ Cat->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));
+ Qot = *buf;
+
+ if (Qot && Quoted < 0)
+ Quoted = 0;
+ else if (!Qot && Quoted >= 0)
+ 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);
+ return false;
+ } // end of DefineAM
+
+/***********************************************************************/
+/* GetTable: makes a new Table Description Block. */
+/***********************************************************************/
+PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
+ {
+ PTDBASE tdbp;
+
+ if (Catfunc != FNC_COL) {
+ USETEMP tmp = PlgGetUser(g)->UseTemp;
+ bool map = Mapped && mode != MODE_INSERT &&
+ !(tmp != TMP_NO && mode == MODE_UPDATE) &&
+ !(tmp == TMP_FORCE &&
+ (mode == MODE_UPDATE || mode == MODE_DELETE));
+ PTXF txfp;
+
+ /*******************************************************************/
+ /* Allocate a file processing class of the proper type. */
+ /*******************************************************************/
+ if (map) {
+ // Should be now compatible with UNIX
+ txfp = new(g) MAPFAM(this);
+ } else if (Compressed) {
+#if defined(ZIP_SUPPORT)
+ if (Compressed == 1)
+ txfp = new(g) ZIPFAM(this);
+ else
+ txfp = new(g) ZLBFAM(this);
+
+#else // !ZIP_SUPPORT
+ strcpy(g->Message, "Compress not supported");
+ return NULL;
+#endif // !ZIP_SUPPORT
+ } else
+ txfp = new(g) DOSFAM(this);
+
+ /*******************************************************************/
+ /* Allocate a TDB of the proper type. */
+ /* Column blocks will be allocated only when needed. */
+ /*******************************************************************/
+ if (!Fmtd)
+ tdbp = new(g) TDBCSV(this, txfp);
+ else
+ tdbp = new(g) TDBFMT(this, txfp);
+
+ if (Multiple)
+ tdbp = new(g) TDBMUL(tdbp);
+
+ } else
+ tdbp = new(g)TDBCCL(this);
+
+ return tdbp;
+ } // end of GetTable
+
+/* -------------------------- Class TDBCSV --------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBCSV class. */
+/***********************************************************************/
+TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
+ {
+#if defined(_DEBUG)
+ assert (tdp);
+#endif
+ Field = NULL;
+ Offset = NULL;
+ Fldlen = NULL;
+ Fields = 0;
+ Nerr = 0;
+ Quoted = tdp->Quoted;
+ Maxerr = tdp->Maxerr;
+ Accept = tdp->Accept;
+ Header = tdp->Header;
+ Sep = tdp->GetSep();
+ Qot = tdp->GetQot();
+ } // end of TDBCSV standard constructor
+
+TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
+ {
+ Fields = tdbp->Fields;
+
+ if (Fields) {
+ if (tdbp->Offset)
+ Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
+
+ if (tdbp->Fldlen)
+ Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
+
+ Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
+
+ for (int i = 0; i < Fields; i++) {
+ if (Offset)
+ Offset[i] = tdbp->Offset[i];
+
+ if (Fldlen)
+ Fldlen[i] = tdbp->Fldlen[i];
+
+ if (Field) {
+ assert (Fldlen);
+ Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1);
+ Field[i][Fldlen[i]] = '\0';
+ } // endif Field
+
+ } // endfor i
+
+ } else {
+ Field = NULL;
+ Offset = NULL;
+ Fldlen = NULL;
+ } // endif Fields
+
+ Nerr = tdbp->Nerr;
+ Maxerr = tdbp->Maxerr;
+ Quoted = tdbp->Quoted;
+ Accept = tdbp->Accept;
+ Header = tdbp->Header;
+ Sep = tdbp->Sep;
+ Qot = tdbp->Qot;
+ } // end of TDBCSV copy constructor
+
+// Method
+PTDB TDBCSV::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PCSVCOL cp1, cp2;
+ PGLOBAL g = t->G; // Is this really useful ???
+
+ tp = new(g) TDBCSV(g, this);
+
+ for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
+ cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate CSV column description block. */
+/***********************************************************************/
+PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ return new(g) CSVCOL(g, cdp, this, cprec, n);
+ } // end of MakeCol
+
+/***********************************************************************/
+/* Check whether the number of errors is greater than the maximum. */
+/***********************************************************************/
+bool TDBCSV::CheckErr(void)
+ {
+ return (++Nerr) > Maxerr;
+ } // end of CheckErr
+
+/***********************************************************************/
+/* CSV EstimatedLength. Returns an estimated minimum line length. */
+/***********************************************************************/
+int TDBCSV::EstimatedLength(PGLOBAL g)
+ {
+ if (trace)
+ htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns);
+
+ if (!Fields) {
+ PCSVCOL colp;
+
+ for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
+ if (!colp->IsSpecial()) // Not a pseudo column
+ Fields = max(Fields, (int)colp->Fldnum);
+
+ if (Columns)
+ Fields++; // Fldnum was 0 based
+
+ } // endif Fields
+
+ return (int)Fields; // Number of separators if all fields are null
+ } // end of Estimated Length
+
+#if 0
+/***********************************************************************/
+/* CSV tables favor the use temporary files for Update. */
+/***********************************************************************/
+bool TDBCSV::IsUsingTemp(PGLOBAL g)
+ {
+ USETEMP usetemp = PlgGetUser(g)->UseTemp;
+
+ return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
+ (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
+ } // end of IsUsingTemp
+#endif // 0 (Same as TDBDOS one)
+
+/***********************************************************************/
+/* CSV Access Method opening routine. */
+/* First allocate the Offset and Fldlen arrays according to the */
+/* greatest field used in that query. Then call the DOS opening fnc. */
+/***********************************************************************/
+bool TDBCSV::OpenDB(PGLOBAL g)
+ {
+ bool rc = false;
+ PCOLDEF cdp;
+ PDOSDEF tdp = (PDOSDEF)To_Def;
+
+ if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) {
+ // Allocate the storage used to read (or write) records
+ int i, len;
+ PCSVCOL colp;
+
+ if (!Fields) // May have been set in TABFMT::OpenDB
+ if (Mode != MODE_UPDATE && Mode != MODE_INSERT) {
+ for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
+ if (!colp->IsSpecial()) // Not a pseudo column
+ Fields = max(Fields, (int)colp->Fldnum);
+
+ if (Columns)
+ Fields++; // Fldnum was 0 based
+
+ } else
+ for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
+ Fields++;
+
+ Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
+ Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
+
+ if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
+ Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
+ Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields);
+ } // endif Mode
+
+ for (i = 0; i < Fields; i++) {
+ Offset[i] = 0;
+ Fldlen[i] = 0;
+
+ if (Field) {
+ Field[i] = NULL;
+ Fldtyp[i] = false;
+ } // endif Field
+
+ } // endfor i
+
+ if (Field)
+ // Prepare writing fields
+ if (Mode != MODE_UPDATE)
+ for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) {
+ i = colp->Fldnum;
+ len = colp->GetLength();
+ Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
+ Field[i][len] = '\0';
+ Fldlen[i] = len;
+ Fldtyp[i] = IsTypeNum(colp->GetResultType());
+ } // endfor colp
+
+ else // MODE_UPDATE
+ for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) {
+ i = cdp->GetOffset() - 1;
+ len = cdp->GetLength();
+ Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
+ Field[i][len] = '\0';
+ Fldlen[i] = len;
+ Fldtyp[i] = IsTypeNum(cdp->GetType());
+ } // endfor colp
+
+ } // endif Use
+
+ if (Header) {
+ // Check that the Lrecl is at least equal to the header line length
+ int headlen = 0;
+ PCOLDEF cdp;
+ PDOSDEF tdp = (PDOSDEF)To_Def;
+
+ for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
+ headlen += strlen(cdp->GetName()) + 3; // 3 if names are quoted
+
+ if (headlen > Lrecl) {
+ Lrecl = headlen;
+ Txfp->Lrecl = headlen;
+ } // endif headlen
+
+ } // endif Header
+
+ Nerr = 0;
+ rc = TDBDOS::OpenDB(g);
+
+ if (!rc && Mode == MODE_UPDATE && To_Kindex)
+ // Because KINDEX::Init is executed in mode READ, we must restore
+ // the Fldlen array that was modified when reading the table file.
+ for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
+ Fldlen[cdp->GetOffset() - 1] = cdp->GetLength();
+
+ return rc;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* SkipHeader: Physically skip first header line if applicable. */
+/* This is called from TDBDOS::OpenDB and must be executed before */
+/* Kindex construction if the file is accessed using an index. */
+/***********************************************************************/
+bool TDBCSV::SkipHeader(PGLOBAL g)
+ {
+ int len = GetFileLength(g);
+ bool rc = false;
+
+#if defined(_DEBUG)
+ if (len < 0)
+ return true;
+#endif // _DEBUG
+
+ if (Header) {
+ if (Mode == MODE_INSERT) {
+ if (!len) {
+ // New file, the header line must be constructed and written
+ int i, n = 0;
+ int hlen = 0;
+ bool q = Qot && Quoted > 0;
+ PCOLDEF cdp;
+
+ // Estimate the length of the header list
+ for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
+ hlen += (1 + strlen(cdp->GetName()));
+ hlen += ((q) ? 2 : 0);
+ n++; // Calculate the number of columns
+ } // endfor cdp
+
+ if (hlen > Lrecl) {
+ sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen);
+ return true;
+ } // endif hlen
+
+ // File is empty, write a header record
+ memset(To_Line, 0, Lrecl);
+
+ // The column order in the file is given by the offset value
+ for (i = 1; i <= n; i++)
+ for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
+ if (cdp->GetOffset() == i) {
+ if (q)
+ To_Line[strlen(To_Line)] = Qot;
+
+ strcat(To_Line, cdp->GetName());
+
+ if (q)
+ To_Line[strlen(To_Line)] = Qot;
+
+ if (i < n)
+ To_Line[strlen(To_Line)] = Sep;
+
+ } // endif Offset
+
+ rc = (Txfp->WriteBuffer(g) == RC_FX);
+ } // endif !FileLength
+
+ } else if (Mode == MODE_DELETE) {
+ if (len)
+ rc = (Txfp->SkipRecord(g, true) == RC_FX);
+
+ } else if (len) // !Insert && !Delete
+ rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
+
+ } // endif Header
+
+ return rc;
+ } // end of SkipHeader
+
+/***********************************************************************/
+/* ReadBuffer: Physical read routine for the CSV access method. */
+/***********************************************************************/
+int TDBCSV::ReadBuffer(PGLOBAL g)
+ {
+ char *p1, *p2, *p = NULL;
+ int i, n, len, rc = Txfp->ReadBuffer(g);
+ bool bad = false;
+
+ if (trace > 1)
+ htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc);
+
+ if (rc != RC_OK || !Fields)
+ return rc;
+ else
+ p2 = To_Line;
+
+ // Find the offsets and lengths of the columns for this row
+ for (i = 0; i < Fields; i++) {
+ if (!bad) {
+ if (Qot && *p2 == Qot) { // Quoted field
+ for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2)
+ if (*(p + 1) == Qot)
+ n++; // Doubled internal quotes
+ else
+ break; // Final quote
+
+ if (p) {
+ len = p++ - p2;
+
+// if (Sep != ' ')
+// for (; *p == ' '; p++) ; // Skip blanks
+
+ if (*p != Sep && i != Fields - 1) { // Should be the separator
+ if (CheckErr()) {
+ sprintf(g->Message, MSG(MISSING_FIELD),
+ i+1, Name, RowNumber(g));
+ return RC_FX;
+ } else if (Accept)
+ bad = true;
+ else
+ return RC_NF;
+
+ } // endif p
+
+ if (n) {
+ int j, k;
+
+ // Suppress the double of internal quotes
+ for (j = k = 0; j < len; j++, k++) {
+ if (p2[j] == Qot)
+ j++; // skip first one
+
+ p2[k] = p2[j];
+ } // endfor i, j
+
+ len -= n;
+ } // endif n
+
+ } else if (CheckErr()) {
+ sprintf(g->Message, MSG(BAD_QUOTE_FIELD),
+ Name, i+1, RowNumber(g));
+ return RC_FX;
+ } else if (Accept) {
+ len = strlen(p2);
+ bad = true;
+ } else
+ return RC_NF;
+
+ } else if ((p = strchr(p2, Sep)))
+ len = p - p2;
+ else if (i == Fields - 1)
+ len = strlen(p2);
+ else if (Accept && Maxerr == 0) {
+ len = strlen(p2);
+ bad = true;
+ } else if (CheckErr()) {
+ sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g));
+ return RC_FX;
+ } else if (Accept) {
+ len = strlen(p2);
+ bad = true;
+ } else
+ return RC_NF;
+
+ } else
+ len = 0;
+
+ Offset[i] = p2 - To_Line;
+
+ if (Mode != MODE_UPDATE)
+ Fldlen[i] = len;
+ else if (len > Fldlen[i]) {
+ sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g));
+ return RC_FX;
+ } else {
+ strncpy(Field[i], p2, len);
+ Field[i][len] = '\0';
+ } // endif Mode
+
+ if (p)
+ p2 = p + 1;
+
+ } // endfor i
+
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* Data Base write routine CSV file access method. */
+/***********************************************************************/
+int TDBCSV::WriteDB(PGLOBAL g)
+ {
+ char sep[2], qot[2];
+ int i, nlen, oldlen = strlen(To_Line);
+
+ if (trace > 1)
+ htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n",
+ Tdb_No, Mode, To_Key_Col, To_Link);
+
+ // Before writing the line we must check its length
+ if ((nlen = CheckWrite(g)) < 0)
+ return RC_FX;
+
+ // Before writing the line we must make it
+ sep[0] = Sep;
+ sep[1] = '\0';
+ qot[0] = Qot;
+ qot[1] = '\0';
+ *To_Line = '\0';
+
+ for (i = 0; i < Fields; i++) {
+ if (i)
+ strcat(To_Line, sep);
+
+ if (Field[i])
+ if (!strlen(Field[i])) {
+ // Generally null fields are not quoted
+ if (Quoted > 2)
+ // Except if explicitely required
+ strcat(strcat(To_Line, qot), qot);
+
+ } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
+ || Quoted > 1 || (Quoted == 1 && !Fldtyp[i])))
+ if (strchr(Field[i], Qot)) {
+ // Field contains quotes that must be doubled
+ int j, k = strlen(To_Line), n = strlen(Field[i]);
+
+ To_Line[k++] = Qot;
+
+ for (j = 0; j < n; j++) {
+ if (Field[i][j] == Qot)
+ To_Line[k++] = Qot;
+
+ To_Line[k++] = Field[i][j];
+ } // endfor j
+
+ To_Line[k++] = Qot;
+ To_Line[k] = '\0';
+ } else
+ strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
+
+ else
+ strcat(To_Line, Field[i]);
+
+ } // endfor i
+
+#if defined(_DEBUG)
+ assert ((unsigned)nlen == strlen(To_Line));
+#endif
+
+ if (Mode == MODE_UPDATE && nlen < oldlen
+ && !((PDOSFAM)Txfp)->GetUseTemp()) {
+ // In Update mode with no temp file, line length must not change
+ To_Line[nlen] = Sep;
+
+ for (nlen++; nlen < oldlen; nlen++)
+ To_Line[nlen] = ' ';
+
+ To_Line[nlen] = '\0';
+ } // endif
+
+ if (trace > 1)
+ htrc("Write: line is=%s", To_Line);
+
+ /*********************************************************************/
+ /* Now start the writing process. */
+ /*********************************************************************/
+ return Txfp->WriteBuffer(g);
+ } // end of WriteDB
+
+/***********************************************************************/
+/* Check whether a new line fit in the file lrecl size. */
+/***********************************************************************/
+int TDBCSV::CheckWrite(PGLOBAL g)
+ {
+ int maxlen, n, nlen = (Fields - 1);
+
+ if (trace > 1)
+ htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode);
+
+ // Before writing the line we must check its length
+ maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp())
+ ? strlen(To_Line) : Lrecl;
+
+ // Check whether record is too int
+ for (int i = 0; i < Fields; i++)
+ if (Field[i]) {
+ if (!(n = strlen(Field[i])))
+ n += (Quoted > 2 ? 2 : 0);
+ else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot)
+ || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))
+ if (!Qot) {
+ sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1);
+ return -1;
+ } else {
+ // Quotes inside a quoted field must be doubled
+ char *p1, *p2;
+
+ for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1)
+ n++;
+
+ n += 2; // Outside quotes
+ } // endif
+
+ if ((nlen += n) > maxlen) {
+ strcpy(g->Message, MSG(LINE_TOO_LONG));
+ return -1;
+ } // endif nlen
+
+ } // endif Field
+
+ return nlen;
+ } // end of CheckWrite
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBFMT class. */
+/***********************************************************************/
+TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
+ {
+ FldFormat = tdbp->FldFormat;
+ To_Fld = tdbp->To_Fld;
+ FmtTest = tdbp->FmtTest;
+ Linenum = tdbp->Linenum;
+ } // end of TDBFMT copy constructor
+
+// Method
+PTDB TDBFMT::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PCSVCOL cp1, cp2;
+//PFMTCOL cp1, cp2;
+ PGLOBAL g = t->G; // Is this really useful ???
+
+ tp = new(g) TDBFMT(g, this);
+
+ for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
+//for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) {
+ cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
+// cp2 = new(g) FMTCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate FMT column description block. */
+/***********************************************************************/
+PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ return new(g) CSVCOL(g, cdp, this, cprec, n);
+//return new(g) FMTCOL(cdp, this, cprec, n);
+ } // end of MakeCol
+
+/***********************************************************************/
+/* FMT EstimatedLength. Returns an estimated minimum line length. */
+/* The big problem here is how can we astimated that minimum ? */
+/***********************************************************************/
+int TDBFMT::EstimatedLength(PGLOBAL g)
+ {
+ // This is rather stupid !!!
+ return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
+ } // end of EstimatedLength
+
+/***********************************************************************/
+/* FMT Access Method opening routine. */
+/***********************************************************************/
+bool TDBFMT::OpenDB(PGLOBAL g)
+ {
+ Linenum = 0;
+
+ if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
+ sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
+ return true; // NIY
+ } // endif Mode
+
+ if (Use != USE_OPEN && Columns) {
+ // Make the formats used to read records
+ PSZ pfm;
+ int i, n;
+ PCSVCOL colp;
+ PCOLDEF cdp;
+ PDOSDEF tdp = (PDOSDEF)To_Def;
+
+ for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
+ if (!colp->IsSpecial()) // Not a pseudo column
+ Fields = max(Fields, (int)colp->Fldnum);
+
+ if (Columns)
+ Fields++; // Fldnum was 0 based
+
+ To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1);
+ FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
+ memset(FldFormat, 0, sizeof(PSZ) * Fields);
+ FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
+ memset(FmtTest, 0, sizeof(int) * Fields);
+
+ // Get the column formats
+ for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
+ if ((i = cdp->GetOffset() - 1) < Fields) {
+ if (!(pfm = cdp->GetFmt())) {
+ sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
+ return true;
+ } // endif pfm
+
+ // Roughly check the Fmt format
+ if ((n = strlen(pfm) - 2) < 4) {
+ sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name);
+ return true;
+ } // endif n
+
+ FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
+ strcpy(FldFormat[i], pfm);
+
+ if (!strcmp(pfm + n, "%m")) {
+ // This is a field that can be missing. Flag it so it can
+ // be handled with special processing.
+ FldFormat[i][n+1] = 'n'; // To have sscanf normal processing
+ FmtTest[i] = 2;
+ } else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
+ // There are trailing characters after the field contents
+ // add a marker for the next field start position.
+ strcat(FldFormat[i], "%n");
+ FmtTest[i] = 1;
+ } // endif's
+
+ } // endif i
+
+ } // endif Use
+
+ return TDBCSV::OpenDB(g);
+ } // end of OpenDB
+
+/***********************************************************************/
+/* ReadBuffer: Physical read routine for the FMT access method. */
+/***********************************************************************/
+int TDBFMT::ReadBuffer(PGLOBAL g)
+ {
+ int i, len, n, deb, fin, nwp, pos = 0, rc;
+ bool bad = false;
+
+ if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields)
+ return rc;
+ else
+ ++Linenum;
+
+ if (trace > 1)
+ htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc);
+
+ // Find the offsets and lengths of the columns for this row
+ for (i = 0; i < Fields; i++) {
+ if (!bad) {
+ deb = fin = -1;
+
+ if (!FldFormat[i]) {
+ n = 0;
+ } else if (FmtTest[i] == 1) {
+ nwp = -1;
+ n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp);
+ } else {
+ n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin);
+
+ if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) {
+ // Missing optional field, not an error
+ n = 1;
+
+ if (i == Fields - 1)
+ fin = deb = 0;
+ else
+ fin = deb;
+
+ } // endif n
+
+ nwp = fin;
+ } // endif i
+
+ if (n != 1 || deb < 0 || fin < 0 || nwp < 0) {
+ // This is to avoid a very strange sscanf bug occuring
+ // with fields that ends with a null character.
+ // This bug causes subsequent sscanf to return in error,
+ // so next lines are not parsed correctly.
+ sscanf("a", "%*c"); // Seems to reset things Ok
+
+ if (CheckErr()) {
+ sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name);
+ return RC_FX;
+ } else if (Accept)
+ bad = true;
+ else
+ return RC_NF;
+
+ } // endif n...
+
+ } // endif !bad
+
+ if (!bad) {
+ Offset[i] = pos + deb;
+ len = fin - deb;
+ } else {
+ nwp = 0;
+ Offset[i] = pos;
+ len = 0;
+ } // endif bad
+
+// if (Mode != MODE_UPDATE)
+ Fldlen[i] = len;
+// else if (len > Fldlen[i]) {
+// sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g));
+// return RC_FX;
+// } else {
+// strncpy(Field[i], To_Line + pos, len);
+// Field[i][len] = '\0';
+// } // endif Mode
+
+ pos += nwp;
+ } // endfor i
+
+ if (bad)
+ Nerr++;
+ else
+ sscanf("a", "%*c"); // Seems to reset things Ok
+
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* Data Base write routine FMT file access method. */
+/***********************************************************************/
+int TDBFMT::WriteDB(PGLOBAL g)
+ {
+ sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
+ return RC_FX; // NIY
+ } // end of WriteDB
+
+// ------------------------ CSVCOL functions ----------------------------
+
+/***********************************************************************/
+/* CSVCOL public constructor */
+/***********************************************************************/
+CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
+ : DOSCOL(g, cdp, tdbp, cprec, i, "CSV")
+ {
+ Fldnum = Deplac - 1;
+ Deplac = 0;
+ } // end of CSVCOL constructor
+
+/***********************************************************************/
+/* CSVCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
+ {
+ Fldnum = col1->Fldnum;
+ } // end of CSVCOL 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 a blocked table, */
+/* because if it is updated using a temporary file, the block size */
+/* may be modified. */
+/***********************************************************************/
+bool CSVCOL::VarSize(void)
+ {
+ PTXF txfp = ((PTDBCSV)To_Tdb)->Txfp;
+
+ if (txfp->IsBlocked() && txfp->GetUseTemp())
+ // Blocked table using a temporary file
+ return true;
+ else
+ return false;
+
+ } // end VarSize
+
+/***********************************************************************/
+/* ReadColumn: call DOSCOL::ReadColumn after having set the offet */
+/* and length of the field to read as calculated by TDBCSV::ReadDB. */
+/***********************************************************************/
+void CSVCOL::ReadColumn(PGLOBAL g)
+ {
+ int rc;
+ PTDBCSV tdbp = (PTDBCSV)To_Tdb;
+
+ /*********************************************************************/
+ /* 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], 34);
+ } // endif
+
+ if (tdbp->Mode != MODE_UPDATE) {
+ int colen = Long; // Column length
+
+ // Set the field offset and length for this row
+ Deplac = tdbp->Offset[Fldnum]; // Field offset
+ Long = tdbp->Fldlen[Fldnum]; // Field length
+
+ if (trace > 1)
+ htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n",
+ Name, Fldnum, Deplac, Long);
+
+ if (Long > colen && tdbp->CheckErr()) {
+ Long = colen; // Restore column length
+ sprintf(g->Message, MSG(FLD_TOO_LNG_FOR),
+ Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g));
+ longjmp(g->jumper[g->jump_level], 34);
+ } // endif Long
+
+ // Now do the reading
+ DOSCOL::ReadColumn(g);
+
+ // Restore column length
+ Long = colen;
+ } else { // Mode Update
+ // Field have been copied in TDB Field array
+ PSZ fp = tdbp->Field[Fldnum];
+
+ Value->SetValue_psz(fp);
+
+ // Set null when applicable
+ if (Nullable)
+ Value->SetNull(Value->IsZero());
+
+ } // endif Mode
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: The column is written in TDBCSV matching Field. */
+/***********************************************************************/
+void CSVCOL::WriteColumn(PGLOBAL g)
+ {
+ char *p, buf[32];
+ int flen;
+ PTDBCSV tdbp = (PTDBCSV)To_Tdb;
+
+ if (trace > 1)
+ htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
+ Name, tdbp->GetTdb_No(), ColUse, Status);
+
+ flen = GetLength();
+
+ if (trace > 1)
+ htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n",
+ tdbp->Lrecl, Long, flen, Buf_Type, Value);
+
+ /*********************************************************************/
+ /* Check whether the new value has to be converted to Buf_Type. */
+ /*********************************************************************/
+ if (Value != To_Val)
+ Value->SetValue_pval(To_Val, false); // Convert the updated value
+
+ /*********************************************************************/
+ /* Get the string representation of the column value. */
+ /*********************************************************************/
+ p = Value->ShowValue(buf);
+
+ if (trace > 1)
+ htrc("new length(%p)=%d\n", p, strlen(p));
+
+ if ((signed)strlen(p) > flen) {
+ sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, flen,
+ tdbp->RowNumber(g), tdbp->GetFile(g));
+ longjmp(g->jumper[g->jump_level], 34);
+ } // endif
+
+ if (trace > 1)
+ htrc("buffer=%s\n", p);
+
+ /*********************************************************************/
+ /* Updating must be done also during the first pass so writing the */
+ /* updated record can be checked for acceptable record length. */
+ /*********************************************************************/
+ if (Fldnum < 0) {
+ // This can happen for wrong offset value in XDB files
+ sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name);
+ longjmp(g->jumper[g->jump_level], 34);
+ } else
+ strncpy(tdbp->Field[Fldnum], p, flen);
+
+ if (trace > 1)
+ htrc(" col written: '%s'\n", p);
+
+ } // end of WriteColumn
+
+/* ---------------------------TDBCCL class --------------------------- */
+
+/***********************************************************************/
+/* TDBCCL class constructor. */
+/***********************************************************************/
+TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
+ {
+ Fn = tdp->GetFn();
+ Hdr = tdp->Header;
+ Mxr = tdp->Maxerr;
+ Qtd = tdp->Quoted;
+ Sep = tdp->Sep;
+ } // end of TDBCCL constructor
+
+/***********************************************************************/
+/* GetResult: Get the list the CSV file columns. */
+/***********************************************************************/
+PQRYRES TDBCCL::GetResult(PGLOBAL g)
+ {
+ return CSVColumns(g, Fn, Sep, Qtd, Hdr, Mxr, false);
+ } // end of GetResult
+
+/* ------------------------ End of TabFmt ---------------------------- */
diff --git a/storage/connect/tabfmt.h b/storage/connect/tabfmt.h
index 52b0094bfef..aa14b4481f0 100644
--- a/storage/connect/tabfmt.h
+++ b/storage/connect/tabfmt.h
@@ -1,192 +1,188 @@
-/*************** TabFmt H Declares Source Code File (.H) ***************/
-/* Name: TABFMT.H Version 2.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */
-/* */
-/* This file contains the CSV and FMT classes declares. */
-/***********************************************************************/
-#include "xtable.h" // Base class declares
-#include "tabdos.h"
-
-//pedef struct _tabdesc *PTABD; // For friend setting
-typedef class TDBFMT *PTDBFMT;
-
-/***********************************************************************/
-/* Functions used externally. */
-/***********************************************************************/
-PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
- int hdr, int mxr, bool info);
-
-/***********************************************************************/
-/* CSV table. */
-/***********************************************************************/
-class DllExport CSVDEF : public DOSDEF { /* Logical table description */
- friend class TDBCSV;
- friend class TDBCCL;
- public:
- // Constructor
- CSVDEF(void);
-
- // Implementation
- virtual const char *GetType(void) {return "CSV";}
- char GetSep(void) {return Sep;}
- char GetQot(void) {return Qot;}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE mode);
-
- protected:
- // Members
- bool Fmtd; /* true for formatted files */
-//bool Accept; /* true if wrong lines are accepted */
- bool Header; /* true if first line contains headers */
-//int Maxerr; /* Maximum number of bad records */
- int Quoted; /* Quoting level for quoted fields */
- char Sep; /* Separator for standard CSV files */
- char Qot; /* Character for quoted strings */
- }; // end of CSVDEF
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* that are CSV files with columns separated by the Sep character. */
-/***********************************************************************/
-class TDBCSV : public TDBDOS {
- friend class CSVCOL;
- public:
- // Constructor
- TDBCSV(PCSVDEF tdp, PTXF txfp);
- TDBCSV(PGLOBAL g, PTDBCSV tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_CSV;}
- virtual PTDB Duplicate(PGLOBAL g)
- {return (PTDB)new(g) TDBCSV(g, this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
-//virtual bool IsUsingTemp(PGLOBAL g);
- virtual int GetBadLines(void) {return (int)Nerr;}
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual bool OpenDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int CheckWrite(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g); // Physical file read
-
- // Specific routines
- virtual int EstimatedLength(PGLOBAL g);
- virtual bool SkipHeader(PGLOBAL g);
- virtual bool CheckErr(void);
-
- protected:
- // Members
- PSZ *Field; // Field to write to current line
- int *Offset; // Column offsets for current record
- int *Fldlen; // Column field length for current record
- bool *Fldtyp; // true for numeric fields
- int Fields; // Number of fields to handle
- int Nerr; // Number of bad records
- int Maxerr; // Maximum number of bad records
- int Quoted; // Quoting level for quoted fields
- bool Accept; // true if bad lines are accepted
- bool Header; // true if first line contains column headers
- char Sep; // Separator
- char Qot; // Quoting character
- }; // end of class TDBCSV
-
-/***********************************************************************/
-/* Class CSVCOL: CSV access method column descriptor. */
-/* This A.M. is used for Comma Separated V(?) files. */
-/***********************************************************************/
-class CSVCOL : public DOSCOL {
- friend class TDBCSV;
- friend class TDBFMT;
- public:
- // Constructors
- CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
- CSVCOL(CSVCOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
- virtual int GetAmType() {return TYPE_AM_CSV;}
-
- // Methods
-#if defined(BLK_INDX)
- virtual bool VarSize(void);
-#endif // BLK_INDX
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
-// void Print(FILE *, uint);
-
- protected:
- // Default constructor not to be used
- CSVCOL(void) {}
-
- // Members
- int Fldnum; // Field ordinal number (0 based)
- }; // end of class CSVCOL
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* whose record format is described by a Format keyword. */
-/***********************************************************************/
-class TDBFMT : public TDBCSV {
- friend class CSVCOL;
-//friend class FMTCOL;
- public:
- // Standard constructor
- TDBFMT(PCSVDEF tdp, PTXF txfp) : TDBCSV(tdp, txfp)
- {FldFormat = NULL; To_Fld = NULL; FmtTest = NULL; Linenum = 0;}
-
- // Copy constructor
- TDBFMT(PGLOBAL g, PTDBFMT tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_FMT;}
- virtual PTDB Duplicate(PGLOBAL g)
- {return (PTDB)new(g) TDBFMT(g, this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
-//virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
-//virtual int CheckWrite(PGLOBAL g);
- virtual int ReadBuffer(PGLOBAL g); // Physical file read
-
- // Specific routines
- virtual int EstimatedLength(PGLOBAL g);
-
- protected:
- // Members
- PSZ *FldFormat; // Field read format
- void *To_Fld; // To field test buffer
- int *FmtTest; // Test on ending by %n or %m
- int Linenum; // Last read line
- }; // end of class TDBFMT
-
-/***********************************************************************/
-/* This is the class declaration for the CSV catalog table. */
-/***********************************************************************/
-class TDBCCL : public TDBCAT {
- public:
- // Constructor
- TDBCCL(PCSVDEF tdp);
-
- protected:
- // Specific routines
- virtual PQRYRES GetResult(PGLOBAL g);
-
- // Members
- char *Fn; // The CSV file (path) name
- bool Hdr; // true if first line contains headers
- int Mxr; // Maximum number of bad records
- int Qtd; // Quoting level for quoted fields
- char Sep; // Separator for standard CSV files
- }; // end of class TDBCCL
-
-/* ------------------------- End of TabFmt.H ------------------------- */
+/*************** TabFmt H Declares Source Code File (.H) ***************/
+/* Name: TABFMT.H Version 2.4 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */
+/* */
+/* This file contains the CSV and FMT classes declares. */
+/***********************************************************************/
+#include "xtable.h" // Base class declares
+#include "tabdos.h"
+
+typedef class TDBFMT *PTDBFMT;
+
+/***********************************************************************/
+/* Functions used externally. */
+/***********************************************************************/
+PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
+ int hdr, int mxr, bool info);
+
+/***********************************************************************/
+/* CSV table. */
+/***********************************************************************/
+class DllExport CSVDEF : public DOSDEF { /* Logical table description */
+ friend class TDBCSV;
+ friend class TDBCCL;
+ public:
+ // Constructor
+ CSVDEF(void);
+
+ // Implementation
+ virtual const char *GetType(void) {return "CSV";}
+ char GetSep(void) {return Sep;}
+ char GetQot(void) {return Qot;}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE mode);
+
+ protected:
+ // Members
+ bool Fmtd; /* true for formatted files */
+//bool Accept; /* true if wrong lines are accepted */
+ bool Header; /* true if first line contains headers */
+//int Maxerr; /* Maximum number of bad records */
+ int Quoted; /* Quoting level for quoted fields */
+ char Sep; /* Separator for standard CSV files */
+ char Qot; /* Character for quoted strings */
+ }; // end of CSVDEF
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* that are CSV files with columns separated by the Sep character. */
+/***********************************************************************/
+class TDBCSV : public TDBDOS {
+ friend class CSVCOL;
+ public:
+ // Constructor
+ TDBCSV(PCSVDEF tdp, PTXF txfp);
+ TDBCSV(PGLOBAL g, PTDBCSV tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_CSV;}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBCSV(g, this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+//virtual bool IsUsingTemp(PGLOBAL g);
+ virtual int GetBadLines(void) {return (int)Nerr;}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int CheckWrite(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g); // Physical file read
+
+ // Specific routines
+ virtual int EstimatedLength(PGLOBAL g);
+ virtual bool SkipHeader(PGLOBAL g);
+ virtual bool CheckErr(void);
+
+ protected:
+ // Members
+ PSZ *Field; // Field to write to current line
+ int *Offset; // Column offsets for current record
+ int *Fldlen; // Column field length for current record
+ bool *Fldtyp; // true for numeric fields
+ int Fields; // Number of fields to handle
+ int Nerr; // Number of bad records
+ int Maxerr; // Maximum number of bad records
+ int Quoted; // Quoting level for quoted fields
+ bool Accept; // true if bad lines are accepted
+ bool Header; // true if first line contains column headers
+ char Sep; // Separator
+ char Qot; // Quoting character
+ }; // end of class TDBCSV
+
+/***********************************************************************/
+/* Class CSVCOL: CSV access method column descriptor. */
+/* This A.M. is used for Comma Separated V(?) files. */
+/***********************************************************************/
+class CSVCOL : public DOSCOL {
+ friend class TDBCSV;
+ friend class TDBFMT;
+ public:
+ // Constructors
+ CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
+ CSVCOL(CSVCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType() {return TYPE_AM_CSV;}
+
+ // Methods
+ virtual bool VarSize(void);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ CSVCOL(void) {}
+
+ // Members
+ int Fldnum; // Field ordinal number (0 based)
+ }; // end of class CSVCOL
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* whose record format is described by a Format keyword. */
+/***********************************************************************/
+class TDBFMT : public TDBCSV {
+ friend class CSVCOL;
+//friend class FMTCOL;
+ public:
+ // Standard constructor
+ TDBFMT(PCSVDEF tdp, PTXF txfp) : TDBCSV(tdp, txfp)
+ {FldFormat = NULL; To_Fld = NULL; FmtTest = NULL; Linenum = 0;}
+
+ // Copy constructor
+ TDBFMT(PGLOBAL g, PTDBFMT tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_FMT;}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBFMT(g, this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+//virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+//virtual int CheckWrite(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g); // Physical file read
+
+ // Specific routines
+ virtual int EstimatedLength(PGLOBAL g);
+
+ protected:
+ // Members
+ PSZ *FldFormat; // Field read format
+ void *To_Fld; // To field test buffer
+ int *FmtTest; // Test on ending by %n or %m
+ int Linenum; // Last read line
+ }; // end of class TDBFMT
+
+/***********************************************************************/
+/* This is the class declaration for the CSV catalog table. */
+/***********************************************************************/
+class TDBCCL : public TDBCAT {
+ public:
+ // Constructor
+ TDBCCL(PCSVDEF tdp);
+
+ protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // Members
+ char *Fn; // The CSV file (path) name
+ bool Hdr; // true if first line contains headers
+ int Mxr; // Maximum number of bad records
+ int Qtd; // Quoting level for quoted fields
+ char Sep; // Separator for standard CSV files
+ }; // end of class TDBCCL
+
+/* ------------------------- End of TabFmt.H ------------------------- */
diff --git a/storage/connect/table.cpp b/storage/connect/table.cpp
index d23740394b4..cb7e732d2dd 100644
--- a/storage/connect/table.cpp
+++ b/storage/connect/table.cpp
@@ -1,636 +1,546 @@
-/************** Table C++ Functions Source Code File (.CPP) ************/
-/* Name: TABLE.CPP Version 2.7 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
-/* */
-/* This file contains the TBX, TDB and OPJOIN classes functions. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant MariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/* xobject.h is header containing XOBJECT derived classes declares. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "xtable.h"
-#include "tabcol.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-//#include "catalog.h"
-#include "reldef.h"
-
-int TDB::Tnum = 0;
-
-extern "C" int trace; // The general trace value
-
-/***********************************************************************/
-/* Utility routines. */
-/***********************************************************************/
-void NewPointer(PTABS, void *, void *);
-void AddPointer(PTABS, void *);
-
-/* ---------------------------- class TDB ---------------------------- */
-
-/***********************************************************************/
-/* TDB public constructors. */
-/***********************************************************************/
-TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum)
- {
- Use = USE_NO;
- To_Orig = NULL;
-#if defined(BLK_INDX)
- To_Filter = NULL;
-#endif // BLK_FILTER
- To_CondFil = NULL;
- Next = NULL;
- Name = (tdp) ? tdp->GetName() : NULL;
- To_Table = NULL;
- Columns = NULL;
- Degree = (tdp) ? tdp->GetDegree() : 0;
- Mode = MODE_READ;
- } // end of TDB standard constructor
-
-TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum)
- {
- Use = tdbp->Use;
- To_Orig = tdbp;
-#if defined(BLK_INDX)
- To_Filter = NULL;
-#endif // BLK_FILTER
- To_CondFil = NULL;
- Next = NULL;
- Name = tdbp->Name;
- To_Table = tdbp->To_Table;
- Columns = NULL;
- Degree = tdbp->Degree;
- Mode = tdbp->Mode;
- } // end of TDB copy constructor
-
-/***********************************************************************/
-/* OpenTable: Call AM open routine. */
-/***********************************************************************/
-bool TDB::OpenTable(PGLOBAL g, PSQL sqlp, MODE mode)
- {
- if (trace)
- htrc("Open Tdb_No=%d use=%d type=%d tdb.Mode=%d mode=%d\n",
- Tdb_No, Use, GetAmType(), Mode, mode);
-
- switch (Use) {
- case USE_LIN:
- /*****************************************************************/
- /* If table is read/only, only MODE_READ is allowed. */
- /*****************************************************************/
- if (IsReadOnly() && mode != MODE_READ) {
- strcpy(g->Message, MSG(READ_ONLY));
- return true;
- } // endif ReadOnly
-
- /*****************************************************************/
- /* This could be done in any order. */
- /* Note: for not Read only first table in open in that mode. */
- /*****************************************************************/
- if (Next)
- Next->OpenTable(g, sqlp, MODE_READ);
-
- Mode = mode;
-
- /*****************************************************************/
- /* Pre-opening is done, allocate select buffers now. */
- /*****************************************************************/
- Use = USE_READY;
- break;
-
- case USE_READY:
- /*****************************************************************/
- /* This is to open files in reverse order. */
- /*****************************************************************/
- if (Next)
- if (Next->OpenTable(g, sqlp, mode))
- return true;
-
- /*****************************************************************/
- /* This was moved after filter conversion so filtering can be */
- /* done when making index tables for DOS files. */
- /* Also it was moved after allocating select buffers so some */
- /* data can be pre-read during open to allow storage sorting. */
- /*****************************************************************/
- if (OpenDB(g)) // Do open the table file
- return true;
-
- Use = USE_OPEN;
- break;
-
- case USE_OPEN:
- /*****************************************************************/
- /* Table is already open. */
- /* Call open routine that will just "rewind" the files. */
- /*****************************************************************/
- if (OpenDB(g)) // Rewind the table file
- return true;
-
- break;
-
- default:
- sprintf(g->Message, MSG(TDB_USE_ERROR), Use);
- return true;
- } // endswitch Use
-
- return false;
- } // end of OpenTable
-
-/***********************************************************************/
-/* CloseTable: Close a table of any AM type. */
-/***********************************************************************/
-void TDB::CloseTable(PGLOBAL g)
- {
- if (trace)
- htrc("CloseTable: tdb_no %d use=%d amtype=%d am.Mode=%d\n",
- Tdb_No, Use, GetAmType(), Mode);
-
- CloseDB(g);
- Use = USE_READY; // x'7FFD'
- Mode = MODE_ANY;
- } // end of CloseTable
-
-// Methods
-
-/***********************************************************************/
-/* RowNumber: returns the current row ordinal number. */
-/***********************************************************************/
-int TDB::RowNumber(PGLOBAL g, bool b)
- {
- sprintf(g->Message, MSG(ROWID_NOT_IMPL), GetAmName(g, GetAmType()));
- return 0;
- } // end of RowNumber
-
-PTDB TDB::Copy(PTABS t)
- {
- PTDB tp, tdb1, tdb2 = NULL, outp = NULL;
-//PGLOBAL g = t->G; // Is this really useful ???
-
- for (tdb1 = this; tdb1; tdb1 = tdb1->Next) {
- tp = tdb1->CopyOne(t);
-
- if (!outp)
- outp = tp;
- else
- tdb2->Next = tp;
-
- tdb2 = tp;
- NewPointer(t, tdb1, tdb2);
- } // endfor tdb1
-
- return outp;
- } // end of Copy
-
-void TDB::Print(PGLOBAL g, FILE *f, uint n)
- {
- PCOL cp;
- char m[64];
-
- memset(m, ' ', n); // Make margin string
- m[n] = '\0';
-
- for (PTDB tp = this; tp; tp = tp->Next) {
- fprintf(f, "%sTDB (%p) %s no=%d use=%d type=%d\n", m,
- tp, tp->Name, tp->Tdb_No, tp->Use, tp->GetAmType());
-
- tp->PrintAM(f, m);
- fprintf(f, "%s Columns (deg=%d):\n", m, tp->Degree);
-
- for (cp = tp->Columns; cp; cp = cp->GetNext())
- cp->Print(g, f, n);
-
- } /* endfor tp */
-
- } // end of Print
-
-void TDB::Print(PGLOBAL g, char *ps, uint z)
- {
- sprintf(ps, "R%d.%s", Tdb_No, Name);
- } // end of Print
-
-/* -------------------------- class TDBASE --------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBASE class. This is the base class to all */
-/* classes for tables that can be joined together. */
-/***********************************************************************/
-TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
- {
- To_Def = tdp;
- To_Link = NULL;
- To_Key_Col = NULL;
- To_Kindex = NULL;
- To_SetCols = NULL;
- MaxSize = -1;
- Knum = 0;
- Read_Only = (tdp) ? tdp->IsReadOnly() : false;
- m_data_charset= (tdp) ? tdp->data_charset() : NULL;
- } // end of TDBASE constructor
-
-TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
- {
- To_Def = tdbp->To_Def;
- To_SetCols = tdbp->To_SetCols; // ???
- MaxSize = tdbp->MaxSize;
- Read_Only = tdbp->Read_Only;
- m_data_charset= tdbp->m_data_charset;
- } // end of TDBASE copy constructor
-
-/***********************************************************************/
-/* Return the pointer on the DB catalog this table belongs to. */
-/***********************************************************************/
-PCATLG TDBASE::GetCat(void)
- {
- return (To_Def) ? To_Def->GetCat() : NULL;
- } // end of GetCat
-
-/***********************************************************************/
-/* Return the pointer on the charset of this table. */
-/***********************************************************************/
-CHARSET_INFO *TDBASE::data_charset(void)
- {
- // If no DATA_CHARSET is specified, we assume that character
- // set of the remote data is the same with CHARACTER SET
- // definition of the SQL column.
- return m_data_charset ? m_data_charset : &my_charset_bin;
- } // end of data_charset
-
-/***********************************************************************/
-/* Return the datapath of the DB this table belongs to. */
-/***********************************************************************/
-PSZ TDBASE::GetPath(void)
- {
- return To_Def->GetPath();
- } // end of GetPath
-
-/***********************************************************************/
-/* Initialize TDBASE based column description block construction. */
-/* name is used to call columns by name. */
-/* num is used by TBL to construct columns by index number. */
-/* Note: name=Null and num=0 for constructing all columns (select *) */
-/***********************************************************************/
-PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num)
- {
- int i;
- PCOLDEF cdp;
- PCOL cp, colp = NULL, cprec = NULL;
-
- if (trace)
- htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n",
- GetAmType(), SVP(name), Name, num);
-
- for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
- if ((!name && !num) ||
- (name && !stricmp(cdp->GetName(), name)) || num == i) {
- /*****************************************************************/
- /* Check for existence of desired column. */
- /* Also find where to insert the new block. */
- /*****************************************************************/
- for (cp = Columns; cp; cp = cp->GetNext())
- if (cp->GetIndex() < i)
- cprec = cp;
- else if (cp->GetIndex() == i)
- break;
-
- if (trace)
- htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp);
-
- /*****************************************************************/
- /* Now take care of Column Description Block. */
- /*****************************************************************/
- if (cp)
- colp = cp;
- else if (!(cdp->Flags & U_SPECIAL))
- colp = MakeCol(g, cdp, cprec, i);
- else if (Mode == MODE_READ)
- colp = InsertSpcBlk(g, cdp);
-
- if (trace)
- htrc("colp=%p\n", colp);
-
- if (name || num)
- break;
- else if (colp && !colp->IsSpecial())
- cprec = colp;
-
- } // endif Name
-
- return (colp);
- } // end of ColDB
-
-/***********************************************************************/
-/* InsertSpecialColumn: Put a special column ahead of the column list.*/
-/***********************************************************************/
-PCOL TDBASE::InsertSpecialColumn(PGLOBAL g, PCOL colp)
- {
- if (!colp->IsSpecial())
- return NULL;
-
- colp->SetNext(Columns);
- Columns = colp;
- return colp;
- } // end of InsertSpecialColumn
-
-/***********************************************************************/
-/* Make a special COLBLK to insert in a table. */
-/***********************************************************************/
-PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
- {
-//char *name = cdp->GetName();
- char *name = cdp->GetFmt();
- PCOLUMN cp;
- PCOL colp;
-
- cp= new(g) COLUMN(cdp->GetName());
- cp->SetTo_Table(To_Table);
-
- if (!stricmp(name, "FILEID") ||
- !stricmp(name, "SERVID")) {
- if (!To_Def || !(To_Def->GetPseudo() & 2)) {
- sprintf(g->Message, MSG(BAD_SPEC_COLUMN));
- return NULL;
- } // endif Pseudo
-
- if (!stricmp(name, "FILEID"))
- colp = new(g) FIDBLK(cp);
- else
- colp = new(g) SIDBLK(cp);
-
- } else if (!stricmp(name, "TABID")) {
- colp = new(g) TIDBLK(cp);
-//} else if (!stricmp(name, "CONID")) {
-// colp = new(g) CIDBLK(cp);
- } else if (!stricmp(name, "ROWID")) {
- colp = new(g) RIDBLK(cp, false);
- } else if (!stricmp(name, "ROWNUM")) {
- colp = new(g) RIDBLK(cp, true);
- } else {
- sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
- return NULL;
- } // endif's name
-
- if (!(colp = InsertSpecialColumn(g, colp))) {
- sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
- return NULL;
- } // endif Insert
-
- return (colp);
- } // end of InsertSpcBlk
-
-/***********************************************************************/
-/* ResetTableOpt: Wrong for this table type. */
-/***********************************************************************/
-int TDBASE::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
-{
- strcpy(g->Message, "This table is not indexable");
- return RC_INFO;
-} // end of ResetTableOpt
-
-/***********************************************************************/
-/* SetKindex: set or reset the index pointer. */
-/***********************************************************************/
-void TDBASE::SetKindex(PKXBASE kxp)
- {
- if (To_Kindex)
- To_Kindex->Close(); // Discard old index
-
- To_Kindex = kxp;
- } // end of SetKindex
-
-/***********************************************************************/
-/* SetRecpos: Replace the table at the specified position. */
-/***********************************************************************/
-bool TDBASE::SetRecpos(PGLOBAL g, int recpos)
- {
- strcpy(g->Message, MSG(SETRECPOS_NIY));
- return true;
- } // end of SetRecpos
-
-/***********************************************************************/
-/* Methods */
-/***********************************************************************/
-void TDBASE::PrintAM(FILE *f, char *m)
- {
- fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
- } // end of PrintAM
-
-/***********************************************************************/
-/* Marks DOS/MAP table columns used in internal joins. */
-/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */
-/* points to the currently marked tdb. */
-/* Two questions here: exact meaning of U_J_INT ? */
-/* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */
-/***********************************************************************/
-void TDBASE::MarkDB(PGLOBAL g, PTDB tdb2)
- {
- if (trace)
- htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2);
-
- } // end of MarkDB
-
-/* ---------------------------TDBCAT class --------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBCAT class. */
-/***********************************************************************/
-TDBCAT::TDBCAT(PTABDEF tdp) : TDBASE(tdp)
- {
- Qrp = NULL;
- Init = false;
- N = -1;
- } // end of TDBCAT constructor
-
-/***********************************************************************/
-/* Allocate CAT column description block. */
-/***********************************************************************/
-PCOL TDBCAT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- PCATCOL colp;
-
- colp = (PCATCOL)new(g) CATCOL(cdp, this, n);
-
- if (cprec) {
- colp->SetNext(cprec->GetNext());
- cprec->SetNext(colp);
- } else {
- colp->SetNext(Columns);
- Columns = colp;
- } // endif cprec
-
- return colp;
- } // end of MakeCol
-
-/***********************************************************************/
-/* Initialize: Get the result query block. */
-/***********************************************************************/
-bool TDBCAT::Initialize(PGLOBAL g)
- {
- if (Init)
- return false;
-
- if (!(Qrp = GetResult(g)))
- return true;
-
- if (Qrp->Truncated) {
- sprintf(g->Message, "Result limited to %d lines", Qrp->Maxres);
- PushWarning(g, this);
- } // endif Truncated
-
- if (Qrp->BadLines) {
- sprintf(g->Message, "%d bad lines in result", Qrp->BadLines);
- PushWarning(g, this);
- } // endif Badlines
-
- Init = true;
- return false;
- } // end of Initialize
-
-/***********************************************************************/
-/* CAT: Get the number of properties. */
-/***********************************************************************/
-int TDBCAT::GetMaxSize(PGLOBAL g)
- {
- if (MaxSize < 0) {
-// if (Initialize(g))
-// return -1;
-
-// MaxSize = Qrp->Nblin;
- MaxSize = 10; // To make MariaDB happy
- } // endif MaxSize
-
- return MaxSize;
- } // end of GetMaxSize
-
-/***********************************************************************/
-/* CAT Access Method opening routine. */
-/***********************************************************************/
-bool TDBCAT::OpenDB(PGLOBAL g)
- {
- if (Use == USE_OPEN) {
- /*******************************************************************/
- /* Table already open. */
- /*******************************************************************/
- N = -1;
- return false;
- } // endif use
-
- if (Mode != MODE_READ) {
- /*******************************************************************/
- /* ODBC Info tables cannot be modified. */
- /*******************************************************************/
- strcpy(g->Message, "CAT tables are read only");
- return true;
- } // endif Mode
-
- /*********************************************************************/
- /* Initialize the ODBC processing. */
- /*********************************************************************/
- if (Initialize(g))
- return true;
-
- Use = USE_OPEN;
- return InitCol(g);
- } // end of OpenDB
-
-/***********************************************************************/
-/* Initialize columns. */
-/***********************************************************************/
-bool TDBCAT::InitCol(PGLOBAL g)
- {
- PCATCOL colp;
- PCOLRES crp;
-
- for (colp = (PCATCOL)Columns; colp; colp = (PCATCOL)colp->GetNext()) {
- for (crp = Qrp->Colresp; crp; crp = crp->Next)
- if ((colp->Flag && colp->Flag == crp->Fld) ||
- (!colp->Flag && !stricmp(colp->Name, crp->Name))) {
- colp->Crp = crp;
- break;
- } // endif Flag
-
-
- if (!colp->Crp /*&& !colp->GetValue()->IsConstant()*/) {
- sprintf(g->Message, "Invalid flag %d for column %s",
- colp->Flag, colp->Name);
- return true;
- } // endif Crp
-
- } // endfor colp
-
- return false;
- } // end of InitCol
-
-/***********************************************************************/
-/* SetRecpos: Replace the table at the specified position. */
-/***********************************************************************/
-bool TDBCAT::SetRecpos(PGLOBAL g, int recpos)
- {
- N = recpos - 1;
- return false;
- } // end of SetRecpos
-
-/***********************************************************************/
-/* Data Base read routine for CAT access method. */
-/***********************************************************************/
-int TDBCAT::ReadDB(PGLOBAL g)
- {
- return (++N < Qrp->Nblin) ? RC_OK : RC_EF;
- } // end of ReadDB
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for CAT access methods. */
-/***********************************************************************/
-int TDBCAT::WriteDB(PGLOBAL g)
- {
- strcpy(g->Message, "CAT tables are read only");
- return RC_FX;
- } // end of WriteDB
-
-/***********************************************************************/
-/* Data Base delete line routine for CAT access methods. */
-/***********************************************************************/
-int TDBCAT::DeleteDB(PGLOBAL g, int irc)
- {
- strcpy(g->Message, "Delete not enabled for CAT tables");
- return RC_FX;
- } // end of DeleteDB
-
-/***********************************************************************/
-/* Data Base close routine for WMI access method. */
-/***********************************************************************/
-void TDBCAT::CloseDB(PGLOBAL g)
- {
- // Nothing to do
- } // end of CloseDB
-
-// ------------------------ CATCOL functions ----------------------------
-
-/***********************************************************************/
-/* CATCOL public constructor. */
-/***********************************************************************/
-CATCOL::CATCOL(PCOLDEF cdp, PTDB tdbp, int n)
- : COLBLK(cdp, tdbp, n)
- {
- Tdbp = (PTDBCAT)tdbp;
- Crp = NULL;
- Flag = cdp->GetOffset();
- } // end of WMICOL constructor
-
-/***********************************************************************/
-/* Read the next Data Source elements. */
-/***********************************************************************/
-void CATCOL::ReadColumn(PGLOBAL g)
- {
- // Get the value of the Name or Description property
- Value->SetValue_pvblk(Crp->Kdata, Tdbp->N);
- } // end of ReadColumn
-
+/************** Table C++ Functions Source Code File (.CPP) ************/
+/* Name: TABLE.CPP Version 2.7 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
+/* */
+/* This file contains the TBX, TDB and OPJOIN classes functions. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* xobject.h is header containing XOBJECT derived classes declares. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xtable.h"
+#include "tabcol.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+//#include "catalog.h"
+#include "reldef.h"
+
+int TDB::Tnum = 0;
+
+extern "C" int trace; // The general trace value
+
+/***********************************************************************/
+/* Utility routines. */
+/***********************************************************************/
+void NewPointer(PTABS, void *, void *);
+void AddPointer(PTABS, void *);
+
+/* ---------------------------- class TDB ---------------------------- */
+
+/***********************************************************************/
+/* TDB public constructors. */
+/***********************************************************************/
+TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum)
+ {
+ Use = USE_NO;
+ To_Orig = NULL;
+ To_Filter = NULL;
+ To_CondFil = NULL;
+ Next = NULL;
+ Name = (tdp) ? tdp->GetName() : NULL;
+ To_Table = NULL;
+ Columns = NULL;
+ Degree = (tdp) ? tdp->GetDegree() : 0;
+ Mode = MODE_READ;
+ } // end of TDB standard constructor
+
+TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum)
+ {
+ Use = tdbp->Use;
+ To_Orig = tdbp;
+ To_Filter = NULL;
+ To_CondFil = NULL;
+ Next = NULL;
+ Name = tdbp->Name;
+ To_Table = tdbp->To_Table;
+ Columns = NULL;
+ Degree = tdbp->Degree;
+ Mode = tdbp->Mode;
+ } // end of TDB copy constructor
+
+// Methods
+
+/***********************************************************************/
+/* RowNumber: returns the current row ordinal number. */
+/***********************************************************************/
+int TDB::RowNumber(PGLOBAL g, bool b)
+ {
+ sprintf(g->Message, MSG(ROWID_NOT_IMPL), GetAmName(g, GetAmType()));
+ return 0;
+ } // end of RowNumber
+
+PTDB TDB::Copy(PTABS t)
+ {
+ PTDB tp, tdb1, tdb2 = NULL, outp = NULL;
+//PGLOBAL g = t->G; // Is this really useful ???
+
+ for (tdb1 = this; tdb1; tdb1 = tdb1->Next) {
+ tp = tdb1->CopyOne(t);
+
+ if (!outp)
+ outp = tp;
+ else
+ tdb2->Next = tp;
+
+ tdb2 = tp;
+ NewPointer(t, tdb1, tdb2);
+ } // endfor tdb1
+
+ return outp;
+ } // end of Copy
+
+void TDB::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ PCOL cp;
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+
+ for (PTDB tp = this; tp; tp = tp->Next) {
+ fprintf(f, "%sTDB (%p) %s no=%d use=%d type=%d\n", m,
+ tp, tp->Name, tp->Tdb_No, tp->Use, tp->GetAmType());
+
+ tp->PrintAM(f, m);
+ fprintf(f, "%s Columns (deg=%d):\n", m, tp->Degree);
+
+ for (cp = tp->Columns; cp; cp = cp->GetNext())
+ cp->Print(g, f, n);
+
+ } /* endfor tp */
+
+ } // end of Print
+
+void TDB::Print(PGLOBAL g, char *ps, uint z)
+ {
+ sprintf(ps, "R%d.%s", Tdb_No, Name);
+ } // end of Print
+
+/* -------------------------- class TDBASE --------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBASE class. This is the base class to all */
+/* classes for tables that can be joined together. */
+/***********************************************************************/
+TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
+ {
+ To_Def = tdp;
+ To_Link = NULL;
+ To_Key_Col = NULL;
+ To_Kindex = NULL;
+ To_SetCols = NULL;
+ MaxSize = -1;
+ Knum = 0;
+ Read_Only = (tdp) ? tdp->IsReadOnly() : false;
+ m_data_charset= (tdp) ? tdp->data_charset() : NULL;
+ } // end of TDBASE constructor
+
+TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
+ {
+ To_Def = tdbp->To_Def;
+ To_SetCols = tdbp->To_SetCols; // ???
+ MaxSize = tdbp->MaxSize;
+ Read_Only = tdbp->Read_Only;
+ m_data_charset= tdbp->m_data_charset;
+ } // end of TDBASE copy constructor
+
+/***********************************************************************/
+/* Return the pointer on the DB catalog this table belongs to. */
+/***********************************************************************/
+PCATLG TDBASE::GetCat(void)
+ {
+ return (To_Def) ? To_Def->GetCat() : NULL;
+ } // end of GetCat
+
+/***********************************************************************/
+/* Return the pointer on the charset of this table. */
+/***********************************************************************/
+CHARSET_INFO *TDBASE::data_charset(void)
+ {
+ // If no DATA_CHARSET is specified, we assume that character
+ // set of the remote data is the same with CHARACTER SET
+ // definition of the SQL column.
+ return m_data_charset ? m_data_charset : &my_charset_bin;
+ } // end of data_charset
+
+/***********************************************************************/
+/* Return the datapath of the DB this table belongs to. */
+/***********************************************************************/
+PSZ TDBASE::GetPath(void)
+ {
+ return To_Def->GetPath();
+ } // end of GetPath
+
+/***********************************************************************/
+/* Initialize TDBASE based column description block construction. */
+/* name is used to call columns by name. */
+/* num is used by TBL to construct columns by index number. */
+/* Note: name=Null and num=0 for constructing all columns (select *) */
+/***********************************************************************/
+PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num)
+ {
+ int i;
+ PCOLDEF cdp;
+ PCOL cp, colp = NULL, cprec = NULL;
+
+ if (trace)
+ htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n",
+ GetAmType(), SVP(name), Name, num);
+
+ for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
+ if ((!name && !num) ||
+ (name && !stricmp(cdp->GetName(), name)) || num == i) {
+ /*****************************************************************/
+ /* Check for existence of desired column. */
+ /* Also find where to insert the new block. */
+ /*****************************************************************/
+ for (cp = Columns; cp; cp = cp->GetNext())
+ if (cp->GetIndex() < i)
+ cprec = cp;
+ else if (cp->GetIndex() == i)
+ break;
+
+ if (trace)
+ htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp);
+
+ /*****************************************************************/
+ /* Now take care of Column Description Block. */
+ /*****************************************************************/
+ if (cp)
+ colp = cp;
+ else if (!(cdp->Flags & U_SPECIAL))
+ colp = MakeCol(g, cdp, cprec, i);
+ else if (Mode == MODE_READ)
+ colp = InsertSpcBlk(g, cdp);
+
+ if (trace)
+ htrc("colp=%p\n", colp);
+
+ if (name || num)
+ break;
+ else if (colp && !colp->IsSpecial())
+ cprec = colp;
+
+ } // endif Name
+
+ return (colp);
+ } // end of ColDB
+
+/***********************************************************************/
+/* InsertSpecialColumn: Put a special column ahead of the column list.*/
+/***********************************************************************/
+PCOL TDBASE::InsertSpecialColumn(PGLOBAL g, PCOL colp)
+ {
+ if (!colp->IsSpecial())
+ return NULL;
+
+ colp->SetNext(Columns);
+ Columns = colp;
+ return colp;
+ } // end of InsertSpecialColumn
+
+/***********************************************************************/
+/* Make a special COLBLK to insert in a table. */
+/***********************************************************************/
+PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
+ {
+//char *name = cdp->GetName();
+ char *name = cdp->GetFmt();
+ PCOLUMN cp;
+ PCOL colp;
+
+ cp= new(g) COLUMN(cdp->GetName());
+ cp->SetTo_Table(To_Table);
+
+ if (!stricmp(name, "FILEID") ||
+ !stricmp(name, "SERVID")) {
+ if (!To_Def || !(To_Def->GetPseudo() & 2)) {
+ sprintf(g->Message, MSG(BAD_SPEC_COLUMN));
+ return NULL;
+ } // endif Pseudo
+
+ if (!stricmp(name, "FILEID"))
+ colp = new(g) FIDBLK(cp);
+ else
+ colp = new(g) SIDBLK(cp);
+
+ } else if (!stricmp(name, "TABID")) {
+ colp = new(g) TIDBLK(cp);
+//} else if (!stricmp(name, "CONID")) {
+// colp = new(g) CIDBLK(cp);
+ } else if (!stricmp(name, "ROWID")) {
+ colp = new(g) RIDBLK(cp, false);
+ } else if (!stricmp(name, "ROWNUM")) {
+ colp = new(g) RIDBLK(cp, true);
+ } else {
+ sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
+ return NULL;
+ } // endif's name
+
+ if (!(colp = InsertSpecialColumn(g, colp))) {
+ sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
+ return NULL;
+ } // endif Insert
+
+ return (colp);
+ } // end of InsertSpcBlk
+
+/***********************************************************************/
+/* ResetTableOpt: Wrong for this table type. */
+/***********************************************************************/
+int TDBASE::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
+{
+ strcpy(g->Message, "This table is not indexable");
+ return RC_INFO;
+} // end of ResetTableOpt
+
+/***********************************************************************/
+/* SetKindex: set or reset the index pointer. */
+/***********************************************************************/
+void TDBASE::SetKindex(PKXBASE kxp)
+ {
+ if (To_Kindex)
+ To_Kindex->Close(); // Discard old index
+
+ To_Kindex = kxp;
+ } // end of SetKindex
+
+/***********************************************************************/
+/* SetRecpos: Replace the table at the specified position. */
+/***********************************************************************/
+bool TDBASE::SetRecpos(PGLOBAL g, int recpos)
+ {
+ strcpy(g->Message, MSG(SETRECPOS_NIY));
+ return true;
+ } // end of SetRecpos
+
+/***********************************************************************/
+/* Methods */
+/***********************************************************************/
+void TDBASE::PrintAM(FILE *f, char *m)
+ {
+ fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
+ } // end of PrintAM
+
+/***********************************************************************/
+/* Marks DOS/MAP table columns used in internal joins. */
+/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */
+/* points to the currently marked tdb. */
+/* Two questions here: exact meaning of U_J_INT ? */
+/* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */
+/***********************************************************************/
+void TDBASE::MarkDB(PGLOBAL g, PTDB tdb2)
+ {
+ if (trace)
+ htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2);
+
+ } // end of MarkDB
+
+/* ---------------------------TDBCAT class --------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBCAT class. */
+/***********************************************************************/
+TDBCAT::TDBCAT(PTABDEF tdp) : TDBASE(tdp)
+ {
+ Qrp = NULL;
+ Init = false;
+ N = -1;
+ } // end of TDBCAT constructor
+
+/***********************************************************************/
+/* Allocate CAT column description block. */
+/***********************************************************************/
+PCOL TDBCAT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ PCATCOL colp;
+
+ colp = (PCATCOL)new(g) CATCOL(cdp, this, n);
+
+ if (cprec) {
+ colp->SetNext(cprec->GetNext());
+ cprec->SetNext(colp);
+ } else {
+ colp->SetNext(Columns);
+ Columns = colp;
+ } // endif cprec
+
+ return colp;
+ } // end of MakeCol
+
+/***********************************************************************/
+/* Initialize: Get the result query block. */
+/***********************************************************************/
+bool TDBCAT::Initialize(PGLOBAL g)
+ {
+ if (Init)
+ return false;
+
+ if (!(Qrp = GetResult(g)))
+ return true;
+
+ if (Qrp->Truncated) {
+ sprintf(g->Message, "Result limited to %d lines", Qrp->Maxres);
+ PushWarning(g, this);
+ } // endif Truncated
+
+ if (Qrp->BadLines) {
+ sprintf(g->Message, "%d bad lines in result", Qrp->BadLines);
+ PushWarning(g, this);
+ } // endif Badlines
+
+ Init = true;
+ return false;
+ } // end of Initialize
+
+/***********************************************************************/
+/* CAT: Get the number of properties. */
+/***********************************************************************/
+int TDBCAT::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0) {
+// if (Initialize(g))
+// return -1;
+
+// MaxSize = Qrp->Nblin;
+ MaxSize = 10; // To make MariaDB happy
+ } // endif MaxSize
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* CAT Access Method opening routine. */
+/***********************************************************************/
+bool TDBCAT::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open. */
+ /*******************************************************************/
+ N = -1;
+ return false;
+ } // endif use
+
+ if (Mode != MODE_READ) {
+ /*******************************************************************/
+ /* ODBC Info tables cannot be modified. */
+ /*******************************************************************/
+ strcpy(g->Message, "CAT tables are read only");
+ return true;
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Initialize the ODBC processing. */
+ /*********************************************************************/
+ if (Initialize(g))
+ return true;
+
+ Use = USE_OPEN;
+ return InitCol(g);
+ } // end of OpenDB
+
+/***********************************************************************/
+/* Initialize columns. */
+/***********************************************************************/
+bool TDBCAT::InitCol(PGLOBAL g)
+ {
+ PCATCOL colp;
+ PCOLRES crp;
+
+ for (colp = (PCATCOL)Columns; colp; colp = (PCATCOL)colp->GetNext()) {
+ for (crp = Qrp->Colresp; crp; crp = crp->Next)
+ if ((colp->Flag && colp->Flag == crp->Fld) ||
+ (!colp->Flag && !stricmp(colp->Name, crp->Name))) {
+ colp->Crp = crp;
+ break;
+ } // endif Flag
+
+
+ if (!colp->Crp /*&& !colp->GetValue()->IsConstant()*/) {
+ sprintf(g->Message, "Invalid flag %d for column %s",
+ colp->Flag, colp->Name);
+ return true;
+ } // endif Crp
+
+ } // endfor colp
+
+ return false;
+ } // end of InitCol
+
+/***********************************************************************/
+/* SetRecpos: Replace the table at the specified position. */
+/***********************************************************************/
+bool TDBCAT::SetRecpos(PGLOBAL g, int recpos)
+ {
+ N = recpos - 1;
+ return false;
+ } // end of SetRecpos
+
+/***********************************************************************/
+/* Data Base read routine for CAT access method. */
+/***********************************************************************/
+int TDBCAT::ReadDB(PGLOBAL g)
+ {
+ return (++N < Qrp->Nblin) ? RC_OK : RC_EF;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for CAT access methods. */
+/***********************************************************************/
+int TDBCAT::WriteDB(PGLOBAL g)
+ {
+ strcpy(g->Message, "CAT tables are read only");
+ return RC_FX;
+ } // end of WriteDB
+
+/***********************************************************************/
+/* Data Base delete line routine for CAT access methods. */
+/***********************************************************************/
+int TDBCAT::DeleteDB(PGLOBAL g, int irc)
+ {
+ strcpy(g->Message, "Delete not enabled for CAT tables");
+ return RC_FX;
+ } // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base close routine for WMI access method. */
+/***********************************************************************/
+void TDBCAT::CloseDB(PGLOBAL g)
+ {
+ // Nothing to do
+ } // end of CloseDB
+
+// ------------------------ CATCOL functions ----------------------------
+
+/***********************************************************************/
+/* CATCOL public constructor. */
+/***********************************************************************/
+CATCOL::CATCOL(PCOLDEF cdp, PTDB tdbp, int n)
+ : COLBLK(cdp, tdbp, n)
+ {
+ Tdbp = (PTDBCAT)tdbp;
+ Crp = NULL;
+ Flag = cdp->GetOffset();
+ } // end of WMICOL constructor
+
+/***********************************************************************/
+/* Read the next Data Source elements. */
+/***********************************************************************/
+void CATCOL::ReadColumn(PGLOBAL g)
+ {
+ // Get the value of the Name or Description property
+ Value->SetValue_pvblk(Crp->Kdata, Tdbp->N);
+ } // end of ReadColumn
+
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index ce41a8429be..eb6efca9e00 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1,1620 +1,1620 @@
-/************* TabMySQL C++ Program Source Code File (.CPP) *************/
-/* PROGRAM NAME: TABMYSQL */
-/* ------------- */
-/* Version 1.7 */
-/* */
-/* AUTHOR: */
-/* ------- */
-/* Olivier BERTRAND 2007-2013 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* Implements a table type that are MySQL tables. */
-/* It can optionally use the embedded MySQL library. */
-/* */
-/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
-/* -------------------------------------- */
-/* */
-/* REQUIRED FILES: */
-/* --------------- */
-/* TABMYSQL.CPP - Source code */
-/* PLGDBSEM.H - DB application declaration file */
-/* TABMYSQL.H - TABODBC classes declaration file */
-/* GLOBAL.H - Global declaration file */
-/* */
-/* REQUIRED LIBRARIES: */
-/* ------------------- */
-/* Large model C library */
-/* */
-/* REQUIRED PROGRAMS: */
-/* ------------------ */
-/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
-/* */
-/************************************************************************/
-#define MYSQL_SERVER 1
-#include "my_global.h"
-#include "sql_class.h"
-#include "sql_servers.h"
-#if defined(WIN32)
-//#include <windows.h>
-#else // !WIN32
-//#include <fnmatch.h>
-//#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "osutil.h"
-//#include <io.h>
-//#include <fcntl.h>
-#endif // !WIN32
-
-/***********************************************************************/
-/* Include application header files: */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "xtable.h"
-#include "tabcol.h"
-#include "colblk.h"
-#include "mycat.h"
-#include "reldef.h"
-#include "tabmysql.h"
-#include "valblk.h"
-#include "tabutil.h"
-
-#if defined(_CONSOLE)
-void PrintResult(PGLOBAL, PSEM, PQRYRES);
-#endif // _CONSOLE
-
-extern "C" int trace;
-
-/* -------------- Implementation of the MYSQLDEF class --------------- */
-
-/***********************************************************************/
-/* Constructor. */
-/***********************************************************************/
-MYSQLDEF::MYSQLDEF(void)
- {
- Pseudo = 2; // SERVID is Ok but not ROWID
- Hostname = NULL;
- Database = NULL;
- Tabname = NULL;
- Srcdef = NULL;
- Username = NULL;
- Password = NULL;
- Portnumber = 0;
- Isview = FALSE;
- Bind = FALSE;
- Delayed = FALSE;
- Xsrc = FALSE;
- } // end of MYSQLDEF constructor
-
-/***********************************************************************/
-/* Get connection info from the declared server. */
-/***********************************************************************/
-bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
-{
- THD *thd= current_thd;
- MEM_ROOT *mem= thd->mem_root;
- FOREIGN_SERVER *server, server_buffer;
- DBUG_ENTER("GetServerInfo");
- DBUG_PRINT("info", ("server_name %s", server_name));
-
- if (!server_name || !strlen(server_name)) {
- DBUG_PRINT("info", ("server_name not defined!"));
- strcpy(g->Message, "server_name not defined!");
- DBUG_RETURN(true);
- } // endif server_name
-
- // get_server_by_name() clones the server if exists and allocates
- // copies of strings in the supplied mem_root
- if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
- DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
- /* need to come up with error handling */
- strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
- DBUG_RETURN(true);
- } // endif server
-
- DBUG_PRINT("info", ("get_server_by_name returned server at %lx",
- (long unsigned int) server));
-
- // TODO: We need to examine which of these can really be NULL
- Hostname = PlugDup(g, server->host);
- Database = PlugDup(g, server->db);
- Username = PlugDup(g, server->username);
- Password = PlugDup(g, server->password);
- Portnumber = (server->port) ? server->port : GetDefaultPort();
-
- DBUG_RETURN(false);
-} // end of GetServerInfo
-
-/***********************************************************************/
-/* Parse connection string */
-/* */
-/* SYNOPSIS */
-/* ParseURL() */
-/* url The connection string to parse */
-/* */
-/* DESCRIPTION */
-/* Populates the table with information about the connection */
-/* to the foreign database that will serve as the data source. */
-/* This string must be specified (currently) in the "CONNECTION" */
-/* field, listed in the CREATE TABLE statement. */
-/* */
-/* This string MUST be in the format of any of these: */
-/* */
-/* CONNECTION="scheme://user:pwd@host:port/database/table" */
-/* CONNECTION="scheme://user@host/database/table" */
-/* CONNECTION="scheme://user@host:port/database/table" */
-/* CONNECTION="scheme://user:pwd@host/database/table" */
-/* */
-/* _OR_ */
-/* */
-/* CONNECTION="connection name" (NIY) */
-/* */
-/* An Example: */
-/* */
-/* CREATE TABLE t1 (id int(32)) */
-/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
-/* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */
-/* */
-/* CREATE TABLE t2 ( */
-/* id int(4) NOT NULL auto_increment, */
-/* name varchar(32) NOT NULL, */
-/* PRIMARY KEY(id) */
-/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
-/* CONNECTION="my_conn"; (NIY) */
-/* */
-/* 'password' and 'port' are both optional. */
-/* */
-/* RETURN VALUE */
-/* false success */
-/* true error */
-/* */
-/***********************************************************************/
-bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
- {
- if ((!strstr(url, "://") && (!strchr(url, '@')))) {
- // No :// or @ in connection string. Must be a straight
- // connection name of either "server" or "server/table"
- // ok, so we do a little parsing, but not completely!
- if ((Tabname= strchr(url, '/'))) {
- // If there is a single '/' in the connection string,
- // this means the user is specifying a table name
- *Tabname++= '\0';
-
- // there better not be any more '/'s !
- if (strchr(Tabname, '/'))
- return true;
-
- } else
- // Otherwise, straight server name,
- // use tablename of federatedx table as remote table name
- Tabname= Name;
-
- if (trace)
- htrc("server: %s Tabname: %s", url, Tabname);
-
- Server = url;
- return GetServerInfo(g, url);
- } else {
- // URL, parse it
- char *sport, *scheme = url;
-
- if (!(Username = strstr(url, "://"))) {
- strcpy(g->Message, "Connection is not an URL");
- return true;
- } // endif User
-
- scheme[Username - scheme] = 0;
-
- if (stricmp(scheme, "mysql")) {
- strcpy(g->Message, "scheme must be mysql");
- return true;
- } // endif scheme
-
- Username += 3;
-
- if (!(Hostname = strchr(Username, '@'))) {
- strcpy(g->Message, "No host specified in URL");
- return true;
- } else {
- *Hostname++ = 0; // End Username
- Server = Hostname;
- } // endif Hostname
-
- if ((Password = strchr(Username, ':'))) {
- *Password++ = 0; // End username
-
- // Make sure there isn't an extra / or @
- if ((strchr(Password, '/') || strchr(Hostname, '@'))) {
- strcpy(g->Message, "Syntax error in URL");
- return true;
- } // endif
-
- // Found that if the string is:
- // user:@hostname:port/db/table
- // Then password is a null string, so set to NULL
- if ((Password[0] == 0))
- Password = NULL;
-
- } // endif password
-
- // Make sure there isn't an extra / or @ */
- if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
- strcpy(g->Message, "Syntax error in URL");
- return true;
- } // endif
-
- if ((Database = strchr(Hostname, '/'))) {
- *Database++ = 0;
-
- if ((Tabname = strchr(Database, '/'))) {
- *Tabname++ = 0;
-
- // Make sure there's not an extra /
- if ((strchr(Tabname, '/'))) {
- strcpy(g->Message, "Syntax error in URL");
- return true;
- } // endif /
-
- } // endif Tabname
-
- } // endif database
-
- if ((sport = strchr(Hostname, ':')))
- *sport++ = 0;
-
- // 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;
-
- if (Username[0] == 0)
- Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL;
-
- if (Hostname[0] == 0)
- Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL;
-
- if (!Database || !*Database)
- Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL;
-
- if (!Tabname || !*Tabname)
- Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL;
-
- if (!Password)
- Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL;
- } // endif URL
-
-#if 0
- if (!share->port)
- if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
- share->socket= (char *) MYSQL_UNIX_ADDR;
- else
- share->port= MYSQL_PORT;
-#endif // 0
-
- return false;
- } // end of ParseURL
-
-/***********************************************************************/
-/* DefineAM: define specific AM block values from XCV file. */
-/***********************************************************************/
-bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
- {
- char *url;
-
- Desc = "MySQL Table";
-
- if (stricmp(am, "MYPRX")) {
- // Normal case of specific MYSQL table
- url = Cat->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());
- Server = Hostname;
- } else if (ParseURL(g, url))
- return true;
-
- Bind = !!Cat->GetIntCatInfo("Bind", 0);
- Delayed = !!Cat->GetIntCatInfo("Delayed", 0);
- } else {
- // MYSQL access from a PROXY table
- Database = Cat->GetStringCatInfo(g, "Database", "*");
- Isview = Cat->GetBoolCatInfo("View", FALSE);
-
- // We must get other connection parms from the calling table
- Remove_tshp(Cat);
- url = Cat->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());
- Server = Hostname;
- } else {
- char *locdb = Database;
-
- if (ParseURL(g, url))
- return true;
-
- Database = locdb;
- } // endif url
-
- Tabname = Name;
- } // endif am
-
- if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
- Isview = true;
-
- // Used for Update and Delete
- Qrystr = Cat->GetStringCatInfo(g, "Query_String", "?");
- Quoted = Cat->GetIntCatInfo("Quoted", 0);
-
- // Specific for command executing tables
- Xsrc = Cat->GetBoolCatInfo("Execsrc", false);
- Mxr = Cat->GetIntCatInfo("Maxerr", 0);
- return FALSE;
- } // end of DefineAM
-
-/***********************************************************************/
-/* GetTable: makes a new TDB of the proper type. */
-/***********************************************************************/
-PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
- {
- if (Xsrc)
- return new(g) TDBMYEXC(this);
- else if (Catfunc == FNC_COL)
- return new(g) TDBMCL(this);
- else
- return new(g) TDBMYSQL(this);
-
- } // end of GetTable
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBMYSQL class. */
-/***********************************************************************/
-TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
- {
- if (tdp) {
- Host = tdp->Hostname;
- Database = tdp->Database;
- Tabname = tdp->Tabname;
- Srcdef = tdp->Srcdef;
- User = tdp->Username;
- Pwd = tdp->Password;
- Server = tdp->Server;
- Qrystr = tdp->Qrystr;
- Quoted = max(0, tdp->Quoted);
- Port = tdp->Portnumber;
- Isview = tdp->Isview;
- Prep = tdp->Bind;
- Delayed = tdp->Delayed;
- } else {
- Host = NULL;
- Database = NULL;
- Tabname = NULL;
- Srcdef = NULL;
- User = NULL;
- Pwd = NULL;
- Server = NULL;
- Qrystr = NULL;
- Quoted = 0;
- Port = 0;
- Isview = FALSE;
- Prep = FALSE;
- Delayed = FALSE;
- } // endif tdp
-
- Bind = NULL;
- Query = NULL;
- Qbuf = NULL;
- Fetched = FALSE;
- m_Rc = RC_FX;
- AftRows = 0;
- N = -1;
- Nparm = 0;
- } // end of TDBMYSQL constructor
-
-TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp)
- {
- Host = tdbp->Host;
- Database = tdbp->Database;
- Tabname = tdbp->Tabname;
- Srcdef = tdbp->Srcdef;
- User = tdbp->User;
- Pwd = tdbp->Pwd;
- Qrystr = tdbp->Qrystr;
- Quoted = tdbp->Quoted;
- Port = tdbp->Port;
- Isview = tdbp->Isview;
- Prep = tdbp->Prep;
- Delayed = tdbp->Delayed;
- Bind = NULL;
- Query = tdbp->Query;
- Qbuf = NULL;
- Fetched = tdbp->Fetched;
- m_Rc = tdbp->m_Rc;
- AftRows = tdbp->AftRows;
- N = tdbp->N;
- Nparm = tdbp->Nparm;
- } // end of TDBMYSQL copy constructor
-
-// Is this really useful ???
-PTDB TDBMYSQL::CopyOne(PTABS t)
- {
- PTDB tp;
- PCOL cp1, cp2;
- PGLOBAL g = t->G;
-
- tp = new(g) TDBMYSQL(g, this);
-
- for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
- cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
-
- NewPointer(t, cp1, cp2);
- } // endfor cp1
-
- return tp;
- } // end of CopyOne
-
-/***********************************************************************/
-/* Allocate MYSQL column description block. */
-/***********************************************************************/
-PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- return new(g) MYSQLCOL(cdp, this, cprec, n);
- } // end of MakeCol
-
-/***********************************************************************/
-/* MakeSelect: make the Select statement use with MySQL connection. */
-/* Note: when implementing EOM filtering, column only used in local */
-/* filter should be removed from column list. */
-/***********************************************************************/
-bool TDBMYSQL::MakeSelect(PGLOBAL g)
- {
- char *tk = "`";
- int rank = 0;
- bool b = FALSE;
- PCOL colp;
-//PDBUSER dup = PlgGetUser(g);
-
- if (Query)
- return FALSE; // already done
-
- if (Srcdef) {
- Query = Srcdef;
- return false;
- } // endif Srcdef
-
- //Find the address of the suballocated query
- Query = (char*)PlugSubAlloc(g, NULL, 0);
- strcpy(Query, "SELECT ");
-
- if (Columns) {
- for (colp = Columns; colp; colp = colp->GetNext())
- if (!colp->IsSpecial()) {
-// if (colp->IsSpecial()) {
-// strcpy(g->Message, MSG(NO_SPEC_COL));
-// return TRUE;
-// } else {
- if (b)
- strcat(Query, ", ");
- else
- b = TRUE;
-
- strcat(strcat(strcat(Query, tk), colp->GetName()), tk);
- ((PMYCOL)colp)->Rank = rank++;
- } // endif colp
-
- } else {
- // ncol == 0 can occur for views or queries such as
- // Query count(*) from... for which we will count the rows from
- // Query '*' from...
- // (the use of a char constant minimize the result storage)
- strcat(Query, (Isview) ? "*" : "'*'");
- } // endif ncol
-
- strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);
-
- if (To_CondFil)
- strcat(strcat(Query, " WHERE "), To_CondFil->Body);
-
- if (trace)
- htrc("Query=%s\n", Query);
-
- // Now we know how much to suballocate
- PlugSubAlloc(g, NULL, strlen(Query) + 1);
- return FALSE;
- } // end of MakeSelect
-
-/***********************************************************************/
-/* MakeInsert: make the Insert statement used with MySQL connection. */
-/***********************************************************************/
-bool TDBMYSQL::MakeInsert(PGLOBAL g)
- {
- char *colist, *valist = NULL;
- char *tk = "`";
- int len = 0, qlen = 0;
- bool b = FALSE;
- PCOL colp;
-
- if (Query)
- return FALSE; // already done
-
- for (colp = Columns; colp; colp = colp->GetNext())
- if (!colp->IsSpecial()) {
-// if (colp->IsSpecial()) {
-// strcpy(g->Message, MSG(NO_SPEC_COL));
-// return TRUE;
-// } else {
- len += (strlen(colp->GetName()) + 4);
- ((PMYCOL)colp)->Rank = Nparm++;
- } // endif colp
-
- colist = (char*)PlugSubAlloc(g, NULL, len);
- *colist = '\0';
-
- if (Prep) {
-#if defined(MYSQL_PREPARED_STATEMENTS)
- valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
- *valist = '\0';
-#else // !MYSQL_PREPARED_STATEMENTS
- strcpy(g->Message, "Prepared statements not used (not supported)");
- PushWarning(g, this);
- Prep = FALSE;
-#endif // !MYSQL_PREPARED_STATEMENTS
- } // endif Prep
-
- for (colp = Columns; colp; colp = colp->GetNext()) {
- if (b) {
- strcat(colist, ", ");
- if (Prep) strcat(valist, ",");
- } else
- b = TRUE;
-
- strcat(strcat(strcat(colist, tk), colp->GetName()), tk);
-
- // Parameter marker
- if (!Prep) {
- if (colp->GetResultType() == TYPE_DATE)
- qlen += 20;
- else
- qlen += colp->GetLength();
-
- } // endif Prep
-
- if (Prep)
- strcat(valist, "?");
-
- } // endfor colp
-
- // Below 40 is enough to contain the fixed part of the query
- len = (strlen(Tabname) + strlen(colist)
- + ((Prep) ? strlen(valist) : 0) + 40);
- Query = (char*)PlugSubAlloc(g, NULL, len);
-
- if (Delayed)
- strcpy(Query, "INSERT DELAYED INTO ");
- else
- strcpy(Query, "INSERT INTO ");
-
- strcat(strcat(strcat(Query, tk), Tabname), tk);
- strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
-
- if (Prep)
- strcat(strcat(Query, valist), ")");
- else {
- qlen += (strlen(Query) + Nparm);
- Qbuf = (char *)PlugSubAlloc(g, NULL, qlen);
- } // endelse Prep
-
- return FALSE;
- } // end of MakeInsert
-
-/***********************************************************************/
-/* MakeCommand: make the Update or Delete statement to send to the */
-/* MySQL server. Limited to remote values and filtering. */
-/***********************************************************************/
-int TDBMYSQL::MakeCommand(PGLOBAL g)
- {
- Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
-
- if (Quoted > 0 || stricmp(Name, Tabname)) {
- char *p, *qrystr, name[68];
- bool qtd = Quoted > 0;
-
-
- // Make a lower case copy of the originale query
- qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
- strlwr(strcpy(qrystr, Qrystr));
-
- // Check whether the table name is equal to a keyword
- // If so, it must be quoted in the original query
- strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));
-
- if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
- strlwr(strcpy(name, Name)); // Not a keyword
-
- if ((p = strstr(qrystr, name))) {
- memcpy(Query, Qrystr, p - qrystr);
- Query[p - qrystr] = 0;
-
- if (qtd && *(p-1) == ' ')
- strcat(strcat(strcat(Query, "`"), Tabname), "`");
- else
- strcat(Query, Tabname);
-
- strcat(Query, Qrystr + (p - qrystr) + strlen(name));
- } else {
- sprintf(g->Message, "Cannot use this %s command",
- (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
- return RC_FX;
- } // endif p
-
- } else
- strcpy(Query, Qrystr);
-
- return RC_OK;
- } // end of MakeCommand
-
-#if 0
-/***********************************************************************/
-/* MakeUpdate: make the Update statement use with MySQL connection. */
-/* Limited to remote values and filtering. */
-/***********************************************************************/
-int TDBMYSQL::MakeUpdate(PGLOBAL g)
- {
- char *qc, cmd[8], tab[96], end[1024];
-
- Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
- memset(end, 0, sizeof(end));
-
- if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
- sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
- qc = "`";
- else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
- && !stricmp(tab, Name))
- qc = (Quoted) ? "`" : "";
- else {
- strcpy(g->Message, "Cannot use this UPDATE command");
- return RC_FX;
- } // endif sscanf
-
- assert(!stricmp(cmd, "update"));
- strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc);
- strcat(Query, end);
- return RC_OK;
- } // end of MakeUpdate
-
-/***********************************************************************/
-/* MakeDelete: make the Delete statement used with MySQL connection. */
-/* Limited to remote filtering. */
-/***********************************************************************/
-int TDBMYSQL::MakeDelete(PGLOBAL g)
- {
- char *qc, cmd[8], from[8], tab[96], end[512];
-
- Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
- memset(end, 0, sizeof(end));
-
- if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
- sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
- qc = "`";
- else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
- qc = (Quoted) ? "`" : "";
- else {
- strcpy(g->Message, "Cannot use this DELETE command");
- return RC_FX;
- } // endif sscanf
-
- assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
- strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc);
-
- if (*end)
- strcat(Query, end);
-
- return RC_OK;
- } // end of MakeDelete
-#endif // 0
-
-/***********************************************************************/
-/* XCV GetMaxSize: returns the maximum number of rows in the table. */
-/***********************************************************************/
-int TDBMYSQL::GetMaxSize(PGLOBAL g)
- {
- if (MaxSize < 0) {
-#if 0
- if (MakeSelect(g))
- return -2;
-
- if (!Myc.Connected()) {
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
- return -1;
-
- } // endif connected
-
- if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) {
- Myc.Close();
- return -3;
- } // endif MaxSize
-
- // FIXME: Columns should be known when Info calls GetMaxSize
- if (!Columns)
- Query = NULL; // Must be remade when columns are known
-#endif // 0
-
- // Return 0 in mode DELETE in case of delete all.
- MaxSize = (Mode == MODE_DELETE) ? 0 : 10; // To make MySQL happy
- } // endif MaxSize
-
- return MaxSize;
- } // end of GetMaxSize
-
-/***********************************************************************/
-/* This a fake routine as ROWID does not exist in MySQL. */
-/***********************************************************************/
-int TDBMYSQL::RowNumber(PGLOBAL g, bool b)
- {
- return N;
- } // end of RowNumber
-
-/***********************************************************************/
-/* Return 0 in mode UPDATE to tell that the update is done. */
-/***********************************************************************/
-int TDBMYSQL::GetProgMax(PGLOBAL g)
- {
- return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
- } // end of GetProgMax
-
-/***********************************************************************/
-/* MySQL Bind Parameter function. */
-/***********************************************************************/
-int TDBMYSQL::BindColumns(PGLOBAL g)
- {
-#if defined(MYSQL_PREPARED_STATEMENTS)
- if (Prep) {
- Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
-
- for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
- colp->InitBind(g);
-
- return Myc.BindParams(g, Bind);
- } // endif prep
-#endif // MYSQL_PREPARED_STATEMENTS
-
- return RC_OK;
- } // end of BindColumns
-
-/***********************************************************************/
-/* MySQL Access Method opening routine. */
-/***********************************************************************/
-bool TDBMYSQL::OpenDB(PGLOBAL g)
- {
- if (Use == USE_OPEN) {
- /*******************************************************************/
- /* Table already open, just replace it at its beginning. */
- /*******************************************************************/
- Myc.Rewind();
- return false;
- } // endif use
-
- /*********************************************************************/
- /* Open a MySQL connection for this table. */
- /* Note: this may not be the proper way to do. Perhaps it is better */
- /* to test whether a connection is already open for this server */
- /* and if so to allocate just a new result set. But this only for */
- /* servers allowing concurency in getting results ??? */
- /*********************************************************************/
- if (!Myc.Connected()) {
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
- return true;
-
- } // endif Connected
-
- /*********************************************************************/
- /* Take care of DATE columns. */
- /*********************************************************************/
- for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
- if (colp->Buf_Type == TYPE_DATE)
- // Format must match DATETIME MySQL type
- ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
-
- /*********************************************************************/
- /* Allocate whatever is used for getting results. */
- /*********************************************************************/
- if (Mode == MODE_READ) {
- if (!MakeSelect(g))
- m_Rc = Myc.ExecSQL(g, Query);
-
-#if 0
- if (!Myc.m_Res || !Myc.m_Fields) {
- sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
- Myc.Close();
- return true;
- } // endif m_Res
-#endif // 0
-
- if (!m_Rc && Srcdef)
- if (SetColumnRanks(g))
- return true;
-
- } else if (Mode == MODE_INSERT) {
- if (Srcdef) {
- strcpy(g->Message, "No insert into anonym views");
- return true;
- } // endif Srcdef
-
- if (!MakeInsert(g)) {
-#if defined(MYSQL_PREPARED_STATEMENTS)
- int n = (Prep) ? Myc.PrepareSQL(g, Query) : Nparm;
-
- if (Nparm != n) {
- if (n >= 0) // Other errors return negative values
- strcpy(g->Message, MSG(BAD_PARM_COUNT));
-
- } else
-#endif // MYSQL_PREPARED_STATEMENTS
- m_Rc = BindColumns(g);
-
- } // endif MakeInsert
-
- if (m_Rc != RC_FX) {
- char cmd[64];
- int w;
-
- sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
- m_Rc = Myc.ExecSQL(g, cmd, &w);
- } // endif m_Rc
-
- } else
-// m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
- m_Rc = MakeCommand(g);
-
- if (m_Rc == RC_FX) {
- Myc.Close();
- return true;
- } // endif rc
-
- Use = USE_OPEN;
- return false;
- } // end of OpenDB
-
-/***********************************************************************/
-/* Set the rank of columns in the result set. */
-/***********************************************************************/
-bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
- {
- for (PCOL colp = Columns; colp; colp = colp->GetNext())
- if (((PMYCOL)colp)->FindRank(g))
- return TRUE;
-
- return FALSE;
- } // end of SetColumnRanks
-
-/***********************************************************************/
-/* Called by Parent table to make the columns of a View. */
-/***********************************************************************/
-PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
- {
- int n;
- MYSQL_FIELD *fld;
- PCOL cp, colp = NULL;
-
- for (n = 0; n < Myc.m_Fields; n++) {
- fld = &Myc.m_Res->fields[n];
-
- if (!stricmp(name, fld->name)) {
- colp = new(g) MYSQLCOL(fld, this, n);
-
- if (colp->InitValue(g))
- return NULL;
-
- if (!Columns)
- Columns = colp;
- else for (cp = Columns; cp; cp = cp->GetNext())
- if (!cp->GetNext()) {
- cp->SetNext(colp);
- break;
- } // endif Next
-
- break;
- } // endif name
-
- } // endfor n
-
- if (!colp)
- sprintf(g->Message, "Column %s is not in view", name);
-
- return colp;
- } // end of MakeFieldColumn
-
-/***********************************************************************/
-/* Called by Pivot tables to find default column names in a View */
-/* as the name of last field not equal to the passed name. */
-/***********************************************************************/
-char *TDBMYSQL::FindFieldColumn(char *name)
- {
- int n;
- MYSQL_FIELD *fld;
- char *cp = NULL;
-
- for (n = Myc.m_Fields - 1; n >= 0; n--) {
- fld = &Myc.m_Res->fields[n];
-
- if (!name || stricmp(name, fld->name)) {
- cp = fld->name;
- break;
- } // endif name
-
- } // endfor n
-
- return cp;
- } // end of FindFieldColumn
-
-/***********************************************************************/
-/* Send an UPDATE or DELETE command to the remote server. */
-/***********************************************************************/
-int TDBMYSQL::SendCommand(PGLOBAL g)
- {
- int w;
-
- if (Myc.ExecSQLcmd(g, Query, &w) == RC_NF) {
- AftRows = Myc.m_Afrw;
- sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows);
- PushWarning(g, this, 0); // 0 means a Note
-
- if (trace)
- htrc("%s\n", g->Message);
-
- if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
- // We got warnings from the remote server
- while (Myc.Fetch(g, -1) == RC_OK) {
- sprintf(g->Message, "%s: (%s) %s", Tabname,
- Myc.GetCharField(1), Myc.GetCharField(2));
- PushWarning(g, this);
- } // endwhile Fetch
-
- Myc.FreeResult();
- } // endif w
-
- return RC_EF; // Nothing else to do
- } else
- return RC_FX; // Error
-
- } // end of SendCommand
-
-/***********************************************************************/
-/* Data Base read routine for MYSQL access method. */
-/***********************************************************************/
-int TDBMYSQL::ReadDB(PGLOBAL g)
- {
- int rc;
-
- if (trace > 1)
- htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
- GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
-
- if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
- return SendCommand(g);
-
- /*********************************************************************/
- /* Now start the reading process. */
- /* Here is the place to fetch the line. */
- /*********************************************************************/
- N++;
- Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
-
- if (trace > 1)
- htrc(" Read: rc=%d\n", rc);
-
- return rc;
- } // end of ReadDB
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for MYSQL access methods. */
-/***********************************************************************/
-int TDBMYSQL::WriteDB(PGLOBAL g)
- {
-#if defined(MYSQL_PREPARED_STATEMENTS)
- if (Prep)
- return Myc.ExecStmt(g);
-#endif // MYSQL_PREPARED_STATEMENTS
-
- // Statement was not prepared, we must construct and execute
- // an insert query for each line to insert
- int rc;
- char buf[32];
-
- strcpy(Qbuf, Query);
-
- // Make the Insert command value list
- for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
- if (!colp->GetValue()->IsNull()) {
- if (colp->GetResultType() == TYPE_STRING ||
- colp->GetResultType() == TYPE_DATE)
- strcat(Qbuf, "'");
-
- strcat(Qbuf, colp->GetValue()->GetCharString(buf));
-
- if (colp->GetResultType() == TYPE_STRING ||
- colp->GetResultType() == TYPE_DATE)
- strcat(Qbuf, "'");
-
- } else
- strcat(Qbuf, "NULL");
-
- strcat(Qbuf, (colp->GetNext()) ? "," : ")");
- } // endfor colp
-
- Myc.m_Rows = -1; // To execute the query
- rc = Myc.ExecSQL(g, Qbuf);
- return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok
- } // end of WriteDB
-
-/***********************************************************************/
-/* Data Base delete all routine for MYSQL access methods. */
-/***********************************************************************/
-int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
- {
- if (irc == RC_FX)
- // Send the DELETE (all) command to the remote table
- return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
- else
- return RC_OK; // Ignore
-
- } // end of DeleteDB
-
-/***********************************************************************/
-/* Data Base close routine for MySQL access method. */
-/***********************************************************************/
-void TDBMYSQL::CloseDB(PGLOBAL g)
- {
- if (Myc.Connected()) {
- if (Mode == MODE_INSERT) {
- char cmd[64];
- int w;
- PDBUSER dup = PlgGetUser(g);
-
- dup->Step = "Enabling indexes";
- sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
- Myc.m_Rows = -1; // To execute the query
- m_Rc = Myc.ExecSQL(g, cmd, &w);
- } // endif m_Rc
-
- Myc.Close();
- } // endif Myc
-
- if (trace)
- htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
-
- } // end of CloseDB
-
-// ------------------------ MYSQLCOL functions --------------------------
-
-/***********************************************************************/
-/* MYSQLCOL public constructor. */
-/***********************************************************************/
-MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
- : COLBLK(cdp, tdbp, i)
- {
- if (cprec) {
- Next = cprec->GetNext();
- cprec->SetNext(this);
- } else {
- Next = tdbp->GetColumns();
- tdbp->SetColumns(this);
- } // endif cprec
-
- // Set additional MySQL access method information for column.
- Precision = Long = cdp->GetLong();
- Bind = NULL;
- To_Val = NULL;
- Slen = 0;
- Rank = -1; // Not known yet
-
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
- } // end of MYSQLCOL constructor
-
-/***********************************************************************/
-/* MYSQLCOL public constructor. */
-/***********************************************************************/
-MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
- : COLBLK(NULL, tdbp, i)
- {
- Name = fld->name;
- Opt = 0;
- Precision = Long = fld->length;
- Buf_Type = MYSQLtoPLG(fld->type);
- strcpy(Format.Type, GetFormatType(Buf_Type));
- Format.Length = Long;
- Format.Prec = fld->decimals;
- ColUse = U_P;
- Nullable = !IS_NOT_NULL(fld->flags);
-
- // Set additional MySQL access method information for column.
- Bind = NULL;
- To_Val = NULL;
- Slen = 0;
- Rank = i;
-
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
- } // end of MYSQLCOL constructor
-
-/***********************************************************************/
-/* MYSQLCOL constructor used for copying columns. */
-/* tdbp is the pointer to the new table descriptor. */
-/***********************************************************************/
-MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
- {
- Long = col1->Long;
- Bind = NULL;
- To_Val = NULL;
- Slen = col1->Slen;
- Rank = col1->Rank;
- } // end of MYSQLCOL copy constructor
-
-/***********************************************************************/
-/* FindRank: Find the rank of this column in the result set. */
-/***********************************************************************/
-bool MYSQLCOL::FindRank(PGLOBAL g)
-{
- int n;
- MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;
-
- for (n = 0; n < myc.m_Fields; n++)
- if (!stricmp(Name, myc.m_Res->fields[n].name)) {
- Rank = n;
- return false;
- } // endif Name
-
- sprintf(g->Message, "Column %s not in result set", Name);
- return true;
-} // end of FindRank
-
-/***********************************************************************/
-/* SetBuffer: prepare a column block for write operation. */
-/***********************************************************************/
-bool MYSQLCOL::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
-
- // 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
-
-/***********************************************************************/
-/* InitBind: Initialize the bind structure according to type. */
-/***********************************************************************/
-void MYSQLCOL::InitBind(PGLOBAL g)
- {
- PTDBMY tdbp = (PTDBMY)To_Tdb;
-
- assert(tdbp->Bind && Rank < tdbp->Nparm);
-
- Bind = &tdbp->Bind[Rank];
- memset(Bind, 0, sizeof(MYSQL_BIND));
-
- if (Buf_Type == TYPE_DATE) {
- Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
- Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
- Bind->buffer_length = 20;
- Bind->length = &Slen;
- } else {
- Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
- Bind->buffer = (char *)Value->GetTo_Val();
- Bind->buffer_length = Value->GetClen();
- Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
- } // endif Buf_Type
-
- } // end of InitBind
-
-/***********************************************************************/
-/* ReadColumn: */
-/***********************************************************************/
-void MYSQLCOL::ReadColumn(PGLOBAL g)
- {
- char *p, *buf, tim[20];
- int rc;
- PTDBMY tdbp = (PTDBMY)To_Tdb;
-
- /*********************************************************************/
- /* If physical fetching of the line was deferred, do it now. */
- /*********************************************************************/
- if (!tdbp->Fetched)
- if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
- if (rc == RC_EF)
- sprintf(g->Message, MSG(INV_DEF_READ), rc);
-
- longjmp(g->jumper[g->jump_level], 11);
- } else
- tdbp->Fetched = TRUE;
-
- if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
- if (trace > 1)
- htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);
-
- // TODO: have a true way to differenciate temporal values
- if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
- // This is a TIME value
- p = strcat(strcpy(tim, "1970-01-01 "), buf);
- else
- p = buf;
-
- if (Value->SetValue_char(p, strlen(p))) {
- sprintf(g->Message, "Out of range value for column %s at row %d",
- Name, tdbp->RowNumber(g));
- PushWarning(g, tdbp);
- } // endif SetValue_char
-
- } else {
- if (Nullable)
- Value->SetNull(true);
-
- Value->Reset(); // Null value
- } // endif buf
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* WriteColumn: make sure the bind buffer is updated. */
-/***********************************************************************/
-void MYSQLCOL::WriteColumn(PGLOBAL g)
- {
- /*********************************************************************/
- /* Do convert the column value if necessary. */
- /*********************************************************************/
- if (Value != To_Val)
- Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
-
-#if defined(MYSQL_PREPARED_STATEMENTS)
- if (((PTDBMY)To_Tdb)->Prep) {
- if (Buf_Type == TYPE_DATE) {
- Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
- Slen = strlen((char *)Bind->buffer);
- } else if (IsTypeChar(Buf_Type))
- Slen = strlen(Value->GetCharValue());
-
- } // endif Prep
-#endif // MYSQL_PREPARED_STATEMENTS
-
- } // end of WriteColumn
-
-/* ------------------------------------------------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBMYEXC class. */
-/***********************************************************************/
-TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
-{
- Cmdlist = NULL;
- Cmdcol = NULL;
- Shw = false;
- Havew = false;
- Isw = false;
- Warnings = 0;
- Mxr = tdp->Mxr;
- Nerr = 0;
-} // end of TDBMYEXC constructor
-
-TDBMYEXC::TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
-{
- Cmdlist = tdbp->Cmdlist;
- Cmdcol = tdbp->Cmdcol;
- Shw = tdbp->Shw;
- Havew = tdbp->Havew;
- Isw = tdbp->Isw;
- Mxr = tdbp->Mxr;
- Nerr = tdbp->Nerr;
-} // end of TDBMYEXC copy constructor
-
-// Is this really useful ???
-PTDB TDBMYEXC::CopyOne(PTABS t)
- {
- PTDB tp;
- PCOL cp1, cp2;
- PGLOBAL g = t->G;
-
- tp = new(g) TDBMYEXC(g, this);
-
- for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
- cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
-
- NewPointer(t, cp1, cp2);
- } // endfor cp1
-
- return tp;
- } // end of CopyOne
-
-/***********************************************************************/
-/* Allocate MYSQL column description block. */
-/***********************************************************************/
-PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
-
- if (!colp->Flag)
- Cmdcol = colp->GetName();
-
- return colp;
- } // end of MakeCol
-
-/***********************************************************************/
-/* MakeCMD: make the SQL statement to send to MYSQL connection. */
-/***********************************************************************/
-PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
- {
- PCMD xcmd = NULL;
-
- if (To_CondFil) {
- if (Cmdcol) {
- if (!stricmp(Cmdcol, To_CondFil->Body) &&
- (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
- xcmd = To_CondFil->Cmds;
- } else
- strcpy(g->Message, "Invalid command specification filter");
-
- } else
- strcpy(g->Message, "No command column in select list");
-
- } else if (!Srcdef)
- strcpy(g->Message, "No Srcdef default command");
- else
- xcmd = new(g) CMD(g, Srcdef);
-
- return xcmd;
- } // end of MakeCMD
-
-/***********************************************************************/
-/* EXC GetMaxSize: returns the maximum number of rows in the table. */
-/***********************************************************************/
-int TDBMYEXC::GetMaxSize(PGLOBAL g)
- {
- if (MaxSize < 0) {
- MaxSize = 10; // a guess
- } // endif MaxSize
-
- return MaxSize;
- } // end of GetMaxSize
-
-/***********************************************************************/
-/* MySQL Exec Access Method opening routine. */
-/***********************************************************************/
-bool TDBMYEXC::OpenDB(PGLOBAL g)
- {
- if (Use == USE_OPEN) {
- strcpy(g->Message, "Multiple execution is not allowed");
- return true;
- } // endif use
-
- /*********************************************************************/
- /* Open a MySQL connection for this table. */
- /* Note: this may not be the proper way to do. Perhaps it is better */
- /* to test whether a connection is already open for this server */
- /* and if so to allocate just a new result set. But this only for */
- /* servers allowing concurency in getting results ??? */
- /*********************************************************************/
- if (!Myc.Connected())
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
- return true;
-
- Use = USE_OPEN; // Do it now in case we are recursively called
-
- if (Mode != MODE_READ) {
- strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
- return true;
- } // endif Mode
-
- /*********************************************************************/
- /* Get the command to execute. */
- /*********************************************************************/
- if (!(Cmdlist = MakeCMD(g))) {
- Myc.Close();
- return true;
- } // endif Query
-
- return false;
- } // end of OpenDB
-
-/***********************************************************************/
-/* Data Base read routine for MYSQL access method. */
-/***********************************************************************/
-int TDBMYEXC::ReadDB(PGLOBAL g)
- {
- if (Havew) {
- // Process result set from SHOW WARNINGS
- if (Myc.Fetch(g, -1) != RC_OK) {
- Myc.FreeResult();
- Havew = Isw = false;
- } else {
- N++;
- Isw = true;
- return RC_OK;
- } // endif Fetch
-
- } // endif m_Res
-
- if (Cmdlist) {
- // Process query to send
- int rc;
-
- do {
- Query = Cmdlist->Cmd;
-
- switch (rc = Myc.ExecSQLcmd(g, Query, &Warnings)) {
- case RC_NF:
- AftRows = Myc.m_Afrw;
- strcpy(g->Message, "Affected rows");
- break;
- case RC_OK:
- AftRows = Myc.m_Fields;
- strcpy(g->Message, "Result set columns");
- break;
- case RC_FX:
- AftRows = Myc.m_Afrw;
- Nerr++;
- break;
- case RC_INFO:
- Shw = true;
- } // endswitch rc
-
- Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
- } while (rc == RC_INFO);
-
- if (Shw && Warnings)
- Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);
-
- ++N;
- return RC_OK;
- } else
- return RC_EF;
-
- } // end of ReadDB
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for Exec MYSQL access methods. */
-/***********************************************************************/
-int TDBMYEXC::WriteDB(PGLOBAL g)
- {
- strcpy(g->Message, "EXEC MYSQL tables are read only");
- return RC_FX;
- } // end of WriteDB
-
-// ------------------------- MYXCOL functions ---------------------------
-
-/***********************************************************************/
-/* MYXCOL public constructor. */
-/***********************************************************************/
-MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
- : MYSQLCOL(cdp, tdbp, cprec, i, am)
- {
- // Set additional EXEC MYSQL access method information for column.
- Flag = cdp->GetOffset();
- } // end of MYSQLCOL constructor
-
-/***********************************************************************/
-/* MYSQLCOL public constructor. */
-/***********************************************************************/
-MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
- : MYSQLCOL(fld, tdbp, i, am)
- {
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
- } // end of MYSQLCOL constructor
-
-/***********************************************************************/
-/* MYXCOL constructor used for copying columns. */
-/* tdbp is the pointer to the new table descriptor. */
-/***********************************************************************/
-MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
- {
- Flag = col1->Flag;
- } // end of MYXCOL copy constructor
-
-/***********************************************************************/
-/* ReadColumn: */
-/***********************************************************************/
-void MYXCOL::ReadColumn(PGLOBAL g)
- {
- PTDBMYX tdbp = (PTDBMYX)To_Tdb;
-
- if (tdbp->Isw) {
- char *buf = NULL;
-
- if (Flag < 3) {
- buf = tdbp->Myc.GetCharField(Flag);
- Value->SetValue_psz(buf);
- } else
- Value->Reset();
-
- } else
- switch (Flag) {
- case 0: Value->SetValue_psz(tdbp->Query); break;
- case 1: Value->SetValue(tdbp->AftRows); break;
- case 2: Value->SetValue_psz(g->Message); break;
- case 3: Value->SetValue(tdbp->Warnings); break;
- default: Value->SetValue_psz("Invalid Flag"); break;
- } // endswitch Flag
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* WriteColumn: should never be called. */
-/***********************************************************************/
-void MYXCOL::WriteColumn(PGLOBAL g)
- {
- assert(false);
- } // end of WriteColumn
-
-/* ---------------------------TDBMCL class --------------------------- */
-
-/***********************************************************************/
-/* TDBMCL class constructor. */
-/***********************************************************************/
-TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
- {
- Host = tdp->Hostname;
- Db = tdp->Database;
- Tab = tdp->Tabname;
- User = tdp->Username;
- Pwd = tdp->Password;
- Port = tdp->Portnumber;
- } // end of TDBMCL constructor
-
-/***********************************************************************/
-/* GetResult: Get the list the MYSQL table columns. */
-/***********************************************************************/
-PQRYRES TDBMCL::GetResult(PGLOBAL g)
- {
- return MyColumns(g, Host, Db, User, Pwd, Tab, NULL, Port, false);
- } // end of GetResult
+/************* TabMySQL C++ Program Source Code File (.CPP) *************/
+/* PROGRAM NAME: TABMYSQL */
+/* ------------- */
+/* Version 1.7 */
+/* */
+/* AUTHOR: */
+/* ------- */
+/* Olivier BERTRAND 2007-2013 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* Implements a table type that are MySQL tables. */
+/* It can optionally use the embedded MySQL library. */
+/* */
+/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
+/* -------------------------------------- */
+/* */
+/* REQUIRED FILES: */
+/* --------------- */
+/* TABMYSQL.CPP - Source code */
+/* PLGDBSEM.H - DB application declaration file */
+/* TABMYSQL.H - TABODBC classes declaration file */
+/* GLOBAL.H - Global declaration file */
+/* */
+/* REQUIRED LIBRARIES: */
+/* ------------------- */
+/* Large model C library */
+/* */
+/* REQUIRED PROGRAMS: */
+/* ------------------ */
+/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
+/* */
+/************************************************************************/
+#define MYSQL_SERVER 1
+#include "my_global.h"
+#include "sql_class.h"
+#include "sql_servers.h"
+#if defined(WIN32)
+//#include <windows.h>
+#else // !WIN32
+//#include <fnmatch.h>
+//#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "osutil.h"
+//#include <io.h>
+//#include <fcntl.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include application header files: */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xtable.h"
+#include "tabcol.h"
+#include "colblk.h"
+#include "mycat.h"
+#include "reldef.h"
+#include "tabmysql.h"
+#include "valblk.h"
+#include "tabutil.h"
+
+#if defined(_CONSOLE)
+void PrintResult(PGLOBAL, PSEM, PQRYRES);
+#endif // _CONSOLE
+
+extern "C" int trace;
+
+/* -------------- Implementation of the MYSQLDEF class --------------- */
+
+/***********************************************************************/
+/* Constructor. */
+/***********************************************************************/
+MYSQLDEF::MYSQLDEF(void)
+ {
+ Pseudo = 2; // SERVID is Ok but not ROWID
+ Hostname = NULL;
+ Database = NULL;
+ Tabname = NULL;
+ Srcdef = NULL;
+ Username = NULL;
+ Password = NULL;
+ Portnumber = 0;
+ Isview = FALSE;
+ Bind = FALSE;
+ Delayed = FALSE;
+ Xsrc = FALSE;
+ } // end of MYSQLDEF constructor
+
+/***********************************************************************/
+/* Get connection info from the declared server. */
+/***********************************************************************/
+bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
+{
+ THD *thd= current_thd;
+ MEM_ROOT *mem= thd->mem_root;
+ FOREIGN_SERVER *server, server_buffer;
+ DBUG_ENTER("GetServerInfo");
+ DBUG_PRINT("info", ("server_name %s", server_name));
+
+ if (!server_name || !strlen(server_name)) {
+ DBUG_PRINT("info", ("server_name not defined!"));
+ strcpy(g->Message, "server_name not defined!");
+ DBUG_RETURN(true);
+ } // endif server_name
+
+ // get_server_by_name() clones the server if exists and allocates
+ // copies of strings in the supplied mem_root
+ if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
+ DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
+ /* need to come up with error handling */
+ strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
+ DBUG_RETURN(true);
+ } // endif server
+
+ DBUG_PRINT("info", ("get_server_by_name returned server at %lx",
+ (long unsigned int) server));
+
+ // TODO: We need to examine which of these can really be NULL
+ Hostname = PlugDup(g, server->host);
+ Database = PlugDup(g, server->db);
+ Username = PlugDup(g, server->username);
+ Password = PlugDup(g, server->password);
+ Portnumber = (server->port) ? server->port : GetDefaultPort();
+
+ DBUG_RETURN(false);
+} // end of GetServerInfo
+
+/***********************************************************************/
+/* Parse connection string */
+/* */
+/* SYNOPSIS */
+/* ParseURL() */
+/* url The connection string to parse */
+/* */
+/* DESCRIPTION */
+/* Populates the table with information about the connection */
+/* to the foreign database that will serve as the data source. */
+/* This string must be specified (currently) in the "CONNECTION" */
+/* field, listed in the CREATE TABLE statement. */
+/* */
+/* This string MUST be in the format of any of these: */
+/* */
+/* CONNECTION="scheme://user:pwd@host:port/database/table" */
+/* CONNECTION="scheme://user@host/database/table" */
+/* CONNECTION="scheme://user@host:port/database/table" */
+/* CONNECTION="scheme://user:pwd@host/database/table" */
+/* */
+/* _OR_ */
+/* */
+/* CONNECTION="connection name" (NIY) */
+/* */
+/* An Example: */
+/* */
+/* CREATE TABLE t1 (id int(32)) */
+/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
+/* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */
+/* */
+/* CREATE TABLE t2 ( */
+/* id int(4) NOT NULL auto_increment, */
+/* name varchar(32) NOT NULL, */
+/* PRIMARY KEY(id) */
+/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
+/* CONNECTION="my_conn"; (NIY) */
+/* */
+/* 'password' and 'port' are both optional. */
+/* */
+/* RETURN VALUE */
+/* false success */
+/* true error */
+/* */
+/***********************************************************************/
+bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
+ {
+ if ((!strstr(url, "://") && (!strchr(url, '@')))) {
+ // No :// or @ in connection string. Must be a straight
+ // connection name of either "server" or "server/table"
+ // ok, so we do a little parsing, but not completely!
+ if ((Tabname= strchr(url, '/'))) {
+ // If there is a single '/' in the connection string,
+ // this means the user is specifying a table name
+ *Tabname++= '\0';
+
+ // there better not be any more '/'s !
+ if (strchr(Tabname, '/'))
+ return true;
+
+ } else
+ // Otherwise, straight server name,
+ // use tablename of federatedx table as remote table name
+ Tabname= Name;
+
+ if (trace)
+ htrc("server: %s Tabname: %s", url, Tabname);
+
+ Server = url;
+ return GetServerInfo(g, url);
+ } else {
+ // URL, parse it
+ char *sport, *scheme = url;
+
+ if (!(Username = strstr(url, "://"))) {
+ strcpy(g->Message, "Connection is not an URL");
+ return true;
+ } // endif User
+
+ scheme[Username - scheme] = 0;
+
+ if (stricmp(scheme, "mysql")) {
+ strcpy(g->Message, "scheme must be mysql");
+ return true;
+ } // endif scheme
+
+ Username += 3;
+
+ if (!(Hostname = strchr(Username, '@'))) {
+ strcpy(g->Message, "No host specified in URL");
+ return true;
+ } else {
+ *Hostname++ = 0; // End Username
+ Server = Hostname;
+ } // endif Hostname
+
+ if ((Password = strchr(Username, ':'))) {
+ *Password++ = 0; // End username
+
+ // Make sure there isn't an extra / or @
+ if ((strchr(Password, '/') || strchr(Hostname, '@'))) {
+ strcpy(g->Message, "Syntax error in URL");
+ return true;
+ } // endif
+
+ // Found that if the string is:
+ // user:@hostname:port/db/table
+ // Then password is a null string, so set to NULL
+ if ((Password[0] == 0))
+ Password = NULL;
+
+ } // endif password
+
+ // Make sure there isn't an extra / or @ */
+ if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
+ strcpy(g->Message, "Syntax error in URL");
+ return true;
+ } // endif
+
+ if ((Database = strchr(Hostname, '/'))) {
+ *Database++ = 0;
+
+ if ((Tabname = strchr(Database, '/'))) {
+ *Tabname++ = 0;
+
+ // Make sure there's not an extra /
+ if ((strchr(Tabname, '/'))) {
+ strcpy(g->Message, "Syntax error in URL");
+ return true;
+ } // endif /
+
+ } // endif Tabname
+
+ } // endif database
+
+ if ((sport = strchr(Hostname, ':')))
+ *sport++ = 0;
+
+ // 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;
+
+ if (Username[0] == 0)
+ Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL;
+
+ if (Hostname[0] == 0)
+ Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL;
+
+ if (!Database || !*Database)
+ Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL;
+
+ if (!Tabname || !*Tabname)
+ Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL;
+
+ if (!Password)
+ Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL;
+ } // endif URL
+
+#if 0
+ if (!share->port)
+ if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
+ share->socket= (char *) MYSQL_UNIX_ADDR;
+ else
+ share->port= MYSQL_PORT;
+#endif // 0
+
+ return false;
+ } // end of ParseURL
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from XCV file. */
+/***********************************************************************/
+bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+ {
+ char *url;
+
+ Desc = "MySQL Table";
+
+ if (stricmp(am, "MYPRX")) {
+ // Normal case of specific MYSQL table
+ url = Cat->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());
+ Server = Hostname;
+ } else if (ParseURL(g, url))
+ return true;
+
+ Bind = !!Cat->GetIntCatInfo("Bind", 0);
+ Delayed = !!Cat->GetIntCatInfo("Delayed", 0);
+ } else {
+ // MYSQL access from a PROXY table
+ Database = Cat->GetStringCatInfo(g, "Database", "*");
+ Isview = Cat->GetBoolCatInfo("View", FALSE);
+
+ // We must get other connection parms from the calling table
+ Remove_tshp(Cat);
+ url = Cat->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());
+ Server = Hostname;
+ } else {
+ char *locdb = Database;
+
+ if (ParseURL(g, url))
+ return true;
+
+ Database = locdb;
+ } // endif url
+
+ Tabname = Name;
+ } // endif am
+
+ if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
+ Isview = true;
+
+ // Used for Update and Delete
+ Qrystr = Cat->GetStringCatInfo(g, "Query_String", "?");
+ Quoted = Cat->GetIntCatInfo("Quoted", 0);
+
+ // Specific for command executing tables
+ Xsrc = Cat->GetBoolCatInfo("Execsrc", false);
+ Mxr = Cat->GetIntCatInfo("Maxerr", 0);
+ return FALSE;
+ } // end of DefineAM
+
+/***********************************************************************/
+/* GetTable: makes a new TDB of the proper type. */
+/***********************************************************************/
+PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
+ {
+ if (Xsrc)
+ return new(g) TDBMYEXC(this);
+ else if (Catfunc == FNC_COL)
+ return new(g) TDBMCL(this);
+ else
+ return new(g) TDBMYSQL(this);
+
+ } // end of GetTable
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBMYSQL class. */
+/***********************************************************************/
+TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
+ {
+ if (tdp) {
+ Host = tdp->Hostname;
+ Database = tdp->Database;
+ Tabname = tdp->Tabname;
+ Srcdef = tdp->Srcdef;
+ User = tdp->Username;
+ Pwd = tdp->Password;
+ Server = tdp->Server;
+ Qrystr = tdp->Qrystr;
+ Quoted = max(0, tdp->Quoted);
+ Port = tdp->Portnumber;
+ Isview = tdp->Isview;
+ Prep = tdp->Bind;
+ Delayed = tdp->Delayed;
+ } else {
+ Host = NULL;
+ Database = NULL;
+ Tabname = NULL;
+ Srcdef = NULL;
+ User = NULL;
+ Pwd = NULL;
+ Server = NULL;
+ Qrystr = NULL;
+ Quoted = 0;
+ Port = 0;
+ Isview = FALSE;
+ Prep = FALSE;
+ Delayed = FALSE;
+ } // endif tdp
+
+ Bind = NULL;
+ Query = NULL;
+ Qbuf = NULL;
+ Fetched = FALSE;
+ m_Rc = RC_FX;
+ AftRows = 0;
+ N = -1;
+ Nparm = 0;
+ } // end of TDBMYSQL constructor
+
+TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp)
+ {
+ Host = tdbp->Host;
+ Database = tdbp->Database;
+ Tabname = tdbp->Tabname;
+ Srcdef = tdbp->Srcdef;
+ User = tdbp->User;
+ Pwd = tdbp->Pwd;
+ Qrystr = tdbp->Qrystr;
+ Quoted = tdbp->Quoted;
+ Port = tdbp->Port;
+ Isview = tdbp->Isview;
+ Prep = tdbp->Prep;
+ Delayed = tdbp->Delayed;
+ Bind = NULL;
+ Query = tdbp->Query;
+ Qbuf = NULL;
+ Fetched = tdbp->Fetched;
+ m_Rc = tdbp->m_Rc;
+ AftRows = tdbp->AftRows;
+ N = tdbp->N;
+ Nparm = tdbp->Nparm;
+ } // end of TDBMYSQL copy constructor
+
+// Is this really useful ???
+PTDB TDBMYSQL::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBMYSQL(g, this);
+
+ for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
+ cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
+
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate MYSQL column description block. */
+/***********************************************************************/
+PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ return new(g) MYSQLCOL(cdp, this, cprec, n);
+ } // end of MakeCol
+
+/***********************************************************************/
+/* MakeSelect: make the Select statement use with MySQL connection. */
+/* Note: when implementing EOM filtering, column only used in local */
+/* filter should be removed from column list. */
+/***********************************************************************/
+bool TDBMYSQL::MakeSelect(PGLOBAL g)
+ {
+ char *tk = "`";
+ int rank = 0;
+ bool b = FALSE;
+ PCOL colp;
+//PDBUSER dup = PlgGetUser(g);
+
+ if (Query)
+ return FALSE; // already done
+
+ if (Srcdef) {
+ Query = Srcdef;
+ return false;
+ } // endif Srcdef
+
+ //Find the address of the suballocated query
+ Query = (char*)PlugSubAlloc(g, NULL, 0);
+ strcpy(Query, "SELECT ");
+
+ if (Columns) {
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (!colp->IsSpecial()) {
+// if (colp->IsSpecial()) {
+// strcpy(g->Message, MSG(NO_SPEC_COL));
+// return TRUE;
+// } else {
+ if (b)
+ strcat(Query, ", ");
+ else
+ b = TRUE;
+
+ strcat(strcat(strcat(Query, tk), colp->GetName()), tk);
+ ((PMYCOL)colp)->Rank = rank++;
+ } // endif colp
+
+ } else {
+ // ncol == 0 can occur for views or queries such as
+ // Query count(*) from... for which we will count the rows from
+ // Query '*' from...
+ // (the use of a char constant minimize the result storage)
+ strcat(Query, (Isview) ? "*" : "'*'");
+ } // endif ncol
+
+ strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);
+
+ if (To_CondFil)
+ strcat(strcat(Query, " WHERE "), To_CondFil->Body);
+
+ if (trace)
+ htrc("Query=%s\n", Query);
+
+ // Now we know how much to suballocate
+ PlugSubAlloc(g, NULL, strlen(Query) + 1);
+ return FALSE;
+ } // end of MakeSelect
+
+/***********************************************************************/
+/* MakeInsert: make the Insert statement used with MySQL connection. */
+/***********************************************************************/
+bool TDBMYSQL::MakeInsert(PGLOBAL g)
+ {
+ char *colist, *valist = NULL;
+ char *tk = "`";
+ int len = 0, qlen = 0;
+ bool b = FALSE;
+ PCOL colp;
+
+ if (Query)
+ return FALSE; // already done
+
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (!colp->IsSpecial()) {
+// if (colp->IsSpecial()) {
+// strcpy(g->Message, MSG(NO_SPEC_COL));
+// return TRUE;
+// } else {
+ len += (strlen(colp->GetName()) + 4);
+ ((PMYCOL)colp)->Rank = Nparm++;
+ } // endif colp
+
+ colist = (char*)PlugSubAlloc(g, NULL, len);
+ *colist = '\0';
+
+ if (Prep) {
+#if defined(MYSQL_PREPARED_STATEMENTS)
+ valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
+ *valist = '\0';
+#else // !MYSQL_PREPARED_STATEMENTS
+ strcpy(g->Message, "Prepared statements not used (not supported)");
+ PushWarning(g, this);
+ Prep = FALSE;
+#endif // !MYSQL_PREPARED_STATEMENTS
+ } // endif Prep
+
+ for (colp = Columns; colp; colp = colp->GetNext()) {
+ if (b) {
+ strcat(colist, ", ");
+ if (Prep) strcat(valist, ",");
+ } else
+ b = TRUE;
+
+ strcat(strcat(strcat(colist, tk), colp->GetName()), tk);
+
+ // Parameter marker
+ if (!Prep) {
+ if (colp->GetResultType() == TYPE_DATE)
+ qlen += 20;
+ else
+ qlen += colp->GetLength();
+
+ } // endif Prep
+
+ if (Prep)
+ strcat(valist, "?");
+
+ } // endfor colp
+
+ // Below 40 is enough to contain the fixed part of the query
+ len = (strlen(Tabname) + strlen(colist)
+ + ((Prep) ? strlen(valist) : 0) + 40);
+ Query = (char*)PlugSubAlloc(g, NULL, len);
+
+ if (Delayed)
+ strcpy(Query, "INSERT DELAYED INTO ");
+ else
+ strcpy(Query, "INSERT INTO ");
+
+ strcat(strcat(strcat(Query, tk), Tabname), tk);
+ strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
+
+ if (Prep)
+ strcat(strcat(Query, valist), ")");
+ else {
+ qlen += (strlen(Query) + Nparm);
+ Qbuf = (char *)PlugSubAlloc(g, NULL, qlen);
+ } // endelse Prep
+
+ return FALSE;
+ } // end of MakeInsert
+
+/***********************************************************************/
+/* MakeCommand: make the Update or Delete statement to send to the */
+/* MySQL server. Limited to remote values and filtering. */
+/***********************************************************************/
+int TDBMYSQL::MakeCommand(PGLOBAL g)
+ {
+ Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+
+ if (Quoted > 0 || stricmp(Name, Tabname)) {
+ char *p, *qrystr, name[68];
+ bool qtd = Quoted > 0;
+
+
+ // Make a lower case copy of the originale query
+ qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
+ strlwr(strcpy(qrystr, Qrystr));
+
+ // Check whether the table name is equal to a keyword
+ // If so, it must be quoted in the original query
+ strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));
+
+ if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
+ strlwr(strcpy(name, Name)); // Not a keyword
+
+ if ((p = strstr(qrystr, name))) {
+ memcpy(Query, Qrystr, p - qrystr);
+ Query[p - qrystr] = 0;
+
+ if (qtd && *(p-1) == ' ')
+ strcat(strcat(strcat(Query, "`"), Tabname), "`");
+ else
+ strcat(Query, Tabname);
+
+ strcat(Query, Qrystr + (p - qrystr) + strlen(name));
+ } else {
+ sprintf(g->Message, "Cannot use this %s command",
+ (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
+ return RC_FX;
+ } // endif p
+
+ } else
+ strcpy(Query, Qrystr);
+
+ return RC_OK;
+ } // end of MakeCommand
+
+#if 0
+/***********************************************************************/
+/* MakeUpdate: make the Update statement use with MySQL connection. */
+/* Limited to remote values and filtering. */
+/***********************************************************************/
+int TDBMYSQL::MakeUpdate(PGLOBAL g)
+ {
+ char *qc, cmd[8], tab[96], end[1024];
+
+ Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+ memset(end, 0, sizeof(end));
+
+ if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
+ sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
+ qc = "`";
+ else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
+ && !stricmp(tab, Name))
+ qc = (Quoted) ? "`" : "";
+ else {
+ strcpy(g->Message, "Cannot use this UPDATE command");
+ return RC_FX;
+ } // endif sscanf
+
+ assert(!stricmp(cmd, "update"));
+ strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc);
+ strcat(Query, end);
+ return RC_OK;
+ } // end of MakeUpdate
+
+/***********************************************************************/
+/* MakeDelete: make the Delete statement used with MySQL connection. */
+/* Limited to remote filtering. */
+/***********************************************************************/
+int TDBMYSQL::MakeDelete(PGLOBAL g)
+ {
+ char *qc, cmd[8], from[8], tab[96], end[512];
+
+ Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+ memset(end, 0, sizeof(end));
+
+ if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
+ sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
+ qc = "`";
+ else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
+ qc = (Quoted) ? "`" : "";
+ else {
+ strcpy(g->Message, "Cannot use this DELETE command");
+ return RC_FX;
+ } // endif sscanf
+
+ assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
+ strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc);
+
+ if (*end)
+ strcat(Query, end);
+
+ return RC_OK;
+ } // end of MakeDelete
+#endif // 0
+
+/***********************************************************************/
+/* XCV GetMaxSize: returns the maximum number of rows in the table. */
+/***********************************************************************/
+int TDBMYSQL::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0) {
+#if 0
+ if (MakeSelect(g))
+ return -2;
+
+ if (!Myc.Connected()) {
+ if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ return -1;
+
+ } // endif connected
+
+ if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) {
+ Myc.Close();
+ return -3;
+ } // endif MaxSize
+
+ // FIXME: Columns should be known when Info calls GetMaxSize
+ if (!Columns)
+ Query = NULL; // Must be remade when columns are known
+#endif // 0
+
+ // Return 0 in mode DELETE in case of delete all.
+ MaxSize = (Mode == MODE_DELETE) ? 0 : 10; // To make MySQL happy
+ } // endif MaxSize
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* This a fake routine as ROWID does not exist in MySQL. */
+/***********************************************************************/
+int TDBMYSQL::RowNumber(PGLOBAL g, bool b)
+ {
+ return N;
+ } // end of RowNumber
+
+/***********************************************************************/
+/* Return 0 in mode UPDATE to tell that the update is done. */
+/***********************************************************************/
+int TDBMYSQL::GetProgMax(PGLOBAL g)
+ {
+ return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
+ } // end of GetProgMax
+
+/***********************************************************************/
+/* MySQL Bind Parameter function. */
+/***********************************************************************/
+int TDBMYSQL::BindColumns(PGLOBAL g)
+ {
+#if defined(MYSQL_PREPARED_STATEMENTS)
+ if (Prep) {
+ Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
+
+ for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
+ colp->InitBind(g);
+
+ return Myc.BindParams(g, Bind);
+ } // endif prep
+#endif // MYSQL_PREPARED_STATEMENTS
+
+ return RC_OK;
+ } // end of BindColumns
+
+/***********************************************************************/
+/* MySQL Access Method opening routine. */
+/***********************************************************************/
+bool TDBMYSQL::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open, just replace it at its beginning. */
+ /*******************************************************************/
+ Myc.Rewind();
+ return false;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open a MySQL connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this server */
+ /* and if so to allocate just a new result set. But this only for */
+ /* servers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Myc.Connected()) {
+ if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ return true;
+
+ } // endif Connected
+
+ /*********************************************************************/
+ /* Take care of DATE columns. */
+ /*********************************************************************/
+ for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
+ if (colp->Buf_Type == TYPE_DATE)
+ // Format must match DATETIME MySQL type
+ ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
+
+ /*********************************************************************/
+ /* Allocate whatever is used for getting results. */
+ /*********************************************************************/
+ if (Mode == MODE_READ) {
+ if (!MakeSelect(g))
+ m_Rc = Myc.ExecSQL(g, Query);
+
+#if 0
+ if (!Myc.m_Res || !Myc.m_Fields) {
+ sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
+ Myc.Close();
+ return true;
+ } // endif m_Res
+#endif // 0
+
+ if (!m_Rc && Srcdef)
+ if (SetColumnRanks(g))
+ return true;
+
+ } else if (Mode == MODE_INSERT) {
+ if (Srcdef) {
+ strcpy(g->Message, "No insert into anonym views");
+ return true;
+ } // endif Srcdef
+
+ if (!MakeInsert(g)) {
+#if defined(MYSQL_PREPARED_STATEMENTS)
+ int n = (Prep) ? Myc.PrepareSQL(g, Query) : Nparm;
+
+ if (Nparm != n) {
+ if (n >= 0) // Other errors return negative values
+ strcpy(g->Message, MSG(BAD_PARM_COUNT));
+
+ } else
+#endif // MYSQL_PREPARED_STATEMENTS
+ m_Rc = BindColumns(g);
+
+ } // endif MakeInsert
+
+ if (m_Rc != RC_FX) {
+ char cmd[64];
+ int w;
+
+ sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
+ m_Rc = Myc.ExecSQL(g, cmd, &w);
+ } // endif m_Rc
+
+ } else
+// m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
+ m_Rc = MakeCommand(g);
+
+ if (m_Rc == RC_FX) {
+ Myc.Close();
+ return true;
+ } // endif rc
+
+ Use = USE_OPEN;
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* Set the rank of columns in the result set. */
+/***********************************************************************/
+bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
+ {
+ for (PCOL colp = Columns; colp; colp = colp->GetNext())
+ if (((PMYCOL)colp)->FindRank(g))
+ return TRUE;
+
+ return FALSE;
+ } // end of SetColumnRanks
+
+/***********************************************************************/
+/* Called by Parent table to make the columns of a View. */
+/***********************************************************************/
+PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
+ {
+ int n;
+ MYSQL_FIELD *fld;
+ PCOL cp, colp = NULL;
+
+ for (n = 0; n < Myc.m_Fields; n++) {
+ fld = &Myc.m_Res->fields[n];
+
+ if (!stricmp(name, fld->name)) {
+ colp = new(g) MYSQLCOL(fld, this, n);
+
+ if (colp->InitValue(g))
+ return NULL;
+
+ if (!Columns)
+ Columns = colp;
+ else for (cp = Columns; cp; cp = cp->GetNext())
+ if (!cp->GetNext()) {
+ cp->SetNext(colp);
+ break;
+ } // endif Next
+
+ break;
+ } // endif name
+
+ } // endfor n
+
+ if (!colp)
+ sprintf(g->Message, "Column %s is not in view", name);
+
+ return colp;
+ } // end of MakeFieldColumn
+
+/***********************************************************************/
+/* Called by Pivot tables to find default column names in a View */
+/* as the name of last field not equal to the passed name. */
+/***********************************************************************/
+char *TDBMYSQL::FindFieldColumn(char *name)
+ {
+ int n;
+ MYSQL_FIELD *fld;
+ char *cp = NULL;
+
+ for (n = Myc.m_Fields - 1; n >= 0; n--) {
+ fld = &Myc.m_Res->fields[n];
+
+ if (!name || stricmp(name, fld->name)) {
+ cp = fld->name;
+ break;
+ } // endif name
+
+ } // endfor n
+
+ return cp;
+ } // end of FindFieldColumn
+
+/***********************************************************************/
+/* Send an UPDATE or DELETE command to the remote server. */
+/***********************************************************************/
+int TDBMYSQL::SendCommand(PGLOBAL g)
+ {
+ int w;
+
+ if (Myc.ExecSQLcmd(g, Query, &w) == RC_NF) {
+ AftRows = Myc.m_Afrw;
+ sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows);
+ PushWarning(g, this, 0); // 0 means a Note
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
+ // We got warnings from the remote server
+ while (Myc.Fetch(g, -1) == RC_OK) {
+ sprintf(g->Message, "%s: (%s) %s", Tabname,
+ Myc.GetCharField(1), Myc.GetCharField(2));
+ PushWarning(g, this);
+ } // endwhile Fetch
+
+ Myc.FreeResult();
+ } // endif w
+
+ return RC_EF; // Nothing else to do
+ } else
+ return RC_FX; // Error
+
+ } // end of SendCommand
+
+/***********************************************************************/
+/* Data Base read routine for MYSQL access method. */
+/***********************************************************************/
+int TDBMYSQL::ReadDB(PGLOBAL g)
+ {
+ int rc;
+
+ if (trace > 1)
+ htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
+ GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
+
+ if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
+ return SendCommand(g);
+
+ /*********************************************************************/
+ /* Now start the reading process. */
+ /* Here is the place to fetch the line. */
+ /*********************************************************************/
+ N++;
+ Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
+
+ if (trace > 1)
+ htrc(" Read: rc=%d\n", rc);
+
+ return rc;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for MYSQL access methods. */
+/***********************************************************************/
+int TDBMYSQL::WriteDB(PGLOBAL g)
+ {
+#if defined(MYSQL_PREPARED_STATEMENTS)
+ if (Prep)
+ return Myc.ExecStmt(g);
+#endif // MYSQL_PREPARED_STATEMENTS
+
+ // Statement was not prepared, we must construct and execute
+ // an insert query for each line to insert
+ int rc;
+ char buf[32];
+
+ strcpy(Qbuf, Query);
+
+ // Make the Insert command value list
+ for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
+ if (!colp->GetValue()->IsNull()) {
+ if (colp->GetResultType() == TYPE_STRING ||
+ colp->GetResultType() == TYPE_DATE)
+ strcat(Qbuf, "'");
+
+ strcat(Qbuf, colp->GetValue()->GetCharString(buf));
+
+ if (colp->GetResultType() == TYPE_STRING ||
+ colp->GetResultType() == TYPE_DATE)
+ strcat(Qbuf, "'");
+
+ } else
+ strcat(Qbuf, "NULL");
+
+ strcat(Qbuf, (colp->GetNext()) ? "," : ")");
+ } // endfor colp
+
+ Myc.m_Rows = -1; // To execute the query
+ rc = Myc.ExecSQL(g, Qbuf);
+ return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok
+ } // end of WriteDB
+
+/***********************************************************************/
+/* Data Base delete all routine for MYSQL access methods. */
+/***********************************************************************/
+int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
+ {
+ if (irc == RC_FX)
+ // Send the DELETE (all) command to the remote table
+ return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
+ else
+ return RC_OK; // Ignore
+
+ } // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base close routine for MySQL access method. */
+/***********************************************************************/
+void TDBMYSQL::CloseDB(PGLOBAL g)
+ {
+ if (Myc.Connected()) {
+ if (Mode == MODE_INSERT) {
+ char cmd[64];
+ int w;
+ PDBUSER dup = PlgGetUser(g);
+
+ dup->Step = "Enabling indexes";
+ sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
+ Myc.m_Rows = -1; // To execute the query
+ m_Rc = Myc.ExecSQL(g, cmd, &w);
+ } // endif m_Rc
+
+ Myc.Close();
+ } // endif Myc
+
+ if (trace)
+ htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
+
+ } // end of CloseDB
+
+// ------------------------ MYSQLCOL functions --------------------------
+
+/***********************************************************************/
+/* MYSQLCOL public constructor. */
+/***********************************************************************/
+MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : COLBLK(cdp, tdbp, i)
+ {
+ if (cprec) {
+ Next = cprec->GetNext();
+ cprec->SetNext(this);
+ } else {
+ Next = tdbp->GetColumns();
+ tdbp->SetColumns(this);
+ } // endif cprec
+
+ // Set additional MySQL access method information for column.
+ Precision = Long = cdp->GetLong();
+ Bind = NULL;
+ To_Val = NULL;
+ Slen = 0;
+ Rank = -1; // Not known yet
+
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYSQLCOL public constructor. */
+/***********************************************************************/
+MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
+ : COLBLK(NULL, tdbp, i)
+ {
+ Name = fld->name;
+ Opt = 0;
+ Precision = Long = fld->length;
+ Buf_Type = MYSQLtoPLG(fld->type);
+ strcpy(Format.Type, GetFormatType(Buf_Type));
+ Format.Length = Long;
+ Format.Prec = fld->decimals;
+ ColUse = U_P;
+ Nullable = !IS_NOT_NULL(fld->flags);
+
+ // Set additional MySQL access method information for column.
+ Bind = NULL;
+ To_Val = NULL;
+ Slen = 0;
+ Rank = i;
+
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYSQLCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+ {
+ Long = col1->Long;
+ Bind = NULL;
+ To_Val = NULL;
+ Slen = col1->Slen;
+ Rank = col1->Rank;
+ } // end of MYSQLCOL copy constructor
+
+/***********************************************************************/
+/* FindRank: Find the rank of this column in the result set. */
+/***********************************************************************/
+bool MYSQLCOL::FindRank(PGLOBAL g)
+{
+ int n;
+ MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;
+
+ for (n = 0; n < myc.m_Fields; n++)
+ if (!stricmp(Name, myc.m_Res->fields[n].name)) {
+ Rank = n;
+ return false;
+ } // endif Name
+
+ sprintf(g->Message, "Column %s not in result set", Name);
+ return true;
+} // end of FindRank
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool MYSQLCOL::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
+
+ // 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
+
+/***********************************************************************/
+/* InitBind: Initialize the bind structure according to type. */
+/***********************************************************************/
+void MYSQLCOL::InitBind(PGLOBAL g)
+ {
+ PTDBMY tdbp = (PTDBMY)To_Tdb;
+
+ assert(tdbp->Bind && Rank < tdbp->Nparm);
+
+ Bind = &tdbp->Bind[Rank];
+ memset(Bind, 0, sizeof(MYSQL_BIND));
+
+ if (Buf_Type == TYPE_DATE) {
+ Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
+ Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
+ Bind->buffer_length = 20;
+ Bind->length = &Slen;
+ } else {
+ Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
+ Bind->buffer = (char *)Value->GetTo_Val();
+ Bind->buffer_length = Value->GetClen();
+ Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
+ } // endif Buf_Type
+
+ } // end of InitBind
+
+/***********************************************************************/
+/* ReadColumn: */
+/***********************************************************************/
+void MYSQLCOL::ReadColumn(PGLOBAL g)
+ {
+ char *p, *buf, tim[20];
+ int rc;
+ PTDBMY tdbp = (PTDBMY)To_Tdb;
+
+ /*********************************************************************/
+ /* If physical fetching of the line was deferred, do it now. */
+ /*********************************************************************/
+ if (!tdbp->Fetched)
+ if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
+ if (rc == RC_EF)
+ sprintf(g->Message, MSG(INV_DEF_READ), rc);
+
+ longjmp(g->jumper[g->jump_level], 11);
+ } else
+ tdbp->Fetched = TRUE;
+
+ if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
+ if (trace > 1)
+ htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);
+
+ // TODO: have a true way to differenciate temporal values
+ if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
+ // This is a TIME value
+ p = strcat(strcpy(tim, "1970-01-01 "), buf);
+ else
+ p = buf;
+
+ if (Value->SetValue_char(p, strlen(p))) {
+ sprintf(g->Message, "Out of range value for column %s at row %d",
+ Name, tdbp->RowNumber(g));
+ PushWarning(g, tdbp);
+ } // endif SetValue_char
+
+ } else {
+ if (Nullable)
+ Value->SetNull(true);
+
+ Value->Reset(); // Null value
+ } // endif buf
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: make sure the bind buffer is updated. */
+/***********************************************************************/
+void MYSQLCOL::WriteColumn(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Do convert the column value if necessary. */
+ /*********************************************************************/
+ if (Value != To_Val)
+ Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
+
+#if defined(MYSQL_PREPARED_STATEMENTS)
+ if (((PTDBMY)To_Tdb)->Prep) {
+ if (Buf_Type == TYPE_DATE) {
+ Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
+ Slen = strlen((char *)Bind->buffer);
+ } else if (IsTypeChar(Buf_Type))
+ Slen = strlen(Value->GetCharValue());
+
+ } // endif Prep
+#endif // MYSQL_PREPARED_STATEMENTS
+
+ } // end of WriteColumn
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBMYEXC class. */
+/***********************************************************************/
+TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
+{
+ Cmdlist = NULL;
+ Cmdcol = NULL;
+ Shw = false;
+ Havew = false;
+ Isw = false;
+ Warnings = 0;
+ Mxr = tdp->Mxr;
+ Nerr = 0;
+} // end of TDBMYEXC constructor
+
+TDBMYEXC::TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
+{
+ Cmdlist = tdbp->Cmdlist;
+ Cmdcol = tdbp->Cmdcol;
+ Shw = tdbp->Shw;
+ Havew = tdbp->Havew;
+ Isw = tdbp->Isw;
+ Mxr = tdbp->Mxr;
+ Nerr = tdbp->Nerr;
+} // end of TDBMYEXC copy constructor
+
+// Is this really useful ???
+PTDB TDBMYEXC::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBMYEXC(g, this);
+
+ for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
+ cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
+
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate MYSQL column description block. */
+/***********************************************************************/
+PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
+
+ if (!colp->Flag)
+ Cmdcol = colp->GetName();
+
+ return colp;
+ } // end of MakeCol
+
+/***********************************************************************/
+/* MakeCMD: make the SQL statement to send to MYSQL connection. */
+/***********************************************************************/
+PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
+ {
+ PCMD xcmd = NULL;
+
+ if (To_CondFil) {
+ if (Cmdcol) {
+ if (!stricmp(Cmdcol, To_CondFil->Body) &&
+ (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
+ xcmd = To_CondFil->Cmds;
+ } else
+ strcpy(g->Message, "Invalid command specification filter");
+
+ } else
+ strcpy(g->Message, "No command column in select list");
+
+ } else if (!Srcdef)
+ strcpy(g->Message, "No Srcdef default command");
+ else
+ xcmd = new(g) CMD(g, Srcdef);
+
+ return xcmd;
+ } // end of MakeCMD
+
+/***********************************************************************/
+/* EXC GetMaxSize: returns the maximum number of rows in the table. */
+/***********************************************************************/
+int TDBMYEXC::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0) {
+ MaxSize = 10; // a guess
+ } // endif MaxSize
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* MySQL Exec Access Method opening routine. */
+/***********************************************************************/
+bool TDBMYEXC::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ strcpy(g->Message, "Multiple execution is not allowed");
+ return true;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open a MySQL connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this server */
+ /* and if so to allocate just a new result set. But this only for */
+ /* servers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Myc.Connected())
+ if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ return true;
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ if (Mode != MODE_READ) {
+ strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
+ return true;
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Get the command to execute. */
+ /*********************************************************************/
+ if (!(Cmdlist = MakeCMD(g))) {
+ Myc.Close();
+ return true;
+ } // endif Query
+
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* Data Base read routine for MYSQL access method. */
+/***********************************************************************/
+int TDBMYEXC::ReadDB(PGLOBAL g)
+ {
+ if (Havew) {
+ // Process result set from SHOW WARNINGS
+ if (Myc.Fetch(g, -1) != RC_OK) {
+ Myc.FreeResult();
+ Havew = Isw = false;
+ } else {
+ N++;
+ Isw = true;
+ return RC_OK;
+ } // endif Fetch
+
+ } // endif m_Res
+
+ if (Cmdlist) {
+ // Process query to send
+ int rc;
+
+ do {
+ Query = Cmdlist->Cmd;
+
+ switch (rc = Myc.ExecSQLcmd(g, Query, &Warnings)) {
+ case RC_NF:
+ AftRows = Myc.m_Afrw;
+ strcpy(g->Message, "Affected rows");
+ break;
+ case RC_OK:
+ AftRows = Myc.m_Fields;
+ strcpy(g->Message, "Result set columns");
+ break;
+ case RC_FX:
+ AftRows = Myc.m_Afrw;
+ Nerr++;
+ break;
+ case RC_INFO:
+ Shw = true;
+ } // endswitch rc
+
+ Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
+ } while (rc == RC_INFO);
+
+ if (Shw && Warnings)
+ Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);
+
+ ++N;
+ return RC_OK;
+ } else
+ return RC_EF;
+
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for Exec MYSQL access methods. */
+/***********************************************************************/
+int TDBMYEXC::WriteDB(PGLOBAL g)
+ {
+ strcpy(g->Message, "EXEC MYSQL tables are read only");
+ return RC_FX;
+ } // end of WriteDB
+
+// ------------------------- MYXCOL functions ---------------------------
+
+/***********************************************************************/
+/* MYXCOL public constructor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : MYSQLCOL(cdp, tdbp, cprec, i, am)
+ {
+ // Set additional EXEC MYSQL access method information for column.
+ Flag = cdp->GetOffset();
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYSQLCOL public constructor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
+ : MYSQLCOL(fld, tdbp, i, am)
+ {
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYXCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
+ {
+ Flag = col1->Flag;
+ } // end of MYXCOL copy constructor
+
+/***********************************************************************/
+/* ReadColumn: */
+/***********************************************************************/
+void MYXCOL::ReadColumn(PGLOBAL g)
+ {
+ PTDBMYX tdbp = (PTDBMYX)To_Tdb;
+
+ if (tdbp->Isw) {
+ char *buf = NULL;
+
+ if (Flag < 3) {
+ buf = tdbp->Myc.GetCharField(Flag);
+ Value->SetValue_psz(buf);
+ } else
+ Value->Reset();
+
+ } else
+ switch (Flag) {
+ case 0: Value->SetValue_psz(tdbp->Query); break;
+ case 1: Value->SetValue(tdbp->AftRows); break;
+ case 2: Value->SetValue_psz(g->Message); break;
+ case 3: Value->SetValue(tdbp->Warnings); break;
+ default: Value->SetValue_psz("Invalid Flag"); break;
+ } // endswitch Flag
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: should never be called. */
+/***********************************************************************/
+void MYXCOL::WriteColumn(PGLOBAL g)
+ {
+ assert(false);
+ } // end of WriteColumn
+
+/* ---------------------------TDBMCL class --------------------------- */
+
+/***********************************************************************/
+/* TDBMCL class constructor. */
+/***********************************************************************/
+TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
+ {
+ Host = tdp->Hostname;
+ Db = tdp->Database;
+ Tab = tdp->Tabname;
+ User = tdp->Username;
+ Pwd = tdp->Password;
+ Port = tdp->Portnumber;
+ } // end of TDBMCL constructor
+
+/***********************************************************************/
+/* GetResult: Get the list the MYSQL table columns. */
+/***********************************************************************/
+PQRYRES TDBMCL::GetResult(PGLOBAL g)
+ {
+ return MyColumns(g, Host, Db, User, Pwd, Tab, NULL, Port, false);
+ } // end of GetResult
diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h
index 74b1d49e227..2ecfe800f88 100644
--- a/storage/connect/tabmysql.h
+++ b/storage/connect/tabmysql.h
@@ -185,14 +185,7 @@ class TDBMYEXC : public TDBMYSQL {
// Methods
virtual PTDB CopyOne(PTABS t);
-//virtual int GetAffectedRows(void) {return AftRows;}
-//virtual int GetRecpos(void) {return N;}
-//virtual int GetProgMax(PGLOBAL g);
-//virtual void ResetDB(void) {N = 0;}
-//virtual int RowNumber(PGLOBAL g, bool b = FALSE);
virtual bool IsView(void) {return Isview;}
-//virtual PSZ GetServer(void) {return Server;}
-// void SetDatabase(LPCSTR db) {Database = (char*)db;}
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
@@ -200,20 +193,10 @@ class TDBMYEXC : public TDBMYSQL {
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
-//virtual int DeleteDB(PGLOBAL g, int irc);
-//virtual void CloseDB(PGLOBAL g);
-
- // Specific routines
-// bool SetColumnRanks(PGLOBAL g);
-// PCOL MakeFieldColumn(PGLOBAL g, char *name);
-// PSZ FindFieldColumn(char *name);
protected:
// Internal functions
PCMD MakeCMD(PGLOBAL g);
-//bool MakeSelect(PGLOBAL g);
-//bool MakeInsert(PGLOBAL g);
-//int BindColumns(PGLOBAL g);
// Members
PCMD Cmdlist; // The commands to execute
@@ -237,15 +220,9 @@ class MYXCOL : public MYSQLCOL {
MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am = "MYSQL");
MYXCOL(MYXCOL *colp, PTDB tdbp); // Constructor used in copy process
- // Implementation
-//virtual int GetAmType(void) {return TYPE_AM_MYSQL;}
-// void InitBind(PGLOBAL g);
-
// Methods
-//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
-// bool FindRank(PGLOBAL g);
protected:
// Default constructor not to be used
diff --git a/storage/connect/taboccur.h b/storage/connect/taboccur.h
index 0bee074234c..4538d3d71f2 100644
--- a/storage/connect/taboccur.h
+++ b/storage/connect/taboccur.h
@@ -68,7 +68,6 @@ class TDBOCCUR : public TDBPRX {
protected:
// Members
-//PTDBASE Tdbp; // To the source table or view
LPCSTR Tabname; // Name of source table
char *Colist; // Source column list
char *Xcolumn; // Occurence column name
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index 35864bab5ef..34c2bb8cfc7 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -158,7 +158,6 @@ class ODBCCOL : public COLBLK {
// PVBLK GetBlkp(void) {return Blkp;}
// Methods
-//virtual bool CheckLocal(PTDB tdbp);
virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
diff --git a/storage/connect/tabsys.h b/storage/connect/tabsys.h
index 714f2475873..b508aa5fe96 100644
--- a/storage/connect/tabsys.h
+++ b/storage/connect/tabsys.h
@@ -1,182 +1,181 @@
-/*************** TabSys H Declares Source Code File (.H) ***************/
-/* Name: TABSYS.H Version 2.2 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
-/* */
-/* This file contains the XDB system tables classes declares. */
-/***********************************************************************/
-typedef class INIDEF *PINIDEF;
-typedef class TDBINI *PTDBINI;
-typedef class INICOL *PINICOL;
-typedef class TDBXIN *PTDBXIN;
-typedef class XINCOL *PXINCOL;
-
-/* --------------------------- INI classes --------------------------- */
-
-/***********************************************************************/
-/* INI, XDB and XCL tables. */
-/***********************************************************************/
-class DllExport INIDEF : public TABDEF { /* INI table description */
- friend class TDBINI;
- friend class TDBXIN;
- friend class TDBXTB;
- friend class TDBRTB;
- friend class TDBXCL;
- public:
- // Constructor
- INIDEF(void);
-
- // Implementation
- virtual const char *GetType(void) {return "INI";}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE m);
-//virtual bool DeleteTableFile(PGLOBAL g);
-
- protected:
- // Members
- char *Fn; /* Path/Name of corresponding file */
- char *Xname; /* The eventual table name */
- char Layout; /* R: Row, C: Column */
- int Ln; /* Length of section list buffer */
- }; // end of INIDEF
-
-/***********************************************************************/
-/* This is the class declaration for the INI tables. */
-/* These are tables represented by a INI like file. */
-/***********************************************************************/
-class TDBINI : public TDBASE {
- friend class INICOL;
- public:
- // Constructor
- TDBINI(PINIDEF tdp);
- TDBINI(PTDBINI tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_INI;}
- virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBINI(this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
- virtual int GetRecpos(void) {return N;}
- virtual int GetProgCur(void) {return N;}
- virtual int GetAffectedRows(void) {return 0;}
- virtual PSZ GetFile(PGLOBAL g) {return Ifile;}
- virtual void SetFile(PGLOBAL g, PSZ fn) {Ifile = fn;}
- virtual void ResetDB(void) {Seclist = Section = NULL; N = 0;}
- virtual void ResetSize(void) {MaxSize = -1; Seclist = NULL;}
- virtual int RowNumber(PGLOBAL g, bool b = false) {return N;}
- char *GetSeclist(PGLOBAL g);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
-
- protected:
- // Members
- char *Ifile; // The INI file
- char *Seclist; // The section list
- char *Section; // The current section
- int Seclen; // Length of seclist buffer
- int N; // The current section index
- }; // end of class TDBINI
-
-/***********************************************************************/
-/* Class INICOL: XDB table access method column descriptor. */
-/***********************************************************************/
-class INICOL : public COLBLK {
- public:
- // Constructors
- INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI");
- INICOL(INICOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_INI;}
- virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
-
- // Methods
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- virtual void AllocBuf(PGLOBAL g);
-
- protected:
- // Default constructor not to be used
- INICOL(void) {}
-
- // Members
- char *Valbuf; // To the key value buffer
- int Flag; // Tells what set in value
- int Long; // Buffer length
- PVAL To_Val; // To value used for Update/Insert
- }; // end of class INICOL
-
-/* --------------------------- XINI class ---------------------------- */
-
-/***********************************************************************/
-/* This is the class declaration for the XINI tables. */
-/* These are tables represented by a INI like file */
-/* having 3 columns Section, Key, and Value. */
-/***********************************************************************/
-class TDBXIN : public TDBINI {
- friend class XINCOL;
- public:
- // Constructor
- TDBXIN(PINIDEF tdp);
- TDBXIN(PTDBXIN tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_INI;}
- virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXIN(this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
- virtual int GetRecpos(void);
- virtual bool SetRecpos(PGLOBAL g, int recpos);
- virtual void ResetDB(void)
- {Seclist = Section = Keycur = NULL; N = 0; Oldsec = -1;}
- char *GetKeylist(PGLOBAL g, char *sec);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
-
- protected:
- // Members
- char *Keylist; // The key list
- char *Keycur; // The current key
- int Keylen; // Length of keylist buffer
- short Oldsec; // Last current section
- }; // end of class TDBXIN
-
-/***********************************************************************/
-/* Class XINCOL: XIN table access method column descriptor. */
-/***********************************************************************/
-class XINCOL : public INICOL {
- public:
- // Constructors
- XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI");
- XINCOL(XINCOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
-
- protected:
- // Default constructor not to be used
- XINCOL(void) {}
-
- // Members
- }; // end of class XINICOL
+/*************** TabSys H Declares Source Code File (.H) ***************/
+/* Name: TABSYS.H Version 2.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
+/* */
+/* This file contains the XDB system tables classes declares. */
+/***********************************************************************/
+typedef class INIDEF *PINIDEF;
+typedef class TDBINI *PTDBINI;
+typedef class INICOL *PINICOL;
+typedef class TDBXIN *PTDBXIN;
+typedef class XINCOL *PXINCOL;
+
+/* --------------------------- INI classes --------------------------- */
+
+/***********************************************************************/
+/* INI, XDB and XCL tables. */
+/***********************************************************************/
+class DllExport INIDEF : public TABDEF { /* INI table description */
+ friend class TDBINI;
+ friend class TDBXIN;
+ friend class TDBXTB;
+ friend class TDBRTB;
+ friend class TDBXCL;
+ public:
+ // Constructor
+ INIDEF(void);
+
+ // Implementation
+ virtual const char *GetType(void) {return "INI";}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+ protected:
+ // Members
+ char *Fn; /* Path/Name of corresponding file */
+ char *Xname; /* The eventual table name */
+ char Layout; /* R: Row, C: Column */
+ int Ln; /* Length of section list buffer */
+ }; // end of INIDEF
+
+/***********************************************************************/
+/* This is the class declaration for the INI tables. */
+/* These are tables represented by a INI like file. */
+/***********************************************************************/
+class TDBINI : public TDBASE {
+ friend class INICOL;
+ public:
+ // Constructor
+ TDBINI(PINIDEF tdp);
+ TDBINI(PTDBINI tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_INI;}
+ virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBINI(this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+ virtual int GetRecpos(void) {return N;}
+ virtual int GetProgCur(void) {return N;}
+ virtual int GetAffectedRows(void) {return 0;}
+ virtual PSZ GetFile(PGLOBAL g) {return Ifile;}
+ virtual void SetFile(PGLOBAL g, PSZ fn) {Ifile = fn;}
+ virtual void ResetDB(void) {Seclist = Section = NULL; N = 0;}
+ virtual void ResetSize(void) {MaxSize = -1; Seclist = NULL;}
+ virtual int RowNumber(PGLOBAL g, bool b = false) {return N;}
+ char *GetSeclist(PGLOBAL g);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+
+ protected:
+ // Members
+ char *Ifile; // The INI file
+ char *Seclist; // The section list
+ char *Section; // The current section
+ int Seclen; // Length of seclist buffer
+ int N; // The current section index
+ }; // end of class TDBINI
+
+/***********************************************************************/
+/* Class INICOL: XDB table access method column descriptor. */
+/***********************************************************************/
+class INICOL : public COLBLK {
+ public:
+ // Constructors
+ INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI");
+ INICOL(INICOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_INI;}
+ virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
+
+ // Methods
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ virtual void AllocBuf(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ INICOL(void) {}
+
+ // Members
+ char *Valbuf; // To the key value buffer
+ int Flag; // Tells what set in value
+ int Long; // Buffer length
+ PVAL To_Val; // To value used for Update/Insert
+ }; // end of class INICOL
+
+/* --------------------------- XINI class ---------------------------- */
+
+/***********************************************************************/
+/* This is the class declaration for the XINI tables. */
+/* These are tables represented by a INI like file */
+/* having 3 columns Section, Key, and Value. */
+/***********************************************************************/
+class TDBXIN : public TDBINI {
+ friend class XINCOL;
+ public:
+ // Constructor
+ TDBXIN(PINIDEF tdp);
+ TDBXIN(PTDBXIN tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_INI;}
+ virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXIN(this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+ virtual int GetRecpos(void);
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
+ virtual void ResetDB(void)
+ {Seclist = Section = Keycur = NULL; N = 0; Oldsec = -1;}
+ char *GetKeylist(PGLOBAL g, char *sec);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+
+ protected:
+ // Members
+ char *Keylist; // The key list
+ char *Keycur; // The current key
+ int Keylen; // Length of keylist buffer
+ short Oldsec; // Last current section
+ }; // end of class TDBXIN
+
+/***********************************************************************/
+/* Class XINCOL: XIN table access method column descriptor. */
+/***********************************************************************/
+class XINCOL : public INICOL {
+ public:
+ // Constructors
+ XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI");
+ XINCOL(XINCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ XINCOL(void) {}
+
+ // Members
+ }; // end of class XINICOL
diff --git a/storage/connect/tabtbl.h b/storage/connect/tabtbl.h
index 0ff6c37eaed..783f3da2c9e 100644
--- a/storage/connect/tabtbl.h
+++ b/storage/connect/tabtbl.h
@@ -1,169 +1,159 @@
-/*************** TabTbl H Declares Source Code File (.H) ***************/
-/* Name: TABTBL.H Version 1.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2008-2013 */
-/* */
-/* This file contains the TDBTBL classes declares. */
-/***********************************************************************/
-#include "block.h"
-#include "colblk.h"
-#include "tabutil.h"
-
-typedef class TBLDEF *PTBLDEF;
-typedef class TDBTBL *PTDBTBL;
-typedef class TDBTBM *PTDBTBM;
-typedef class MYSQLC *PMYC;
-
-/***********************************************************************/
-/* Defines the structures used for distributed TBM tables. */
-/***********************************************************************/
-typedef struct _TBMtable *PTBMT;
-
-typedef struct _TBMtable {
- PTBMT Next; // Points to next data table struct
- PTABLE Tap; // Points to the sub table
- PGLOBAL G; // Needed in thread routine
- bool Complete; // TRUE when all results are read
- bool Ready; // TRUE when results are there
- int Rows; // Total number of rows read so far
- int ProgCur; // Current pos
- int ProgMax; // Max pos
- int Rc; // Return code
- THD *Thd;
- pthread_attr_t attr; // ???
- pthread_t Tid; // CheckOpen thread ID
- } TBMT;
-
-/***********************************************************************/
-/* TBL table. */
-/***********************************************************************/
-class DllExport TBLDEF : public PRXDEF { /* Logical table description */
- friend class TDBTBL;
- friend class TDBTBC;
- public:
- // Constructor
- TBLDEF(void);
-
- // Implementation
- virtual const char *GetType(void) {return "TBL";}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE m);
-
- protected:
- // Members
- bool Accept; /* TRUE if bad tables are accepted */
- bool Thread; /* Use thread for remote tables */
- int Maxerr; /* Maximum number of bad tables */
- int Ntables; /* Number of tables */
- }; // end of TBLDEF
-
-/***********************************************************************/
-/* This is the TBL Access Method class declaration. */
-/***********************************************************************/
-class DllExport TDBTBL : public TDBPRX {
- friend class TBTBLK;
- public:
- // Constructor
- TDBTBL(PTBLDEF tdp = NULL);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_TBL;}
-
- // Methods
- virtual void ResetDB(void);
- virtual int GetRecpos(void) {return Rows;}
- virtual int GetBadLines(void) {return (int)Nbc;}
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual int RowNumber(PGLOBAL g, bool b = FALSE);
- virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL scp);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
-
- protected:
- // Internal functions
- bool InitTableList(PGLOBAL g);
- bool TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp);
-
- // Members
- PTABLE Tablist; // Points to the table list
- PTABLE CurTable; // Points to the current table
- bool Accept; // TRUE if bad tables are accepted
- int Maxerr; // Maximum number of bad tables
- int Nbc; // Number of bad connections
- int Rows; // Used for RowID
- int Crp; // Used for CurPos
- }; // end of class TDBTBL
-
-/***********************************************************************/
-/* Class TBTBLK: TDBPLG TABID special column descriptor. */
-/***********************************************************************/
-class TBTBLK : public TIDBLK {
- public:
- // The constructor must restore Value because XOBJECT has a void
- // constructor called by default that set Value to NULL
- TBTBLK(PVAL valp) {Value = valp;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
-
- // Fake operator new used to change TIDBLK into SDTBLK
- void * operator new(size_t size, TIDBLK *sp) {return sp;}
-
-#if !defined(__BORLANDC__)
- // Avoid warning C4291 by defining a matching dummy delete operator
- void operator delete(void *, TIDBLK*) {}
- void operator delete(void *, size_t size) {}
-#endif
-
- protected:
- // Must not have additional members
- }; // end of class TBTBLK
-
-/***********************************************************************/
-/* This is the TBM Access Method class declaration. */
-/***********************************************************************/
-class DllExport TDBTBM : public TDBTBL {
- friend class TBTBLK;
- public:
- // Constructor
- TDBTBM(PTBLDEF tdp = NULL);
-
- // Implementation
-//virtual AMT GetAmType(void) {return TYPE_AM_TBL;}
-
- // Methods
- virtual void ResetDB(void);
-//virtual int GetRecpos(void) {return Rows;}
-//virtual int GetBadLines(void) {return (int)Nbc;}
-
- // Database routines
-//virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g) {return 10;} // Temporary
- virtual int RowNumber(PGLOBAL g, bool b = FALSE);
-//virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL scp);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
-
- protected:
- // Internal functions
-//bool InitTableList(PGLOBAL g);
-//bool TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp);
- bool OpenTables(PGLOBAL g);
- int ReadNextRemote(PGLOBAL g);
-
- // Members
- PTBMT Tmp; // To data table TBMT structures
- PTBMT Cmp; // Current data table PLGF (to move to TDBTBL)
- PTBMT Bmp; // To bad (unconnected) PLGF structures
- bool Done; // TRUE after first GetAllResults
- int Nrc; // Number of remote connections
- int Nlc; // Number of local connections
- }; // end of class TDBTBM
-
-
-pthread_handler_t ThreadOpen(void *p);
+/*************** TabTbl H Declares Source Code File (.H) ***************/
+/* Name: TABTBL.H Version 1.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2008-2013 */
+/* */
+/* This file contains the TDBTBL classes declares. */
+/***********************************************************************/
+#include "block.h"
+#include "colblk.h"
+#include "tabutil.h"
+
+typedef class TBLDEF *PTBLDEF;
+typedef class TDBTBL *PTDBTBL;
+typedef class TDBTBM *PTDBTBM;
+typedef class MYSQLC *PMYC;
+
+/***********************************************************************/
+/* Defines the structures used for distributed TBM tables. */
+/***********************************************************************/
+typedef struct _TBMtable *PTBMT;
+
+typedef struct _TBMtable {
+ PTBMT Next; // Points to next data table struct
+ PTABLE Tap; // Points to the sub table
+ PGLOBAL G; // Needed in thread routine
+ bool Complete; // TRUE when all results are read
+ bool Ready; // TRUE when results are there
+ int Rows; // Total number of rows read so far
+ int ProgCur; // Current pos
+ int ProgMax; // Max pos
+ int Rc; // Return code
+ THD *Thd;
+ pthread_attr_t attr; // ???
+ pthread_t Tid; // CheckOpen thread ID
+ } TBMT;
+
+/***********************************************************************/
+/* TBL table. */
+/***********************************************************************/
+class DllExport TBLDEF : public PRXDEF { /* Logical table description */
+ friend class TDBTBL;
+ friend class TDBTBC;
+ public:
+ // Constructor
+ TBLDEF(void);
+
+ // Implementation
+ virtual const char *GetType(void) {return "TBL";}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+ protected:
+ // Members
+ bool Accept; /* TRUE if bad tables are accepted */
+ bool Thread; /* Use thread for remote tables */
+ int Maxerr; /* Maximum number of bad tables */
+ int Ntables; /* Number of tables */
+ }; // end of TBLDEF
+
+/***********************************************************************/
+/* This is the TBL Access Method class declaration. */
+/***********************************************************************/
+class DllExport TDBTBL : public TDBPRX {
+ friend class TBTBLK;
+ public:
+ // Constructor
+ TDBTBL(PTBLDEF tdp = NULL);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_TBL;}
+
+ // Methods
+ virtual void ResetDB(void);
+ virtual int GetRecpos(void) {return Rows;}
+ virtual int GetBadLines(void) {return (int)Nbc;}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual int RowNumber(PGLOBAL g, bool b = FALSE);
+ virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL scp);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+
+ protected:
+ // Internal functions
+ bool InitTableList(PGLOBAL g);
+ bool TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp);
+
+ // Members
+ PTABLE Tablist; // Points to the table list
+ PTABLE CurTable; // Points to the current table
+ bool Accept; // TRUE if bad tables are accepted
+ int Maxerr; // Maximum number of bad tables
+ int Nbc; // Number of bad connections
+ int Rows; // Used for RowID
+ int Crp; // Used for CurPos
+ }; // end of class TDBTBL
+
+/***********************************************************************/
+/* Class TBTBLK: TDBPLG TABID special column descriptor. */
+/***********************************************************************/
+class TBTBLK : public TIDBLK {
+ public:
+ // The constructor must restore Value because XOBJECT has a void
+ // constructor called by default that set Value to NULL
+ TBTBLK(PVAL valp) {Value = valp;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+
+ // Fake operator new used to change TIDBLK into SDTBLK
+ void * operator new(size_t size, TIDBLK *sp) {return sp;}
+
+#if !defined(__BORLANDC__)
+ // Avoid warning C4291 by defining a matching dummy delete operator
+ void operator delete(void *, TIDBLK*) {}
+ void operator delete(void *, size_t size) {}
+#endif
+
+ protected:
+ // Must not have additional members
+ }; // end of class TBTBLK
+
+/***********************************************************************/
+/* This is the TBM Access Method class declaration. */
+/***********************************************************************/
+class DllExport TDBTBM : public TDBTBL {
+ friend class TBTBLK;
+ public:
+ // Constructor
+ TDBTBM(PTBLDEF tdp = NULL);
+
+ // Methods
+ virtual void ResetDB(void);
+
+ // Database routines
+ virtual int GetMaxSize(PGLOBAL g) {return 10;} // Temporary
+ virtual int RowNumber(PGLOBAL g, bool b = FALSE);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+
+ protected:
+ // Internal functions
+ bool OpenTables(PGLOBAL g);
+ int ReadNextRemote(PGLOBAL g);
+
+ // Members
+ PTBMT Tmp; // To data table TBMT structures
+ PTBMT Cmp; // Current data table PLGF (to move to TDBTBL)
+ PTBMT Bmp; // To bad (unconnected) PLGF structures
+ bool Done; // TRUE after first GetAllResults
+ int Nrc; // Number of remote connections
+ int Nlc; // Number of local connections
+ }; // end of class TDBTBM
+
+pthread_handler_t ThreadOpen(void *p);
diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h
index 7b2161a4b8b..c87065befba 100644
--- a/storage/connect/tabutil.h
+++ b/storage/connect/tabutil.h
@@ -54,7 +54,6 @@ class DllExport PRXDEF : public TABDEF { /* Logical table description */
/* This is the class declaration for the XCSV table. */
/***********************************************************************/
class DllExport TDBPRX : public TDBASE {
-//friend class MULINDX;
friend class PRXDEF;
friend class PRXCOL;
public:
@@ -126,8 +125,6 @@ class TDBTBC : public TDBCAT {
public:
// Constructors
TDBTBC(PPRXDEF tdp);
-//TDBTBC(PTBLDEF tdp);
-//TDBTBC(PXCLDEF tdp);
protected:
// Specific routines
diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp
index cb5e6f3200c..baa13fe36e7 100644
--- a/storage/connect/tabvct.cpp
+++ b/storage/connect/tabvct.cpp
@@ -301,9 +301,7 @@ bool TDBVCT::OpenDB(PGLOBAL g)
To_Kindex->Reset();
Txfp->Rewind();
-#if defined(BLK_INDX)
ResetBlockFilter(g);
-#endif // BLK_INDX
return false;
} // endif Use
@@ -325,12 +323,10 @@ bool TDBVCT::OpenDB(PGLOBAL g)
// This was not done in previous version
Use = USE_OPEN; // Do it now in case we are recursively called
-#if defined(BLK_INDX)
/*********************************************************************/
/* Allocate the block filter tree if evaluation is possible. */
/*********************************************************************/
To_BlkFil = InitBlockFilter(g, To_Filter);
-#endif // BLK_INDX
/*********************************************************************/
/* Reset buffer access according to indexing and to mode. */
diff --git a/storage/connect/tabvct.h b/storage/connect/tabvct.h
index f1c0a8a3a98..e15150ab356 100644
--- a/storage/connect/tabvct.h
+++ b/storage/connect/tabvct.h
@@ -1,123 +1,121 @@
-/*************** TabVct H Declares Source Code File (.H) ***************/
-/* Name: TABVCT.H Version 3.4 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2011 */
-/* */
-/* This file contains the TDBVCT class declares. */
-/***********************************************************************/
-#ifndef __TABVCT__
-#define __TABVCT__
-
-#include "tabfix.h"
-#if defined(UNIX)
-//#include <string.h.SUNWCCh>
-#endif
-
-typedef class TDBVCT *PTDBVCT;
-typedef class VCTCOL *PVCTCOL;
-
-/***********************************************************************/
-/* VCT table. */
-/***********************************************************************/
-class DllExport VCTDEF : public DOSDEF { /* Logical table description */
- friend class VCTFAM;
- friend class VECFAM;
- friend class VMPFAM;
- public:
- // Constructor
- VCTDEF(void) {Split = Estimate = Header = 0;}
-
- // Implementation
- virtual const char *GetType(void) {return "VCT";}
- int GetEstimate(void) {return Estimate;}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE mode);
-
- protected:
- // Specific file erase routine for vertical tables
-//virtual bool Erase(char *filename);
- int MakeFnPattern(char *fpat);
-
- // Members
- int Split; /* Columns in separate files */
- int Estimate; /* Estimated maximum size of table */
- int Header; /* 0: no, 1: separate, 2: in data file */
- }; // end of VCTDEF
-
-/***********************************************************************/
-/* This is the DOS/UNIX Access Method class declaration for files */
-/* in blocked vector format. In each block containing "Elements" */
-/* records, values of each columns are consecutively stored (vector). */
-/***********************************************************************/
-class DllExport TDBVCT : public TDBFIX {
- friend class VCTCOL;
- friend class VCTFAM;
- friend class VCMFAM;
- friend class VECFAM;
- friend class VMPFAM;
- public:
- // Constructors
- TDBVCT(PVCTDEF tdp, PTXF txfp);
- TDBVCT(PGLOBAL g, PTDBVCT tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
- virtual PTDB Duplicate(PGLOBAL g)
- {return (PTDB)new(g) TDBVCT(g, this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual void CloseDB(PGLOBAL g);
-
- protected:
- // Members
- }; // end of class TDBVCT
-
-/***********************************************************************/
-/* Class VCTCOL: VCT access method column descriptor. */
-/* This A.M. is used for file having column wise organization. */
-/***********************************************************************/
-class DllExport VCTCOL : public DOSCOL {
- friend class TDBVCT;
- friend class VCTFAM;
- friend class VCMFAM;
- friend class VECFAM;
- friend class VMPFAM;
- friend class BGVFAM;
- public:
- // Constructors
- VCTCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
- VCTCOL(VCTCOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_VCT;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void SetOk(void);
-
- protected:
- virtual void ReadBlock(PGLOBAL g);
- virtual void WriteBlock(PGLOBAL g);
-
- VCTCOL(void) {} // Default constructor not to be used
-
- // Members
- PVBLK Blk; // Block buffer
- int Clen; // Internal length in table
- int ColBlk; // Block pointed by column
- int ColPos; // Last position read
- int Modif; // Number of modified lines in block
- }; // end of class VCTCOL
-
-#endif // __TABVCT__
-
+/*************** TabVct H Declares Source Code File (.H) ***************/
+/* Name: TABVCT.H Version 3.4 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2011 */
+/* */
+/* This file contains the TDBVCT class declares. */
+/***********************************************************************/
+#ifndef __TABVCT__
+#define __TABVCT__
+
+#include "tabfix.h"
+#if defined(UNIX)
+//#include <string.h.SUNWCCh>
+#endif
+
+typedef class TDBVCT *PTDBVCT;
+typedef class VCTCOL *PVCTCOL;
+
+/***********************************************************************/
+/* VCT table. */
+/***********************************************************************/
+class DllExport VCTDEF : public DOSDEF { /* Logical table description */
+ friend class VCTFAM;
+ friend class VECFAM;
+ friend class VMPFAM;
+ public:
+ // Constructor
+ VCTDEF(void) {Split = Estimate = Header = 0;}
+
+ // Implementation
+ virtual const char *GetType(void) {return "VCT";}
+ int GetEstimate(void) {return Estimate;}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE mode);
+
+ protected:
+ int MakeFnPattern(char *fpat);
+
+ // Members
+ int Split; /* Columns in separate files */
+ int Estimate; /* Estimated maximum size of table */
+ int Header; /* 0: no, 1: separate, 2: in data file */
+ }; // end of VCTDEF
+
+/***********************************************************************/
+/* This is the DOS/UNIX Access Method class declaration for files */
+/* in blocked vector format. In each block containing "Elements" */
+/* records, values of each columns are consecutively stored (vector). */
+/***********************************************************************/
+class DllExport TDBVCT : public TDBFIX {
+ friend class VCTCOL;
+ friend class VCTFAM;
+ friend class VCMFAM;
+ friend class VECFAM;
+ friend class VMPFAM;
+ public:
+ // Constructors
+ TDBVCT(PVCTDEF tdp, PTXF txfp);
+ TDBVCT(PGLOBAL g, PTDBVCT tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBVCT(g, this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual void CloseDB(PGLOBAL g);
+
+ protected:
+ // Members
+ }; // end of class TDBVCT
+
+/***********************************************************************/
+/* Class VCTCOL: VCT access method column descriptor. */
+/* This A.M. is used for file having column wise organization. */
+/***********************************************************************/
+class DllExport VCTCOL : public DOSCOL {
+ friend class TDBVCT;
+ friend class VCTFAM;
+ friend class VCMFAM;
+ friend class VECFAM;
+ friend class VMPFAM;
+ friend class BGVFAM;
+ public:
+ // Constructors
+ VCTCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
+ VCTCOL(VCTCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_VCT;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void SetOk(void);
+
+ protected:
+ virtual void ReadBlock(PGLOBAL g);
+ virtual void WriteBlock(PGLOBAL g);
+
+ VCTCOL(void) {} // Default constructor not to be used
+
+ // Members
+ PVBLK Blk; // Block buffer
+ int Clen; // Internal length in table
+ int ColBlk; // Block pointed by column
+ int ColPos; // Last position read
+ int Modif; // Number of modified lines in block
+ }; // end of class VCTCOL
+
+#endif // __TABVCT__
+
diff --git a/storage/connect/tabwmi.h b/storage/connect/tabwmi.h
index 558e527773e..b26f66fb04b 100644
--- a/storage/connect/tabwmi.h
+++ b/storage/connect/tabwmi.h
@@ -1,151 +1,150 @@
-// TABWMI.H Olivier Bertrand 2012
-// WMI: Virtual table to Get WMI information
-#define _WIN32_DCOM
-#include <wbemidl.h>
-# pragma comment(lib, "wbemuuid.lib")
-#include <iostream>
-using namespace std;
-#include <comdef.h>
-
-/***********************************************************************/
-/* Definitions. */
-/***********************************************************************/
-typedef class WMIDEF *PWMIDEF;
-typedef class TDBWMI *PTDBWMI;
-typedef class WMICOL *PWMICOL;
-typedef class TDBWCL *PTDBWCL;
-typedef class WCLCOL *PWCLCOL;
-
-/***********************************************************************/
-/* Structure used by WMI column info functions. */
-/***********************************************************************/
-typedef struct _WMIutil {
- IWbemServices *Svc;
- IWbemClassObject *Cobj;
-} WMIUTIL, *PWMIUT;
-
-/***********************************************************************/
-/* Functions used externally. */
-/***********************************************************************/
-PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info);
-
-/* -------------------------- WMI classes ---------------------------- */
-
-/***********************************************************************/
-/* WMI: Virtual table to get the WMI information. */
-/***********************************************************************/
-class WMIDEF : public TABDEF { /* Logical table description */
- friend class TDBWMI;
- friend class TDBWCL;
- friend class TDBWCX;
- public:
- // Constructor
- WMIDEF(void) {Pseudo = 3; Nspace = NULL; Wclass = NULL; Ems = 0;}
-
- // Implementation
- virtual const char *GetType(void) {return "WMI";}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE m);
-//virtual bool DeleteTableFile(PGLOBAL g) {return true;}
-
- protected:
- // Members
- char *Nspace;
- char *Wclass;
- int Ems;
- }; // end of WMIDEF
-
-/***********************************************************************/
-/* This is the class declaration for the WMI table. */
-/***********************************************************************/
-class TDBWMI : public TDBASE {
- friend class WMICOL;
- public:
- // Constructor
- TDBWMI(PWMIDEF tdp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_WMI;}
-
- // Methods
- virtual int GetRecpos(void);
- virtual int GetProgCur(void) {return N;}
- virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
-
- protected:
- // Specific routines
- bool Initialize(PGLOBAL g);
- char *MakeWQL(PGLOBAL g);
- void DoubleSlash(PGLOBAL g);
- bool GetWMIInfo(PGLOBAL g);
-
- // Members
- IWbemServices *Svc; // IWbemServices pointer
- IEnumWbemClassObject *Enumerator;
- IWbemClassObject *ClsObj;
- char *Nspace; // Namespace
- char *Wclass; // Class name
- char *ObjPath; // Used for direct access
- char *Kvp; // Itou
- int Ems; // Estimated max size
- PCOL Kcol; // Key column
- HRESULT Res;
- PVBLK Vbp;
- bool Init;
- bool Done;
- ULONG Rc;
- int N; // Row number
- }; // end of class TDBWMI
-
-/***********************************************************************/
-/* Class WMICOL: WMI Address column. */
-/***********************************************************************/
-class WMICOL : public COLBLK {
- friend class TDBWMI;
- public:
- // Constructors
- WMICOL(PCOLDEF cdp, PTDB tdbp, int n);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_WMI;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- WMICOL(void) {} // Default constructor not to be used
-
- // Members
- PTDBWMI Tdbp; // Points to WMI table block
- VARIANT Prop; // Property value
- CIMTYPE Ctype; // CIM Type
- HRESULT Res;
- }; // end of class WMICOL
-
-/***********************************************************************/
-/* This is the class declaration for the WMI catalog table. */
-/***********************************************************************/
-class TDBWCL : public TDBCAT {
- public:
- // Constructor
- TDBWCL(PWMIDEF tdp);
-
- protected:
- // Specific routines
- virtual PQRYRES GetResult(PGLOBAL g);
-
- // Members
- char *Nsp; // Name space
- char *Cls; // Class
- }; // end of class TDBWCL
+// TABWMI.H Olivier Bertrand 2012
+// WMI: Virtual table to Get WMI information
+#define _WIN32_DCOM
+#include <wbemidl.h>
+# pragma comment(lib, "wbemuuid.lib")
+#include <iostream>
+using namespace std;
+#include <comdef.h>
+
+/***********************************************************************/
+/* Definitions. */
+/***********************************************************************/
+typedef class WMIDEF *PWMIDEF;
+typedef class TDBWMI *PTDBWMI;
+typedef class WMICOL *PWMICOL;
+typedef class TDBWCL *PTDBWCL;
+typedef class WCLCOL *PWCLCOL;
+
+/***********************************************************************/
+/* Structure used by WMI column info functions. */
+/***********************************************************************/
+typedef struct _WMIutil {
+ IWbemServices *Svc;
+ IWbemClassObject *Cobj;
+} WMIUTIL, *PWMIUT;
+
+/***********************************************************************/
+/* Functions used externally. */
+/***********************************************************************/
+PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info);
+
+/* -------------------------- WMI classes ---------------------------- */
+
+/***********************************************************************/
+/* WMI: Virtual table to get the WMI information. */
+/***********************************************************************/
+class WMIDEF : public TABDEF { /* Logical table description */
+ friend class TDBWMI;
+ friend class TDBWCL;
+ friend class TDBWCX;
+ public:
+ // Constructor
+ WMIDEF(void) {Pseudo = 3; Nspace = NULL; Wclass = NULL; Ems = 0;}
+
+ // Implementation
+ virtual const char *GetType(void) {return "WMI";}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+ protected:
+ // Members
+ char *Nspace;
+ char *Wclass;
+ int Ems;
+ }; // end of WMIDEF
+
+/***********************************************************************/
+/* This is the class declaration for the WMI table. */
+/***********************************************************************/
+class TDBWMI : public TDBASE {
+ friend class WMICOL;
+ public:
+ // Constructor
+ TDBWMI(PWMIDEF tdp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_WMI;}
+
+ // Methods
+ virtual int GetRecpos(void);
+ virtual int GetProgCur(void) {return N;}
+ virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+
+ protected:
+ // Specific routines
+ bool Initialize(PGLOBAL g);
+ char *MakeWQL(PGLOBAL g);
+ void DoubleSlash(PGLOBAL g);
+ bool GetWMIInfo(PGLOBAL g);
+
+ // Members
+ IWbemServices *Svc; // IWbemServices pointer
+ IEnumWbemClassObject *Enumerator;
+ IWbemClassObject *ClsObj;
+ char *Nspace; // Namespace
+ char *Wclass; // Class name
+ char *ObjPath; // Used for direct access
+ char *Kvp; // Itou
+ int Ems; // Estimated max size
+ PCOL Kcol; // Key column
+ HRESULT Res;
+ PVBLK Vbp;
+ bool Init;
+ bool Done;
+ ULONG Rc;
+ int N; // Row number
+ }; // end of class TDBWMI
+
+/***********************************************************************/
+/* Class WMICOL: WMI Address column. */
+/***********************************************************************/
+class WMICOL : public COLBLK {
+ friend class TDBWMI;
+ public:
+ // Constructors
+ WMICOL(PCOLDEF cdp, PTDB tdbp, int n);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_WMI;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ WMICOL(void) {} // Default constructor not to be used
+
+ // Members
+ PTDBWMI Tdbp; // Points to WMI table block
+ VARIANT Prop; // Property value
+ CIMTYPE Ctype; // CIM Type
+ HRESULT Res;
+ }; // end of class WMICOL
+
+/***********************************************************************/
+/* This is the class declaration for the WMI catalog table. */
+/***********************************************************************/
+class TDBWCL : public TDBCAT {
+ public:
+ // Constructor
+ TDBWCL(PWMIDEF tdp);
+
+ protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // Members
+ char *Nsp; // Name space
+ char *Cls; // Class
+ }; // end of class TDBWCL
diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h
index 20998589967..e6145f0ff88 100644
--- a/storage/connect/tabxml.h
+++ b/storage/connect/tabxml.h
@@ -1,246 +1,240 @@
-
-/*************** Tabxml H Declares Source Code File (.H) ***************/
-/* Name: TABXML.H Version 1.6 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2007-2013 */
-/* */
-/* This file contains the XML table classes declares. */
-/***********************************************************************/
-typedef class XMLDEF *PXMLDEF;
-typedef class TDBXML *PTDBXML;
-typedef class XMLCOL *PXMLCOL;
-
-// These functions are exported from the Extended.dll
-//PTABDEF __stdcall GetXML(PGLOBAL g, void *memp);
-
-/* --------------------------- XML classes --------------------------- */
-
-/***********************************************************************/
-/* XML table. */
-/***********************************************************************/
-class DllExport XMLDEF : public TABDEF { /* Logical table description */
- friend class TDBXML;
- public:
- // Constructor
- XMLDEF(void);
-
- // Implementation
- virtual const char *GetType(void) {return "XML";}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE m);
-//virtual bool DeleteTableFile(PGLOBAL g);
-
- protected:
- // Members
- char *Fn; /* Path/Name of corresponding file */
- char *Encoding; /* New XML table file encoding */
- char *Tabname; /* Name of Table node */
- char *Rowname; /* Name of first level nodes */
- char *Colname; /* Name of second level nodes */
- char *Mulnode; /* Name of multiple node */
- char *XmlDB; /* Name of XML DB node */
- char *Nslist; /* List of namespaces to register */
- char *DefNs; /* Dummy name of default namespace */
- char *Attrib; /* Table node attributes */
- char *Hdattr; /* Header node attributes */
- int Coltype; /* Default column type */
- int Limit; /* Limit of multiple values */
- int Header; /* n first rows are header rows */
- bool Xpand; /* Put multiple tags in several rows */
- bool Usedom; /* True: DOM, False: libxml2 */
- }; // end of XMLDEF
-
-#if defined(INCLUDE_TDBXML)
-
-/***********************************************************************/
-/* This is the class declaration for the simple XML tables. */
-/***********************************************************************/
-class DllExport TDBXML : public TDBASE {
- friend class XMLCOL;
- friend class XMULCOL;
- friend class XPOSCOL;
- public:
- // Constructor
- TDBXML(PXMLDEF tdp);
- TDBXML(PTDBXML tdbp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_XML;}
- virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXML(this);}
-
- // Methods
- virtual PTDB CopyOne(PTABS t);
- virtual int GetRecpos(void);
- virtual int GetProgCur(void) {return N;}
- virtual PSZ GetFile(PGLOBAL g) {return Xfile;}
- virtual void SetFile(PGLOBAL g, PSZ fn) {Xfile = fn;}
- virtual void ResetDB(void) {N = 0;}
- virtual void ResetSize(void) {MaxSize = -1;}
- virtual int RowNumber(PGLOBAL g, bool b = false);
- int LoadTableFile(PGLOBAL g, char *filename);
- bool Initialize(PGLOBAL g);
- bool SetTabNode(PGLOBAL g);
- void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node);
- bool CheckRow(PGLOBAL g, bool b);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
-//virtual int GetMaxSame(PGLOBAL g) {return (Xpand) ? Limit : 1;}
- virtual int Cardinality(PGLOBAL g);
- virtual int GetMaxSize(PGLOBAL g);
-//virtual bool NeedIndexing(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
- virtual int CheckWrite(PGLOBAL g) {Checked = true; return 0;}
- virtual const CHARSET_INFO *data_charset()
- {return &my_charset_utf8_general_ci;}
-
- protected:
- // Members
- PXDOC Docp;
- PXNODE Root;
- PXNODE Curp;
- PXNODE DBnode;
- PXNODE TabNode;
- PXNODE RowNode;
- PXNODE ColNode;
- PXLIST Nlist;
- PXLIST Clist;
- PFBLOCK To_Xb; // Pointer to XML file block
- PCOL Colp; // The multiple column
- bool Changed; // After Update, Insert or Delete
- bool Checked; // After Update check pass
- bool NextSame; // Same next row
- bool Xpand; // Put multiple tags in several rows
- bool NewRow; // True when inserting a new row
- bool Hasnod; // True if rows have subnodes
- bool Write; // True for Insert and Update
- bool Usedom; // True for DOM, False for libxml2
- bool Bufdone; // True when column buffers allocated
- bool Nodedone; // True when column nodes allocated
- bool Void; // True if the file does not exist
- char *Xfile; // The XML file
- char *Enc; // New XML table file encoding
- char *Tabname; // Name of Table node
- char *Rowname; // Name of first level nodes
- char *Colname; // Name of second level nodes
- char *Mulnode; // Name of multiple node
- char *XmlDB; // Name of XML DB node
- char *Nslist; // List of namespaces to register
- char *DefNs; // Dummy name of default namespace
- char *Attrib; // Table node attribut(s)
- char *Hdattr; // Header node attribut(s)
- int Coltype; // Default column type
- int Limit; // Limit of multiple values
- int Header; // n first rows are header rows
- int Multiple; // If multiple files
- int Nrow; // The table cardinality
- int Irow; // The current row index
- int Nsub; // The current subrow index
- int N; // The current Rowid
- }; // end of class TDBXML
-
-/***********************************************************************/
-/* Class XMLCOL: XDB table access method column descriptor. */
-/***********************************************************************/
-class XMLCOL : public COLBLK {
- public:
- // Constructors
- XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "XML");
- XMLCOL(XMLCOL *colp, PTDB tdbp); // Constructor used in copy process
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_XML;}
- virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
- bool ParseXpath(PGLOBAL g, bool mode);
-
- // Methods
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- bool AllocBuf(PGLOBAL g, bool mode);
- void AllocNodes(PGLOBAL g, PXDOC dp);
-
- protected:
-//xmlNodePtr SelectSingleNode(xmlNodePtr node, char *name);
-
- // Default constructor not to be used
- XMLCOL(void) : COLBLK(1) {}
-
- // Members
- PXLIST Nl;
- PXLIST Nlx;
- PXNODE ColNode;
- PXNODE ValNode;
- PXNODE Cxnp;
- PXNODE Vxnp;
- PXATTR Vxap;
- PXATTR AttNode;
- PTDBXML Tdbp;
- char *Valbuf; // To the node value buffer
- char *Xname; // The node or attribute name
- char* *Nodes; // The intermediate nodes
- int Type; // 0: Attribute, 1: Tag, 2: position
- int Nod; // The number of intermediate nodes
- int Inod; // Index of multiple node
- int Rank; // Position
- bool Mul; // true for multiple column
- bool Checked; // Was checked while Updating
- int Long; // Buffer length
- int Nx; // The last read row
- int Sx; // The last read sub-row
- PVAL To_Val; // To value used for Update/Insert
- }; // end of class XMLCOL
-
-/***********************************************************************/
-/* Derived class XMLCOLX: used to replace a multiple XMLCOL by the */
-/* derived class XMULCOL that has specialize read and write functions.*/
-/* Note: this works only if the members of the derived class are the */
-/* same than the ones of the original class (NO added members). */
-/***********************************************************************/
-class XMLCOLX : public XMLCOL {
- public:
- // Fake operator new used to change a filter into a derived filter
- void * operator new(size_t size, PXMLCOL colp) {return colp;}
-#if !defined(__BORLANDC__)
- // Avoid warning C4291 by defining a matching dummy delete operator
- void operator delete(void *, size_t size) {}
- void operator delete(void *, PXMLCOL) {}
-#endif
- }; // end of class XMLCOLX
-
-/***********************************************************************/
-/* Class XMULCOL: XML table access method multiple column descriptor. */
-/***********************************************************************/
-class XMULCOL : public XMLCOLX {
- public:
- // The constructor must restore Value because XOBJECT has a void
- // constructor called by default that set Value to NULL
- XMULCOL(PVAL valp) {Value = valp; Mul = true;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- }; // end of class XMULCOL
-
-/***********************************************************************/
-/* Class XPOSCOL: XML table column accessed by position. */
-/***********************************************************************/
-class XPOSCOL : public XMLCOLX {
- public:
- // The constructor must restore Value because XOBJECT has a void
- // constructor called by default that set Value to NULL
- XPOSCOL(PVAL valp) {Value = valp;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
- }; // end of class XPOSCOL
-
-#endif // INCLUDE_TDBXML
+
+/*************** Tabxml H Declares Source Code File (.H) ***************/
+/* Name: TABXML.H Version 1.6 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2007-2013 */
+/* */
+/* This file contains the XML table classes declares. */
+/***********************************************************************/
+typedef class XMLDEF *PXMLDEF;
+typedef class TDBXML *PTDBXML;
+typedef class XMLCOL *PXMLCOL;
+
+/* --------------------------- XML classes --------------------------- */
+
+/***********************************************************************/
+/* XML table. */
+/***********************************************************************/
+class DllExport XMLDEF : public TABDEF { /* Logical table description */
+ friend class TDBXML;
+ public:
+ // Constructor
+ XMLDEF(void);
+
+ // Implementation
+ virtual const char *GetType(void) {return "XML";}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+ protected:
+ // Members
+ char *Fn; /* Path/Name of corresponding file */
+ char *Encoding; /* New XML table file encoding */
+ char *Tabname; /* Name of Table node */
+ char *Rowname; /* Name of first level nodes */
+ char *Colname; /* Name of second level nodes */
+ char *Mulnode; /* Name of multiple node */
+ char *XmlDB; /* Name of XML DB node */
+ char *Nslist; /* List of namespaces to register */
+ char *DefNs; /* Dummy name of default namespace */
+ char *Attrib; /* Table node attributes */
+ char *Hdattr; /* Header node attributes */
+ int Coltype; /* Default column type */
+ int Limit; /* Limit of multiple values */
+ int Header; /* n first rows are header rows */
+ bool Xpand; /* Put multiple tags in several rows */
+ bool Usedom; /* True: DOM, False: libxml2 */
+ }; // end of XMLDEF
+
+#if defined(INCLUDE_TDBXML)
+/***********************************************************************/
+/* This is the class declaration for the simple XML tables. */
+/***********************************************************************/
+class DllExport TDBXML : public TDBASE {
+ friend class XMLCOL;
+ friend class XMULCOL;
+ friend class XPOSCOL;
+ public:
+ // Constructor
+ TDBXML(PXMLDEF tdp);
+ TDBXML(PTDBXML tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_XML;}
+ virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXML(this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+ virtual int GetRecpos(void);
+ virtual int GetProgCur(void) {return N;}
+ virtual PSZ GetFile(PGLOBAL g) {return Xfile;}
+ virtual void SetFile(PGLOBAL g, PSZ fn) {Xfile = fn;}
+ virtual void ResetDB(void) {N = 0;}
+ virtual void ResetSize(void) {MaxSize = -1;}
+ virtual int RowNumber(PGLOBAL g, bool b = false);
+ int LoadTableFile(PGLOBAL g, char *filename);
+ bool Initialize(PGLOBAL g);
+ bool SetTabNode(PGLOBAL g);
+ void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node);
+ bool CheckRow(PGLOBAL g, bool b);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
+//virtual int GetMaxSame(PGLOBAL g) {return (Xpand) ? Limit : 1;}
+ virtual int Cardinality(PGLOBAL g);
+ virtual int GetMaxSize(PGLOBAL g);
+//virtual bool NeedIndexing(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+ virtual int CheckWrite(PGLOBAL g) {Checked = true; return 0;}
+ virtual const CHARSET_INFO *data_charset()
+ {return &my_charset_utf8_general_ci;}
+
+ protected:
+ // Members
+ PXDOC Docp;
+ PXNODE Root;
+ PXNODE Curp;
+ PXNODE DBnode;
+ PXNODE TabNode;
+ PXNODE RowNode;
+ PXNODE ColNode;
+ PXLIST Nlist;
+ PXLIST Clist;
+ PFBLOCK To_Xb; // Pointer to XML file block
+ PCOL Colp; // The multiple column
+ bool Changed; // After Update, Insert or Delete
+ bool Checked; // After Update check pass
+ bool NextSame; // Same next row
+ bool Xpand; // Put multiple tags in several rows
+ bool NewRow; // True when inserting a new row
+ bool Hasnod; // True if rows have subnodes
+ bool Write; // True for Insert and Update
+ bool Usedom; // True for DOM, False for libxml2
+ bool Bufdone; // True when column buffers allocated
+ bool Nodedone; // True when column nodes allocated
+ bool Void; // True if the file does not exist
+ char *Xfile; // The XML file
+ char *Enc; // New XML table file encoding
+ char *Tabname; // Name of Table node
+ char *Rowname; // Name of first level nodes
+ char *Colname; // Name of second level nodes
+ char *Mulnode; // Name of multiple node
+ char *XmlDB; // Name of XML DB node
+ char *Nslist; // List of namespaces to register
+ char *DefNs; // Dummy name of default namespace
+ char *Attrib; // Table node attribut(s)
+ char *Hdattr; // Header node attribut(s)
+ int Coltype; // Default column type
+ int Limit; // Limit of multiple values
+ int Header; // n first rows are header rows
+ int Multiple; // If multiple files
+ int Nrow; // The table cardinality
+ int Irow; // The current row index
+ int Nsub; // The current subrow index
+ int N; // The current Rowid
+ }; // end of class TDBXML
+
+/***********************************************************************/
+/* Class XMLCOL: XDB table access method column descriptor. */
+/***********************************************************************/
+class XMLCOL : public COLBLK {
+ public:
+ // Constructors
+ XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "XML");
+ XMLCOL(XMLCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_XML;}
+ virtual void SetTo_Val(PVAL valp) {To_Val = valp;}
+ bool ParseXpath(PGLOBAL g, bool mode);
+
+ // Methods
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ bool AllocBuf(PGLOBAL g, bool mode);
+ void AllocNodes(PGLOBAL g, PXDOC dp);
+
+ protected:
+//xmlNodePtr SelectSingleNode(xmlNodePtr node, char *name);
+
+ // Default constructor not to be used
+ XMLCOL(void) : COLBLK(1) {}
+
+ // Members
+ PXLIST Nl;
+ PXLIST Nlx;
+ PXNODE ColNode;
+ PXNODE ValNode;
+ PXNODE Cxnp;
+ PXNODE Vxnp;
+ PXATTR Vxap;
+ PXATTR AttNode;
+ PTDBXML Tdbp;
+ char *Valbuf; // To the node value buffer
+ char *Xname; // The node or attribute name
+ char* *Nodes; // The intermediate nodes
+ int Type; // 0: Attribute, 1: Tag, 2: position
+ int Nod; // The number of intermediate nodes
+ int Inod; // Index of multiple node
+ int Rank; // Position
+ bool Mul; // true for multiple column
+ bool Checked; // Was checked while Updating
+ int Long; // Buffer length
+ int Nx; // The last read row
+ int Sx; // The last read sub-row
+ PVAL To_Val; // To value used for Update/Insert
+ }; // end of class XMLCOL
+
+/***********************************************************************/
+/* Derived class XMLCOLX: used to replace a multiple XMLCOL by the */
+/* derived class XMULCOL that has specialize read and write functions.*/
+/* Note: this works only if the members of the derived class are the */
+/* same than the ones of the original class (NO added members). */
+/***********************************************************************/
+class XMLCOLX : public XMLCOL {
+ public:
+ // Fake operator new used to change a filter into a derived filter
+ void * operator new(size_t size, PXMLCOL colp) {return colp;}
+#if !defined(__BORLANDC__)
+ // Avoid warning C4291 by defining a matching dummy delete operator
+ void operator delete(void *, size_t size) {}
+ void operator delete(void *, PXMLCOL) {}
+#endif
+ }; // end of class XMLCOLX
+
+/***********************************************************************/
+/* Class XMULCOL: XML table access method multiple column descriptor. */
+/***********************************************************************/
+class XMULCOL : public XMLCOLX {
+ public:
+ // The constructor must restore Value because XOBJECT has a void
+ // constructor called by default that set Value to NULL
+ XMULCOL(PVAL valp) {Value = valp; Mul = true;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ }; // end of class XMULCOL
+
+/***********************************************************************/
+/* Class XPOSCOL: XML table column accessed by position. */
+/***********************************************************************/
+class XPOSCOL : public XMLCOLX {
+ public:
+ // The constructor must restore Value because XOBJECT has a void
+ // constructor called by default that set Value to NULL
+ XPOSCOL(PVAL valp) {Value = valp;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ }; // end of class XPOSCOL
+#endif // INCLUDE_TDBXML
diff --git a/storage/connect/user_connect.cc b/storage/connect/user_connect.cc
index 3a48c491cb8..165ef423e7c 100644
--- a/storage/connect/user_connect.cc
+++ b/storage/connect/user_connect.cc
@@ -1,163 +1,161 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2012
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/**
- @file user_connect.cc
-
- @brief
- Implements the user_connect class.
-
- @details
- To support multi_threading, each query creates and use a PlugDB "user"
- that is a connection with its personnal memory allocation.
-
- @note
-
-*/
-
-/****************************************************************************/
-/* Author: Olivier Bertrand -- bertrandop@gmail.com -- 2004-2012 */
-/****************************************************************************/
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#define DONT_DEFINE_VOID
-#define MYSQL_SERVER
-#include "sql_class.h"
-#undef OFFSET
-
-#define NOPARSE
-#include "osutil.h"
-#include "global.h"
-#include "plgdbsem.h"
-#include "connect.h"
-#include "user_connect.h"
-#include "mycat.h"
-
-extern "C" char plgxini[];
-extern int xtrace;
-
-/****************************************************************************/
-/* Initialize the user_connect static member. */
-/****************************************************************************/
-PCONNECT user_connect::to_users= NULL;
-
-/* -------------------------- class user_connect -------------------------- */
-
-/****************************************************************************/
-/* Constructor. */
-/****************************************************************************/
-user_connect::user_connect(THD *thd, const char *dbn)
-{
- thdp= thd;
- next= NULL;
- previous= NULL;
- g= NULL;
- last_query_id= 0;
- count= 0;
-
- // Statistics
- nrd= fnd= nfd= 0;
- tb1= 0;
-} // end of user_connect constructor
-
-
-/****************************************************************************/
-/* Destructor. */
-/****************************************************************************/
-user_connect::~user_connect()
-{
- // Terminate CONNECT and Plug-like environment, should return NULL
- g= CntExit(g);
-} // end of user_connect destructor
-
-
-/****************************************************************************/
-/* Initialization. */
-/****************************************************************************/
-bool user_connect::user_init()
-{
- // Initialize Plug-like environment
- PACTIVITY ap= NULL;
- PDBUSER dup= NULL;
-
- // Areasize= 64M because of VEC tables. Should be parameterisable
- g= PlugInit(NULL, 67108864);
-//g= PlugInit(NULL, 134217728); // 128M was because of old embedded tests
-
- // Check whether the initialization is complete
- if (!g || !g->Sarea || PlugSubSet(g, g->Sarea, g->Sarea_Size)
- || !(dup= PlgMakeUser(g))) {
- if (g)
- printf("%s\n", g->Message);
-
- int rc= PlugExit(g);
- g= NULL;
- free(dup);
- return true;
- } // endif g->
-
- dup->Catalog= new MYCAT(NULL);
-
- ap= new ACTIVITY;
- memset(ap, 0, sizeof(ACTIVITY));
- strcpy(ap->Ap_Name, "CONNECT");
- g->Activityp= ap;
- g->Activityp->Aptr= dup;
- next= to_users;
- to_users= this;
-
- if (next)
- next->previous= this;
-
- last_query_id= thdp->query_id;
- count= 1;
- return false;
-} // end of user_init
-
-
-void user_connect::SetHandler(ha_connect *hc)
-{
- PDBUSER dup= (PDBUSER)g->Activityp->Aptr;
- MYCAT *mc= (MYCAT*)dup->Catalog;
- mc->SetHandler(hc);
-}
-
-/****************************************************************************/
-/* Check whether we begin a new query and if so cleanup the previous one. */
-/****************************************************************************/
-bool user_connect::CheckCleanup(void)
-{
- if (thdp->query_id > last_query_id) {
- PlugCleanup(g, true);
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- g->Xchk = NULL;
- g->Createas = 0;
- g->Alchecked = 0;
-#if defined(MRRBKA_SUPPORT)
- g->Mrr = 0;
-#endif // MRRBKA_SUPPORT
- last_query_id= thdp->query_id;
-
- if (xtrace)
- printf("=====> Begin new query %llu\n", last_query_id);
-
- return true;
- } // endif query_id
-
- return false;
-} // end of CheckCleanup
-
+/* Copyright (C) Olivier Bertrand 2004 - 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file user_connect.cc
+
+ @brief
+ Implements the user_connect class.
+
+ @details
+ To support multi_threading, each query creates and use a PlugDB "user"
+ that is a connection with its personnal memory allocation.
+
+ @note
+
+*/
+
+/****************************************************************************/
+/* Author: Olivier Bertrand -- bertrandop@gmail.com -- 2004-2012 */
+/****************************************************************************/
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#define DONT_DEFINE_VOID
+#define MYSQL_SERVER
+#include "sql_class.h"
+#undef OFFSET
+
+#define NOPARSE
+#include "osutil.h"
+#include "global.h"
+#include "plgdbsem.h"
+#include "connect.h"
+#include "user_connect.h"
+#include "mycat.h"
+
+extern "C" char plgxini[];
+extern int xtrace;
+
+/****************************************************************************/
+/* Initialize the user_connect static member. */
+/****************************************************************************/
+PCONNECT user_connect::to_users= NULL;
+
+/* -------------------------- class user_connect -------------------------- */
+
+/****************************************************************************/
+/* Constructor. */
+/****************************************************************************/
+user_connect::user_connect(THD *thd, const char *dbn)
+{
+ thdp= thd;
+ next= NULL;
+ previous= NULL;
+ g= NULL;
+ last_query_id= 0;
+ count= 0;
+
+ // Statistics
+ nrd= fnd= nfd= 0;
+ tb1= 0;
+} // end of user_connect constructor
+
+
+/****************************************************************************/
+/* Destructor. */
+/****************************************************************************/
+user_connect::~user_connect()
+{
+ // Terminate CONNECT and Plug-like environment, should return NULL
+ g= CntExit(g);
+} // end of user_connect destructor
+
+
+/****************************************************************************/
+/* Initialization. */
+/****************************************************************************/
+bool user_connect::user_init()
+{
+ // Initialize Plug-like environment
+ PACTIVITY ap= NULL;
+ PDBUSER dup= NULL;
+
+ // Areasize= 64M because of VEC tables. Should be parameterisable
+ g= PlugInit(NULL, 67108864);
+//g= PlugInit(NULL, 134217728); // 128M was because of old embedded tests
+
+ // Check whether the initialization is complete
+ if (!g || !g->Sarea || PlugSubSet(g, g->Sarea, g->Sarea_Size)
+ || !(dup= PlgMakeUser(g))) {
+ if (g)
+ printf("%s\n", g->Message);
+
+ int rc= PlugExit(g);
+ g= NULL;
+ free(dup);
+ return true;
+ } // endif g->
+
+ dup->Catalog= new MYCAT(NULL);
+
+ ap= new ACTIVITY;
+ memset(ap, 0, sizeof(ACTIVITY));
+ strcpy(ap->Ap_Name, "CONNECT");
+ g->Activityp= ap;
+ g->Activityp->Aptr= dup;
+ next= to_users;
+ to_users= this;
+
+ if (next)
+ next->previous= this;
+
+ last_query_id= thdp->query_id;
+ count= 1;
+ return false;
+} // end of user_init
+
+
+void user_connect::SetHandler(ha_connect *hc)
+{
+ PDBUSER dup= (PDBUSER)g->Activityp->Aptr;
+ MYCAT *mc= (MYCAT*)dup->Catalog;
+ mc->SetHandler(hc);
+}
+
+/****************************************************************************/
+/* Check whether we begin a new query and if so cleanup the previous one. */
+/****************************************************************************/
+bool user_connect::CheckCleanup(void)
+{
+ if (thdp->query_id > last_query_id) {
+ PlugCleanup(g, true);
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Xchk = NULL;
+ g->Createas = 0;
+ g->Alchecked = 0;
+ g->Mrr = 0;
+ last_query_id= thdp->query_id;
+
+ if (xtrace)
+ printf("=====> Begin new query %llu\n", last_query_id);
+
+ return true;
+ } // endif query_id
+
+ return false;
+} // end of CheckCleanup
+
diff --git a/storage/connect/user_connect.h b/storage/connect/user_connect.h
index ef17a958824..44e4e94fa8a 100644
--- a/storage/connect/user_connect.h
+++ b/storage/connect/user_connect.h
@@ -63,11 +63,10 @@ public:
PCONNECT next; // Next user in chain
PCONNECT previous; // Previous user in chain
PGLOBAL g; // The common handle to CONNECT
-//char dbname[32]; // The DBCONNECT database
query_id_t last_query_id; // the latest user query id
int count; // if used by several handlers
// Statistics
ulong nrd, fnd, nfd;
- ulonglong tb1;
+ ulonglong tb1;
}; // end of user_connect class definition
diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp
index e8da2044a47..49766bbd447 100644
--- a/storage/connect/valblk.cpp
+++ b/storage/connect/valblk.cpp
@@ -448,7 +448,6 @@ template <>
uchar TYPBLK<uchar>::GetTypedValue(PVBLK blk, int n)
{return blk->GetUTinyValue(n);}
-#if defined(BLK_INDX)
/***********************************************************************/
/* Set one value in a block if val is less than the current value. */
/***********************************************************************/
@@ -478,7 +477,6 @@ void TYPBLK<TYPE>::SetMax(PVAL valp, int n)
tmin = tval;
} // end of SetMax
-#endif // BLK_INDX
#if 0
/***********************************************************************/
@@ -812,7 +810,6 @@ void CHRBLK::SetValue(PVBLK pv, int n1, int n2)
SetNull(n1, b);
} // end of SetValue
-#if defined(BLK_INDX)
/***********************************************************************/
/* Set one value in a block if val is less than the current value. */
/***********************************************************************/
@@ -842,7 +839,6 @@ void CHRBLK::SetMax(PVAL valp, int n)
memcpy(bp, vp, Long);
} // end of SetMax
-#endif // BLK_INDX
#if 0
/***********************************************************************/
@@ -1166,7 +1162,6 @@ void STRBLK::SetValue(char *sp, uint len, int n)
Strp[n] = p;
} // end of SetValue
-#if defined(BLK_INDX)
/***********************************************************************/
/* Set one value in a block if val is less than the current value. */
/***********************************************************************/
@@ -1194,7 +1189,6 @@ void STRBLK::SetMax(PVAL valp, int n)
SetValue(valp, n);
} // end of SetMax
-#endif // BLK_INDX
/***********************************************************************/
/* Move one value from i to j. */
@@ -1335,7 +1329,6 @@ void DATBLK::SetValue(PSZ p, int n)
} // end of SetValue
-#if defined(BLK_INDX)
/* -------------------------- Class MBVALS --------------------------- */
/***********************************************************************/
@@ -1379,7 +1372,6 @@ void MBVALS::Free(void)
PlgDBfree(Mblk);
Vblk = NULL;
} // end of Free
-#endif // BLK_INDX
/* ------------------------- End of Valblk --------------------------- */
diff --git a/storage/connect/valblk.h b/storage/connect/valblk.h
index 6b427512332..f85d34d6b77 100644
--- a/storage/connect/valblk.h
+++ b/storage/connect/valblk.h
@@ -1,351 +1,331 @@
-/*************** Valblk H Declares Source Code File (.H) ***************/
-/* Name: VALBLK.H Version 2.1 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* This file contains the VALBLK and derived classes declares. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include required application header files */
-/* assert.h is header required when using the assert function. */
-/* block.h is header containing Block global declarations. */
-/***********************************************************************/
-#ifndef __VALBLK__H__
-#define __VALBLK__H__
-#include "value.h"
-
-/***********************************************************************/
-/* Utility used to allocate value blocks. */
-/***********************************************************************/
-DllExport PVBLK AllocValBlock(PGLOBAL, void*, int, int, int, int,
- bool, bool, bool);
-const char *GetFmt(int type, bool un = false);
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* DB static external variables. */
-/***********************************************************************/
-extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
-
-/***********************************************************************/
-/* Class MBVALS is a utility class for (re)allocating VALBLK's. */
-/***********************************************************************/
-class MBVALS : public BLOCK {
-//friend class LSTBLK;
- friend class ARRAY;
- public:
- // Constructors
- MBVALS(void) {Vblk = NULL; Mblk = Nmblk;}
-
- // Methods
- void *GetMemp(void) {return Mblk.Memp;}
- PVBLK Allocate(PGLOBAL g, int type, int len, int prec,
- int n, bool sub = FALSE);
- bool ReAllocate(PGLOBAL g, int n);
- void Free(void);
-
- protected:
- // Members
- PVBLK Vblk; // Pointer to VALBLK
- MBLOCK Mblk; // The memory block
- }; // end of class MBVALS
-
-typedef class MBVALS *PMBV;
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* Class VALBLK represent a base class for variable blocks. */
-/***********************************************************************/
-class VALBLK : public BLOCK {
-//friend void SemColData(PGLOBAL g, PSEM semp);
- public:
- // Constructors
- VALBLK(void *mp, int type, int nval, bool un = false);
-
- // Implementation
- int GetNval(void) {return Nval;}
- void SetNval(int n) {Nval = n;}
- void *GetValPointer(void) {return Blkp;}
- void SetValPointer(void *mp) {Blkp = mp;}
- int GetType(void) {return Type;}
- int GetPrec(void) {return Prec;}
- void SetCheck(bool b) {Check = b;}
- void MoveNull(int i, int j)
- {if (To_Nulls) To_Nulls[j] = To_Nulls[j];}
- virtual void SetNull(int n, bool b)
- {if (To_Nulls) {To_Nulls[n] = (b) ? '*' : 0;}}
- virtual bool IsNull(int n) {return To_Nulls && To_Nulls[n];}
- virtual void SetNullable(bool b);
- virtual bool IsUnsigned(void) {return Unsigned;}
- virtual void Init(PGLOBAL g, bool check) = 0;
- virtual int GetVlen(void) = 0;
- virtual PSZ GetCharValue(int n);
- virtual char GetTinyValue(int n) = 0;
- virtual uchar GetUTinyValue(int n) = 0;
- virtual short GetShortValue(int n) = 0;
- virtual ushort GetUShortValue(int n) = 0;
- virtual int GetIntValue(int n) = 0;
- virtual uint GetUIntValue(int n) = 0;
- virtual longlong GetBigintValue(int n) = 0;
- virtual ulonglong GetUBigintValue(int n) = 0;
- virtual double GetFloatValue(int n) = 0;
- virtual char *GetCharString(char *p, int n) = 0;
- virtual void ReAlloc(void *mp, int n) {Blkp = mp; Nval = n;}
- virtual void Reset(int n) = 0;
- virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
- virtual void SetPrec(int p) {}
- virtual bool IsCi(void) {return false;}
-
- // Methods
- virtual void SetValue(short sval, int n) {assert(false);}
- virtual void SetValue(ushort sval, int n) {assert(false);}
- virtual void SetValue(int lval, int n) {assert(false);}
- virtual void SetValue(uint lval, int n) {assert(false);}
- virtual void SetValue(longlong lval, int n) {assert(false);}
- virtual void SetValue(ulonglong lval, int n) {assert(false);}
- virtual void SetValue(double fval, int n) {assert(false);}
- virtual void SetValue(char cval, int n) {assert(false);}
- virtual void SetValue(uchar cval, int n) {assert(false);}
- virtual void SetValue(PSZ sp, int n) {assert(false);}
- virtual void SetValue(char *sp, uint len, int n) {assert(false);}
- virtual void SetValue(PVAL valp, int n) = 0;
- virtual void SetValue(PVBLK pv, int n1, int n2) = 0;
-#if defined(BLK_INDX)
- virtual void SetMin(PVAL valp, int n) = 0;
- virtual void SetMax(PVAL valp, int n) = 0;
-#endif // BLK_INDX
-#if 0
- virtual void SetValues(PVBLK pv, int i, int n) = 0;
- virtual void AddMinus1(PVBLK pv, int n1, int n2) {assert(false);}
-#endif // 0
- virtual void Move(int i, int j) = 0;
- virtual int CompVal(PVAL vp, int n) = 0;
- virtual int CompVal(int i1, int i2) = 0;
- virtual void *GetValPtr(int n) = 0;
- virtual void *GetValPtrEx(int n) = 0;
- virtual int Find(PVAL vp) = 0;
- virtual int GetMaxLength(void) = 0;
- bool Locate(PVAL vp, int& i);
-
- protected:
- void ChkIndx(int n);
- void ChkTyp(PVAL v);
- void ChkTyp(PVBLK vb);
-
- // Members
- PGLOBAL Global; // Used for messages and allocation
- char *To_Nulls; // Null values array
- void *Blkp; // To value block
- bool Check; // If true SetValue types must match
- bool Nullable; // True if values can be null
- bool Unsigned; // True if values are unsigned
- int Type; // Type of individual values
- int Nval; // Max number of values in block
- int Prec; // Precision of float values
- }; // end of class VALBLK
-
-/***********************************************************************/
-/* Class TYPBLK: represents a block of typed values. */
-/***********************************************************************/
-template <class TYPE>
-class TYPBLK : public VALBLK {
- public:
- // Constructors
- TYPBLK(void *mp, int size, int type, int prec = 0, bool un = false);
-//TYPBLK(void *mp, int size, int prec, int type);
-
- // Implementation
- virtual void Init(PGLOBAL g, bool check);
- virtual int GetVlen(void) {return sizeof(TYPE);}
-//virtual PSZ GetCharValue(int n);
- virtual char GetTinyValue(int n) {return (char)Typp[n];}
- virtual uchar GetUTinyValue(int n) {return (uchar)Typp[n];}
- virtual short GetShortValue(int n) {return (short)Typp[n];}
- virtual ushort GetUShortValue(int n) {return (ushort)Typp[n];}
- virtual int GetIntValue(int n) {return (int)Typp[n];}
- virtual uint GetUIntValue(int n) {return (uint)Typp[n];}
- virtual longlong GetBigintValue(int n) {return (longlong)Typp[n];}
- virtual ulonglong GetUBigintValue(int n) {return (ulonglong)Typp[n];}
- virtual double GetFloatValue(int n) {return (double)Typp[n];}
- virtual char *GetCharString(char *p, int n);
- virtual void Reset(int n) {Typp[n] = 0;}
-
- // Methods
- virtual void SetValue(PSZ sp, int n);
- virtual void SetValue(char *sp, uint len, int n);
- virtual void SetValue(short sval, int n)
- {Typp[n] = (TYPE)sval; SetNull(n, false);}
- virtual void SetValue(ushort sval, int n)
- {Typp[n] = (TYPE)sval; SetNull(n, false);}
- virtual void SetValue(int lval, int n)
- {Typp[n] = (TYPE)lval; SetNull(n, false);}
- virtual void SetValue(uint lval, int n)
- {Typp[n] = (TYPE)lval; SetNull(n, false);}
- virtual void SetValue(longlong lval, int n)
- {Typp[n] = (TYPE)lval; SetNull(n, false);}
- virtual void SetValue(ulonglong lval, int n)
- {Typp[n] = (TYPE)lval; SetNull(n, false);}
- virtual void SetValue(double fval, int n)
- {Typp[n] = (TYPE)fval; SetNull(n, false);}
- virtual void SetValue(char cval, int n)
- {Typp[n] = (TYPE)cval; SetNull(n, false);}
- virtual void SetValue(uchar cval, int n)
- {Typp[n] = (TYPE)cval; SetNull(n, false);}
- virtual void SetValue(PVAL valp, int n);
- virtual void SetValue(PVBLK pv, int n1, int n2);
-//virtual void SetValues(PVBLK pv, int k, int n);
-#if defined(BLK_INDX)
- virtual void SetMin(PVAL valp, int n);
- virtual void SetMax(PVAL valp, int n);
-#endif // BLK_INDX
- virtual void Move(int i, int j);
- virtual int CompVal(PVAL vp, int n);
- virtual int CompVal(int i1, int i2);
- virtual void *GetValPtr(int n);
- virtual void *GetValPtrEx(int n);
- virtual int Find(PVAL vp);
- virtual int GetMaxLength(void);
-
- protected:
- // Specialized functions
- static ulonglong MaxVal(void);
- TYPE GetTypedValue(PVAL vp);
- TYPE GetTypedValue(PVBLK blk, int n);
-
- // Members
- TYPE* const &Typp;
- const char *Fmt;
- }; // end of class TYPBLK
-
-/***********************************************************************/
-/* Class CHRBLK: represent a block of fixed length strings. */
-/***********************************************************************/
-class CHRBLK : public VALBLK {
- public:
- // Constructors
- CHRBLK(void *mp, int size, int len, int prec, bool b);
-
- // Implementation
- virtual void Init(PGLOBAL g, bool check);
- virtual int GetVlen(void) {return Long;}
- virtual PSZ GetCharValue(int n);
- virtual char GetTinyValue(int n);
- virtual uchar GetUTinyValue(int n);
- virtual short GetShortValue(int n);
- virtual ushort GetUShortValue(int n);
- virtual int GetIntValue(int n);
- virtual uint GetUIntValue(int n);
- virtual longlong GetBigintValue(int n);
- virtual ulonglong GetUBigintValue(int n);
- virtual double GetFloatValue(int n);
- virtual char *GetCharString(char *p, int n);
- virtual void Reset(int n);
- virtual void SetPrec(int p) {Ci = (p != 0);}
- virtual bool IsCi(void) {return Ci;}
-
- // Methods
- virtual void SetValue(PSZ sp, int n);
- virtual void SetValue(char *sp, uint len, int n);
- virtual void SetValue(PVAL valp, int n);
- virtual void SetValue(PVBLK pv, int n1, int n2);
-//virtual void SetValues(PVBLK pv, int k, int n);
-#if defined(BLK_INDX)
- virtual void SetMin(PVAL valp, int n);
- virtual void SetMax(PVAL valp, int n);
-#endif // BLK_INDX
- virtual void Move(int i, int j);
- virtual int CompVal(PVAL vp, int n);
- virtual int CompVal(int i1, int i2);
- virtual void *GetValPtr(int n);
- virtual void *GetValPtrEx(int n);
- virtual int Find(PVAL vp);
- virtual int GetMaxLength(void);
-
- protected:
- // Members
- char* const &Chrp; // Pointer to char buffer
- PSZ Valp; // Used to make a zero ended value
- bool Blanks; // True for right filling with blanks
- bool Ci; // True if case insensitive
- int Long; // Length of each string
- }; // end of class CHRBLK
-
-/***********************************************************************/
-/* Class STRBLK: represent a block of string pointers. */
-/* Currently this class is used only by the DECODE scalar function */
-/* and by the MyColumn function to store date formats. */
-/***********************************************************************/
-class STRBLK : public VALBLK {
- public:
- // Constructors
- STRBLK(PGLOBAL g, void *mp, int size);
-
- // Implementation
- virtual void SetNull(int n, bool b) {if (b) {Strp[n] = NULL;}}
- virtual bool IsNull(int n) {return Strp[n] == NULL;}
- virtual void SetNullable(bool b) {} // Always nullable
- virtual void Init(PGLOBAL g, bool check);
- virtual int GetVlen(void) {return sizeof(PSZ);}
- virtual PSZ GetCharValue(int n) {return Strp[n];}
- virtual char GetTinyValue(int n);
- virtual uchar GetUTinyValue(int n);
- virtual short GetShortValue(int n);
- virtual ushort GetUShortValue(int n);
- virtual int GetIntValue(int n);
- virtual uint GetUIntValue(int n);
- virtual longlong GetBigintValue(int n);
- virtual ulonglong GetUBigintValue(int n);
- virtual double GetFloatValue(int n) {return atof(Strp[n]);}
- virtual char *GetCharString(char *p, int n) {return Strp[n];}
- virtual void Reset(int n) {Strp[n] = NULL;}
-
- // Methods
- virtual void SetValue(PSZ sp, int n);
- virtual void SetValue(char *sp, uint len, int n);
- virtual void SetValue(PVAL valp, int n);
- virtual void SetValue(PVBLK pv, int n1, int n2);
-//virtual void SetValues(PVBLK pv, int k, int n);
-#if defined(BLK_INDX)
- virtual void SetMin(PVAL valp, int n);
- virtual void SetMax(PVAL valp, int n);
-#endif // BLK_INDX
- virtual void Move(int i, int j);
- virtual int CompVal(PVAL vp, int n);
- virtual int CompVal(int i1, int i2);
- virtual void *GetValPtr(int n);
- virtual void *GetValPtrEx(int n);
- virtual int Find(PVAL vp);
- virtual int GetMaxLength(void);
-
- // Specific
- void SetSorted(bool b) {Sorted = b;}
-
- protected:
- // Members
- PSZ* const &Strp; // Pointer to PSZ buffer
- bool Sorted; // Values are (semi?) sorted
- }; // end of class STRBLK
-
-/***********************************************************************/
-/* Class DATBLK: represents a block of time stamp values. */
-/***********************************************************************/
-class DATBLK : public TYPBLK<int> {
- public:
- // Constructor
- DATBLK(void *mp, int size);
-
- // Implementation
- virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
- virtual char *GetCharString(char *p, int n);
-
- // Methods
- virtual void SetValue(PSZ sp, int n);
-
- protected:
- // Members
- PVAL Dvalp; // Date value used to convert string
- }; // end of class DATBLK
-
-#endif // __VALBLK__H__
-
+/*************** Valblk H Declares Source Code File (.H) ***************/
+/* Name: VALBLK.H Version 2.1 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* This file contains the VALBLK and derived classes declares. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include required application header files */
+/* assert.h is header required when using the assert function. */
+/* block.h is header containing Block global declarations. */
+/***********************************************************************/
+#ifndef __VALBLK__H__
+#define __VALBLK__H__
+#include "value.h"
+
+/***********************************************************************/
+/* Utility used to allocate value blocks. */
+/***********************************************************************/
+DllExport PVBLK AllocValBlock(PGLOBAL, void*, int, int, int, int,
+ bool, bool, bool);
+const char *GetFmt(int type, bool un = false);
+
+/***********************************************************************/
+/* DB static external variables. */
+/***********************************************************************/
+extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
+
+/***********************************************************************/
+/* Class MBVALS is a utility class for (re)allocating VALBLK's. */
+/***********************************************************************/
+class MBVALS : public BLOCK {
+//friend class LSTBLK;
+ friend class ARRAY;
+ public:
+ // Constructors
+ MBVALS(void) {Vblk = NULL; Mblk = Nmblk;}
+
+ // Methods
+ void *GetMemp(void) {return Mblk.Memp;}
+ PVBLK Allocate(PGLOBAL g, int type, int len, int prec,
+ int n, bool sub = FALSE);
+ bool ReAllocate(PGLOBAL g, int n);
+ void Free(void);
+
+ protected:
+ // Members
+ PVBLK Vblk; // Pointer to VALBLK
+ MBLOCK Mblk; // The memory block
+ }; // end of class MBVALS
+
+typedef class MBVALS *PMBV;
+
+/***********************************************************************/
+/* Class VALBLK represent a base class for variable blocks. */
+/***********************************************************************/
+class VALBLK : public BLOCK {
+ public:
+ // Constructors
+ VALBLK(void *mp, int type, int nval, bool un = false);
+
+ // Implementation
+ int GetNval(void) {return Nval;}
+ void SetNval(int n) {Nval = n;}
+ void *GetValPointer(void) {return Blkp;}
+ void SetValPointer(void *mp) {Blkp = mp;}
+ int GetType(void) {return Type;}
+ int GetPrec(void) {return Prec;}
+ void SetCheck(bool b) {Check = b;}
+ void MoveNull(int i, int j)
+ {if (To_Nulls) To_Nulls[j] = To_Nulls[j];}
+ virtual void SetNull(int n, bool b)
+ {if (To_Nulls) {To_Nulls[n] = (b) ? '*' : 0;}}
+ virtual bool IsNull(int n) {return To_Nulls && To_Nulls[n];}
+ virtual void SetNullable(bool b);
+ virtual bool IsUnsigned(void) {return Unsigned;}
+ virtual void Init(PGLOBAL g, bool check) = 0;
+ virtual int GetVlen(void) = 0;
+ virtual PSZ GetCharValue(int n);
+ virtual char GetTinyValue(int n) = 0;
+ virtual uchar GetUTinyValue(int n) = 0;
+ virtual short GetShortValue(int n) = 0;
+ virtual ushort GetUShortValue(int n) = 0;
+ virtual int GetIntValue(int n) = 0;
+ virtual uint GetUIntValue(int n) = 0;
+ virtual longlong GetBigintValue(int n) = 0;
+ virtual ulonglong GetUBigintValue(int n) = 0;
+ virtual double GetFloatValue(int n) = 0;
+ virtual char *GetCharString(char *p, int n) = 0;
+ virtual void ReAlloc(void *mp, int n) {Blkp = mp; Nval = n;}
+ virtual void Reset(int n) = 0;
+ virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
+ virtual void SetPrec(int p) {}
+ virtual bool IsCi(void) {return false;}
+
+ // Methods
+ virtual void SetValue(short sval, int n) {assert(false);}
+ virtual void SetValue(ushort sval, int n) {assert(false);}
+ virtual void SetValue(int lval, int n) {assert(false);}
+ virtual void SetValue(uint lval, int n) {assert(false);}
+ virtual void SetValue(longlong lval, int n) {assert(false);}
+ virtual void SetValue(ulonglong lval, int n) {assert(false);}
+ virtual void SetValue(double fval, int n) {assert(false);}
+ virtual void SetValue(char cval, int n) {assert(false);}
+ virtual void SetValue(uchar cval, int n) {assert(false);}
+ virtual void SetValue(PSZ sp, int n) {assert(false);}
+ virtual void SetValue(char *sp, uint len, int n) {assert(false);}
+ virtual void SetValue(PVAL valp, int n) = 0;
+ virtual void SetValue(PVBLK pv, int n1, int n2) = 0;
+ virtual void SetMin(PVAL valp, int n) = 0;
+ virtual void SetMax(PVAL valp, int n) = 0;
+ virtual void Move(int i, int j) = 0;
+ virtual int CompVal(PVAL vp, int n) = 0;
+ virtual int CompVal(int i1, int i2) = 0;
+ virtual void *GetValPtr(int n) = 0;
+ virtual void *GetValPtrEx(int n) = 0;
+ virtual int Find(PVAL vp) = 0;
+ virtual int GetMaxLength(void) = 0;
+ bool Locate(PVAL vp, int& i);
+
+ protected:
+ void ChkIndx(int n);
+ void ChkTyp(PVAL v);
+ void ChkTyp(PVBLK vb);
+
+ // Members
+ PGLOBAL Global; // Used for messages and allocation
+ char *To_Nulls; // Null values array
+ void *Blkp; // To value block
+ bool Check; // If true SetValue types must match
+ bool Nullable; // True if values can be null
+ bool Unsigned; // True if values are unsigned
+ int Type; // Type of individual values
+ int Nval; // Max number of values in block
+ int Prec; // Precision of float values
+ }; // end of class VALBLK
+
+/***********************************************************************/
+/* Class TYPBLK: represents a block of typed values. */
+/***********************************************************************/
+template <class TYPE>
+class TYPBLK : public VALBLK {
+ public:
+ // Constructors
+ TYPBLK(void *mp, int size, int type, int prec = 0, bool un = false);
+
+ // Implementation
+ virtual void Init(PGLOBAL g, bool check);
+ virtual int GetVlen(void) {return sizeof(TYPE);}
+ virtual char GetTinyValue(int n) {return (char)Typp[n];}
+ virtual uchar GetUTinyValue(int n) {return (uchar)Typp[n];}
+ virtual short GetShortValue(int n) {return (short)Typp[n];}
+ virtual ushort GetUShortValue(int n) {return (ushort)Typp[n];}
+ virtual int GetIntValue(int n) {return (int)Typp[n];}
+ virtual uint GetUIntValue(int n) {return (uint)Typp[n];}
+ virtual longlong GetBigintValue(int n) {return (longlong)Typp[n];}
+ virtual ulonglong GetUBigintValue(int n) {return (ulonglong)Typp[n];}
+ virtual double GetFloatValue(int n) {return (double)Typp[n];}
+ virtual char *GetCharString(char *p, int n);
+ virtual void Reset(int n) {Typp[n] = 0;}
+
+ // Methods
+ virtual void SetValue(PSZ sp, int n);
+ virtual void SetValue(char *sp, uint len, int n);
+ virtual void SetValue(short sval, int n)
+ {Typp[n] = (TYPE)sval; SetNull(n, false);}
+ virtual void SetValue(ushort sval, int n)
+ {Typp[n] = (TYPE)sval; SetNull(n, false);}
+ virtual void SetValue(int lval, int n)
+ {Typp[n] = (TYPE)lval; SetNull(n, false);}
+ virtual void SetValue(uint lval, int n)
+ {Typp[n] = (TYPE)lval; SetNull(n, false);}
+ virtual void SetValue(longlong lval, int n)
+ {Typp[n] = (TYPE)lval; SetNull(n, false);}
+ virtual void SetValue(ulonglong lval, int n)
+ {Typp[n] = (TYPE)lval; SetNull(n, false);}
+ virtual void SetValue(double fval, int n)
+ {Typp[n] = (TYPE)fval; SetNull(n, false);}
+ virtual void SetValue(char cval, int n)
+ {Typp[n] = (TYPE)cval; SetNull(n, false);}
+ virtual void SetValue(uchar cval, int n)
+ {Typp[n] = (TYPE)cval; SetNull(n, false);}
+ virtual void SetValue(PVAL valp, int n);
+ virtual void SetValue(PVBLK pv, int n1, int n2);
+ virtual void SetMin(PVAL valp, int n);
+ virtual void SetMax(PVAL valp, int n);
+ virtual void Move(int i, int j);
+ virtual int CompVal(PVAL vp, int n);
+ virtual int CompVal(int i1, int i2);
+ virtual void *GetValPtr(int n);
+ virtual void *GetValPtrEx(int n);
+ virtual int Find(PVAL vp);
+ virtual int GetMaxLength(void);
+
+ protected:
+ // Specialized functions
+ static ulonglong MaxVal(void);
+ TYPE GetTypedValue(PVAL vp);
+ TYPE GetTypedValue(PVBLK blk, int n);
+
+ // Members
+ TYPE* const &Typp;
+ const char *Fmt;
+ }; // end of class TYPBLK
+
+/***********************************************************************/
+/* Class CHRBLK: represent a block of fixed length strings. */
+/***********************************************************************/
+class CHRBLK : public VALBLK {
+ public:
+ // Constructors
+ CHRBLK(void *mp, int size, int len, int prec, bool b);
+
+ // Implementation
+ virtual void Init(PGLOBAL g, bool check);
+ virtual int GetVlen(void) {return Long;}
+ virtual PSZ GetCharValue(int n);
+ virtual char GetTinyValue(int n);
+ virtual uchar GetUTinyValue(int n);
+ virtual short GetShortValue(int n);
+ virtual ushort GetUShortValue(int n);
+ virtual int GetIntValue(int n);
+ virtual uint GetUIntValue(int n);
+ virtual longlong GetBigintValue(int n);
+ virtual ulonglong GetUBigintValue(int n);
+ virtual double GetFloatValue(int n);
+ virtual char *GetCharString(char *p, int n);
+ virtual void Reset(int n);
+ virtual void SetPrec(int p) {Ci = (p != 0);}
+ virtual bool IsCi(void) {return Ci;}
+
+ // Methods
+ virtual void SetValue(PSZ sp, int n);
+ virtual void SetValue(char *sp, uint len, int n);
+ virtual void SetValue(PVAL valp, int n);
+ virtual void SetValue(PVBLK pv, int n1, int n2);
+ virtual void SetMin(PVAL valp, int n);
+ virtual void SetMax(PVAL valp, int n);
+ virtual void Move(int i, int j);
+ virtual int CompVal(PVAL vp, int n);
+ virtual int CompVal(int i1, int i2);
+ virtual void *GetValPtr(int n);
+ virtual void *GetValPtrEx(int n);
+ virtual int Find(PVAL vp);
+ virtual int GetMaxLength(void);
+
+ protected:
+ // Members
+ char* const &Chrp; // Pointer to char buffer
+ PSZ Valp; // Used to make a zero ended value
+ bool Blanks; // True for right filling with blanks
+ bool Ci; // True if case insensitive
+ int Long; // Length of each string
+ }; // end of class CHRBLK
+
+/***********************************************************************/
+/* Class STRBLK: represent a block of string pointers. */
+/* Currently this class is used only by the DECODE scalar function */
+/* and by the MyColumn function to store date formats. */
+/***********************************************************************/
+class STRBLK : public VALBLK {
+ public:
+ // Constructors
+ STRBLK(PGLOBAL g, void *mp, int size);
+
+ // Implementation
+ virtual void SetNull(int n, bool b) {if (b) {Strp[n] = NULL;}}
+ virtual bool IsNull(int n) {return Strp[n] == NULL;}
+ virtual void SetNullable(bool b) {} // Always nullable
+ virtual void Init(PGLOBAL g, bool check);
+ virtual int GetVlen(void) {return sizeof(PSZ);}
+ virtual PSZ GetCharValue(int n) {return Strp[n];}
+ virtual char GetTinyValue(int n);
+ virtual uchar GetUTinyValue(int n);
+ virtual short GetShortValue(int n);
+ virtual ushort GetUShortValue(int n);
+ virtual int GetIntValue(int n);
+ virtual uint GetUIntValue(int n);
+ virtual longlong GetBigintValue(int n);
+ virtual ulonglong GetUBigintValue(int n);
+ virtual double GetFloatValue(int n) {return atof(Strp[n]);}
+ virtual char *GetCharString(char *p, int n) {return Strp[n];}
+ virtual void Reset(int n) {Strp[n] = NULL;}
+
+ // Methods
+ virtual void SetValue(PSZ sp, int n);
+ virtual void SetValue(char *sp, uint len, int n);
+ virtual void SetValue(PVAL valp, int n);
+ virtual void SetValue(PVBLK pv, int n1, int n2);
+ virtual void SetMin(PVAL valp, int n);
+ virtual void SetMax(PVAL valp, int n);
+ virtual void Move(int i, int j);
+ virtual int CompVal(PVAL vp, int n);
+ virtual int CompVal(int i1, int i2);
+ virtual void *GetValPtr(int n);
+ virtual void *GetValPtrEx(int n);
+ virtual int Find(PVAL vp);
+ virtual int GetMaxLength(void);
+
+ // Specific
+ void SetSorted(bool b) {Sorted = b;}
+
+ protected:
+ // Members
+ PSZ* const &Strp; // Pointer to PSZ buffer
+ bool Sorted; // Values are (semi?) sorted
+ }; // end of class STRBLK
+
+/***********************************************************************/
+/* Class DATBLK: represents a block of time stamp values. */
+/***********************************************************************/
+class DATBLK : public TYPBLK<int> {
+ public:
+ // Constructor
+ DATBLK(void *mp, int size);
+
+ // Implementation
+ virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
+ virtual char *GetCharString(char *p, int n);
+
+ // Methods
+ virtual void SetValue(PSZ sp, int n);
+
+ protected:
+ // Members
+ PVAL Dvalp; // Date value used to convert string
+ }; // end of class DATBLK
+
+#endif // __VALBLK__H__
+
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index 7653b222a41..5f89c1ac23c 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -1,2219 +1,2205 @@
-/************* Value C++ Functions Source Code File (.CPP) *************/
-/* Name: VALUE.CPP Version 2.4 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */
-/* */
-/* This file contains the VALUE and derived classes family functions. */
-/* These classes contain values of different types. They are used so */
-/* new object types can be defined and added to the processing simply */
-/* (hopefully) adding their specific functions in this file. */
-/* First family is VALUE that represent single typed objects. It is */
-/* used by columns (COLBLK), SELECT and FILTER (derived) objects. */
-/* Second family is VALBLK, representing simple suballocated arrays */
-/* of values treated sequentially by FIX, BIN and VCT tables and */
-/* columns, as well for min/max blocks as for VCT column blocks. */
-/* Q&A: why not using only one family ? Simple values are arrays that */
-/* have only one element and arrays could have functions for all kind */
-/* of processing. The answer is a-because historically it was simpler */
-/* to do that way, b-because of performance on single values, and c- */
-/* to avoid too complicated classes and unuseful duplication of many */
-/* functions used on one family only. The drawback is that for new */
-/* types of objects, we shall have more classes to update. */
-/* Currently the only implemented types are STRING, INT, SHORT, TINY, */
-/* DATE and LONGLONG. Recently we added some UNSIGNED types. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant MariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-#include "sql_class.h"
-#include "sql_time.h"
-
-#if defined(WIN32)
-//#include <windows.h>
-#else // !WIN32
-#include <string.h>
-#endif // !WIN32
-
-#include <math.h>
-
-#undef DOMAIN // Was defined in math.h
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "preparse.h" // For DATPAR
-//#include "value.h"
-#include "valblk.h"
-#define NO_FUNC // Already defined in ODBConn
-#include "plgcnx.h" // For DB types
-#include "osutil.h"
-
-/***********************************************************************/
-/* Check macro's. */
-/***********************************************************************/
-#if defined(_DEBUG)
-#define CheckType(V) if (Type != V->GetType()) { \
- PGLOBAL& g = Global; \
- strcpy(g->Message, MSG(VALTYPE_NOMATCH)); \
- longjmp(g->jumper[g->jump_level], Type); }
-#else
-#define CheckType(V)
-#endif
-
-#define FOURYEARS 126230400 // Four years in seconds (1 leap)
-
-/***********************************************************************/
-/* Static variables. */
-/***********************************************************************/
-
-extern "C" int trace;
-
-/***********************************************************************/
-/* Initialize the DTVAL static member. */
-/***********************************************************************/
-int DTVAL::Shift = 0;
-
-/***********************************************************************/
-/* Routines called externally. */
-/***********************************************************************/
-bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
-
-#if !defined(WIN32)
-extern "C" {
-PSZ strupr(PSZ s);
-PSZ strlwr(PSZ s);
-}
-#endif // !WIN32
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Returns the bitmap representing the conditions that must not be */
-/* met when returning from TestValue for a given operator. */
-/* Bit one is EQ, bit 2 is LT, and bit 3 is GT. */
-/***********************************************************************/
-BYTE OpBmp(PGLOBAL g, OPVAL opc)
- {
- BYTE bt;
-
- switch (opc) {
- case OP_IN:
- case OP_EQ: bt = 0x06; break;
- case OP_NE: bt = 0x01; break;
- case OP_GT: bt = 0x03; break;
- case OP_GE: bt = 0x02; break;
- case OP_LT: bt = 0x05; break;
- case OP_LE: bt = 0x04; break;
- case OP_EXIST: bt = 0x00; break;
- default:
- sprintf(g->Message, MSG(BAD_FILTER_OP), opc);
- longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
- } // endswitch opc
-
- return bt;
- } // end of OpBmp
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* Get a long long number from its character representation. */
-/* IN p: Pointer to the numeric string */
-/* IN n: The string length */
-/* IN maxval: The number max value */
-/* IN un: True if the number must be unsigned */
-/* OUT rc: Set to TRUE for out of range value */
-/* OUT minus: Set to true if the number is negative */
-/* Returned val: The resulting number */
-/***********************************************************************/
-ulonglong CharToNumber(char *p, int n, ulonglong maxval,
- bool un, bool *minus, bool *rc)
-{
- char *p2;
- uchar c;
- ulonglong val;
-
- if (minus) *minus = false;
- if (rc) *rc = false;
-
- // Eliminate leading blanks or 0
- for (p2 = p + n; p < p2 && (*p == ' ' || *p == '0'); p++) ;
-
- // Get an eventual sign character
- switch (*p) {
- case '-':
- if (un) {
- if (rc) *rc = true;
- return 0;
- } else {
- maxval++;
- if (minus) *minus = true;
- } // endif Unsigned
-
- case '+':
- p++;
- break;
- } // endswitch *p
-
- for (val = 0; p < p2 && (c = (uchar)(*p - '0')) < 10; p++)
- if (val > (maxval - c) / 10) {
- val = maxval;
- if (rc) *rc = true;
- break;
- } else
- val = val * 10 + c;
-
- return val;
-} // end of CharToNumber
-
-/***********************************************************************/
-/* GetTypeName: returns the PlugDB internal type name. */
-/***********************************************************************/
-PSZ GetTypeName(int type)
- {
- PSZ name;
-
- switch (type) {
- case TYPE_STRING: name = "CHAR"; break;
- case TYPE_SHORT: name = "SMALLINT"; break;
- case TYPE_INT: name = "INTEGER"; break;
- case TYPE_BIGINT: name = "BIGINT"; break;
- case TYPE_DATE: name = "DATE"; break;
- case TYPE_DOUBLE: name = "DOUBLE"; break;
- case TYPE_TINY: name = "TINY"; break;
- case TYPE_DECIM: name = "DECIMAL"; break;
- default: name = "UNKNOWN"; break;
- } // endswitch type
-
- return name;
- } // end of GetTypeName
-
-/***********************************************************************/
-/* GetTypeSize: returns the PlugDB internal type size. */
-/***********************************************************************/
-int GetTypeSize(int type, int len)
- {
- switch (type) {
- case TYPE_DECIM:
- case TYPE_STRING: len = len * sizeof(char); break;
- case TYPE_SHORT: len = sizeof(short); break;
- case TYPE_INT: len = sizeof(int); break;
- case TYPE_BIGINT: len = sizeof(longlong); break;
- case TYPE_DATE: len = sizeof(int); break;
- case TYPE_DOUBLE: len = sizeof(double); break;
- case TYPE_TINY: len = sizeof(char); break;
- default: len = 0;
- } // endswitch type
-
- return len;
- } // end of GetTypeSize
-
-/***********************************************************************/
-/* GetFormatType: returns the FORMAT character(s) according to type. */
-/***********************************************************************/
-char *GetFormatType(int type)
- {
- char *c = "X";
-
- switch (type) {
- case TYPE_STRING: c = "C"; break;
- case TYPE_SHORT: c = "S"; break;
- case TYPE_INT: c = "N"; break;
- case TYPE_BIGINT: c = "L"; break;
- case TYPE_DOUBLE: c = "F"; break;
- case TYPE_DATE: c = "D"; break;
- case TYPE_TINY: c = "T"; break;
- case TYPE_DECIM: c = "M"; break;
- } // endswitch type
-
- return c;
- } // end of GetFormatType
-
-/***********************************************************************/
-/* GetFormatType: returns the FORMAT type according to character. */
-/***********************************************************************/
-int GetFormatType(char c)
- {
- int type = TYPE_ERROR;
-
- switch (c) {
- case 'C': type = TYPE_STRING; break;
- case 'S': type = TYPE_SHORT; break;
- case 'N': type = TYPE_INT; break;
- case 'L': type = TYPE_BIGINT; break;
- case 'F': type = TYPE_DOUBLE; break;
- case 'D': type = TYPE_DATE; break;
- case 'T': type = TYPE_TINY; break;
- case 'M': type = TYPE_DECIM; break;
- } // endswitch type
-
- return type;
- } // end of GetFormatType
-
-/***********************************************************************/
-/* IsTypeChar: returns true for character type(s). */
-/***********************************************************************/
-bool IsTypeChar(int type)
- {
- switch (type) {
- case TYPE_STRING:
- case TYPE_DECIM:
- return true;
- } // endswitch type
-
- return false;
- } // end of IsTypeChar
-
-/***********************************************************************/
-/* IsTypeNum: returns true for numeric types. */
-/***********************************************************************/
-bool IsTypeNum(int type)
- {
- switch (type) {
- case TYPE_INT:
- case TYPE_BIGINT:
- case TYPE_DATE:
- case TYPE_DOUBLE:
- case TYPE_SHORT:
- case TYPE_NUM:
- case TYPE_TINY:
- case TYPE_DECIM:
- return true;
- } // endswitch type
-
- return false;
- } // end of IsTypeNum
-
-/***********************************************************************/
-/* GetFmt: returns the format to use with a typed value. */
-/***********************************************************************/
-const char *GetFmt(int type, bool un)
- {
- const char *fmt;
-
- switch (type) {
- case TYPE_DECIM:
- case TYPE_STRING: fmt = "%s"; break;
- case TYPE_SHORT: fmt = (un) ? "%hu" : "%hd"; break;
- case TYPE_BIGINT: fmt = (un) ? "%llu" : "%lld"; break;
- case TYPE_DOUBLE: fmt = "%.*lf"; break;
- default: fmt = (un) ? "%u" : "%d"; break;
- } // endswitch Type
-
- return fmt;
- } // end of GetFmt
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* ConvertType: what this function does is to determine the type to */
-/* which should be converted a value so no precision would be lost. */
-/* This can be a numeric type if num is true or non numeric if false. */
-/* Note: this is an ultra simplified version of this function that */
-/* should become more and more complex as new types are added. */
-/* Not evaluated types (TYPE_VOID or TYPE_UNDEF) return false from */
-/* IsType... functions so match does not prevent correct setting. */
-/***********************************************************************/
-int ConvertType(int target, int type, CONV kind, bool match)
- {
- switch (kind) {
- case CNV_CHAR:
- if (match && (!IsTypeChar(target) || !IsTypeChar(type)))
- return TYPE_ERROR;
-
- return TYPE_STRING;
- case CNV_NUM:
- if (match && (!IsTypeNum(target) || !IsTypeNum(type)))
- return TYPE_ERROR;
-
- return (target == TYPE_DOUBLE || type == TYPE_DOUBLE) ? TYPE_DOUBLE
- : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE
- : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT
- : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT
- : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT
- : TYPE_TINY;
- default:
- if (target == TYPE_ERROR || target == type)
- return type;
-
- if (match && ((IsTypeChar(target) && !IsTypeChar(type)) ||
- (IsTypeNum(target) && !IsTypeNum(type))))
- return TYPE_ERROR;
-
- return (target == TYPE_DOUBLE || type == TYPE_DOUBLE) ? TYPE_DOUBLE
- : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE
- : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT
- : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT
- : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT
- : (target == TYPE_STRING || type == TYPE_STRING) ? TYPE_STRING
- : (target == TYPE_TINY || type == TYPE_TINY) ? TYPE_TINY
- : TYPE_ERROR;
- } // endswitch kind
-
- } // end of ConvertType
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* AllocateConstant: allocates a constant Value. */
-/***********************************************************************/
-PVAL AllocateValue(PGLOBAL g, void *value, short type)
- {
- PVAL valp;
-
- if (trace)
- htrc("AllocateConstant: value=%p type=%hd\n", value, type);
-
- switch (type) {
- case TYPE_STRING:
- valp = new(g) TYPVAL<PSZ>((PSZ)value);
- break;
- case TYPE_SHORT:
- valp = new(g) TYPVAL<short>(*(short*)value, TYPE_SHORT);
- break;
- case TYPE_INT:
- valp = new(g) TYPVAL<int>(*(int*)value, TYPE_INT);
- break;
- case TYPE_BIGINT:
- valp = new(g) TYPVAL<longlong>(*(longlong*)value, TYPE_BIGINT);
- break;
- case TYPE_DOUBLE:
- valp = new(g) TYPVAL<double>(*(double *)value, TYPE_DOUBLE, 2);
- break;
- case TYPE_TINY:
- valp = new(g) TYPVAL<char>(*(char *)value, TYPE_TINY);
- break;
- default:
- sprintf(g->Message, MSG(BAD_VALUE_TYPE), type);
- return NULL;
- } // endswitch Type
-
- valp->SetGlobal(g);
- return valp;
- } // end of AllocateValue
-
-/***********************************************************************/
-/* Allocate a variable Value according to type, length and precision. */
-/***********************************************************************/
-PVAL AllocateValue(PGLOBAL g, int type, int len, int prec,
- bool uns, PSZ fmt)
- {
- PVAL valp;
-
- switch (type) {
- case TYPE_STRING:
- valp = new(g) TYPVAL<PSZ>(g, (PSZ)NULL, len, prec);
- break;
- case TYPE_DATE:
- valp = new(g) DTVAL(g, len, prec, fmt);
- break;
- case TYPE_INT:
- if (uns)
- valp = new(g) TYPVAL<uint>((uint)0, TYPE_INT, 0, true);
- else
- valp = new(g) TYPVAL<int>((int)0, TYPE_INT);
-
- break;
- case TYPE_BIGINT:
- if (uns)
- valp = new(g) TYPVAL<ulonglong>((ulonglong)0, TYPE_BIGINT, 0, true);
- else
- valp = new(g) TYPVAL<longlong>((longlong)0, TYPE_BIGINT);
-
- break;
- case TYPE_SHORT:
- if (uns)
- valp = new(g) TYPVAL<ushort>((ushort)0, TYPE_SHORT, 0, true);
- else
- valp = new(g) TYPVAL<short>((short)0, TYPE_SHORT);
-
- break;
- case TYPE_DOUBLE:
- valp = new(g) TYPVAL<double>(0.0, TYPE_DOUBLE, prec);
- break;
- case TYPE_TINY:
- if (uns)
- valp = new(g) TYPVAL<uchar>((uchar)0, TYPE_TINY, 0, true);
- else
- valp = new(g) TYPVAL<char>((char)0, TYPE_TINY);
-
- break;
- case TYPE_DECIM:
- valp = new(g) DECVAL(g, (PSZ)NULL, len, prec, uns);
- break;
- default:
- sprintf(g->Message, MSG(BAD_VALUE_TYPE), type);
- return NULL;
- } // endswitch type
-
- valp->SetGlobal(g);
- return valp;
- } // end of AllocateValue
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Allocate a constant Value converted to newtype. */
-/* Can also be used to copy a Value eventually converted. */
-/***********************************************************************/
-PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
- {
- PSZ p, sp;
- bool un = (uns < 0) ? false : (uns > 0) ? true : valp->IsUnsigned();
-
- if (newtype == TYPE_VOID) // Means allocate a value of the same type
- newtype = valp->GetType();
-
- switch (newtype) {
- case TYPE_STRING:
- p = (PSZ)PlugSubAlloc(g, NULL, 1 + valp->GetValLen());
-
- if ((sp = valp->GetCharString(p)) != p)
- strcpy (p, sp);
-
- valp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
- break;
- case TYPE_SHORT:
- if (un)
- valp = new(g) TYPVAL<ushort>(valp->GetUShortValue(),
- TYPE_SHORT, 0, true);
- else
- valp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT);
-
- break;
- case TYPE_INT:
- if (un)
- valp = new(g) TYPVAL<uint>(valp->GetUIntValue(), TYPE_INT, 0, true);
- else
- valp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT);
-
- break;
- case TYPE_BIGINT:
- if (un)
- valp = new(g) TYPVAL<ulonglong>(valp->GetUBigintValue(),
- TYPE_BIGINT, 0, true);
- else
- valp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT);
-
- break;
- case TYPE_DATE:
- valp = new(g) DTVAL(g, valp->GetIntValue());
- break;
- case TYPE_DOUBLE:
- valp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_DOUBLE,
- valp->GetValPrec());
- break;
- case TYPE_TINY:
- if (un)
- valp = new(g) TYPVAL<uchar>(valp->GetUTinyValue(),
- TYPE_TINY, 0, true);
- else
- valp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY);
-
- break;
- default:
- sprintf(g->Message, MSG(BAD_VALUE_TYPE), newtype);
- return NULL;
- } // endswitch type
-
- valp->SetGlobal(g);
- return valp;
- } // end of AllocateValue
-#endif // BLK_INDX
-
-/* -------------------------- Class VALUE ---------------------------- */
-
-/***********************************************************************/
-/* Class VALUE protected constructor. */
-/***********************************************************************/
-VALUE::VALUE(int type, bool un) : Type(type)
- {
- Null = false;
- Nullable = false;
- Unsigned = un;
- Clen = 0;
- Prec = 0;
- Fmt = GetFmt(Type, Unsigned);
- Xfmt = GetXfmt();
- } // end of VALUE constructor
-
-/***********************************************************************/
-/* VALUE GetXfmt: returns the extended format to use with typed value. */
-/***********************************************************************/
-const char *VALUE::GetXfmt(void)
- {
- const char *fmt;
-
- switch (Type) {
- case TYPE_DECIM:
- case TYPE_STRING: fmt = "%*s"; break;
- case TYPE_SHORT: fmt = (Unsigned) ? "%*hu" : "%*hd"; break;
- case TYPE_BIGINT: fmt = (Unsigned) ? "%*llu" : "%*lld"; break;
- case TYPE_DOUBLE: fmt = "%*.*lf"; break;
- default: fmt = (Unsigned) ? "%*u" : "%*d"; break;
- } // endswitch Type
-
- return fmt;
- } // end of GetFmt
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Returns a BYTE indicating the comparison between two values. */
-/* Bit 1 indicates equality, Bit 2 less than, and Bit3 greater than. */
-/* More than 1 bit can be set only in the case of TYPE_LIST. */
-/***********************************************************************/
-BYTE VALUE::TestValue(PVAL vp)
- {
- int n = CompareValue(vp);
-
- return (n > 0) ? 0x04 : (n < 0) ? 0x02 : 0x01;
- } // end of TestValue
-#endif // BLK_INDX
-
-/* -------------------------- Class TYPVAL ---------------------------- */
-
-/***********************************************************************/
-/* TYPVAL public constructor from a constant typed value. */
-/***********************************************************************/
-template <class TYPE>
-TYPVAL<TYPE>::TYPVAL(TYPE n, int type, int prec, bool un)
- : VALUE(type, un)
- {
- Tval = n;
- Clen = sizeof(TYPE);
- Prec = prec;
- } // end of TYPVAL constructor
-
-/***********************************************************************/
-/* Return unsigned max value for the type. */
-/***********************************************************************/
-template <class TYPE>
-ulonglong TYPVAL<TYPE>::MaxVal(void) {DBUG_ASSERT(false); return 0;}
-
-template <>
-ulonglong TYPVAL<short>::MaxVal(void) {return INT_MAX16;}
-
-template <>
-ulonglong TYPVAL<ushort>::MaxVal(void) {return UINT_MAX16;}
-
-template <>
-ulonglong TYPVAL<int>::MaxVal(void) {return INT_MAX32;}
-
-template <>
-ulonglong TYPVAL<uint>::MaxVal(void) {return UINT_MAX32;}
-
-template <>
-ulonglong TYPVAL<char>::MaxVal(void) {return INT_MAX8;}
-
-template <>
-ulonglong TYPVAL<uchar>::MaxVal(void) {return UINT_MAX8;}
-
-template <>
-ulonglong TYPVAL<longlong>::MaxVal(void) {return INT_MAX64;}
-
-template <>
-ulonglong TYPVAL<ulonglong>::MaxVal(void) {return ULONGLONG_MAX;}
-
-/***********************************************************************/
-/* TYPVAL GetValLen: returns the print length of the typed object. */
-/***********************************************************************/
-template <class TYPE>
-int TYPVAL<TYPE>::GetValLen(void)
- {
- char c[32];
-
- return sprintf(c, Fmt, Tval);
- } // end of GetValLen
-
-template <>
-int TYPVAL<double>::GetValLen(void)
- {
- char c[32];
-
- return sprintf(c, Fmt, Prec, Tval);
- } // end of GetValLen
-
-/***********************************************************************/
-/* TYPVAL SetValue: copy the value of another Value object. */
-/* This function allows conversion if chktype is false. */
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::SetValue_pval(PVAL valp, bool chktype)
- {
- if (chktype && Type != valp->GetType())
- return true;
-
- if (!(Null = valp->IsNull() && Nullable))
- Tval = GetTypedValue(valp);
- else
- Reset();
-
- return false;
- } // end of SetValue
-
-template <>
-short TYPVAL<short>::GetTypedValue(PVAL valp)
- {return valp->GetShortValue();}
-
-template <>
-ushort TYPVAL<ushort>::GetTypedValue(PVAL valp)
- {return valp->GetUShortValue();}
-
-template <>
-int TYPVAL<int>::GetTypedValue(PVAL valp)
- {return valp->GetIntValue();}
-
-template <>
-uint TYPVAL<uint>::GetTypedValue(PVAL valp)
- {return valp->GetUIntValue();}
-
-template <>
-longlong TYPVAL<longlong>::GetTypedValue(PVAL valp)
- {return valp->GetBigintValue();}
-
-template <>
-ulonglong TYPVAL<ulonglong>::GetTypedValue(PVAL valp)
- {return valp->GetUBigintValue();}
-
-template <>
-double TYPVAL<double>::GetTypedValue(PVAL valp)
- {return valp->GetFloatValue();}
-
-template <>
-char TYPVAL<char>::GetTypedValue(PVAL valp)
- {return valp->GetTinyValue();}
-
-template <>
-uchar TYPVAL<uchar>::GetTypedValue(PVAL valp)
- {return valp->GetUTinyValue();}
-
-/***********************************************************************/
-/* TYPVAL SetValue: convert chars extracted from a line to TYPE value.*/
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::SetValue_char(char *p, int n)
- {
- bool rc, minus;
- ulonglong maxval = MaxVal();
- ulonglong val = CharToNumber(p, n, maxval, Unsigned, &minus, &rc);
-
- if (minus && val < maxval)
- Tval = (TYPE)(-(signed)val);
- else
- Tval = (TYPE)val;
-
- if (trace > 1) {
- char buf[64];
- htrc(strcat(strcat(strcpy(buf, " setting %s to: "), Fmt), "\n"),
- GetTypeName(Type), Tval);
- } // endif trace
-
- Null = false;
- return rc;
- } // end of SetValue
-
-template <>
-bool TYPVAL<double>::SetValue_char(char *p, int n)
- {
- if (p) {
- char buf[32];
-
- for (; n > 0 && *p == ' '; p++)
- n--;
-
- memcpy(buf, p, min(n, 31));
- buf[n] = '\0';
- Tval = atof(buf);
-
- if (trace > 1)
- htrc(" setting double: '%s' -> %lf\n", buf, Tval);
-
- Null = false;
- } else {
- Reset();
- Null = Nullable;
- } // endif p
-
- return false;
- } // end of SetValue
-
-/***********************************************************************/
-/* TYPVAL SetValue: fill a typed value from a string. */
-/***********************************************************************/
-template <class TYPE>
-void TYPVAL<TYPE>::SetValue_psz(PSZ s)
- {
- if (s) {
- SetValue_char(s, (int)strlen(s));
- Null = false;
- } else {
- Reset();
- Null = Nullable;
- } // endif p
-
- } // end of SetValue
-
-/***********************************************************************/
-/* TYPVAL SetValue: set value with a TYPE extracted from a block. */
-/***********************************************************************/
-template <class TYPE>
-void TYPVAL<TYPE>::SetValue_pvblk(PVBLK blk, int n)
- {
- Tval = GetTypedValue(blk, n);
- Null = false;
- } // end of SetValue
-
-template <>
-int TYPVAL<int>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetIntValue(n);}
-
-template <>
-uint TYPVAL<uint>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetUIntValue(n);}
-
-template <>
-short TYPVAL<short>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetShortValue(n);}
-
-template <>
-ushort TYPVAL<ushort>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetUShortValue(n);}
-
-template <>
-longlong TYPVAL<longlong>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetBigintValue(n);}
-
-template <>
-ulonglong TYPVAL<ulonglong>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetUBigintValue(n);}
-
-template <>
-double TYPVAL<double>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetFloatValue(n);}
-
-template <>
-char TYPVAL<char>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetTinyValue(n);}
-
-template <>
-uchar TYPVAL<uchar>::GetTypedValue(PVBLK blk, int n)
- {return blk->GetUTinyValue(n);}
-
-/***********************************************************************/
-/* TYPVAL SetBinValue: with bytes extracted from a line. */
-/***********************************************************************/
-template <class TYPE>
-void TYPVAL<TYPE>::SetBinValue(void *p)
- {
- Tval = *(TYPE *)p;
- Null = false;
- } // end of SetBinValue
-
-/***********************************************************************/
-/* GetBinValue: fill a buffer with the internal binary value. */
-/* This function checks whether the buffer length is enough and */
-/* returns true if not. Actual filling occurs only if go is true. */
-/* Currently used by WriteColumn of binary files. */
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::GetBinValue(void *buf, int buflen, bool go)
- {
- // Test on length was removed here until a variable in column give the
- // real field length. For BIN files the field length logically cannot
- // be different from the variable length because no conversion is done.
- // Therefore this test is useless anyway.
-//#if defined(_DEBUG)
-// if (sizeof(TYPE) > buflen)
-// return true;
-//#endif
-
- if (go)
- *(TYPE *)buf = Tval;
-
- Null = false;
- return false;
- } // end of GetBinValue
-
-/***********************************************************************/
-/* TYPVAL ShowValue: get string representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::ShowValue(char *buf, int len)
- {
- sprintf(buf, Xfmt, len, Tval);
- return buf;
- } // end of ShowValue
-
-template <>
-char *TYPVAL<double>::ShowValue(char *buf, int len)
- {
- // TODO: use snprintf to avoid possible overflow
- sprintf(buf, Xfmt, len, Prec, Tval);
- return buf;
- } // end of ShowValue
-
-/***********************************************************************/
-/* TYPVAL GetCharString: get string representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetCharString(char *p)
- {
- sprintf(p, Fmt, Tval);
- return p;
- } // end of GetCharString
-
-template <>
-char *TYPVAL<double>::GetCharString(char *p)
- {
- sprintf(p, Fmt, Prec, Tval);
- return p;
- } // end of GetCharString
-
-#if 0
-/***********************************************************************/
-/* TYPVAL GetShortString: get short representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetShortString(char *p, int n)
- {
- sprintf(p, "%*hd", n, (short)Tval);
- return p;
- } // end of GetShortString
-
-/***********************************************************************/
-/* TYPVAL GetIntString: get int representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetIntString(char *p, int n)
- {
- sprintf(p, "%*d", n, (int)Tval);
- return p;
- } // end of GetIntString
-
-/***********************************************************************/
-/* TYPVAL GetBigintString: get big int representation of a TYPE value.*/
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetBigintString(char *p, int n)
- {
- sprintf(p, "%*lld", n, (longlong)Tval);
- return p;
- } // end of GetBigintString
-
-/***********************************************************************/
-/* TYPVAL GetFloatString: get double representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetFloatString(char *p, int n, int prec)
- {
- sprintf(p, "%*.*lf", n, (prec < 0) ? 2 : prec, (double)Tval);
- return p;
- } // end of GetFloatString
-
-/***********************************************************************/
-/* TYPVAL GetTinyString: get char representation of a typed value. */
-/***********************************************************************/
-template <class TYPE>
-char *TYPVAL<TYPE>::GetTinyString(char *p, int n)
- {
- sprintf(p, "%*d", n, (int)(char)Tval);
- return p;
- } // end of GetIntString
-#endif // 0
-
-/***********************************************************************/
-/* TYPVAL compare value with another Value. */
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::IsEqual(PVAL vp, bool chktype)
- {
- if (this == vp)
- return true;
- else if (chktype && Type != vp->GetType())
- return false;
- else if (chktype && Unsigned != vp->IsUnsigned())
- return false;
- else if (Null || vp->IsNull())
- return false;
- else
- return (Tval == GetTypedValue(vp));
-
- } // end of IsEqual
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Compare values and returns 1, 0 or -1 according to comparison. */
-/* This function is used for evaluation of numeric filters. */
-/***********************************************************************/
-template <class TYPE>
-int TYPVAL<TYPE>::CompareValue(PVAL vp)
- {
-//assert(vp->GetType() == Type);
-
- // Process filtering on numeric values.
- TYPE n = GetTypedValue(vp);
-
-//if (trace)
-// htrc(" Comparing: val=%d,%d\n", Tval, n);
-
- return (Tval > n) ? 1 : (Tval < n) ? (-1) : 0;
- } // end of CompareValue
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* FormatValue: This function set vp (a STRING value) to the string */
-/* constructed from its own value formated using the fmt format. */
-/* This function assumes that the format matches the value type. */
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::FormatValue(PVAL vp, char *fmt)
- {
- char *buf = (char*)vp->GetTo_Val(); // Should be big enough
- int n = sprintf(buf, fmt, Tval);
-
- return (n > vp->GetValLen());
- } // end of FormatValue
-
-/***********************************************************************/
-/* TYPVAL SetFormat function (used to set SELECT output format). */
-/***********************************************************************/
-template <class TYPE>
-bool TYPVAL<TYPE>::SetConstFormat(PGLOBAL g, FORMAT& fmt)
- {
- char c[32];
-
- fmt.Type[0] = *GetFormatType(Type);
- fmt.Length = sprintf(c, Fmt, Tval);
- fmt.Prec = Prec;
- return false;
- } // end of SetConstFormat
-
-/***********************************************************************/
-/* Make file output of a typed object. */
-/***********************************************************************/
-template <class TYPE>
-void TYPVAL<TYPE>::Print(PGLOBAL g, FILE *f, uint n)
- {
- char m[64], buf[12];
-
- memset(m, ' ', n); /* Make margin string */
- m[n] = '\0';
-
- if (Null)
- fprintf(f, "%s<null>\n", m);
- else
- fprintf(f, strcat(strcat(strcpy(buf, "%s"), Fmt), "\n"), m, Tval);
-
- } /* end of Print */
-
-/***********************************************************************/
-/* Make string output of a int object. */
-/***********************************************************************/
-template <class TYPE>
-void TYPVAL<TYPE>::Print(PGLOBAL g, char *ps, uint z)
- {
- if (Null)
- strcpy(ps, "<null>");
- else
- sprintf(ps, Fmt, Tval);
-
- } /* end of Print */
-
-/* -------------------------- Class STRING --------------------------- */
-
-/***********************************************************************/
-/* STRING public constructor from a constant string. */
-/***********************************************************************/
-TYPVAL<PSZ>::TYPVAL(PSZ s) : VALUE(TYPE_STRING)
- {
- Strp = s;
- Len = strlen(s);
- Clen = Len;
- Ci = false;
- } // end of STRING constructor
-
-/***********************************************************************/
-/* STRING public constructor from char. */
-/***********************************************************************/
-TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c)
- : VALUE(TYPE_STRING)
- {
- Len = (g) ? n : strlen(s);
-
- if (!s) {
- if (g) {
- Strp = (char *)PlugSubAlloc(g, NULL, Len + 1);
- Strp[Len] = '\0';
- } else
- assert(false);
-
- } else
- Strp = s;
-
- Clen = Len;
- Ci = (c != 0);
- } // end of STRING constructor
-
-/***********************************************************************/
-/* Get the tiny value represented by the Strp string. */
-/***********************************************************************/
-char TYPVAL<PSZ>::GetTinyValue(void)
- {
- bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX8, false, &m);
-
- return (m && val < INT_MAX8) ? (char)(-(signed)val) : (char)val;
- } // end of GetTinyValue
-
-/***********************************************************************/
-/* Get the unsigned tiny value represented by the Strp string. */
-/***********************************************************************/
-uchar TYPVAL<PSZ>::GetUTinyValue(void)
- {
- return (uchar)CharToNumber(Strp, strlen(Strp), UINT_MAX8, true);
- } // end of GetUTinyValue
-
-/***********************************************************************/
-/* Get the short value represented by the Strp string. */
-/***********************************************************************/
-short TYPVAL<PSZ>::GetShortValue(void)
- {
- bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX16, false, &m);
-
- return (m && val < INT_MAX16) ? (short)(-(signed)val) : (short)val;
- } // end of GetShortValue
-
-/***********************************************************************/
-/* Get the unsigned short value represented by the Strp string. */
-/***********************************************************************/
-ushort TYPVAL<PSZ>::GetUShortValue(void)
- {
- return (ushort)CharToNumber(Strp, strlen(Strp), UINT_MAX16, true);
- } // end of GetUshortValue
-
-/***********************************************************************/
-/* Get the integer value represented by the Strp string. */
-/***********************************************************************/
-int TYPVAL<PSZ>::GetIntValue(void)
- {
- bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX32, false, &m);
-
- return (m && val < INT_MAX32) ? (int)(-(signed)val) : (int)val;
- } // end of GetIntValue
-
-/***********************************************************************/
-/* Get the unsigned integer value represented by the Strp string. */
-/***********************************************************************/
-uint TYPVAL<PSZ>::GetUIntValue(void)
- {
- return (uint)CharToNumber(Strp, strlen(Strp), UINT_MAX32, true);
- } // end of GetUintValue
-
-/***********************************************************************/
-/* Get the big integer value represented by the Strp string. */
-/***********************************************************************/
-longlong TYPVAL<PSZ>::GetBigintValue(void)
- {
- bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX64, false, &m);
-
- return (m && val < INT_MAX64) ? (-(signed)val) : (longlong)val;
- } // end of GetBigintValue
-
-/***********************************************************************/
-/* Get the unsigned big integer value represented by the Strp string. */
-/***********************************************************************/
-ulonglong TYPVAL<PSZ>::GetUBigintValue(void)
- {
- return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true);
- } // end of GetUBigintValue
-
-/***********************************************************************/
-/* STRING SetValue: copy the value of another Value object. */
-/***********************************************************************/
-bool TYPVAL<PSZ>::SetValue_pval(PVAL valp, bool chktype)
- {
- if (chktype && (valp->GetType() != Type || valp->GetSize() > Len))
- return true;
-
- char buf[32];
-
- if (!(Null = valp->IsNull() && Nullable))
- strncpy(Strp, valp->GetCharString(buf), Len);
- else
- Reset();
-
- return false;
- } // end of SetValue_pval
-
-/***********************************************************************/
-/* STRING SetValue: fill string with chars extracted from a line. */
-/***********************************************************************/
-bool TYPVAL<PSZ>::SetValue_char(char *p, int n)
- {
- bool rc;
-
- if (p) {
- rc = n > Len;
-
- if ((n = min(n, Len))) {
- strncpy(Strp, p, n);
-
-// for (p = Strp + n - 1; p >= Strp && (*p == ' ' || *p == '\0'); p--) ;
- for (p = Strp + n - 1; p >= Strp; p--)
- if (*p && *p != ' ')
- break;
-
- *(++p) = '\0';
-
- if (trace > 1)
- htrc(" Setting string to: '%s'\n", Strp);
-
- } else
- Reset();
-
- Null = false;
- } else {
- rc = false;
- Reset();
- Null = Nullable;
- } // endif p
-
- return rc;
- } // end of SetValue_char
-
-/***********************************************************************/
-/* STRING SetValue: fill string with another string. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue_psz(PSZ s)
- {
- if (s) {
- strncpy(Strp, s, Len);
- Null = false;
- } else {
- Reset();
- Null = Nullable;
- } // endif s
-
- } // end of SetValue_psz
-
-/***********************************************************************/
-/* STRING SetValue: fill string with a string extracted from a block. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue_pvblk(PVBLK blk, int n)
- {
- // STRBLK's can return a NULL pointer
- PSZ vp = blk->GetCharString(Strp, n);
-
- if (vp != Strp)
- SetValue_psz(vp);
-
- } // end of SetValue_pvblk
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of an integer. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(int n)
- {
- char buf[16];
- PGLOBAL& g = Global;
- int k = sprintf(buf, "%d", n);
-
- if (k > Len) {
- sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
- longjmp(g->jumper[g->jump_level], 138);
- } else
- SetValue_psz(buf);
-
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of an uint. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(uint n)
- {
- char buf[16];
- PGLOBAL& g = Global;
- int k = sprintf(buf, "%u", n);
-
- if (k > Len) {
- sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
- longjmp(g->jumper[g->jump_level], 138);
- } else
- SetValue_psz(buf);
-
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a short int. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(short i)
- {
- SetValue((int)i);
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a ushort int. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(ushort i)
- {
- SetValue((uint)i);
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a big integer.*/
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(longlong n)
- {
- char buf[24];
- PGLOBAL& g = Global;
- int k = sprintf(buf, "%lld", n);
-
- if (k > Len) {
- sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
- longjmp(g->jumper[g->jump_level], 138);
- } else
- SetValue_psz(buf);
-
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a big integer.*/
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(ulonglong n)
- {
- char buf[24];
- PGLOBAL& g = Global;
- int k = sprintf(buf, "%llu", n);
-
- if (k > Len) {
- sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
- longjmp(g->jumper[g->jump_level], 138);
- } else
- SetValue_psz(buf);
-
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a double. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(double f)
- {
- char *p, buf[32];
- PGLOBAL& g = Global;
- int k = sprintf(buf, "%lf", f);
-
- for (p = buf + k - 1; p >= buf; p--)
- if (*p == '0') {
- *p = 0;
- k--;
- } else
- break;
-
- if (k > Len) {
- sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
- longjmp(g->jumper[g->jump_level], 138);
- } else
- SetValue_psz(buf);
-
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a tiny int. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(char c)
- {
- SetValue((int)c);
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetValue: get the character representation of a tiny int. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetValue(uchar c)
- {
- SetValue((uint)c);
- Null = false;
- } // end of SetValue
-
-/***********************************************************************/
-/* STRING SetBinValue: fill string with chars extracted from a line. */
-/***********************************************************************/
-void TYPVAL<PSZ>::SetBinValue(void *p)
- {
- SetValue_char((char *)p, Len);
- } // end of SetBinValue
-
-/***********************************************************************/
-/* GetBinValue: fill a buffer with the internal binary value. */
-/* This function checks whether the buffer length is enough and */
-/* returns true if not. Actual filling occurs only if go is true. */
-/* Currently used by WriteColumn of binary files. */
-/***********************************************************************/
-bool TYPVAL<PSZ>::GetBinValue(void *buf, int buflen, bool go)
- {
- int len = (Null) ? 0 : strlen(Strp);
-
- if (len > buflen)
- return true;
- else if (go) {
- memset(buf, ' ', buflen);
- memcpy(buf, Strp, len);
- } // endif go
-
- return false;
- } // end of GetBinValue
-
-/***********************************************************************/
-/* STRING ShowValue: get string representation of a char value. */
-/***********************************************************************/
-char *TYPVAL<PSZ>::ShowValue(char *buf, int len)
- {
- return Strp;
- } // end of ShowValue
-
-/***********************************************************************/
-/* STRING GetCharString: get string representation of a char value. */
-/***********************************************************************/
-char *TYPVAL<PSZ>::GetCharString(char *p)
- {
- return Strp;
- } // end of GetCharString
-
-/***********************************************************************/
-/* STRING compare value with another Value. */
-/***********************************************************************/
-bool TYPVAL<PSZ>::IsEqual(PVAL vp, bool chktype)
- {
- if (this == vp)
- return true;
- else if (chktype && Type != vp->GetType())
- return false;
- else if (Null || vp->IsNull())
- return false;
-
- char buf[32];
-
- if (Ci || vp->IsCi())
- return !stricmp(Strp, vp->GetCharString(buf));
- else // (!Ci)
- return !strcmp(Strp, vp->GetCharString(buf));
-
- } // end of IsEqual
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Compare values and returns 1, 0 or -1 according to comparison. */
-/* This function is used for evaluation of numeric filters. */
-/***********************************************************************/
-int TYPVAL<PSZ>::CompareValue(PVAL vp)
- {
- int n;
-//assert(vp->GetType() == Type);
-
- if (trace)
- htrc(" Comparing: val='%s','%s'\n", Strp, vp->GetCharValue());
-
- // Process filtering on character strings.
- if (Ci || vp->IsCi())
- n = stricmp(Strp, vp->GetCharValue());
- else
- n = strcmp(Strp, vp->GetCharValue());
-
-#if defined(WIN32)
- if (n == _NLSCMPERROR)
- return n; // Here we should raise an error
-#endif // WIN32
-
- return (n > 0) ? 1 : (n < 0) ? -1 : 0;
- } // end of CompareValue
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* FormatValue: This function set vp (a STRING value) to the string */
-/* constructed from its own value formated using the fmt format. */
-/* This function assumes that the format matches the value type. */
-/***********************************************************************/
-bool TYPVAL<PSZ>::FormatValue(PVAL vp, char *fmt)
- {
- char *buf = (char*)vp->GetTo_Val(); // Should be big enough
- int n = sprintf(buf, fmt, Strp);
-
- return (n > vp->GetValLen());
- } // end of FormatValue
-
-/***********************************************************************/
-/* STRING SetFormat function (used to set SELECT output format). */
-/***********************************************************************/
-bool TYPVAL<PSZ>::SetConstFormat(PGLOBAL g, FORMAT& fmt)
- {
- fmt.Type[0] = 'C';
- fmt.Length = Len;
- fmt.Prec = 0;
- return false;
- } // end of SetConstFormat
-
-/* -------------------------- Class DECIMAL -------------------------- */
-
-/***********************************************************************/
-/* DECIMAL public constructor from a constant string. */
-/***********************************************************************/
-DECVAL::DECVAL(PSZ s) : TYPVAL<PSZ>(s)
- {
- if (s) {
- char *p = strchr(Strp, '.');
-
- Prec = (p) ? Len - (p - Strp) : 0;
- } // endif s
-
- Type = TYPE_DECIM;
- } // end of DECVAL constructor
-
-/***********************************************************************/
-/* DECIMAL public constructor from char. */
-/***********************************************************************/
-DECVAL::DECVAL(PGLOBAL g, PSZ s, int n, int prec, bool uns)
- : TYPVAL<PSZ>(g, s, n + (prec ? 1 : 0) + (uns ? 0 : 1), 0)
- {
- Prec = prec;
- Unsigned = uns;
- Type = TYPE_DECIM;
- } // end of DECVAL constructor
-
-/***********************************************************************/
-/* DECIMAL: Check whether the numerica value is equal to 0. */
-/***********************************************************************/
-bool DECVAL::IsZero(void)
- {
- for (int i = 0; Strp[i]; i++)
- if (!strchr("0 +-.", Strp[i]))
- return false;
-
- return true;
- } // end of IsZero
-
-/***********************************************************************/
-/* DECIMAL: Reset value to zero. */
-/***********************************************************************/
-void DECVAL::Reset(void)
-{
- int i = 0;
-
- Strp[i++] = '0';
-
- if (Prec) {
- Strp[i++] = '.';
-
- do {
- Strp[i++] = '0';
- } while (i < Prec + 2);
-
- } // endif Prec
-
- Strp[i] = 0;
-} // end of Reset
-
-/***********************************************************************/
-/* DECIMAL ShowValue: get string representation right justified. */
-/***********************************************************************/
-char *DECVAL::ShowValue(char *buf, int len)
- {
- sprintf(buf, Xfmt, len, Strp);
- return buf;
- } // end of ShowValue
-
-/***********************************************************************/
-/* GetBinValue: fill a buffer with the internal binary value. */
-/* This function checks whether the buffer length is enough and */
-/* returns true if not. Actual filling occurs only if go is true. */
-/* Currently used by WriteColumn of binary files. */
-/***********************************************************************/
-bool DECVAL::GetBinValue(void *buf, int buflen, bool go)
- {
- int len = (Null) ? 0 : strlen(Strp);
-
- if (len > buflen)
- return true;
- else if (go) {
- memset(buf, ' ', buflen - len);
- memcpy((char*)buf + buflen - len, Strp, len);
- } // endif go
-
- return false;
- } // end of GetBinValue
-
-#if 0
-/***********************************************************************/
-/* DECIMAL SetValue: copy the value of another Value object. */
-/***********************************************************************/
-bool DECVAL::SetValue_pval(PVAL valp, bool chktype)
- {
- if (chktype && (valp->GetType() != Type || valp->GetSize() > Len))
- return true;
-
- char buf[32];
-
- if (!(Null = valp->IsNull() && Nullable))
- strncpy(Strp, valp->GetCharString(buf), Len);
- else
- Reset();
-
- return false;
- } // end of SetValue_pval
-
-/***********************************************************************/
-/* DECIMAL SetValue: fill string with chars extracted from a line. */
-/***********************************************************************/
-bool DECVAL::SetValue_char(char *p, int n)
- {
- bool rc;
-
- if (p) {
- rc = n > Len;
-
- if ((n = min(n, Len))) {
- strncpy(Strp, p, n);
-
-// for (p = Strp + n - 1; p >= Strp && (*p == ' ' || *p == '\0'); p--) ;
- for (p = Strp + n - 1; p >= Strp; p--)
- if (*p && *p != ' ')
- break;
-
- *(++p) = '\0';
-
- if (trace > 1)
- htrc(" Setting string to: '%s'\n", Strp);
-
- } else
- Reset();
-
- Null = false;
- } else {
- rc = false;
- Reset();
- Null = Nullable;
- } // endif p
-
- return rc;
- } // end of SetValue_char
-
-/***********************************************************************/
-/* DECIMAL SetValue: fill string with another string. */
-/***********************************************************************/
-void DECVAL::SetValue_psz(PSZ s)
- {
- if (s) {
- strncpy(Strp, s, Len);
- Null = false;
- } else {
- Reset();
- Null = Nullable;
- } // endif s
-
- } // end of SetValue_psz
-
-/***********************************************************************/
-/* DECIMAL SetValue: fill string with a string extracted from a block.*/
-/***********************************************************************/
-void DECVAL::SetValue_pvblk(PVBLK blk, int n)
- {
- // STRBLK's can return a NULL pointer
- SetValue_psz(blk->GetCharValue(n));
- } // end of SetValue_pvblk
-
-/***********************************************************************/
-/* DECIMAL SetBinValue: fill string with chars extracted from a line. */
-/***********************************************************************/
-void DECVAL::SetBinValue(void *p)
- {
- SetValue_char((char *)p, Len);
- } // end of SetBinValue
-
-/***********************************************************************/
-/* DECIMAL GetCharString: get string representation of a char value. */
-/***********************************************************************/
-char *DECVAL::GetCharString(char *p)
- {
- return Strp;
- } // end of GetCharString
-#endif // 0
-
-/***********************************************************************/
-/* DECIMAL compare value with another Value. */
-/***********************************************************************/
-bool DECVAL::IsEqual(PVAL vp, bool chktype)
- {
- if (this == vp)
- return true;
- else if (chktype && Type != vp->GetType())
- return false;
- else if (Null || vp->IsNull())
- return false;
-
- char buf[32];
-
- return !strcmp(Strp, vp->GetCharString(buf));
- } // end of IsEqual
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Compare values and returns 1, 0 or -1 according to comparison. */
-/* This function is used for evaluation of numeric filters. */
-/***********************************************************************/
-int DECVAL::CompareValue(PVAL vp)
- {
-//assert(vp->GetType() == Type);
-
- // Process filtering on numeric values.
- double f = atof(Strp), n = vp->GetFloatValue();
-
-//if (trace)
-// htrc(" Comparing: val=%d,%d\n", f, n);
-
- return (f > n) ? 1 : (f < n) ? (-1) : 0;
- } // end of CompareValue
-#endif // BLK_INDX
-
-#if 0
-/***********************************************************************/
-/* FormatValue: This function set vp (a STRING value) to the string */
-/* constructed from its own value formated using the fmt format. */
-/* This function assumes that the format matches the value type. */
-/***********************************************************************/
-bool DECVAL::FormatValue(PVAL vp, char *fmt)
- {
- char *buf = (char*)vp->GetTo_Val(); // Should be big enough
- int n = sprintf(buf, fmt, Strp);
-
- return (n > vp->GetValLen());
- } // end of FormatValue
-
-/***********************************************************************/
-/* DECIMAL SetFormat function (used to set SELECT output format). */
-/***********************************************************************/
-bool DECVAL::SetConstFormat(PGLOBAL g, FORMAT& fmt)
- {
- fmt.Type[0] = 'C';
- fmt.Length = Len;
- fmt.Prec = 0;
- return false;
- } // end of SetConstFormat
-#endif // 0
-
-/* -------------------------- Class DTVAL ---------------------------- */
-
-/***********************************************************************/
-/* DTVAL public constructor for new void values. */
-/***********************************************************************/
-DTVAL::DTVAL(PGLOBAL g, int n, int prec, PSZ fmt)
- : TYPVAL<int>((int)0, TYPE_DATE)
- {
- if (!fmt) {
- Pdtp = NULL;
- Sdate = NULL;
- DefYear = 0;
- Len = n;
- } else
- SetFormat(g, fmt, n, prec);
-
-//Type = TYPE_DATE;
- } // end of DTVAL constructor
-
-/***********************************************************************/
-/* DTVAL public constructor from int. */
-/***********************************************************************/
-DTVAL::DTVAL(PGLOBAL g, int n) : TYPVAL<int>(n, TYPE_DATE)
- {
- Pdtp = NULL;
- Len = 19;
-//Type = TYPE_DATE;
- Sdate = NULL;
- DefYear = 0;
- } // end of DTVAL constructor
-
-/***********************************************************************/
-/* Set format so formatted dates can be converted on input/output. */
-/***********************************************************************/
-bool DTVAL::SetFormat(PGLOBAL g, PSZ fmt, int len, int year)
- {
- Pdtp = MakeDateFormat(g, fmt, true, true, (year > 9999) ? 1 : 0);
- Sdate = (char*)PlugSubAlloc(g, NULL, len + 1);
- DefYear = (int)((year > 9999) ? (year - 10000) : year);
- Len = len;
- return false;
- } // end of SetFormat
-
-/***********************************************************************/
-/* Set format from the format of another date value. */
-/***********************************************************************/
-bool DTVAL::SetFormat(PGLOBAL g, PVAL valp)
- {
- DTVAL *vp;
-
- if (valp->GetType() != TYPE_DATE) {
- sprintf(g->Message, MSG(NO_FORMAT_TYPE), valp->GetType());
- return true;
- } else
- vp = (DTVAL*)valp;
-
- Len = vp->Len;
- Pdtp = vp->Pdtp;
- Sdate = (char*)PlugSubAlloc(g, NULL, Len + 1);
- DefYear = vp->DefYear;
- return false;
- } // end of SetFormat
-
-/***********************************************************************/
-/* We need TimeShift because the mktime C function does a correction */
-/* for local time zone that we want to override for DB operations. */
-/***********************************************************************/
-void DTVAL::SetTimeShift(void)
- {
- struct tm dtm;
- memset(&dtm, 0, sizeof(dtm));
- dtm.tm_mday=2;
- dtm.tm_mon=0;
- dtm.tm_year=70;
-
- Shift = (int)mktime(&dtm) - 86400;
-
- if (trace)
- htrc("DTVAL Shift=%d\n", Shift);
-
- } // end of SetTimeShift
-
-// Added by Alexander Barkov
-static void TIME_to_localtime(struct tm *tm, const MYSQL_TIME *ltime)
-{
- bzero(tm, sizeof(*tm));
- tm->tm_year= ltime->year - 1900;
- tm->tm_mon= ltime->month - 1;
- tm->tm_mday= ltime->day;
- tm->tm_hour= ltime->hour;
- tm->tm_min= ltime->minute;
- tm->tm_sec= ltime->second;
-}
-
-// Added by Alexander Barkov
-static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm)
-{
- MYSQL_TIME ltime;
- thd_gmt_sec_to_TIME(current_thd, &ltime, (my_time_t) *timep);
- TIME_to_localtime(tm, &ltime);
- return tm;
-}
-
-/***********************************************************************/
-/* GetGmTime: returns a pointer to a static tm structure obtained */
-/* though the gmtime C function. The purpose of this function is to */
-/* extend the range of valid dates by accepting negative time values. */
-/***********************************************************************/
-struct tm *DTVAL::GetGmTime(struct tm *tm_buffer)
- {
- struct tm *datm;
- time_t t = (time_t)Tval;
-
- if (Tval < 0) {
- int n;
-
- for (n = 0; t < 0; n += 4)
- t += FOURYEARS;
-
- datm = gmtime_mysql(&t, tm_buffer);
-
- if (datm)
- datm->tm_year -= n;
-
- } else
- datm = gmtime_mysql(&t, tm_buffer);
-
- return datm;
- } // end of GetGmTime
-
-// Added by Alexander Barkov
-static time_t mktime_mysql(struct tm *ptm)
-{
- MYSQL_TIME ltime;
- localtime_to_TIME(&ltime, ptm);
- ltime.time_type= MYSQL_TIMESTAMP_DATETIME;
- uint error_code;
- time_t t= TIME_to_timestamp(current_thd, &ltime, &error_code);
- return error_code ? (time_t) -1 : t;
-}
-
-/***********************************************************************/
-/* MakeTime: calculates a date value from a tm structures using the */
-/* mktime C function. The purpose of this function is to extend the */
-/* range of valid dates by accepting to set negative time values. */
-/***********************************************************************/
-bool DTVAL::MakeTime(struct tm *ptm)
- {
- int n, y = ptm->tm_year;
- time_t t = mktime_mysql(ptm);
-
- if (trace > 1)
- htrc("MakeTime from (%d,%d,%d,%d,%d,%d)\n",
- ptm->tm_year, ptm->tm_mon, ptm->tm_mday,
- ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
-
- if (t == -1) {
- if (y < 1 || y > 71)
- return true;
-
- for (n = 0; t == -1 && n < 20; n++) {
- ptm->tm_year += 4;
- t = mktime_mysql(ptm);
- } // endfor t
-
- if (t == -1)
- return true;
-
- if ((t -= (n * FOURYEARS)) > 2000000000)
- return true;
-
- }
- Tval= (int) t;
-
- if (trace > 1)
- htrc("MakeTime Ival=%d\n", Tval);
-
- return false;
- } // end of MakeTime
-
-/***********************************************************************/
-/* Make a time_t datetime from its components (YY, MM, DD, hh, mm, ss) */
-/***********************************************************************/
-bool DTVAL::MakeDate(PGLOBAL g, int *val, int nval)
- {
- int i, m;
- int n;
- bool rc = false;
- struct tm datm;
- bzero(&datm, sizeof(datm));
- datm.tm_mday=1;
- datm.tm_mon=0;
- datm.tm_year=70;
-
- if (trace > 1)
- htrc("MakeDate from(%d,%d,%d,%d,%d,%d) nval=%d\n",
- val[0], val[1], val[2], val[3], val[4], val[5], nval);
-
- for (i = 0; i < nval; i++) {
- n = val[i];
-
-// if (trace > 1)
-// htrc("i=%d n=%d\n", i, n);
-
- switch (i) {
- case 0:
- if (n >= 1900)
- n -= 1900;
-
- datm.tm_year = n;
-
-// if (trace > 1)
-// htrc("n=%d tm_year=%d\n", n, datm.tm_year);
-
- break;
- case 1:
- // If mktime handles apparently correctly large or negative
- // day values, it is not the same for months. Therefore we
- // do the ajustment here, thus mktime has not to do it.
- if (n > 0) {
- m = (n - 1) % 12;
- n = (n - 1) / 12;
- } else {
- m = 11 + n % 12;
- n = n / 12 - 1;
- } // endfi n
-
- datm.tm_mon = m;
- datm.tm_year += n;
-
-// if (trace > 1)
-// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon);
-
- break;
- case 2:
- // For days, big or negative values may also cause problems
- m = n % 1461;
- n = 4 * (n / 1461);
-
- if (m < 0) {
- m += 1461;
- n -= 4;
- } // endif m
-
- datm.tm_mday = m;
- datm.tm_year += n;
-
-// if (trace > 1)
-// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon);
-
- break;
- case 3: datm.tm_hour = n; break;
- case 4: datm.tm_min = n; break;
- case 5: datm.tm_sec = n; break;
- } // endswitch i
-
- } // endfor i
-
- if (trace > 1)
- htrc("MakeDate datm=(%d,%d,%d,%d,%d,%d)\n",
- datm.tm_year, datm.tm_mon, datm.tm_mday,
- datm.tm_hour, datm.tm_min, datm.tm_sec);
-
- // Pass g to have an error return or NULL to set invalid dates to 0
- if (MakeTime(&datm))
- if (g) {
- strcpy(g->Message, MSG(BAD_DATETIME));
- rc = true;
- } else
- Tval = 0;
-
- return rc;
- } // end of MakeDate
-
-/***********************************************************************/
-/* DTVAL SetValue: copy the value of another Value object. */
-/* This function allows conversion if chktype is false. */
-/***********************************************************************/
-bool DTVAL::SetValue_pval(PVAL valp, bool chktype)
- {
- if (chktype && Type != valp->GetType())
- return true;
-
- if (!(Null = valp->IsNull() && Nullable)) {
- if (Pdtp && !valp->IsTypeNum()) {
- int ndv;
- int dval[6];
-
- ndv = ExtractDate(valp->GetCharValue(), Pdtp, DefYear, dval);
- MakeDate(NULL, dval, ndv);
- } else
- Tval = valp->GetIntValue();
-
- } else
- Reset();
-
- return false;
- } // end of SetValue
-
-/***********************************************************************/
-/* SetValue: convert chars extracted from a line to date value. */
-/***********************************************************************/
-bool DTVAL::SetValue_char(char *p, int n)
- {
- bool rc;
-
- if (Pdtp) {
- char *p2;
- int ndv;
- int dval[6];
-
- // Trim trailing blanks
- for (p2 = p + n -1; p < p2 && *p2 == ' '; p2--) ;
-
- if ((rc = (n = p2 - p + 1) > Len))
- n = Len;
-
- memcpy(Sdate, p, n);
- Sdate[n] = '\0';
-
- ndv = ExtractDate(Sdate, Pdtp, DefYear, dval);
- MakeDate(NULL, dval, ndv);
-
- if (trace > 1)
- htrc(" setting date: '%s' -> %d\n", Sdate, Tval);
-
- Null = false;
- } else
- rc = TYPVAL<int>::SetValue_char(p, n);
-
- return rc;
- } // end of SetValue
-
-/***********************************************************************/
-/* SetValue: convert a char string to date value. */
-/***********************************************************************/
-void DTVAL::SetValue_psz(PSZ p)
- {
- if (Pdtp) {
- int ndv;
- int dval[6];
-
- strncpy(Sdate, p, Len);
- Sdate[Len] = '\0';
-
- ndv = ExtractDate(Sdate, Pdtp, DefYear, dval);
- MakeDate(NULL, dval, ndv);
-
- if (trace > 1)
- htrc(" setting date: '%s' -> %d\n", Sdate, Tval);
-
- Null = false;
- } else
- TYPVAL<int>::SetValue_psz(p);
-
- } // end of SetValue
-
-/***********************************************************************/
-/* DTVAL SetValue: set value with a value extracted from a block. */
-/***********************************************************************/
-void DTVAL::SetValue_pvblk(PVBLK blk, int n)
- {
- if (Pdtp && !::IsTypeNum(blk->GetType())) {
- int ndv;
- int dval[6];
-
- ndv = ExtractDate(blk->GetCharValue(n), Pdtp, DefYear, dval);
- MakeDate(NULL, dval, ndv);
- } else
- Tval = blk->GetIntValue(n);
-
- } // end of SetValue
-
-/***********************************************************************/
-/* DTVAL GetCharString: get string representation of a date value. */
-/***********************************************************************/
-char *DTVAL::GetCharString(char *p)
- {
- if (Pdtp) {
- size_t n = 0;
- struct tm tm, *ptm= GetGmTime(&tm);
-
- if (ptm)
- n = strftime(Sdate, Len + 1, Pdtp->OutFmt, ptm);
-
- if (!n) {
- *Sdate = '\0';
- strncat(Sdate, "Error", Len + 1);
- } // endif n
-
- return Sdate;
- } else
- sprintf(p, "%d", Tval);
-
- Null = false;
- return p;
- } // end of GetCharString
-
-/***********************************************************************/
-/* DTVAL ShowValue: get string representation of a date value. */
-/***********************************************************************/
-char *DTVAL::ShowValue(char *buf, int len)
- {
- if (Pdtp) {
- char *p;
- size_t m, n = 0;
- struct tm tm, *ptm = GetGmTime(&tm);
-
- if (Len < len) {
- p = buf;
- m = len;
- } else {
- p = Sdate;
- m = Len + 1;
- } // endif Len
-
- if (ptm)
- n = strftime(p, m, Pdtp->OutFmt, ptm);
-
- if (!n) {
- *p = '\0';
- strncat(p, "Error", m);
- } // endif n
-
- return p;
- } else
- return TYPVAL<int>::ShowValue(buf, len);
-
- } // end of ShowValue
-
-#if 0 // Not used by CONNECT
-/***********************************************************************/
-/* Returns a member of the struct tm representation of the date. */
-/***********************************************************************/
-bool DTVAL::GetTmMember(OPVAL op, int& mval)
- {
- bool rc = false;
- struct tm tm, *ptm = GetGmTime(&tm);
-
- switch (op) {
- case OP_MDAY: mval = ptm->tm_mday; break;
- case OP_MONTH: mval = ptm->tm_mon + 1; break;
- case OP_YEAR: mval = ptm->tm_year + 1900; break;
- case OP_WDAY: mval = ptm->tm_wday + 1; break;
- case OP_YDAY: mval = ptm->tm_yday + 1; break;
- case OP_QUART: mval = ptm->tm_mon / 3 + 1; break;
- default:
- rc = true;
- } // endswitch op
-
- return rc;
- } // end of GetTmMember
-
-/***********************************************************************/
-/* Calculates the week number of the year for the internal date value.*/
-/* The International Standard ISO 8601 has decreed that Monday shall */
-/* be the first day of the week. A week that lies partly in one year */
-/* and partly in another is assigned a number in the year in which */
-/* most of its days lie. That means that week number 1 of any year is */
-/* the week that contains the January 4th. */
-/***********************************************************************/
-bool DTVAL::WeekNum(PGLOBAL g, int& nval)
- {
- // w is the start of the week SUN=0, MON=1, etc.
- int m, n, w = nval % 7;
- struct tm tm, *ptm = GetGmTime(&tm);
-
- // Which day is January 4th of this year?
- m = (367 + ptm->tm_wday - ptm->tm_yday) % 7;
-
- // When does the first week begins?
- n = 3 - (7 + m - w) % 7;
-
- // Now calculate the week number
- if (!(nval = (7 + ptm->tm_yday - n) / 7))
- nval = 52;
-
- // Everything should be Ok
- return false;
- } // end of WeekNum
-#endif // 0
-
-/***********************************************************************/
-/* FormatValue: This function set vp (a STRING value) to the string */
-/* constructed from its own value formated using the fmt format. */
-/* This function assumes that the format matches the value type. */
-/***********************************************************************/
-bool DTVAL::FormatValue(PVAL vp, char *fmt)
- {
- char *buf = (char*)vp->GetTo_Val(); // Should be big enough
- struct tm tm, *ptm = GetGmTime(&tm);
-
- if (trace > 1)
- htrc("FormatValue: ptm=%p len=%d\n", ptm, vp->GetValLen());
-
- if (ptm) {
- size_t n = strftime(buf, vp->GetValLen(), fmt, ptm);
-
- if (trace > 1)
- htrc("strftime: n=%d buf=%s\n", n, (n) ? buf : "???");
-
- return (n == 0);
- } else
- return true;
-
- } // end of FormatValue
-
-/* -------------------------- End of Value --------------------------- */
+/************* Value C++ Functions Source Code File (.CPP) *************/
+/* Name: VALUE.CPP Version 2.4 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */
+/* */
+/* This file contains the VALUE and derived classes family functions. */
+/* These classes contain values of different types. They are used so */
+/* new object types can be defined and added to the processing simply */
+/* (hopefully) adding their specific functions in this file. */
+/* First family is VALUE that represent single typed objects. It is */
+/* used by columns (COLBLK), SELECT and FILTER (derived) objects. */
+/* Second family is VALBLK, representing simple suballocated arrays */
+/* of values treated sequentially by FIX, BIN and VCT tables and */
+/* columns, as well for min/max blocks as for VCT column blocks. */
+/* Q&A: why not using only one family ? Simple values are arrays that */
+/* have only one element and arrays could have functions for all kind */
+/* of processing. The answer is a-because historically it was simpler */
+/* to do that way, b-because of performance on single values, and c- */
+/* to avoid too complicated classes and unuseful duplication of many */
+/* functions used on one family only. The drawback is that for new */
+/* types of objects, we shall have more classes to update. */
+/* Currently the only implemented types are STRING, INT, SHORT, TINY, */
+/* DATE and LONGLONG. Recently we added some UNSIGNED types. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#include "sql_class.h"
+#include "sql_time.h"
+
+#if defined(WIN32)
+//#include <windows.h>
+#else // !WIN32
+#include <string.h>
+#endif // !WIN32
+
+#include <math.h>
+
+#undef DOMAIN // Was defined in math.h
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "preparse.h" // For DATPAR
+//#include "value.h"
+#include "valblk.h"
+#define NO_FUNC // Already defined in ODBConn
+#include "plgcnx.h" // For DB types
+#include "osutil.h"
+
+/***********************************************************************/
+/* Check macro's. */
+/***********************************************************************/
+#if defined(_DEBUG)
+#define CheckType(V) if (Type != V->GetType()) { \
+ PGLOBAL& g = Global; \
+ strcpy(g->Message, MSG(VALTYPE_NOMATCH)); \
+ longjmp(g->jumper[g->jump_level], Type); }
+#else
+#define CheckType(V)
+#endif
+
+#define FOURYEARS 126230400 // Four years in seconds (1 leap)
+
+/***********************************************************************/
+/* Static variables. */
+/***********************************************************************/
+
+extern "C" int trace;
+
+/***********************************************************************/
+/* Initialize the DTVAL static member. */
+/***********************************************************************/
+int DTVAL::Shift = 0;
+
+/***********************************************************************/
+/* Routines called externally. */
+/***********************************************************************/
+bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
+
+#if !defined(WIN32)
+extern "C" {
+PSZ strupr(PSZ s);
+PSZ strlwr(PSZ s);
+}
+#endif // !WIN32
+
+/***********************************************************************/
+/* Returns the bitmap representing the conditions that must not be */
+/* met when returning from TestValue for a given operator. */
+/* Bit one is EQ, bit 2 is LT, and bit 3 is GT. */
+/***********************************************************************/
+BYTE OpBmp(PGLOBAL g, OPVAL opc)
+ {
+ BYTE bt;
+
+ switch (opc) {
+ case OP_IN:
+ case OP_EQ: bt = 0x06; break;
+ case OP_NE: bt = 0x01; break;
+ case OP_GT: bt = 0x03; break;
+ case OP_GE: bt = 0x02; break;
+ case OP_LT: bt = 0x05; break;
+ case OP_LE: bt = 0x04; break;
+ case OP_EXIST: bt = 0x00; break;
+ default:
+ sprintf(g->Message, MSG(BAD_FILTER_OP), opc);
+ longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
+ } // endswitch opc
+
+ return bt;
+ } // end of OpBmp
+
+/***********************************************************************/
+/* Get a long long number from its character representation. */
+/* IN p: Pointer to the numeric string */
+/* IN n: The string length */
+/* IN maxval: The number max value */
+/* IN un: True if the number must be unsigned */
+/* OUT rc: Set to TRUE for out of range value */
+/* OUT minus: Set to true if the number is negative */
+/* Returned val: The resulting number */
+/***********************************************************************/
+ulonglong CharToNumber(char *p, int n, ulonglong maxval,
+ bool un, bool *minus, bool *rc)
+{
+ char *p2;
+ uchar c;
+ ulonglong val;
+
+ if (minus) *minus = false;
+ if (rc) *rc = false;
+
+ // Eliminate leading blanks or 0
+ for (p2 = p + n; p < p2 && (*p == ' ' || *p == '0'); p++) ;
+
+ // Get an eventual sign character
+ switch (*p) {
+ case '-':
+ if (un) {
+ if (rc) *rc = true;
+ return 0;
+ } else {
+ maxval++;
+ if (minus) *minus = true;
+ } // endif Unsigned
+
+ case '+':
+ p++;
+ break;
+ } // endswitch *p
+
+ for (val = 0; p < p2 && (c = (uchar)(*p - '0')) < 10; p++)
+ if (val > (maxval - c) / 10) {
+ val = maxval;
+ if (rc) *rc = true;
+ break;
+ } else
+ val = val * 10 + c;
+
+ return val;
+} // end of CharToNumber
+
+/***********************************************************************/
+/* GetTypeName: returns the PlugDB internal type name. */
+/***********************************************************************/
+PSZ GetTypeName(int type)
+ {
+ PSZ name;
+
+ switch (type) {
+ case TYPE_STRING: name = "CHAR"; break;
+ case TYPE_SHORT: name = "SMALLINT"; break;
+ case TYPE_INT: name = "INTEGER"; break;
+ case TYPE_BIGINT: name = "BIGINT"; break;
+ case TYPE_DATE: name = "DATE"; break;
+ case TYPE_DOUBLE: name = "DOUBLE"; break;
+ case TYPE_TINY: name = "TINY"; break;
+ case TYPE_DECIM: name = "DECIMAL"; break;
+ default: name = "UNKNOWN"; break;
+ } // endswitch type
+
+ return name;
+ } // end of GetTypeName
+
+/***********************************************************************/
+/* GetTypeSize: returns the PlugDB internal type size. */
+/***********************************************************************/
+int GetTypeSize(int type, int len)
+ {
+ switch (type) {
+ case TYPE_DECIM:
+ case TYPE_STRING: len = len * sizeof(char); break;
+ case TYPE_SHORT: len = sizeof(short); break;
+ case TYPE_INT: len = sizeof(int); break;
+ case TYPE_BIGINT: len = sizeof(longlong); break;
+ case TYPE_DATE: len = sizeof(int); break;
+ case TYPE_DOUBLE: len = sizeof(double); break;
+ case TYPE_TINY: len = sizeof(char); break;
+ default: len = 0;
+ } // endswitch type
+
+ return len;
+ } // end of GetTypeSize
+
+/***********************************************************************/
+/* GetFormatType: returns the FORMAT character(s) according to type. */
+/***********************************************************************/
+char *GetFormatType(int type)
+ {
+ char *c = "X";
+
+ switch (type) {
+ case TYPE_STRING: c = "C"; break;
+ case TYPE_SHORT: c = "S"; break;
+ case TYPE_INT: c = "N"; break;
+ case TYPE_BIGINT: c = "L"; break;
+ case TYPE_DOUBLE: c = "F"; break;
+ case TYPE_DATE: c = "D"; break;
+ case TYPE_TINY: c = "T"; break;
+ case TYPE_DECIM: c = "M"; break;
+ } // endswitch type
+
+ return c;
+ } // end of GetFormatType
+
+/***********************************************************************/
+/* GetFormatType: returns the FORMAT type according to character. */
+/***********************************************************************/
+int GetFormatType(char c)
+ {
+ int type = TYPE_ERROR;
+
+ switch (c) {
+ case 'C': type = TYPE_STRING; break;
+ case 'S': type = TYPE_SHORT; break;
+ case 'N': type = TYPE_INT; break;
+ case 'L': type = TYPE_BIGINT; break;
+ case 'F': type = TYPE_DOUBLE; break;
+ case 'D': type = TYPE_DATE; break;
+ case 'T': type = TYPE_TINY; break;
+ case 'M': type = TYPE_DECIM; break;
+ } // endswitch type
+
+ return type;
+ } // end of GetFormatType
+
+/***********************************************************************/
+/* IsTypeChar: returns true for character type(s). */
+/***********************************************************************/
+bool IsTypeChar(int type)
+ {
+ switch (type) {
+ case TYPE_STRING:
+ case TYPE_DECIM:
+ return true;
+ } // endswitch type
+
+ return false;
+ } // end of IsTypeChar
+
+/***********************************************************************/
+/* IsTypeNum: returns true for numeric types. */
+/***********************************************************************/
+bool IsTypeNum(int type)
+ {
+ switch (type) {
+ case TYPE_INT:
+ case TYPE_BIGINT:
+ case TYPE_DATE:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_NUM:
+ case TYPE_TINY:
+ case TYPE_DECIM:
+ return true;
+ } // endswitch type
+
+ return false;
+ } // end of IsTypeNum
+
+/***********************************************************************/
+/* GetFmt: returns the format to use with a typed value. */
+/***********************************************************************/
+const char *GetFmt(int type, bool un)
+ {
+ const char *fmt;
+
+ switch (type) {
+ case TYPE_DECIM:
+ case TYPE_STRING: fmt = "%s"; break;
+ case TYPE_SHORT: fmt = (un) ? "%hu" : "%hd"; break;
+ case TYPE_BIGINT: fmt = (un) ? "%llu" : "%lld"; break;
+ case TYPE_DOUBLE: fmt = "%.*lf"; break;
+ default: fmt = (un) ? "%u" : "%d"; break;
+ } // endswitch Type
+
+ return fmt;
+ } // end of GetFmt
+
+/***********************************************************************/
+/* ConvertType: what this function does is to determine the type to */
+/* which should be converted a value so no precision would be lost. */
+/* This can be a numeric type if num is true or non numeric if false. */
+/* Note: this is an ultra simplified version of this function that */
+/* should become more and more complex as new types are added. */
+/* Not evaluated types (TYPE_VOID or TYPE_UNDEF) return false from */
+/* IsType... functions so match does not prevent correct setting. */
+/***********************************************************************/
+int ConvertType(int target, int type, CONV kind, bool match)
+ {
+ switch (kind) {
+ case CNV_CHAR:
+ if (match && (!IsTypeChar(target) || !IsTypeChar(type)))
+ return TYPE_ERROR;
+
+ return TYPE_STRING;
+ case CNV_NUM:
+ if (match && (!IsTypeNum(target) || !IsTypeNum(type)))
+ return TYPE_ERROR;
+
+ return (target == TYPE_DOUBLE || type == TYPE_DOUBLE) ? TYPE_DOUBLE
+ : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE
+ : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT
+ : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT
+ : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT
+ : TYPE_TINY;
+ default:
+ if (target == TYPE_ERROR || target == type)
+ return type;
+
+ if (match && ((IsTypeChar(target) && !IsTypeChar(type)) ||
+ (IsTypeNum(target) && !IsTypeNum(type))))
+ return TYPE_ERROR;
+
+ return (target == TYPE_DOUBLE || type == TYPE_DOUBLE) ? TYPE_DOUBLE
+ : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE
+ : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT
+ : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT
+ : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT
+ : (target == TYPE_STRING || type == TYPE_STRING) ? TYPE_STRING
+ : (target == TYPE_TINY || type == TYPE_TINY) ? TYPE_TINY
+ : TYPE_ERROR;
+ } // endswitch kind
+
+ } // end of ConvertType
+
+/***********************************************************************/
+/* AllocateConstant: allocates a constant Value. */
+/***********************************************************************/
+PVAL AllocateValue(PGLOBAL g, void *value, short type)
+ {
+ PVAL valp;
+
+ if (trace)
+ htrc("AllocateConstant: value=%p type=%hd\n", value, type);
+
+ switch (type) {
+ case TYPE_STRING:
+ valp = new(g) TYPVAL<PSZ>((PSZ)value);
+ break;
+ case TYPE_SHORT:
+ valp = new(g) TYPVAL<short>(*(short*)value, TYPE_SHORT);
+ break;
+ case TYPE_INT:
+ valp = new(g) TYPVAL<int>(*(int*)value, TYPE_INT);
+ break;
+ case TYPE_BIGINT:
+ valp = new(g) TYPVAL<longlong>(*(longlong*)value, TYPE_BIGINT);
+ break;
+ case TYPE_DOUBLE:
+ valp = new(g) TYPVAL<double>(*(double *)value, TYPE_DOUBLE, 2);
+ break;
+ case TYPE_TINY:
+ valp = new(g) TYPVAL<char>(*(char *)value, TYPE_TINY);
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_VALUE_TYPE), type);
+ return NULL;
+ } // endswitch Type
+
+ valp->SetGlobal(g);
+ return valp;
+ } // end of AllocateValue
+
+/***********************************************************************/
+/* Allocate a variable Value according to type, length and precision. */
+/***********************************************************************/
+PVAL AllocateValue(PGLOBAL g, int type, int len, int prec,
+ bool uns, PSZ fmt)
+ {
+ PVAL valp;
+
+ switch (type) {
+ case TYPE_STRING:
+ valp = new(g) TYPVAL<PSZ>(g, (PSZ)NULL, len, prec);
+ break;
+ case TYPE_DATE:
+ valp = new(g) DTVAL(g, len, prec, fmt);
+ break;
+ case TYPE_INT:
+ if (uns)
+ valp = new(g) TYPVAL<uint>((uint)0, TYPE_INT, 0, true);
+ else
+ valp = new(g) TYPVAL<int>((int)0, TYPE_INT);
+
+ break;
+ case TYPE_BIGINT:
+ if (uns)
+ valp = new(g) TYPVAL<ulonglong>((ulonglong)0, TYPE_BIGINT, 0, true);
+ else
+ valp = new(g) TYPVAL<longlong>((longlong)0, TYPE_BIGINT);
+
+ break;
+ case TYPE_SHORT:
+ if (uns)
+ valp = new(g) TYPVAL<ushort>((ushort)0, TYPE_SHORT, 0, true);
+ else
+ valp = new(g) TYPVAL<short>((short)0, TYPE_SHORT);
+
+ break;
+ case TYPE_DOUBLE:
+ valp = new(g) TYPVAL<double>(0.0, TYPE_DOUBLE, prec);
+ break;
+ case TYPE_TINY:
+ if (uns)
+ valp = new(g) TYPVAL<uchar>((uchar)0, TYPE_TINY, 0, true);
+ else
+ valp = new(g) TYPVAL<char>((char)0, TYPE_TINY);
+
+ break;
+ case TYPE_DECIM:
+ valp = new(g) DECVAL(g, (PSZ)NULL, len, prec, uns);
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_VALUE_TYPE), type);
+ return NULL;
+ } // endswitch type
+
+ valp->SetGlobal(g);
+ return valp;
+ } // end of AllocateValue
+
+/***********************************************************************/
+/* Allocate a constant Value converted to newtype. */
+/* Can also be used to copy a Value eventually converted. */
+/***********************************************************************/
+PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
+ {
+ PSZ p, sp;
+ bool un = (uns < 0) ? false : (uns > 0) ? true : valp->IsUnsigned();
+
+ if (newtype == TYPE_VOID) // Means allocate a value of the same type
+ newtype = valp->GetType();
+
+ switch (newtype) {
+ case TYPE_STRING:
+ p = (PSZ)PlugSubAlloc(g, NULL, 1 + valp->GetValLen());
+
+ if ((sp = valp->GetCharString(p)) != p)
+ strcpy (p, sp);
+
+ valp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
+ break;
+ case TYPE_SHORT:
+ if (un)
+ valp = new(g) TYPVAL<ushort>(valp->GetUShortValue(),
+ TYPE_SHORT, 0, true);
+ else
+ valp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT);
+
+ break;
+ case TYPE_INT:
+ if (un)
+ valp = new(g) TYPVAL<uint>(valp->GetUIntValue(), TYPE_INT, 0, true);
+ else
+ valp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT);
+
+ break;
+ case TYPE_BIGINT:
+ if (un)
+ valp = new(g) TYPVAL<ulonglong>(valp->GetUBigintValue(),
+ TYPE_BIGINT, 0, true);
+ else
+ valp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT);
+
+ break;
+ case TYPE_DATE:
+ valp = new(g) DTVAL(g, valp->GetIntValue());
+ break;
+ case TYPE_DOUBLE:
+ valp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_DOUBLE,
+ valp->GetValPrec());
+ break;
+ case TYPE_TINY:
+ if (un)
+ valp = new(g) TYPVAL<uchar>(valp->GetUTinyValue(),
+ TYPE_TINY, 0, true);
+ else
+ valp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY);
+
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_VALUE_TYPE), newtype);
+ return NULL;
+ } // endswitch type
+
+ valp->SetGlobal(g);
+ return valp;
+ } // end of AllocateValue
+
+/* -------------------------- Class VALUE ---------------------------- */
+
+/***********************************************************************/
+/* Class VALUE protected constructor. */
+/***********************************************************************/
+VALUE::VALUE(int type, bool un) : Type(type)
+ {
+ Null = false;
+ Nullable = false;
+ Unsigned = un;
+ Clen = 0;
+ Prec = 0;
+ Fmt = GetFmt(Type, Unsigned);
+ Xfmt = GetXfmt();
+ } // end of VALUE constructor
+
+/***********************************************************************/
+/* VALUE GetXfmt: returns the extended format to use with typed value. */
+/***********************************************************************/
+const char *VALUE::GetXfmt(void)
+ {
+ const char *fmt;
+
+ switch (Type) {
+ case TYPE_DECIM:
+ case TYPE_STRING: fmt = "%*s"; break;
+ case TYPE_SHORT: fmt = (Unsigned) ? "%*hu" : "%*hd"; break;
+ case TYPE_BIGINT: fmt = (Unsigned) ? "%*llu" : "%*lld"; break;
+ case TYPE_DOUBLE: fmt = "%*.*lf"; break;
+ default: fmt = (Unsigned) ? "%*u" : "%*d"; break;
+ } // endswitch Type
+
+ return fmt;
+ } // end of GetFmt
+
+/***********************************************************************/
+/* Returns a BYTE indicating the comparison between two values. */
+/* Bit 1 indicates equality, Bit 2 less than, and Bit3 greater than. */
+/* More than 1 bit can be set only in the case of TYPE_LIST. */
+/***********************************************************************/
+BYTE VALUE::TestValue(PVAL vp)
+ {
+ int n = CompareValue(vp);
+
+ return (n > 0) ? 0x04 : (n < 0) ? 0x02 : 0x01;
+ } // end of TestValue
+
+/* -------------------------- Class TYPVAL ---------------------------- */
+
+/***********************************************************************/
+/* TYPVAL public constructor from a constant typed value. */
+/***********************************************************************/
+template <class TYPE>
+TYPVAL<TYPE>::TYPVAL(TYPE n, int type, int prec, bool un)
+ : VALUE(type, un)
+ {
+ Tval = n;
+ Clen = sizeof(TYPE);
+ Prec = prec;
+ } // end of TYPVAL constructor
+
+/***********************************************************************/
+/* Return unsigned max value for the type. */
+/***********************************************************************/
+template <class TYPE>
+ulonglong TYPVAL<TYPE>::MaxVal(void) {DBUG_ASSERT(false); return 0;}
+
+template <>
+ulonglong TYPVAL<short>::MaxVal(void) {return INT_MAX16;}
+
+template <>
+ulonglong TYPVAL<ushort>::MaxVal(void) {return UINT_MAX16;}
+
+template <>
+ulonglong TYPVAL<int>::MaxVal(void) {return INT_MAX32;}
+
+template <>
+ulonglong TYPVAL<uint>::MaxVal(void) {return UINT_MAX32;}
+
+template <>
+ulonglong TYPVAL<char>::MaxVal(void) {return INT_MAX8;}
+
+template <>
+ulonglong TYPVAL<uchar>::MaxVal(void) {return UINT_MAX8;}
+
+template <>
+ulonglong TYPVAL<longlong>::MaxVal(void) {return INT_MAX64;}
+
+template <>
+ulonglong TYPVAL<ulonglong>::MaxVal(void) {return ULONGLONG_MAX;}
+
+/***********************************************************************/
+/* TYPVAL GetValLen: returns the print length of the typed object. */
+/***********************************************************************/
+template <class TYPE>
+int TYPVAL<TYPE>::GetValLen(void)
+ {
+ char c[32];
+
+ return sprintf(c, Fmt, Tval);
+ } // end of GetValLen
+
+template <>
+int TYPVAL<double>::GetValLen(void)
+ {
+ char c[32];
+
+ return sprintf(c, Fmt, Prec, Tval);
+ } // end of GetValLen
+
+/***********************************************************************/
+/* TYPVAL SetValue: copy the value of another Value object. */
+/* This function allows conversion if chktype is false. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::SetValue_pval(PVAL valp, bool chktype)
+ {
+ if (chktype && Type != valp->GetType())
+ return true;
+
+ if (!(Null = valp->IsNull() && Nullable))
+ Tval = GetTypedValue(valp);
+ else
+ Reset();
+
+ return false;
+ } // end of SetValue
+
+template <>
+short TYPVAL<short>::GetTypedValue(PVAL valp)
+ {return valp->GetShortValue();}
+
+template <>
+ushort TYPVAL<ushort>::GetTypedValue(PVAL valp)
+ {return valp->GetUShortValue();}
+
+template <>
+int TYPVAL<int>::GetTypedValue(PVAL valp)
+ {return valp->GetIntValue();}
+
+template <>
+uint TYPVAL<uint>::GetTypedValue(PVAL valp)
+ {return valp->GetUIntValue();}
+
+template <>
+longlong TYPVAL<longlong>::GetTypedValue(PVAL valp)
+ {return valp->GetBigintValue();}
+
+template <>
+ulonglong TYPVAL<ulonglong>::GetTypedValue(PVAL valp)
+ {return valp->GetUBigintValue();}
+
+template <>
+double TYPVAL<double>::GetTypedValue(PVAL valp)
+ {return valp->GetFloatValue();}
+
+template <>
+char TYPVAL<char>::GetTypedValue(PVAL valp)
+ {return valp->GetTinyValue();}
+
+template <>
+uchar TYPVAL<uchar>::GetTypedValue(PVAL valp)
+ {return valp->GetUTinyValue();}
+
+/***********************************************************************/
+/* TYPVAL SetValue: convert chars extracted from a line to TYPE value.*/
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::SetValue_char(char *p, int n)
+ {
+ bool rc, minus;
+ ulonglong maxval = MaxVal();
+ ulonglong val = CharToNumber(p, n, maxval, Unsigned, &minus, &rc);
+
+ if (minus && val < maxval)
+ Tval = (TYPE)(-(signed)val);
+ else
+ Tval = (TYPE)val;
+
+ if (trace > 1) {
+ char buf[64];
+ htrc(strcat(strcat(strcpy(buf, " setting %s to: "), Fmt), "\n"),
+ GetTypeName(Type), Tval);
+ } // endif trace
+
+ Null = false;
+ return rc;
+ } // end of SetValue
+
+template <>
+bool TYPVAL<double>::SetValue_char(char *p, int n)
+ {
+ if (p) {
+ char buf[32];
+
+ for (; n > 0 && *p == ' '; p++)
+ n--;
+
+ memcpy(buf, p, min(n, 31));
+ buf[n] = '\0';
+ Tval = atof(buf);
+
+ if (trace > 1)
+ htrc(" setting double: '%s' -> %lf\n", buf, Tval);
+
+ Null = false;
+ } else {
+ Reset();
+ Null = Nullable;
+ } // endif p
+
+ return false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* TYPVAL SetValue: fill a typed value from a string. */
+/***********************************************************************/
+template <class TYPE>
+void TYPVAL<TYPE>::SetValue_psz(PSZ s)
+ {
+ if (s) {
+ SetValue_char(s, (int)strlen(s));
+ Null = false;
+ } else {
+ Reset();
+ Null = Nullable;
+ } // endif p
+
+ } // end of SetValue
+
+/***********************************************************************/
+/* TYPVAL SetValue: set value with a TYPE extracted from a block. */
+/***********************************************************************/
+template <class TYPE>
+void TYPVAL<TYPE>::SetValue_pvblk(PVBLK blk, int n)
+ {
+ Tval = GetTypedValue(blk, n);
+ Null = false;
+ } // end of SetValue
+
+template <>
+int TYPVAL<int>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetIntValue(n);}
+
+template <>
+uint TYPVAL<uint>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetUIntValue(n);}
+
+template <>
+short TYPVAL<short>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetShortValue(n);}
+
+template <>
+ushort TYPVAL<ushort>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetUShortValue(n);}
+
+template <>
+longlong TYPVAL<longlong>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetBigintValue(n);}
+
+template <>
+ulonglong TYPVAL<ulonglong>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetUBigintValue(n);}
+
+template <>
+double TYPVAL<double>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetFloatValue(n);}
+
+template <>
+char TYPVAL<char>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetTinyValue(n);}
+
+template <>
+uchar TYPVAL<uchar>::GetTypedValue(PVBLK blk, int n)
+ {return blk->GetUTinyValue(n);}
+
+/***********************************************************************/
+/* TYPVAL SetBinValue: with bytes extracted from a line. */
+/***********************************************************************/
+template <class TYPE>
+void TYPVAL<TYPE>::SetBinValue(void *p)
+ {
+ Tval = *(TYPE *)p;
+ Null = false;
+ } // end of SetBinValue
+
+/***********************************************************************/
+/* GetBinValue: fill a buffer with the internal binary value. */
+/* This function checks whether the buffer length is enough and */
+/* returns true if not. Actual filling occurs only if go is true. */
+/* Currently used by WriteColumn of binary files. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::GetBinValue(void *buf, int buflen, bool go)
+ {
+ // Test on length was removed here until a variable in column give the
+ // real field length. For BIN files the field length logically cannot
+ // be different from the variable length because no conversion is done.
+ // Therefore this test is useless anyway.
+//#if defined(_DEBUG)
+// if (sizeof(TYPE) > buflen)
+// return true;
+//#endif
+
+ if (go)
+ *(TYPE *)buf = Tval;
+
+ Null = false;
+ return false;
+ } // end of GetBinValue
+
+/***********************************************************************/
+/* TYPVAL ShowValue: get string representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::ShowValue(char *buf, int len)
+ {
+ sprintf(buf, Xfmt, len, Tval);
+ return buf;
+ } // end of ShowValue
+
+template <>
+char *TYPVAL<double>::ShowValue(char *buf, int len)
+ {
+ // TODO: use snprintf to avoid possible overflow
+ sprintf(buf, Xfmt, len, Prec, Tval);
+ return buf;
+ } // end of ShowValue
+
+/***********************************************************************/
+/* TYPVAL GetCharString: get string representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetCharString(char *p)
+ {
+ sprintf(p, Fmt, Tval);
+ return p;
+ } // end of GetCharString
+
+template <>
+char *TYPVAL<double>::GetCharString(char *p)
+ {
+ sprintf(p, Fmt, Prec, Tval);
+ return p;
+ } // end of GetCharString
+
+#if 0
+/***********************************************************************/
+/* TYPVAL GetShortString: get short representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetShortString(char *p, int n)
+ {
+ sprintf(p, "%*hd", n, (short)Tval);
+ return p;
+ } // end of GetShortString
+
+/***********************************************************************/
+/* TYPVAL GetIntString: get int representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetIntString(char *p, int n)
+ {
+ sprintf(p, "%*d", n, (int)Tval);
+ return p;
+ } // end of GetIntString
+
+/***********************************************************************/
+/* TYPVAL GetBigintString: get big int representation of a TYPE value.*/
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetBigintString(char *p, int n)
+ {
+ sprintf(p, "%*lld", n, (longlong)Tval);
+ return p;
+ } // end of GetBigintString
+
+/***********************************************************************/
+/* TYPVAL GetFloatString: get double representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetFloatString(char *p, int n, int prec)
+ {
+ sprintf(p, "%*.*lf", n, (prec < 0) ? 2 : prec, (double)Tval);
+ return p;
+ } // end of GetFloatString
+
+/***********************************************************************/
+/* TYPVAL GetTinyString: get char representation of a typed value. */
+/***********************************************************************/
+template <class TYPE>
+char *TYPVAL<TYPE>::GetTinyString(char *p, int n)
+ {
+ sprintf(p, "%*d", n, (int)(char)Tval);
+ return p;
+ } // end of GetIntString
+#endif // 0
+
+/***********************************************************************/
+/* TYPVAL compare value with another Value. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::IsEqual(PVAL vp, bool chktype)
+ {
+ if (this == vp)
+ return true;
+ else if (chktype && Type != vp->GetType())
+ return false;
+ else if (chktype && Unsigned != vp->IsUnsigned())
+ return false;
+ else if (Null || vp->IsNull())
+ return false;
+ else
+ return (Tval == GetTypedValue(vp));
+
+ } // end of IsEqual
+
+/***********************************************************************/
+/* Compare values and returns 1, 0 or -1 according to comparison. */
+/* This function is used for evaluation of numeric filters. */
+/***********************************************************************/
+template <class TYPE>
+int TYPVAL<TYPE>::CompareValue(PVAL vp)
+ {
+//assert(vp->GetType() == Type);
+
+ // Process filtering on numeric values.
+ TYPE n = GetTypedValue(vp);
+
+//if (trace)
+// htrc(" Comparing: val=%d,%d\n", Tval, n);
+
+ return (Tval > n) ? 1 : (Tval < n) ? (-1) : 0;
+ } // end of CompareValue
+
+/***********************************************************************/
+/* FormatValue: This function set vp (a STRING value) to the string */
+/* constructed from its own value formated using the fmt format. */
+/* This function assumes that the format matches the value type. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::FormatValue(PVAL vp, char *fmt)
+ {
+ char *buf = (char*)vp->GetTo_Val(); // Should be big enough
+ int n = sprintf(buf, fmt, Tval);
+
+ return (n > vp->GetValLen());
+ } // end of FormatValue
+
+/***********************************************************************/
+/* TYPVAL SetFormat function (used to set SELECT output format). */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::SetConstFormat(PGLOBAL g, FORMAT& fmt)
+ {
+ char c[32];
+
+ fmt.Type[0] = *GetFormatType(Type);
+ fmt.Length = sprintf(c, Fmt, Tval);
+ fmt.Prec = Prec;
+ return false;
+ } // end of SetConstFormat
+
+/***********************************************************************/
+/* Make file output of a typed object. */
+/***********************************************************************/
+template <class TYPE>
+void TYPVAL<TYPE>::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64], buf[12];
+
+ memset(m, ' ', n); /* Make margin string */
+ m[n] = '\0';
+
+ if (Null)
+ fprintf(f, "%s<null>\n", m);
+ else
+ fprintf(f, strcat(strcat(strcpy(buf, "%s"), Fmt), "\n"), m, Tval);
+
+ } /* end of Print */
+
+/***********************************************************************/
+/* Make string output of a int object. */
+/***********************************************************************/
+template <class TYPE>
+void TYPVAL<TYPE>::Print(PGLOBAL g, char *ps, uint z)
+ {
+ if (Null)
+ strcpy(ps, "<null>");
+ else
+ sprintf(ps, Fmt, Tval);
+
+ } /* end of Print */
+
+/* -------------------------- Class STRING --------------------------- */
+
+/***********************************************************************/
+/* STRING public constructor from a constant string. */
+/***********************************************************************/
+TYPVAL<PSZ>::TYPVAL(PSZ s) : VALUE(TYPE_STRING)
+ {
+ Strp = s;
+ Len = strlen(s);
+ Clen = Len;
+ Ci = false;
+ } // end of STRING constructor
+
+/***********************************************************************/
+/* STRING public constructor from char. */
+/***********************************************************************/
+TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c)
+ : VALUE(TYPE_STRING)
+ {
+ Len = (g) ? n : strlen(s);
+
+ if (!s) {
+ if (g) {
+ Strp = (char *)PlugSubAlloc(g, NULL, Len + 1);
+ Strp[Len] = '\0';
+ } else
+ assert(false);
+
+ } else
+ Strp = s;
+
+ Clen = Len;
+ Ci = (c != 0);
+ } // end of STRING constructor
+
+/***********************************************************************/
+/* Get the tiny value represented by the Strp string. */
+/***********************************************************************/
+char TYPVAL<PSZ>::GetTinyValue(void)
+ {
+ bool m;
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX8, false, &m);
+
+ return (m && val < INT_MAX8) ? (char)(-(signed)val) : (char)val;
+ } // end of GetTinyValue
+
+/***********************************************************************/
+/* Get the unsigned tiny value represented by the Strp string. */
+/***********************************************************************/
+uchar TYPVAL<PSZ>::GetUTinyValue(void)
+ {
+ return (uchar)CharToNumber(Strp, strlen(Strp), UINT_MAX8, true);
+ } // end of GetUTinyValue
+
+/***********************************************************************/
+/* Get the short value represented by the Strp string. */
+/***********************************************************************/
+short TYPVAL<PSZ>::GetShortValue(void)
+ {
+ bool m;
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX16, false, &m);
+
+ return (m && val < INT_MAX16) ? (short)(-(signed)val) : (short)val;
+ } // end of GetShortValue
+
+/***********************************************************************/
+/* Get the unsigned short value represented by the Strp string. */
+/***********************************************************************/
+ushort TYPVAL<PSZ>::GetUShortValue(void)
+ {
+ return (ushort)CharToNumber(Strp, strlen(Strp), UINT_MAX16, true);
+ } // end of GetUshortValue
+
+/***********************************************************************/
+/* Get the integer value represented by the Strp string. */
+/***********************************************************************/
+int TYPVAL<PSZ>::GetIntValue(void)
+ {
+ bool m;
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX32, false, &m);
+
+ return (m && val < INT_MAX32) ? (int)(-(signed)val) : (int)val;
+ } // end of GetIntValue
+
+/***********************************************************************/
+/* Get the unsigned integer value represented by the Strp string. */
+/***********************************************************************/
+uint TYPVAL<PSZ>::GetUIntValue(void)
+ {
+ return (uint)CharToNumber(Strp, strlen(Strp), UINT_MAX32, true);
+ } // end of GetUintValue
+
+/***********************************************************************/
+/* Get the big integer value represented by the Strp string. */
+/***********************************************************************/
+longlong TYPVAL<PSZ>::GetBigintValue(void)
+ {
+ bool m;
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX64, false, &m);
+
+ return (m && val < INT_MAX64) ? (-(signed)val) : (longlong)val;
+ } // end of GetBigintValue
+
+/***********************************************************************/
+/* Get the unsigned big integer value represented by the Strp string. */
+/***********************************************************************/
+ulonglong TYPVAL<PSZ>::GetUBigintValue(void)
+ {
+ return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true);
+ } // end of GetUBigintValue
+
+/***********************************************************************/
+/* STRING SetValue: copy the value of another Value object. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::SetValue_pval(PVAL valp, bool chktype)
+ {
+ if (chktype && (valp->GetType() != Type || valp->GetSize() > Len))
+ return true;
+
+ char buf[32];
+
+ if (!(Null = valp->IsNull() && Nullable))
+ strncpy(Strp, valp->GetCharString(buf), Len);
+ else
+ Reset();
+
+ return false;
+ } // end of SetValue_pval
+
+/***********************************************************************/
+/* STRING SetValue: fill string with chars extracted from a line. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::SetValue_char(char *p, int n)
+ {
+ bool rc;
+
+ if (p) {
+ rc = n > Len;
+
+ if ((n = min(n, Len))) {
+ strncpy(Strp, p, n);
+
+// for (p = Strp + n - 1; p >= Strp && (*p == ' ' || *p == '\0'); p--) ;
+ for (p = Strp + n - 1; p >= Strp; p--)
+ if (*p && *p != ' ')
+ break;
+
+ *(++p) = '\0';
+
+ if (trace > 1)
+ htrc(" Setting string to: '%s'\n", Strp);
+
+ } else
+ Reset();
+
+ Null = false;
+ } else {
+ rc = false;
+ Reset();
+ Null = Nullable;
+ } // endif p
+
+ return rc;
+ } // end of SetValue_char
+
+/***********************************************************************/
+/* STRING SetValue: fill string with another string. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue_psz(PSZ s)
+ {
+ if (s) {
+ strncpy(Strp, s, Len);
+ Null = false;
+ } else {
+ Reset();
+ Null = Nullable;
+ } // endif s
+
+ } // end of SetValue_psz
+
+/***********************************************************************/
+/* STRING SetValue: fill string with a string extracted from a block. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue_pvblk(PVBLK blk, int n)
+ {
+ // STRBLK's can return a NULL pointer
+ PSZ vp = blk->GetCharString(Strp, n);
+
+ if (vp != Strp)
+ SetValue_psz(vp);
+
+ } // end of SetValue_pvblk
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of an integer. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(int n)
+ {
+ char buf[16];
+ PGLOBAL& g = Global;
+ int k = sprintf(buf, "%d", n);
+
+ if (k > Len) {
+ sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
+ longjmp(g->jumper[g->jump_level], 138);
+ } else
+ SetValue_psz(buf);
+
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of an uint. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(uint n)
+ {
+ char buf[16];
+ PGLOBAL& g = Global;
+ int k = sprintf(buf, "%u", n);
+
+ if (k > Len) {
+ sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
+ longjmp(g->jumper[g->jump_level], 138);
+ } else
+ SetValue_psz(buf);
+
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a short int. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(short i)
+ {
+ SetValue((int)i);
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a ushort int. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(ushort i)
+ {
+ SetValue((uint)i);
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a big integer.*/
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(longlong n)
+ {
+ char buf[24];
+ PGLOBAL& g = Global;
+ int k = sprintf(buf, "%lld", n);
+
+ if (k > Len) {
+ sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
+ longjmp(g->jumper[g->jump_level], 138);
+ } else
+ SetValue_psz(buf);
+
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a big integer.*/
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(ulonglong n)
+ {
+ char buf[24];
+ PGLOBAL& g = Global;
+ int k = sprintf(buf, "%llu", n);
+
+ if (k > Len) {
+ sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
+ longjmp(g->jumper[g->jump_level], 138);
+ } else
+ SetValue_psz(buf);
+
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a double. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(double f)
+ {
+ char *p, buf[32];
+ PGLOBAL& g = Global;
+ int k = sprintf(buf, "%lf", f);
+
+ for (p = buf + k - 1; p >= buf; p--)
+ if (*p == '0') {
+ *p = 0;
+ k--;
+ } else
+ break;
+
+ if (k > Len) {
+ sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len);
+ longjmp(g->jumper[g->jump_level], 138);
+ } else
+ SetValue_psz(buf);
+
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a tiny int. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(char c)
+ {
+ SetValue((int)c);
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetValue: get the character representation of a tiny int. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetValue(uchar c)
+ {
+ SetValue((uint)c);
+ Null = false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* STRING SetBinValue: fill string with chars extracted from a line. */
+/***********************************************************************/
+void TYPVAL<PSZ>::SetBinValue(void *p)
+ {
+ SetValue_char((char *)p, Len);
+ } // end of SetBinValue
+
+/***********************************************************************/
+/* GetBinValue: fill a buffer with the internal binary value. */
+/* This function checks whether the buffer length is enough and */
+/* returns true if not. Actual filling occurs only if go is true. */
+/* Currently used by WriteColumn of binary files. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::GetBinValue(void *buf, int buflen, bool go)
+ {
+ int len = (Null) ? 0 : strlen(Strp);
+
+ if (len > buflen)
+ return true;
+ else if (go) {
+ memset(buf, ' ', buflen);
+ memcpy(buf, Strp, len);
+ } // endif go
+
+ return false;
+ } // end of GetBinValue
+
+/***********************************************************************/
+/* STRING ShowValue: get string representation of a char value. */
+/***********************************************************************/
+char *TYPVAL<PSZ>::ShowValue(char *buf, int len)
+ {
+ return Strp;
+ } // end of ShowValue
+
+/***********************************************************************/
+/* STRING GetCharString: get string representation of a char value. */
+/***********************************************************************/
+char *TYPVAL<PSZ>::GetCharString(char *p)
+ {
+ return Strp;
+ } // end of GetCharString
+
+/***********************************************************************/
+/* STRING compare value with another Value. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::IsEqual(PVAL vp, bool chktype)
+ {
+ if (this == vp)
+ return true;
+ else if (chktype && Type != vp->GetType())
+ return false;
+ else if (Null || vp->IsNull())
+ return false;
+
+ char buf[32];
+
+ if (Ci || vp->IsCi())
+ return !stricmp(Strp, vp->GetCharString(buf));
+ else // (!Ci)
+ return !strcmp(Strp, vp->GetCharString(buf));
+
+ } // end of IsEqual
+
+/***********************************************************************/
+/* Compare values and returns 1, 0 or -1 according to comparison. */
+/* This function is used for evaluation of numeric filters. */
+/***********************************************************************/
+int TYPVAL<PSZ>::CompareValue(PVAL vp)
+ {
+ int n;
+//assert(vp->GetType() == Type);
+
+ if (trace)
+ htrc(" Comparing: val='%s','%s'\n", Strp, vp->GetCharValue());
+
+ // Process filtering on character strings.
+ if (Ci || vp->IsCi())
+ n = stricmp(Strp, vp->GetCharValue());
+ else
+ n = strcmp(Strp, vp->GetCharValue());
+
+#if defined(WIN32)
+ if (n == _NLSCMPERROR)
+ return n; // Here we should raise an error
+#endif // WIN32
+
+ return (n > 0) ? 1 : (n < 0) ? -1 : 0;
+ } // end of CompareValue
+
+/***********************************************************************/
+/* FormatValue: This function set vp (a STRING value) to the string */
+/* constructed from its own value formated using the fmt format. */
+/* This function assumes that the format matches the value type. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::FormatValue(PVAL vp, char *fmt)
+ {
+ char *buf = (char*)vp->GetTo_Val(); // Should be big enough
+ int n = sprintf(buf, fmt, Strp);
+
+ return (n > vp->GetValLen());
+ } // end of FormatValue
+
+/***********************************************************************/
+/* STRING SetFormat function (used to set SELECT output format). */
+/***********************************************************************/
+bool TYPVAL<PSZ>::SetConstFormat(PGLOBAL g, FORMAT& fmt)
+ {
+ fmt.Type[0] = 'C';
+ fmt.Length = Len;
+ fmt.Prec = 0;
+ return false;
+ } // end of SetConstFormat
+
+/* -------------------------- Class DECIMAL -------------------------- */
+
+/***********************************************************************/
+/* DECIMAL public constructor from a constant string. */
+/***********************************************************************/
+DECVAL::DECVAL(PSZ s) : TYPVAL<PSZ>(s)
+ {
+ if (s) {
+ char *p = strchr(Strp, '.');
+
+ Prec = (p) ? Len - (p - Strp) : 0;
+ } // endif s
+
+ Type = TYPE_DECIM;
+ } // end of DECVAL constructor
+
+/***********************************************************************/
+/* DECIMAL public constructor from char. */
+/***********************************************************************/
+DECVAL::DECVAL(PGLOBAL g, PSZ s, int n, int prec, bool uns)
+ : TYPVAL<PSZ>(g, s, n + (prec ? 1 : 0) + (uns ? 0 : 1), 0)
+ {
+ Prec = prec;
+ Unsigned = uns;
+ Type = TYPE_DECIM;
+ } // end of DECVAL constructor
+
+/***********************************************************************/
+/* DECIMAL: Check whether the numerica value is equal to 0. */
+/***********************************************************************/
+bool DECVAL::IsZero(void)
+ {
+ for (int i = 0; Strp[i]; i++)
+ if (!strchr("0 +-.", Strp[i]))
+ return false;
+
+ return true;
+ } // end of IsZero
+
+/***********************************************************************/
+/* DECIMAL: Reset value to zero. */
+/***********************************************************************/
+void DECVAL::Reset(void)
+{
+ int i = 0;
+
+ Strp[i++] = '0';
+
+ if (Prec) {
+ Strp[i++] = '.';
+
+ do {
+ Strp[i++] = '0';
+ } while (i < Prec + 2);
+
+ } // endif Prec
+
+ Strp[i] = 0;
+} // end of Reset
+
+/***********************************************************************/
+/* DECIMAL ShowValue: get string representation right justified. */
+/***********************************************************************/
+char *DECVAL::ShowValue(char *buf, int len)
+ {
+ sprintf(buf, Xfmt, len, Strp);
+ return buf;
+ } // end of ShowValue
+
+/***********************************************************************/
+/* GetBinValue: fill a buffer with the internal binary value. */
+/* This function checks whether the buffer length is enough and */
+/* returns true if not. Actual filling occurs only if go is true. */
+/* Currently used by WriteColumn of binary files. */
+/***********************************************************************/
+bool DECVAL::GetBinValue(void *buf, int buflen, bool go)
+ {
+ int len = (Null) ? 0 : strlen(Strp);
+
+ if (len > buflen)
+ return true;
+ else if (go) {
+ memset(buf, ' ', buflen - len);
+ memcpy((char*)buf + buflen - len, Strp, len);
+ } // endif go
+
+ return false;
+ } // end of GetBinValue
+
+#if 0
+/***********************************************************************/
+/* DECIMAL SetValue: copy the value of another Value object. */
+/***********************************************************************/
+bool DECVAL::SetValue_pval(PVAL valp, bool chktype)
+ {
+ if (chktype && (valp->GetType() != Type || valp->GetSize() > Len))
+ return true;
+
+ char buf[32];
+
+ if (!(Null = valp->IsNull() && Nullable))
+ strncpy(Strp, valp->GetCharString(buf), Len);
+ else
+ Reset();
+
+ return false;
+ } // end of SetValue_pval
+
+/***********************************************************************/
+/* DECIMAL SetValue: fill string with chars extracted from a line. */
+/***********************************************************************/
+bool DECVAL::SetValue_char(char *p, int n)
+ {
+ bool rc;
+
+ if (p) {
+ rc = n > Len;
+
+ if ((n = min(n, Len))) {
+ strncpy(Strp, p, n);
+
+// for (p = Strp + n - 1; p >= Strp && (*p == ' ' || *p == '\0'); p--) ;
+ for (p = Strp + n - 1; p >= Strp; p--)
+ if (*p && *p != ' ')
+ break;
+
+ *(++p) = '\0';
+
+ if (trace > 1)
+ htrc(" Setting string to: '%s'\n", Strp);
+
+ } else
+ Reset();
+
+ Null = false;
+ } else {
+ rc = false;
+ Reset();
+ Null = Nullable;
+ } // endif p
+
+ return rc;
+ } // end of SetValue_char
+
+/***********************************************************************/
+/* DECIMAL SetValue: fill string with another string. */
+/***********************************************************************/
+void DECVAL::SetValue_psz(PSZ s)
+ {
+ if (s) {
+ strncpy(Strp, s, Len);
+ Null = false;
+ } else {
+ Reset();
+ Null = Nullable;
+ } // endif s
+
+ } // end of SetValue_psz
+
+/***********************************************************************/
+/* DECIMAL SetValue: fill string with a string extracted from a block.*/
+/***********************************************************************/
+void DECVAL::SetValue_pvblk(PVBLK blk, int n)
+ {
+ // STRBLK's can return a NULL pointer
+ SetValue_psz(blk->GetCharValue(n));
+ } // end of SetValue_pvblk
+
+/***********************************************************************/
+/* DECIMAL SetBinValue: fill string with chars extracted from a line. */
+/***********************************************************************/
+void DECVAL::SetBinValue(void *p)
+ {
+ SetValue_char((char *)p, Len);
+ } // end of SetBinValue
+
+/***********************************************************************/
+/* DECIMAL GetCharString: get string representation of a char value. */
+/***********************************************************************/
+char *DECVAL::GetCharString(char *p)
+ {
+ return Strp;
+ } // end of GetCharString
+#endif // 0
+
+/***********************************************************************/
+/* DECIMAL compare value with another Value. */
+/***********************************************************************/
+bool DECVAL::IsEqual(PVAL vp, bool chktype)
+ {
+ if (this == vp)
+ return true;
+ else if (chktype && Type != vp->GetType())
+ return false;
+ else if (Null || vp->IsNull())
+ return false;
+
+ char buf[32];
+
+ return !strcmp(Strp, vp->GetCharString(buf));
+ } // end of IsEqual
+
+/***********************************************************************/
+/* Compare values and returns 1, 0 or -1 according to comparison. */
+/* This function is used for evaluation of numeric filters. */
+/***********************************************************************/
+int DECVAL::CompareValue(PVAL vp)
+ {
+//assert(vp->GetType() == Type);
+
+ // Process filtering on numeric values.
+ double f = atof(Strp), n = vp->GetFloatValue();
+
+//if (trace)
+// htrc(" Comparing: val=%d,%d\n", f, n);
+
+ return (f > n) ? 1 : (f < n) ? (-1) : 0;
+ } // end of CompareValue
+
+#if 0
+/***********************************************************************/
+/* FormatValue: This function set vp (a STRING value) to the string */
+/* constructed from its own value formated using the fmt format. */
+/* This function assumes that the format matches the value type. */
+/***********************************************************************/
+bool DECVAL::FormatValue(PVAL vp, char *fmt)
+ {
+ char *buf = (char*)vp->GetTo_Val(); // Should be big enough
+ int n = sprintf(buf, fmt, Strp);
+
+ return (n > vp->GetValLen());
+ } // end of FormatValue
+
+/***********************************************************************/
+/* DECIMAL SetFormat function (used to set SELECT output format). */
+/***********************************************************************/
+bool DECVAL::SetConstFormat(PGLOBAL g, FORMAT& fmt)
+ {
+ fmt.Type[0] = 'C';
+ fmt.Length = Len;
+ fmt.Prec = 0;
+ return false;
+ } // end of SetConstFormat
+#endif // 0
+
+/* -------------------------- Class DTVAL ---------------------------- */
+
+/***********************************************************************/
+/* DTVAL public constructor for new void values. */
+/***********************************************************************/
+DTVAL::DTVAL(PGLOBAL g, int n, int prec, PSZ fmt)
+ : TYPVAL<int>((int)0, TYPE_DATE)
+ {
+ if (!fmt) {
+ Pdtp = NULL;
+ Sdate = NULL;
+ DefYear = 0;
+ Len = n;
+ } else
+ SetFormat(g, fmt, n, prec);
+
+//Type = TYPE_DATE;
+ } // end of DTVAL constructor
+
+/***********************************************************************/
+/* DTVAL public constructor from int. */
+/***********************************************************************/
+DTVAL::DTVAL(PGLOBAL g, int n) : TYPVAL<int>(n, TYPE_DATE)
+ {
+ Pdtp = NULL;
+ Len = 19;
+//Type = TYPE_DATE;
+ Sdate = NULL;
+ DefYear = 0;
+ } // end of DTVAL constructor
+
+/***********************************************************************/
+/* Set format so formatted dates can be converted on input/output. */
+/***********************************************************************/
+bool DTVAL::SetFormat(PGLOBAL g, PSZ fmt, int len, int year)
+ {
+ Pdtp = MakeDateFormat(g, fmt, true, true, (year > 9999) ? 1 : 0);
+ Sdate = (char*)PlugSubAlloc(g, NULL, len + 1);
+ DefYear = (int)((year > 9999) ? (year - 10000) : year);
+ Len = len;
+ return false;
+ } // end of SetFormat
+
+/***********************************************************************/
+/* Set format from the format of another date value. */
+/***********************************************************************/
+bool DTVAL::SetFormat(PGLOBAL g, PVAL valp)
+ {
+ DTVAL *vp;
+
+ if (valp->GetType() != TYPE_DATE) {
+ sprintf(g->Message, MSG(NO_FORMAT_TYPE), valp->GetType());
+ return true;
+ } else
+ vp = (DTVAL*)valp;
+
+ Len = vp->Len;
+ Pdtp = vp->Pdtp;
+ Sdate = (char*)PlugSubAlloc(g, NULL, Len + 1);
+ DefYear = vp->DefYear;
+ return false;
+ } // end of SetFormat
+
+/***********************************************************************/
+/* We need TimeShift because the mktime C function does a correction */
+/* for local time zone that we want to override for DB operations. */
+/***********************************************************************/
+void DTVAL::SetTimeShift(void)
+ {
+ struct tm dtm;
+ memset(&dtm, 0, sizeof(dtm));
+ dtm.tm_mday=2;
+ dtm.tm_mon=0;
+ dtm.tm_year=70;
+
+ Shift = (int)mktime(&dtm) - 86400;
+
+ if (trace)
+ htrc("DTVAL Shift=%d\n", Shift);
+
+ } // end of SetTimeShift
+
+// Added by Alexander Barkov
+static void TIME_to_localtime(struct tm *tm, const MYSQL_TIME *ltime)
+{
+ bzero(tm, sizeof(*tm));
+ tm->tm_year= ltime->year - 1900;
+ tm->tm_mon= ltime->month - 1;
+ tm->tm_mday= ltime->day;
+ tm->tm_hour= ltime->hour;
+ tm->tm_min= ltime->minute;
+ tm->tm_sec= ltime->second;
+}
+
+// Added by Alexander Barkov
+static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm)
+{
+ MYSQL_TIME ltime;
+ thd_gmt_sec_to_TIME(current_thd, &ltime, (my_time_t) *timep);
+ TIME_to_localtime(tm, &ltime);
+ return tm;
+}
+
+/***********************************************************************/
+/* GetGmTime: returns a pointer to a static tm structure obtained */
+/* though the gmtime C function. The purpose of this function is to */
+/* extend the range of valid dates by accepting negative time values. */
+/***********************************************************************/
+struct tm *DTVAL::GetGmTime(struct tm *tm_buffer)
+ {
+ struct tm *datm;
+ time_t t = (time_t)Tval;
+
+ if (Tval < 0) {
+ int n;
+
+ for (n = 0; t < 0; n += 4)
+ t += FOURYEARS;
+
+ datm = gmtime_mysql(&t, tm_buffer);
+
+ if (datm)
+ datm->tm_year -= n;
+
+ } else
+ datm = gmtime_mysql(&t, tm_buffer);
+
+ return datm;
+ } // end of GetGmTime
+
+// Added by Alexander Barkov
+static time_t mktime_mysql(struct tm *ptm)
+{
+ MYSQL_TIME ltime;
+ localtime_to_TIME(&ltime, ptm);
+ ltime.time_type= MYSQL_TIMESTAMP_DATETIME;
+ uint error_code;
+ time_t t= TIME_to_timestamp(current_thd, &ltime, &error_code);
+ return error_code ? (time_t) -1 : t;
+}
+
+/***********************************************************************/
+/* MakeTime: calculates a date value from a tm structures using the */
+/* mktime C function. The purpose of this function is to extend the */
+/* range of valid dates by accepting to set negative time values. */
+/***********************************************************************/
+bool DTVAL::MakeTime(struct tm *ptm)
+ {
+ int n, y = ptm->tm_year;
+ time_t t = mktime_mysql(ptm);
+
+ if (trace > 1)
+ htrc("MakeTime from (%d,%d,%d,%d,%d,%d)\n",
+ ptm->tm_year, ptm->tm_mon, ptm->tm_mday,
+ ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+
+ if (t == -1) {
+ if (y < 1 || y > 71)
+ return true;
+
+ for (n = 0; t == -1 && n < 20; n++) {
+ ptm->tm_year += 4;
+ t = mktime_mysql(ptm);
+ } // endfor t
+
+ if (t == -1)
+ return true;
+
+ if ((t -= (n * FOURYEARS)) > 2000000000)
+ return true;
+
+ }
+ Tval= (int) t;
+
+ if (trace > 1)
+ htrc("MakeTime Ival=%d\n", Tval);
+
+ return false;
+ } // end of MakeTime
+
+/***********************************************************************/
+/* Make a time_t datetime from its components (YY, MM, DD, hh, mm, ss) */
+/***********************************************************************/
+bool DTVAL::MakeDate(PGLOBAL g, int *val, int nval)
+ {
+ int i, m;
+ int n;
+ bool rc = false;
+ struct tm datm;
+ bzero(&datm, sizeof(datm));
+ datm.tm_mday=1;
+ datm.tm_mon=0;
+ datm.tm_year=70;
+
+ if (trace > 1)
+ htrc("MakeDate from(%d,%d,%d,%d,%d,%d) nval=%d\n",
+ val[0], val[1], val[2], val[3], val[4], val[5], nval);
+
+ for (i = 0; i < nval; i++) {
+ n = val[i];
+
+// if (trace > 1)
+// htrc("i=%d n=%d\n", i, n);
+
+ switch (i) {
+ case 0:
+ if (n >= 1900)
+ n -= 1900;
+
+ datm.tm_year = n;
+
+// if (trace > 1)
+// htrc("n=%d tm_year=%d\n", n, datm.tm_year);
+
+ break;
+ case 1:
+ // If mktime handles apparently correctly large or negative
+ // day values, it is not the same for months. Therefore we
+ // do the ajustment here, thus mktime has not to do it.
+ if (n > 0) {
+ m = (n - 1) % 12;
+ n = (n - 1) / 12;
+ } else {
+ m = 11 + n % 12;
+ n = n / 12 - 1;
+ } // endfi n
+
+ datm.tm_mon = m;
+ datm.tm_year += n;
+
+// if (trace > 1)
+// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon);
+
+ break;
+ case 2:
+ // For days, big or negative values may also cause problems
+ m = n % 1461;
+ n = 4 * (n / 1461);
+
+ if (m < 0) {
+ m += 1461;
+ n -= 4;
+ } // endif m
+
+ datm.tm_mday = m;
+ datm.tm_year += n;
+
+// if (trace > 1)
+// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon);
+
+ break;
+ case 3: datm.tm_hour = n; break;
+ case 4: datm.tm_min = n; break;
+ case 5: datm.tm_sec = n; break;
+ } // endswitch i
+
+ } // endfor i
+
+ if (trace > 1)
+ htrc("MakeDate datm=(%d,%d,%d,%d,%d,%d)\n",
+ datm.tm_year, datm.tm_mon, datm.tm_mday,
+ datm.tm_hour, datm.tm_min, datm.tm_sec);
+
+ // Pass g to have an error return or NULL to set invalid dates to 0
+ if (MakeTime(&datm))
+ if (g) {
+ strcpy(g->Message, MSG(BAD_DATETIME));
+ rc = true;
+ } else
+ Tval = 0;
+
+ return rc;
+ } // end of MakeDate
+
+/***********************************************************************/
+/* DTVAL SetValue: copy the value of another Value object. */
+/* This function allows conversion if chktype is false. */
+/***********************************************************************/
+bool DTVAL::SetValue_pval(PVAL valp, bool chktype)
+ {
+ if (chktype && Type != valp->GetType())
+ return true;
+
+ if (!(Null = valp->IsNull() && Nullable)) {
+ if (Pdtp && !valp->IsTypeNum()) {
+ int ndv;
+ int dval[6];
+
+ ndv = ExtractDate(valp->GetCharValue(), Pdtp, DefYear, dval);
+ MakeDate(NULL, dval, ndv);
+ } else
+ Tval = valp->GetIntValue();
+
+ } else
+ Reset();
+
+ return false;
+ } // end of SetValue
+
+/***********************************************************************/
+/* SetValue: convert chars extracted from a line to date value. */
+/***********************************************************************/
+bool DTVAL::SetValue_char(char *p, int n)
+ {
+ bool rc;
+
+ if (Pdtp) {
+ char *p2;
+ int ndv;
+ int dval[6];
+
+ // Trim trailing blanks
+ for (p2 = p + n -1; p < p2 && *p2 == ' '; p2--) ;
+
+ if ((rc = (n = p2 - p + 1) > Len))
+ n = Len;
+
+ memcpy(Sdate, p, n);
+ Sdate[n] = '\0';
+
+ ndv = ExtractDate(Sdate, Pdtp, DefYear, dval);
+ MakeDate(NULL, dval, ndv);
+
+ if (trace > 1)
+ htrc(" setting date: '%s' -> %d\n", Sdate, Tval);
+
+ Null = false;
+ } else
+ rc = TYPVAL<int>::SetValue_char(p, n);
+
+ return rc;
+ } // end of SetValue
+
+/***********************************************************************/
+/* SetValue: convert a char string to date value. */
+/***********************************************************************/
+void DTVAL::SetValue_psz(PSZ p)
+ {
+ if (Pdtp) {
+ int ndv;
+ int dval[6];
+
+ strncpy(Sdate, p, Len);
+ Sdate[Len] = '\0';
+
+ ndv = ExtractDate(Sdate, Pdtp, DefYear, dval);
+ MakeDate(NULL, dval, ndv);
+
+ if (trace > 1)
+ htrc(" setting date: '%s' -> %d\n", Sdate, Tval);
+
+ Null = false;
+ } else
+ TYPVAL<int>::SetValue_psz(p);
+
+ } // end of SetValue
+
+/***********************************************************************/
+/* DTVAL SetValue: set value with a value extracted from a block. */
+/***********************************************************************/
+void DTVAL::SetValue_pvblk(PVBLK blk, int n)
+ {
+ if (Pdtp && !::IsTypeNum(blk->GetType())) {
+ int ndv;
+ int dval[6];
+
+ ndv = ExtractDate(blk->GetCharValue(n), Pdtp, DefYear, dval);
+ MakeDate(NULL, dval, ndv);
+ } else
+ Tval = blk->GetIntValue(n);
+
+ } // end of SetValue
+
+/***********************************************************************/
+/* DTVAL GetCharString: get string representation of a date value. */
+/***********************************************************************/
+char *DTVAL::GetCharString(char *p)
+ {
+ if (Pdtp) {
+ size_t n = 0;
+ struct tm tm, *ptm= GetGmTime(&tm);
+
+ if (ptm)
+ n = strftime(Sdate, Len + 1, Pdtp->OutFmt, ptm);
+
+ if (!n) {
+ *Sdate = '\0';
+ strncat(Sdate, "Error", Len + 1);
+ } // endif n
+
+ return Sdate;
+ } else
+ sprintf(p, "%d", Tval);
+
+ Null = false;
+ return p;
+ } // end of GetCharString
+
+/***********************************************************************/
+/* DTVAL ShowValue: get string representation of a date value. */
+/***********************************************************************/
+char *DTVAL::ShowValue(char *buf, int len)
+ {
+ if (Pdtp) {
+ char *p;
+ size_t m, n = 0;
+ struct tm tm, *ptm = GetGmTime(&tm);
+
+ if (Len < len) {
+ p = buf;
+ m = len;
+ } else {
+ p = Sdate;
+ m = Len + 1;
+ } // endif Len
+
+ if (ptm)
+ n = strftime(p, m, Pdtp->OutFmt, ptm);
+
+ if (!n) {
+ *p = '\0';
+ strncat(p, "Error", m);
+ } // endif n
+
+ return p;
+ } else
+ return TYPVAL<int>::ShowValue(buf, len);
+
+ } // end of ShowValue
+
+#if 0 // Not used by CONNECT
+/***********************************************************************/
+/* Returns a member of the struct tm representation of the date. */
+/***********************************************************************/
+bool DTVAL::GetTmMember(OPVAL op, int& mval)
+ {
+ bool rc = false;
+ struct tm tm, *ptm = GetGmTime(&tm);
+
+ switch (op) {
+ case OP_MDAY: mval = ptm->tm_mday; break;
+ case OP_MONTH: mval = ptm->tm_mon + 1; break;
+ case OP_YEAR: mval = ptm->tm_year + 1900; break;
+ case OP_WDAY: mval = ptm->tm_wday + 1; break;
+ case OP_YDAY: mval = ptm->tm_yday + 1; break;
+ case OP_QUART: mval = ptm->tm_mon / 3 + 1; break;
+ default:
+ rc = true;
+ } // endswitch op
+
+ return rc;
+ } // end of GetTmMember
+
+/***********************************************************************/
+/* Calculates the week number of the year for the internal date value.*/
+/* The International Standard ISO 8601 has decreed that Monday shall */
+/* be the first day of the week. A week that lies partly in one year */
+/* and partly in another is assigned a number in the year in which */
+/* most of its days lie. That means that week number 1 of any year is */
+/* the week that contains the January 4th. */
+/***********************************************************************/
+bool DTVAL::WeekNum(PGLOBAL g, int& nval)
+ {
+ // w is the start of the week SUN=0, MON=1, etc.
+ int m, n, w = nval % 7;
+ struct tm tm, *ptm = GetGmTime(&tm);
+
+ // Which day is January 4th of this year?
+ m = (367 + ptm->tm_wday - ptm->tm_yday) % 7;
+
+ // When does the first week begins?
+ n = 3 - (7 + m - w) % 7;
+
+ // Now calculate the week number
+ if (!(nval = (7 + ptm->tm_yday - n) / 7))
+ nval = 52;
+
+ // Everything should be Ok
+ return false;
+ } // end of WeekNum
+#endif // 0
+
+/***********************************************************************/
+/* FormatValue: This function set vp (a STRING value) to the string */
+/* constructed from its own value formated using the fmt format. */
+/* This function assumes that the format matches the value type. */
+/***********************************************************************/
+bool DTVAL::FormatValue(PVAL vp, char *fmt)
+ {
+ char *buf = (char*)vp->GetTo_Val(); // Should be big enough
+ struct tm tm, *ptm = GetGmTime(&tm);
+
+ if (trace > 1)
+ htrc("FormatValue: ptm=%p len=%d\n", ptm, vp->GetValLen());
+
+ if (ptm) {
+ size_t n = strftime(buf, vp->GetValLen(), fmt, ptm);
+
+ if (trace > 1)
+ htrc("strftime: n=%d buf=%s\n", n, (n) ? buf : "???");
+
+ return (n == 0);
+ } else
+ return true;
+
+ } // end of FormatValue
+
+/* -------------------------- End of Value --------------------------- */
diff --git a/storage/connect/value.h b/storage/connect/value.h
index 39fee7f73bb..e9a899302c9 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -1,354 +1,333 @@
-/**************** Value H Declares Source Code File (.H) ***************/
-/* Name: VALUE.H Version 2.0 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */
-/* */
-/* This file contains the VALUE and derived classes declares. */
-/***********************************************************************/
-#ifndef __VALUE__H__
-#define __VALUE__H__
-
-/***********************************************************************/
-/* Include required application header files */
-/* assert.h is header required when using the assert function. */
-/* block.h is header containing Block global declarations. */
-/***********************************************************************/
-#include "assert.h"
-#include "block.h"
-
-/***********************************************************************/
-/* Types used in some class definitions. */
-/***********************************************************************/
-enum CONV {CNV_ANY = 0, /* Convert to any type */
- CNV_CHAR = 1, /* Convert to character type */
- CNV_NUM = 2}; /* Convert to numeric type */
-
-/***********************************************************************/
-/* Types used in some class definitions. */
-/***********************************************************************/
-class CONSTANT; // For friend setting
-typedef struct _datpar *PDTP; // For DTVAL
-
-
-/***********************************************************************/
-/* Utilities used to test types and to allocated values. */
-/***********************************************************************/
-PVAL AllocateValue(PGLOBAL, void *, short);
-
-// Exported functions
-DllExport PSZ GetTypeName(int);
-DllExport int GetTypeSize(int, int);
-#ifdef ODBC_SUPPORT
-/* This function is exported for use in EOM table type DLLs */
-DllExport int TranslateSQLType(int stp, int prec, int& len, char& v);
-#endif
-DllExport char *GetFormatType(int);
-DllExport int GetFormatType(char);
-DllExport bool IsTypeChar(int type);
-DllExport bool IsTypeNum(int type);
-#if defined(BLK_INDX)
-DllExport int ConvertType(int, int, CONV, bool match = false);
-DllExport PVAL AllocateValue(PGLOBAL, PVAL, int = TYPE_VOID, int = 0);
-#endif // BLK_INDX
-DllExport PVAL AllocateValue(PGLOBAL, int, int len = 0, int prec = 0,
- bool uns = false, PSZ fmt = NULL);
-DllExport ulonglong CharToNumber(char *, int, ulonglong, bool,
- bool *minus = NULL, bool *rc = NULL);
-
-/***********************************************************************/
-/* Class VALUE represents a constant or variable of any valid type. */
-/***********************************************************************/
-class DllExport VALUE : public BLOCK {
- friend class CONSTANT; // The only object allowed to use SetConstFormat
- public:
- // Constructors
-
- // Implementation
- virtual bool IsTypeNum(void) = 0;
- virtual bool IsZero(void) = 0;
- virtual bool IsCi(void) {return false;}
- virtual bool IsUnsigned(void) {return Unsigned;}
- virtual void Reset(void) = 0;
- virtual int GetSize(void) = 0;
- virtual int GetValLen(void) = 0;
- virtual int GetValPrec(void) = 0;
- virtual int GetLength(void) {return 1;}
- virtual PSZ GetCharValue(void) {assert(false); return NULL;}
- virtual char GetTinyValue(void) {assert(false); return 0;}
- virtual uchar GetUTinyValue(void) {assert(false); return 0;}
- virtual short GetShortValue(void) {assert(false); return 0;}
- virtual ushort GetUShortValue(void) {assert(false); return 0;}
- virtual int GetIntValue(void) = 0;
- virtual uint GetUIntValue(void) = 0;
- virtual longlong GetBigintValue(void) = 0;
- virtual ulonglong GetUBigintValue(void) = 0;
- virtual double GetFloatValue(void) = 0;
- virtual void *GetTo_Val(void) = 0;
- virtual void SetPrec(int prec) {Prec = prec;}
- bool IsNull(void) {return Null;}
- void SetNull(bool b) {Null = b;}
- bool GetNullable(void) {return Nullable;}
- void SetNullable(bool b) {Nullable = b;}
- int GetType(void) {return Type;}
- int GetClen(void) {return Clen;}
- void SetGlobal(PGLOBAL g) {Global = g;}
-
- // Methods
- virtual bool SetValue_pval(PVAL valp, bool chktype = false) = 0;
- virtual bool SetValue_char(char *p, int n) = 0;
- virtual void SetValue_psz(PSZ s) = 0;
-#if defined(BLK_INDX)
- virtual void SetValue_bool(bool b) {assert(FALSE);}
- virtual int CompareValue(PVAL vp) = 0;
- virtual BYTE TestValue(PVAL vp);
-#endif // BLK_INDX
- virtual void SetValue(char c) {assert(false);}
- virtual void SetValue(uchar c) {assert(false);}
- virtual void SetValue(short i) {assert(false);}
- virtual void SetValue(ushort i) {assert(false);}
- virtual void SetValue(int n) {assert(false);}
- virtual void SetValue(uint n) {assert(false);}
- virtual void SetValue(longlong n) {assert(false);}
- virtual void SetValue(ulonglong n) {assert(false);}
- virtual void SetValue(double f) {assert(false);}
- virtual void SetValue_pvblk(PVBLK blk, int n) = 0;
- virtual void SetBinValue(void *p) = 0;
- virtual bool GetBinValue(void *buf, int buflen, bool go) = 0;
- virtual char *ShowValue(char *buf, int len = 0) = 0;
- virtual char *GetCharString(char *p) = 0;
- virtual bool IsEqual(PVAL vp, bool chktype) = 0;
- virtual bool FormatValue(PVAL vp, char *fmt) = 0;
-
- protected:
- virtual bool SetConstFormat(PGLOBAL, FORMAT&) = 0;
- const char *GetXfmt(void);
-
- // Constructor used by derived classes
- VALUE(int type, bool un = false);
-
- // Members
- PGLOBAL Global; // To reduce arglist
- const char *Fmt;
- const char *Xfmt;
- bool Nullable; // True if value can be null
- bool Null; // True if value is null
- bool Unsigned; // True if unsigned
- int Type; // The value type
- int Clen; // Internal value length
- int Prec;
- }; // end of class VALUE
-
-/***********************************************************************/
-/* Class TYPVAL: represents a typed value. */
-/***********************************************************************/
-template <class TYPE>
-class DllExport TYPVAL : public VALUE {
- public:
- // Constructor
- TYPVAL(TYPE n, int type, int prec = 0, bool un = false);
-
- // Implementation
- virtual bool IsTypeNum(void) {return true;}
- virtual bool IsZero(void) {return Tval == 0;}
- virtual void Reset(void) {Tval = 0;}
- virtual int GetValLen(void);
- virtual int GetValPrec() {return 0;}
- virtual int GetSize(void) {return sizeof(TYPE);}
- virtual PSZ GetCharValue(void) {return VALUE::GetCharValue();}
- virtual char GetTinyValue(void) {return (char)Tval;}
- virtual uchar GetUTinyValue(void) {return (uchar)Tval;}
- virtual short GetShortValue(void) {return (short)Tval;}
- virtual ushort GetUShortValue(void) {return (ushort)Tval;}
- virtual int GetIntValue(void) {return (int)Tval;}
- virtual uint GetUIntValue(void) {return (uint)Tval;}
- virtual longlong GetBigintValue(void) {return (longlong)Tval;}
- virtual ulonglong GetUBigintValue(void) {return (ulonglong)Tval;}
- virtual double GetFloatValue(void) {return (double)Tval;}
- virtual void *GetTo_Val(void) {return &Tval;}
-
- // Methods
- virtual bool SetValue_pval(PVAL valp, bool chktype);
- virtual bool SetValue_char(char *p, int n);
- virtual void SetValue_psz(PSZ s);
-#if defined(BLK_INDX)
- virtual void SetValue_bool(bool b) {Tval = (b) ? 1 : 0;}
- virtual int CompareValue(PVAL vp);
-#endif // BLK_INDX
- virtual void SetValue(char c) {Tval = (TYPE)c; Null = false;}
- virtual void SetValue(uchar c) {Tval = (TYPE)c; Null = false;}
- virtual void SetValue(short i) {Tval = (TYPE)i; Null = false;}
- virtual void SetValue(ushort i) {Tval = (TYPE)i; Null = false;}
- virtual void SetValue(int n) {Tval = (TYPE)n; Null = false;}
- virtual void SetValue(uint n) {Tval = (TYPE)n; Null = false;}
- virtual void SetValue(longlong n) {Tval = (TYPE)n; Null = false;}
- virtual void SetValue(ulonglong n) {Tval = (TYPE)n; Null = false;}
- virtual void SetValue(double f) {Tval = (TYPE)f; Null = false;}
- virtual void SetValue_pvblk(PVBLK blk, int n);
- virtual void SetBinValue(void *p);
- virtual bool GetBinValue(void *buf, int buflen, bool go);
- virtual char *ShowValue(char *buf, int);
- virtual char *GetCharString(char *p);
- virtual bool IsEqual(PVAL vp, bool chktype);
- virtual bool SetConstFormat(PGLOBAL, FORMAT&);
- virtual bool FormatValue(PVAL vp, char *fmt);
- virtual void Print(PGLOBAL g, FILE *, uint);
- virtual void Print(PGLOBAL g, char *, uint);
-
- protected:
- // Default constructor not to be used
- TYPVAL(void) : VALUE(TYPE_ERROR) {}
-
- // Specialized functions
- static ulonglong MaxVal(void);
- TYPE GetTypedValue(PVAL vp);
- TYPE GetTypedValue(PVBLK blk, int n);
-// TYPE GetTypedValue(PSZ s);
-
- // Members
- TYPE Tval;
- }; // end of class TYPVAL
-
-/***********************************************************************/
-/* Specific STRING class. */
-/***********************************************************************/
-template <>
-class DllExport TYPVAL<PSZ>: public VALUE {
- public:
- // Constructors
- TYPVAL(PSZ s);
- TYPVAL(PGLOBAL g, PSZ s, int n, int c);
-
- // Implementation
- virtual bool IsTypeNum(void) {return false;}
- virtual bool IsZero(void) {return *Strp == 0;}
- virtual void Reset(void) {*Strp = 0;}
- virtual int GetValLen(void) {return Len;};
- virtual int GetValPrec() {return (Ci) ? 1 : 0;}
- virtual int GetSize(void) {return (Strp) ? strlen(Strp) : 0;}
- virtual PSZ GetCharValue(void) {return Strp;}
- virtual char GetTinyValue(void);
- virtual uchar GetUTinyValue(void);
- virtual short GetShortValue(void);
- virtual ushort GetUShortValue(void);
- virtual int GetIntValue(void);
- virtual uint GetUIntValue(void);
- virtual longlong GetBigintValue(void);
- virtual ulonglong GetUBigintValue(void);
- virtual double GetFloatValue(void) {return atof(Strp);}
- virtual void *GetTo_Val(void) {return Strp;}
- virtual void SetPrec(int prec) {Ci = prec != 0;}
-
- // Methods
- virtual bool SetValue_pval(PVAL valp, bool chktype);
- virtual bool SetValue_char(char *p, int n);
- virtual void SetValue_psz(PSZ s);
- virtual void SetValue_pvblk(PVBLK blk, int n);
- virtual void SetValue(char c);
- virtual void SetValue(uchar c);
- virtual void SetValue(short i);
- virtual void SetValue(ushort i);
- virtual void SetValue(int n);
- virtual void SetValue(uint n);
- virtual void SetValue(longlong n);
- virtual void SetValue(ulonglong n);
- virtual void SetValue(double f);
- virtual void SetBinValue(void *p);
-#if defined(BLK_INDX)
- virtual int CompareValue(PVAL vp);
-#endif // BLK_INDX
- virtual bool GetBinValue(void *buf, int buflen, bool go);
- virtual char *ShowValue(char *buf, int);
- virtual char *GetCharString(char *p);
- virtual bool IsEqual(PVAL vp, bool chktype);
- virtual bool FormatValue(PVAL vp, char *fmt);
- virtual bool SetConstFormat(PGLOBAL, FORMAT&);
-
- // Members
- PSZ Strp;
- bool Ci; // true if case insensitive
- int Len;
- }; // end of class TYPVAL<PSZ>
-
-/***********************************************************************/
-/* Specific DECIMAL class. */
-/***********************************************************************/
-class DllExport DECVAL: public TYPVAL<PSZ> {
- public:
- // Constructors
- DECVAL(PSZ s);
- DECVAL(PGLOBAL g, PSZ s, int n, int prec, bool uns);
-
- // Implementation
- virtual bool IsTypeNum(void) {return true;}
- virtual bool IsZero(void);
- virtual void Reset(void);
- virtual int GetValPrec() {return Prec;}
-
- // Methods
-//virtual bool SetValue_pval(PVAL valp, bool chktype);
-//virtual bool SetValue_char(char *p, int n);
-//virtual void SetValue_psz(PSZ s);
-//virtual void SetValue_pvblk(PVBLK blk, int n);
-//virtual void SetBinValue(void *p);
- virtual bool GetBinValue(void *buf, int buflen, bool go);
- virtual char *ShowValue(char *buf, int);
-//virtual char *GetCharString(char *p);
- virtual bool IsEqual(PVAL vp, bool chktype);
-#if defined(BLK_INDX)
- virtual int CompareValue(PVAL vp);
-#endif // BLK_INDX
-//virtual bool FormatValue(PVAL vp, char *fmt);
-//virtual bool SetConstFormat(PGLOBAL, FORMAT&);
-
- // Members
- }; // end of class DECVAL
-
-/***********************************************************************/
-/* Class DTVAL: represents a time stamp value. */
-/***********************************************************************/
-class DllExport DTVAL : public TYPVAL<int> {
- public:
- // Constructors
- DTVAL(PGLOBAL g, int n, int p, PSZ fmt);
- DTVAL(PGLOBAL g, PSZ s, int n);
- DTVAL(PGLOBAL g, short i);
- DTVAL(PGLOBAL g, int n);
- DTVAL(PGLOBAL g, longlong n);
- DTVAL(PGLOBAL g, double f);
-
- // Implementation
- virtual bool SetValue_pval(PVAL valp, bool chktype);
- virtual bool SetValue_char(char *p, int n);
- virtual void SetValue_psz(PSZ s);
- virtual void SetValue_pvblk(PVBLK blk, int n);
- virtual char *GetCharString(char *p);
- virtual char *ShowValue(char *buf, int);
- virtual bool FormatValue(PVAL vp, char *fmt);
- bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
- bool SetFormat(PGLOBAL g, PVAL valp);
- bool IsFormatted(void) {return Pdtp != NULL;}
-// bool GetTmMember(OPVAL op, int& mval);
-// bool DateDiff(DTVAL *dtp, OPVAL op, int& tdif);
- bool MakeTime(struct tm *ptm);
- static void SetTimeShift(void);
- static int GetShift(void) {return Shift;}
-
- // Methods
- bool MakeDate(PGLOBAL g, int *val, int nval);
-// bool WeekNum(PGLOBAL g, int& nval);
-
- struct tm *GetGmTime(struct tm *);
-
- protected:
- // Default constructor not to be used
- DTVAL(void) : TYPVAL<int>() {}
-
- // Members
- static int Shift; // Time zone shift in seconds
- PDTP Pdtp; // To the DATPAR structure
- char *Sdate; // Utility char buffer
- int DefYear; // Used by ExtractDate
- int Len; // Used by CHAR scalar function
- }; // end of class DTVAL
-
-#endif // __VALUE__H__
+/**************** Value H Declares Source Code File (.H) ***************/
+/* Name: VALUE.H Version 2.0 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */
+/* */
+/* This file contains the VALUE and derived classes declares. */
+/***********************************************************************/
+#ifndef __VALUE__H__
+#define __VALUE__H__
+
+/***********************************************************************/
+/* Include required application header files */
+/* assert.h is header required when using the assert function. */
+/* block.h is header containing Block global declarations. */
+/***********************************************************************/
+#include "assert.h"
+#include "block.h"
+
+/***********************************************************************/
+/* Types used in some class definitions. */
+/***********************************************************************/
+enum CONV {CNV_ANY = 0, /* Convert to any type */
+ CNV_CHAR = 1, /* Convert to character type */
+ CNV_NUM = 2}; /* Convert to numeric type */
+
+/***********************************************************************/
+/* Types used in some class definitions. */
+/***********************************************************************/
+class CONSTANT; // For friend setting
+typedef struct _datpar *PDTP; // For DTVAL
+
+
+/***********************************************************************/
+/* Utilities used to test types and to allocated values. */
+/***********************************************************************/
+PVAL AllocateValue(PGLOBAL, void *, short);
+
+// Exported functions
+DllExport PSZ GetTypeName(int);
+DllExport int GetTypeSize(int, int);
+#ifdef ODBC_SUPPORT
+/* This function is exported for use in EOM table type DLLs */
+DllExport int TranslateSQLType(int stp, int prec, int& len, char& v);
+#endif
+DllExport char *GetFormatType(int);
+DllExport int GetFormatType(char);
+DllExport bool IsTypeChar(int type);
+DllExport bool IsTypeNum(int type);
+DllExport int ConvertType(int, int, CONV, bool match = false);
+DllExport PVAL AllocateValue(PGLOBAL, PVAL, int = TYPE_VOID, int = 0);
+DllExport PVAL AllocateValue(PGLOBAL, int, int len = 0, int prec = 0,
+ bool uns = false, PSZ fmt = NULL);
+DllExport ulonglong CharToNumber(char *, int, ulonglong, bool,
+ bool *minus = NULL, bool *rc = NULL);
+
+/***********************************************************************/
+/* Class VALUE represents a constant or variable of any valid type. */
+/***********************************************************************/
+class DllExport VALUE : public BLOCK {
+ friend class CONSTANT; // The only object allowed to use SetConstFormat
+ public:
+ // Constructors
+
+ // Implementation
+ virtual bool IsTypeNum(void) = 0;
+ virtual bool IsZero(void) = 0;
+ virtual bool IsCi(void) {return false;}
+ virtual bool IsUnsigned(void) {return Unsigned;}
+ virtual void Reset(void) = 0;
+ virtual int GetSize(void) = 0;
+ virtual int GetValLen(void) = 0;
+ virtual int GetValPrec(void) = 0;
+ virtual int GetLength(void) {return 1;}
+ virtual PSZ GetCharValue(void) {assert(false); return NULL;}
+ virtual char GetTinyValue(void) {assert(false); return 0;}
+ virtual uchar GetUTinyValue(void) {assert(false); return 0;}
+ virtual short GetShortValue(void) {assert(false); return 0;}
+ virtual ushort GetUShortValue(void) {assert(false); return 0;}
+ virtual int GetIntValue(void) = 0;
+ virtual uint GetUIntValue(void) = 0;
+ virtual longlong GetBigintValue(void) = 0;
+ virtual ulonglong GetUBigintValue(void) = 0;
+ virtual double GetFloatValue(void) = 0;
+ virtual void *GetTo_Val(void) = 0;
+ virtual void SetPrec(int prec) {Prec = prec;}
+ bool IsNull(void) {return Null;}
+ void SetNull(bool b) {Null = b;}
+ bool GetNullable(void) {return Nullable;}
+ void SetNullable(bool b) {Nullable = b;}
+ int GetType(void) {return Type;}
+ int GetClen(void) {return Clen;}
+ void SetGlobal(PGLOBAL g) {Global = g;}
+
+ // Methods
+ virtual bool SetValue_pval(PVAL valp, bool chktype = false) = 0;
+ virtual bool SetValue_char(char *p, int n) = 0;
+ virtual void SetValue_psz(PSZ s) = 0;
+ virtual void SetValue_bool(bool b) {assert(FALSE);}
+ virtual int CompareValue(PVAL vp) = 0;
+ virtual BYTE TestValue(PVAL vp);
+ virtual void SetValue(char c) {assert(false);}
+ virtual void SetValue(uchar c) {assert(false);}
+ virtual void SetValue(short i) {assert(false);}
+ virtual void SetValue(ushort i) {assert(false);}
+ virtual void SetValue(int n) {assert(false);}
+ virtual void SetValue(uint n) {assert(false);}
+ virtual void SetValue(longlong n) {assert(false);}
+ virtual void SetValue(ulonglong n) {assert(false);}
+ virtual void SetValue(double f) {assert(false);}
+ virtual void SetValue_pvblk(PVBLK blk, int n) = 0;
+ virtual void SetBinValue(void *p) = 0;
+ virtual bool GetBinValue(void *buf, int buflen, bool go) = 0;
+ virtual char *ShowValue(char *buf, int len = 0) = 0;
+ virtual char *GetCharString(char *p) = 0;
+ virtual bool IsEqual(PVAL vp, bool chktype) = 0;
+ virtual bool FormatValue(PVAL vp, char *fmt) = 0;
+
+ protected:
+ virtual bool SetConstFormat(PGLOBAL, FORMAT&) = 0;
+ const char *GetXfmt(void);
+
+ // Constructor used by derived classes
+ VALUE(int type, bool un = false);
+
+ // Members
+ PGLOBAL Global; // To reduce arglist
+ const char *Fmt;
+ const char *Xfmt;
+ bool Nullable; // True if value can be null
+ bool Null; // True if value is null
+ bool Unsigned; // True if unsigned
+ int Type; // The value type
+ int Clen; // Internal value length
+ int Prec;
+ }; // end of class VALUE
+
+/***********************************************************************/
+/* Class TYPVAL: represents a typed value. */
+/***********************************************************************/
+template <class TYPE>
+class DllExport TYPVAL : public VALUE {
+ public:
+ // Constructor
+ TYPVAL(TYPE n, int type, int prec = 0, bool un = false);
+
+ // Implementation
+ virtual bool IsTypeNum(void) {return true;}
+ virtual bool IsZero(void) {return Tval == 0;}
+ virtual void Reset(void) {Tval = 0;}
+ virtual int GetValLen(void);
+ virtual int GetValPrec() {return 0;}
+ virtual int GetSize(void) {return sizeof(TYPE);}
+ virtual PSZ GetCharValue(void) {return VALUE::GetCharValue();}
+ virtual char GetTinyValue(void) {return (char)Tval;}
+ virtual uchar GetUTinyValue(void) {return (uchar)Tval;}
+ virtual short GetShortValue(void) {return (short)Tval;}
+ virtual ushort GetUShortValue(void) {return (ushort)Tval;}
+ virtual int GetIntValue(void) {return (int)Tval;}
+ virtual uint GetUIntValue(void) {return (uint)Tval;}
+ virtual longlong GetBigintValue(void) {return (longlong)Tval;}
+ virtual ulonglong GetUBigintValue(void) {return (ulonglong)Tval;}
+ virtual double GetFloatValue(void) {return (double)Tval;}
+ virtual void *GetTo_Val(void) {return &Tval;}
+
+ // Methods
+ virtual bool SetValue_pval(PVAL valp, bool chktype);
+ virtual bool SetValue_char(char *p, int n);
+ virtual void SetValue_psz(PSZ s);
+ virtual void SetValue_bool(bool b) {Tval = (b) ? 1 : 0;}
+ virtual int CompareValue(PVAL vp);
+ virtual void SetValue(char c) {Tval = (TYPE)c; Null = false;}
+ virtual void SetValue(uchar c) {Tval = (TYPE)c; Null = false;}
+ virtual void SetValue(short i) {Tval = (TYPE)i; Null = false;}
+ virtual void SetValue(ushort i) {Tval = (TYPE)i; Null = false;}
+ virtual void SetValue(int n) {Tval = (TYPE)n; Null = false;}
+ virtual void SetValue(uint n) {Tval = (TYPE)n; Null = false;}
+ virtual void SetValue(longlong n) {Tval = (TYPE)n; Null = false;}
+ virtual void SetValue(ulonglong n) {Tval = (TYPE)n; Null = false;}
+ virtual void SetValue(double f) {Tval = (TYPE)f; Null = false;}
+ virtual void SetValue_pvblk(PVBLK blk, int n);
+ virtual void SetBinValue(void *p);
+ virtual bool GetBinValue(void *buf, int buflen, bool go);
+ virtual char *ShowValue(char *buf, int);
+ virtual char *GetCharString(char *p);
+ virtual bool IsEqual(PVAL vp, bool chktype);
+ virtual bool SetConstFormat(PGLOBAL, FORMAT&);
+ virtual bool FormatValue(PVAL vp, char *fmt);
+ virtual void Print(PGLOBAL g, FILE *, uint);
+ virtual void Print(PGLOBAL g, char *, uint);
+
+ protected:
+ // Default constructor not to be used
+ TYPVAL(void) : VALUE(TYPE_ERROR) {}
+
+ // Specialized functions
+ static ulonglong MaxVal(void);
+ TYPE GetTypedValue(PVAL vp);
+ TYPE GetTypedValue(PVBLK blk, int n);
+// TYPE GetTypedValue(PSZ s);
+
+ // Members
+ TYPE Tval;
+ }; // end of class TYPVAL
+
+/***********************************************************************/
+/* Specific STRING class. */
+/***********************************************************************/
+template <>
+class DllExport TYPVAL<PSZ>: public VALUE {
+ public:
+ // Constructors
+ TYPVAL(PSZ s);
+ TYPVAL(PGLOBAL g, PSZ s, int n, int c);
+
+ // Implementation
+ virtual bool IsTypeNum(void) {return false;}
+ virtual bool IsZero(void) {return *Strp == 0;}
+ virtual void Reset(void) {*Strp = 0;}
+ virtual int GetValLen(void) {return Len;};
+ virtual int GetValPrec() {return (Ci) ? 1 : 0;}
+ virtual int GetSize(void) {return (Strp) ? strlen(Strp) : 0;}
+ virtual PSZ GetCharValue(void) {return Strp;}
+ virtual char GetTinyValue(void);
+ virtual uchar GetUTinyValue(void);
+ virtual short GetShortValue(void);
+ virtual ushort GetUShortValue(void);
+ virtual int GetIntValue(void);
+ virtual uint GetUIntValue(void);
+ virtual longlong GetBigintValue(void);
+ virtual ulonglong GetUBigintValue(void);
+ virtual double GetFloatValue(void) {return atof(Strp);}
+ virtual void *GetTo_Val(void) {return Strp;}
+ virtual void SetPrec(int prec) {Ci = prec != 0;}
+
+ // Methods
+ virtual bool SetValue_pval(PVAL valp, bool chktype);
+ virtual bool SetValue_char(char *p, int n);
+ virtual void SetValue_psz(PSZ s);
+ virtual void SetValue_pvblk(PVBLK blk, int n);
+ virtual void SetValue(char c);
+ virtual void SetValue(uchar c);
+ virtual void SetValue(short i);
+ virtual void SetValue(ushort i);
+ virtual void SetValue(int n);
+ virtual void SetValue(uint n);
+ virtual void SetValue(longlong n);
+ virtual void SetValue(ulonglong n);
+ virtual void SetValue(double f);
+ virtual void SetBinValue(void *p);
+ virtual int CompareValue(PVAL vp);
+ virtual bool GetBinValue(void *buf, int buflen, bool go);
+ virtual char *ShowValue(char *buf, int);
+ virtual char *GetCharString(char *p);
+ virtual bool IsEqual(PVAL vp, bool chktype);
+ virtual bool FormatValue(PVAL vp, char *fmt);
+ virtual bool SetConstFormat(PGLOBAL, FORMAT&);
+
+ // Members
+ PSZ Strp;
+ bool Ci; // true if case insensitive
+ int Len;
+ }; // end of class TYPVAL<PSZ>
+
+/***********************************************************************/
+/* Specific DECIMAL class. */
+/***********************************************************************/
+class DllExport DECVAL: public TYPVAL<PSZ> {
+ public:
+ // Constructors
+ DECVAL(PSZ s);
+ DECVAL(PGLOBAL g, PSZ s, int n, int prec, bool uns);
+
+ // Implementation
+ virtual bool IsTypeNum(void) {return true;}
+ virtual bool IsZero(void);
+ virtual void Reset(void);
+ virtual int GetValPrec() {return Prec;}
+
+ // Methods
+ virtual bool GetBinValue(void *buf, int buflen, bool go);
+ virtual char *ShowValue(char *buf, int);
+ virtual bool IsEqual(PVAL vp, bool chktype);
+ virtual int CompareValue(PVAL vp);
+
+ // Members
+ }; // end of class DECVAL
+
+/***********************************************************************/
+/* Class DTVAL: represents a time stamp value. */
+/***********************************************************************/
+class DllExport DTVAL : public TYPVAL<int> {
+ public:
+ // Constructors
+ DTVAL(PGLOBAL g, int n, int p, PSZ fmt);
+ DTVAL(PGLOBAL g, PSZ s, int n);
+ DTVAL(PGLOBAL g, short i);
+ DTVAL(PGLOBAL g, int n);
+ DTVAL(PGLOBAL g, longlong n);
+ DTVAL(PGLOBAL g, double f);
+
+ // Implementation
+ virtual bool SetValue_pval(PVAL valp, bool chktype);
+ virtual bool SetValue_char(char *p, int n);
+ virtual void SetValue_psz(PSZ s);
+ virtual void SetValue_pvblk(PVBLK blk, int n);
+ virtual char *GetCharString(char *p);
+ virtual char *ShowValue(char *buf, int);
+ virtual bool FormatValue(PVAL vp, char *fmt);
+ bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0);
+ bool SetFormat(PGLOBAL g, PVAL valp);
+ bool IsFormatted(void) {return Pdtp != NULL;}
+ bool MakeTime(struct tm *ptm);
+ static void SetTimeShift(void);
+ static int GetShift(void) {return Shift;}
+
+ // Methods
+ bool MakeDate(PGLOBAL g, int *val, int nval);
+
+ struct tm *GetGmTime(struct tm *);
+
+ protected:
+ // Default constructor not to be used
+ DTVAL(void) : TYPVAL<int>() {}
+
+ // Members
+ static int Shift; // Time zone shift in seconds
+ PDTP Pdtp; // To the DATPAR structure
+ char *Sdate; // Utility char buffer
+ int DefYear; // Used by ExtractDate
+ int Len; // Used by CHAR scalar function
+ }; // end of class DTVAL
+
+#endif // __VALUE__H__
diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp
index 601374ef5d7..3037b0a829a 100755
--- a/storage/connect/xindex.cpp
+++ b/storage/connect/xindex.cpp
@@ -1,3028 +1,3028 @@
-/***************** Xindex C++ Class Xindex Code (.CPP) *****************/
-/* Name: XINDEX.CPP Version 2.8 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
-/* */
-/* This file contains the class XINDEX implementation code. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#else // !UNIX
-#include <io.h>
-#endif // !UNIX
-#include <fcntl.h>
-#endif // !WIN32
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/* kindex.h is header containing the KINDEX class definition. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "osutil.h"
-#include "maputil.h"
-//nclude "filter.h"
-#include "tabcol.h"
-#include "xindex.h"
-#include "xobject.h"
-//nclude "scalfnc.h"
-//nclude "array.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-
-/***********************************************************************/
-/* Macro or external routine definition */
-/***********************************************************************/
-#define NZ 7
-#define NW 5
-#define MAX_INDX 10
-#ifndef INVALID_SET_FILE_POINTER
-#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
-#endif
-
-/***********************************************************************/
-/* DB static external variables. */
-/***********************************************************************/
-extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
-
-/***********************************************************************/
-/* Last two parameters are true to enable type checking, and last one */
-/* to have rows filled by blanks to be compatible with QRY blocks. */
-/***********************************************************************/
-PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
- bool check = true, bool blank = true, bool un = false);
-
-/***********************************************************************/
-/* Check whether we have to create/update permanent indexes. */
-/***********************************************************************/
-int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
- {
- int rc;
- PTABLE tablep;
- PTDBDOS tdbp;
- PCATLG cat = PlgGetCatalog(g, true);
-
- /*********************************************************************/
- /* Open a new table in mode read and with only the keys columns. */
- /*********************************************************************/
- tablep = new(g) XTAB(name);
-
- if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep)))
- rc = RC_NF;
- else if (!tdbp->GetDef()->Indexable()) {
- sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
- rc = RC_NF;
- } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
- rc = RC_OK; // No index
-
- return rc;
- } // end of PlgMakeIndex
-
-/* -------------------------- Class INDEXDEF ------------------------- */
-
-/***********************************************************************/
-/* INDEXDEF Constructor. */
-/***********************************************************************/
-INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
- {
-//To_Def = NULL;
- Next = NULL;
- ToKeyParts = NULL;
- Name = name;
- Unique = uniq;
- Invalid = false;
- AutoInc = false;
- Nparts = 0;
- ID = n;
-//Offset = 0;
-//Offhigh = 0;
-//Size = 0;
- MaxSame = 1;
- } // end of INDEXDEF constructor
-
-/***********************************************************************/
-/* Set the max same values for each colum after making the index. */
-/***********************************************************************/
-void INDEXDEF::SetMxsame(PXINDEX x)
- {
- PKPDEF kdp;
- PXCOL xcp;
-
- for (kdp = ToKeyParts, xcp = x->To_KeyCol;
- kdp && xcp; kdp = kdp->Next, xcp = xcp->Next)
- kdp->Mxsame = xcp->Mxs;
- } // end of SetMxsame
-
-/* -------------------------- Class KPARTDEF ------------------------- */
-
-/***********************************************************************/
-/* KPARTDEF Constructor. */
-/***********************************************************************/
-KPARTDEF::KPARTDEF(PSZ name, int n)
- {
- Next = NULL;
- Name = name;
- Mxsame = 0;
- Ncol = n;
- Klen = 0;
- } // end of KPARTDEF constructor
-
-/* -------------------------- XXBASE Class --------------------------- */
-
-/***********************************************************************/
-/* XXBASE public constructor. */
-/***********************************************************************/
-XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
- To_Rec((int*&)Record.Memp)
- {
- Tbxp = tbxp;
- Record = Nmblk;
- Cur_K = -1;
- Old_K = -1;
- Num_K = 0;
- Ndif = 0;
- Bot = Top = Inf = Sup = 0;
- Op = OP_EQ;
- To_KeyCol = NULL;
- Mul = false;
- Val_K = -1;
- Nblk = Sblk = 0;
- Thresh = 7;
- ID = -1;
- Nth = 0;
- } // end of XXBASE constructor
-
-/***********************************************************************/
-/* Make file output of XINDEX contents. */
-/***********************************************************************/
-void XXBASE::Print(PGLOBAL g, FILE *f, uint n)
- {
- char m[64];
-
- memset(m, ' ', n); // Make margin string
- m[n] = '\0';
- fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K);
- } // end of Print
-
-/***********************************************************************/
-/* Make string output of XINDEX contents. */
-/***********************************************************************/
-void XXBASE::Print(PGLOBAL g, char *ps, uint z)
- {
- *ps = '\0';
- strncat(ps, "Xindex", z);
- } // end of Print
-
-/* -------------------------- XINDEX Class --------------------------- */
-
-/***********************************************************************/
-/* XINDEX public constructor. */
-/***********************************************************************/
-XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k)
- : XXBASE(tdbp, !xdp->IsUnique())
- {
- Xdp = xdp;
- ID = xdp->GetID();
- Tdbp = tdbp;
- X = pxp;
- To_LastCol = NULL;
- To_LastVal = NULL;
- To_Cols = cp;
- To_Vals = xp;
- Mul = !xdp->IsUnique();
- Srtd = false;
- Nk = xdp->GetNparts();
- Nval = (k) ? k : Nk;
- Incr = 0;
-//Defoff = xdp->GetOffset();
-//Defhigh = xdp->GetOffhigh();
-//Size = xdp->GetSize();
- MaxSame = xdp->GetMaxSame();
- } // end of XINDEX constructor
-
-/***********************************************************************/
-/* XINDEX Reset: re-initialize a Xindex block. */
-/***********************************************************************/
-void XINDEX::Reset(void)
- {
- for (PXCOL kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = kp->Ndf;
-
- Cur_K = Num_K;
- Old_K = -1; // Needed to avoid not setting CurBlk for Update
- Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST :
- (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ;
- Nth = 0;
- } // end of Reset
-
-/***********************************************************************/
-/* XINDEX Close: terminate index and free all allocated data. */
-/* Do not reset other values that are used at return to make. */
-/***********************************************************************/
-void XINDEX::Close(void)
- {
- // Close file or view of file
- X->Close();
-
- // De-allocate data
- PlgDBfree(Record);
- PlgDBfree(Index);
- PlgDBfree(Offset);
-
- // De-allocate Key data
- for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->FreeData();
-
- // Column values cannot be retrieved from key anymore
- for (int k = 0; k < Nk; k++)
- To_Cols[k]->SetKcol(NULL);
-
- } // end of Close
-
-/***********************************************************************/
-/* XINDEX compare routine for C Quick/Insertion sort. */
-/***********************************************************************/
-int XINDEX::Qcompare(int *i1, int *i2)
- {
- register int k;
- register PXCOL kcp;
-
- for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next)
- if ((k = kcp->Compare(*i1, *i2)))
- break;
-
-#ifdef DEBTRACE
- num_comp++;
-#endif
-
- return k;
- } // end of Qcompare
-
-/***********************************************************************/
-/* Make: Make and index on key column(s). */
-/***********************************************************************/
-bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
- {
- /*********************************************************************/
- /* Table can be accessed through an index. */
- /*********************************************************************/
- int k, rc = RC_OK;
- int *bof, i, j, n, ndf, nkey;
- PKPDEF kdfp = Xdp->GetToKeyParts();
- bool brc = true;
- PCOL colp;
- PXCOL kp, prev = NULL, kcp = NULL;
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- /*********************************************************************/
- /* Allocate the storage that will contain the keys and the file */
- /* positions corresponding to them. */
- /*********************************************************************/
- if ((n = Tdbp->GetMaxSize(g)) < 0)
- return true;
- else if (!n) {
- Num_K = Ndif = 0;
- MaxSame = 1;
-
- // The if condition was suppressed because this may be an existing
- // index that is now void because all table lines were deleted.
-// if (sxp)
- goto nox; // Truncate eventually existing index file
-// else
-// return false;
-
- } // endif n
-
- // File position must be stored
- Record.Size = n * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Record)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n);
- goto err; // Error
- } // endif
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- colp = To_Cols[k];
-
- if (!kdfp) {
- sprintf(g->Message, MSG(INT_COL_ERROR),
- (colp) ? colp->GetName() : "???");
- goto err; // Error
- } // endif kdfp
-
- kcp = new(g) KXYCOL(this);
-
- if (kcp->Init(g, colp, n, true, kdfp->Klen))
- goto err; // Error
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- kdfp = kdfp->Next;
- } // endfor k
-
- To_LastCol = prev;
-
- /*********************************************************************/
- /* Get the starting information for progress. */
- /*********************************************************************/
- dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
- sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
- dup->ProgMax = Tdbp->GetProgMax(g);
- dup->ProgCur = 0;
-
- /*********************************************************************/
- /* Standard init: read the file and construct the index table. */
- /* Note: reading will be sequential as To_Kindex is not set. */
- /*********************************************************************/
- for (i = nkey = 0; i < n && rc != RC_EF; i++) {
-#if defined(THREAD)
- if (!dup->Step) {
- strcpy(g->Message, MSG(QUERY_CANCELLED));
- longjmp(g->jumper[g->jump_level], 99);
- } // endif Step
-#endif // THREAD
-
- /*******************************************************************/
- /* Read a valid record from table file. */
- /*******************************************************************/
- rc = Tdbp->ReadDB(g);
-
- // Update progress information
- dup->ProgCur = Tdbp->GetProgCur();
-
- // Check return code and do whatever must be done according to it
- switch (rc) {
- case RC_OK:
- break;
- case RC_EF:
- goto end_of_file;
- case RC_NF:
- continue;
- default:
- sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
- goto err;
- } // endswitch rc
-
- /*******************************************************************/
- /* Get and Store the file position of the last read record for */
- /* future direct access. */
- /*******************************************************************/
- To_Rec[nkey] = Tdbp->GetRecpos();
-
- /*******************************************************************/
- /* Get the keys and place them in the key blocks. */
- /*******************************************************************/
- for (k = 0, kcp = To_KeyCol;
- k < Nk && kcp;
- k++, kcp = kcp->Next) {
- colp = To_Cols[k];
- colp->Reset();
-
- colp->ReadColumn(g);
-// if (colp->ReadColumn(g))
-// goto err;
-
- kcp->SetValue(colp, nkey);
- } // endfor k
-
- nkey++; // A new valid key was found
- } // endfor i
-
- end_of_file:
-
- // Update progress information
- dup->ProgCur = Tdbp->GetProgMax(g);
-
- /*********************************************************************/
- /* Record the Index size and eventually resize memory allocation. */
- /*********************************************************************/
- if ((Num_K = nkey) < n) {
- PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int));
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->ReAlloc(g, Num_K);
-
- } // endif Num_K
-
- /*********************************************************************/
- /* Sort the index so we can use an optimized Find algorithm. */
- /* Note: for a unique index we use the non conservative sort */
- /* version because normally all index values are different. */
- /* This was set at CSORT class construction. */
- /* For all indexes, an offset array is made so we can check the */
- /* uniqueness of unique indexes. */
- /*********************************************************************/
- Index.Size = Num_K * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Index)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
- goto err; // Error
- } // endif alloc
-
- Offset.Size = (Num_K + 1) * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Offset)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1);
- goto err; // Error
- } // endif alloc
-
- // Call the sort program, it returns the number of distinct values
- if ((Ndif = Qsort(g, Num_K)) < 0)
- goto err; // Error during sort
-
- // Check whether the unique index is unique indeed
- if (!Mul)
- if (Ndif < Num_K) {
- strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
- goto err;
- } else
- PlgDBfree(Offset); // Not used anymore
-
- // Use the index to physically reorder the xindex
- Srtd = Reorder(g);
-
- if (Ndif < Num_K) {
- // Resize the offset array
- PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int));
-
- // Initial value of MaxSame
- MaxSame = Pof[1] - Pof[0];
-
- // Resize the Key array by only keeping the distinct values
- for (i = 1; i < Ndif; i++) {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Move(i, Pof[i]);
-
- MaxSame = max(MaxSame, Pof[i + 1] - Pof[i]);
- } // endfor i
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->ReAlloc(g, Ndif);
-
- } else {
- Mul = false; // Current index is unique
- PlgDBfree(Offset); // Not used anymore
- MaxSame = 1; // Reset it when remaking an index
- } // endif Ndif
-
- /*********************************************************************/
- /* Now do the reduction of the index. Indeed a multi-column index */
- /* can be used for only some of the first columns. For instance if */
- /* an index is defined for column A, B, C PlugDB can use it for */
- /* only the column A or the columns A, B. */
- /* What we do here is to reduce the data so column A will contain */
- /* only the sorted distinct values of A, B will contain data such */
- /* as only distinct values of A,B are stored etc. */
- /* This implies that for each column set an offset array is made */
- /* except if the subset originally contains unique values. */
- /*********************************************************************/
- // Update progress information
- dup->Step = STEP(REDUCE_INDEX);
-
- ndf = Ndif;
- To_LastCol->Mxs = MaxSame;
-
- for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) {
- if (!(bof = kcp->MakeOffset(g, ndf)))
- goto err;
- else
- *bof = 0;
-
- for (n = 0, i = j = 1; i < ndf; i++)
- for (kp = kcp; kp; kp = kp->Previous)
- if (kp->Compare(n, i)) {
- // Values are not equal to last ones
- bof[j++] = n = i;
- break;
- } // endif Compare
-
- if (j < ndf) {
- // Sub-index is multiple
- bof[j] = ndf;
- ndf = j; // New number of distinct values
-
- // Resize the Key array by only keeping the distinct values
- for (kp = kcp; kp; kp = kp->Previous) {
- for (i = 1; i < ndf; i++)
- kp->Move(i, bof[i]);
-
- kp->ReAlloc(g, ndf);
- } // endif kcp
-
- // Resize the offset array
- kcp->MakeOffset(g, ndf);
-
- // Calculate the max same value for this column
- kcp->Mxs = ColMaxSame(kcp);
- } else {
- // Current sub-index is unique
- kcp->MakeOffset(g, 0); // The offset is not used anymore
- kcp->Mxs = 1; // Unique
- } // endif j
-
- } // endfor kcp
-
- /*********************************************************************/
- /* For sorted columns and fixed record size, file position can be */
- /* calculated, so the Record array can be discarted. */
- /* Note: for Num_K = 1 any non null value is Ok. */
- /*********************************************************************/
- if (Srtd && Tdbp->Ftype != RECFM_VAR) {
- Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
- PlgDBfree(Record);
- } // endif Srtd
-
- /*********************************************************************/
- /* Check whether a two-tier find algorithm can be implemented. */
- /* It is currently implemented only for single key indexes. */
- /*********************************************************************/
- if (Nk == 1 && ndf >= 65536) {
- // Implement a two-tier find algorithm
- for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ;
-
- Nblk = (ndf -1) / Sblk + 1;
-
- if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk))
- goto err; // Error
-
- } // endif Num_K
-
- nox:
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
-
- /*********************************************************************/
- /* Save the index so it has not to be recalculated. */
- /*********************************************************************/
- if (!SaveIndex(g, sxp))
- brc = false;
-
- err:
- // We don't need the index anymore
- Close();
-
- if (brc)
- printf("%s\n", g->Message);
-
- return brc;
- } // end of Make
-
-/***********************************************************************/
-/* Return the max size of the intermediate column. */
-/***********************************************************************/
-int XINDEX::ColMaxSame(PXCOL kp)
- {
- int *kof, i, ck1, ck2, ckn = 1;
- PXCOL kcp;
-
- // Calculate the max same value for this column
- for (i = 0; i < kp->Ndf; i++) {
- ck1 = i;
- ck2 = i + 1;
-
- for (kcp = kp; kcp; kcp = kcp->Next) {
- if (!(kof = (kcp->Next) ? kcp->Kof : Pof))
- break;
-
- ck1 = kof[ck1];
- ck2 = kof[ck2];
- } // endfor kcp
-
- ckn = max(ckn, ck2 - ck1);
- } // endfor i
-
- return ckn;
- } // end of ColMaxSame
-
-/***********************************************************************/
-/* Reorder: use the sort index to reorder the data in storage so */
-/* it will be physically sorted and sort index can be removed. */
-/***********************************************************************/
-bool XINDEX::Reorder(PGLOBAL g)
- {
- register int i, j, k, n;
- bool sorted = true;
- PXCOL kcp;
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- if (Num_K > 500000) {
- // Update progress information
- dup->Step = STEP(REORDER_INDEX);
- dup->ProgMax = Num_K;
- dup->ProgCur = 0;
- } else
- dup = NULL;
-
- if (!Pex)
- return Srtd;
-
- for (i = 0; i < Num_K; i++) {
- if (Pex[i] == Num_K) { // Already moved
- continue;
- } else if (Pex[i] == i) { // Already placed
- if (dup)
- dup->ProgCur++;
-
- continue;
- } // endif's Pex
-
- sorted = false;
-
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Save(i);
-
- n = To_Rec[i];
-
- for (j = i;; j = k) {
- k = Pex[j];
- Pex[j] = Num_K; // Mark position as set
-
- if (k == i) {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Restore(j);
-
- To_Rec[j] = n;
- break; // end of loop
- } else {
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Move(j, k); // Move k to j
-
- To_Rec[j] = To_Rec[k];
- } // endif k
-
- if (dup)
- dup->ProgCur++;
-
- } // endfor j
-
- } // endfor i
-
- // The index is not used anymore
- PlgDBfree(Index);
- return sorted;
- } // end of Reorder
-
-/***********************************************************************/
-/* Save the index 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 XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
- {
- char *ftype;
- char fn[_MAX_PATH];
- int n[NZ], nof = (Mul) ? (Ndif + 1) : 0;
- int id = -1, size = 0;
- bool sep, rc = false;
- PXCOL kcp = To_KeyCol;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- PDBUSER dup = PlgGetUser(g);
-
- dup->Step = STEP(SAVING_INDEX);
- dup->ProgMax = 15 + 16 * Nk;
- dup->ProgCur = 0;
-
- switch (Tdbp->Ftype) {
- 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(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) {
- // Index is saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- sxp = NULL;
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
- if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
- printf("%s\n", g->Message);
- return true;
- } // endif Open
-
- if (!Ndif)
- goto end; // Void index
-
- /*********************************************************************/
- /* Write the index values on the index file. */
- /*********************************************************************/
- n[0] = ID; // To check validity
- n[1] = Nk; // The number of indexed columns
- n[2] = nof; // The offset array size or 0
- n[3] = Num_K; // The index size
- n[4] = Incr; // Increment of record positions
- n[5] = Nblk; n[6] = Sblk;
-
-#if defined(TRACE)
- printf("Saving index %s\n", Xdp->GetName());
- printf("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d\n",
- ID, Nk, nof, Num_K, Incr, Nblk, Sblk);
-#endif // TRACE
-
- size = X->Write(g, n, NZ, sizeof(int), rc);
- dup->ProgCur = 1;
-
- if (Mul) // Write the offset array
- size += X->Write(g, Pof, nof, sizeof(int), rc);
-
- dup->ProgCur = 5;
-
- if (!Incr) // Write the record position array(s)
- size += X->Write(g, To_Rec, Num_K, sizeof(int), rc);
-
- dup->ProgCur = 15;
-
- for (; kcp; kcp = kcp->Next) {
- n[0] = kcp->Ndf; // Number of distinct sub-values
- n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique
- n[2] = (kcp == To_KeyCol) ? Nblk : 0;
- n[3] = kcp->Klen; // To be checked later
- n[4] = kcp->Type; // To be checked later
-
- size += X->Write(g, n, NW, sizeof(int), rc);
- dup->ProgCur += 1;
-
- if (n[2])
- size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
-
- dup->ProgCur += 5;
-
- size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
- dup->ProgCur += 5;
-
- if (n[1])
- size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
-
- dup->ProgCur += 5;
- } // endfor kcp
-
-#if defined(TRACE)
- printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size);
-#endif // TRACE
-
- end:
- X->Close(fn, id);
- return rc;
- } // end of SaveIndex
-
-#if !defined(XMAP)
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XINDEX::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* If sorting is required, this will be done later. */
- /*********************************************************************/
- char *ftype;
- char fn[_MAX_PATH];
- int k, n, nv[NZ], id = -1;
- bool estim = false;
- PCOL colp;
- PXCOL prev = NULL, kcp = NULL;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Get the first key column. */
- /*********************************************************************/
- if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } else
- colp = To_Cols[0];
-
- switch (Tdbp->Ftype) {
- 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(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Open the index file and check its validity. */
- /*********************************************************************/
- if (X->Open(g, fn, id, MODE_READ))
- goto err; // No saved values
-
- // Now start the reading process.
- if (X->Read(g, nv, NZ, sizeof(int)))
- goto err;
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d %d %d %d\n",
- nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif
-
- if (nv[2]) {
- Mul = true;
- Ndif = nv[2];
-
- // Allocate the storage that will contain the offset array
- Offset.Size = Ndif * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Offset)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif);
- goto err;
- } // endif
-
- if (X->Read(g, Pof, Ndif, sizeof(int)))
- goto err;
-
- Ndif--; // nv[2] is offset size, equal to Ndif + 1
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
- Incr = nv[4];
- Nblk = nv[5];
- Sblk = nv[6];
-
- if (!Incr) {
- /*******************************************************************/
- /* Allocate the storage that will contain the file positions. */
- /*******************************************************************/
- Record.Size = Num_K * sizeof(int);
-
- if (!PlgDBalloc(g, NULL, Record)) {
- sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
- goto err;
- } // endif
-
- if (X->Read(g, To_Rec, Num_K, sizeof(int)))
- goto err;
-
- } else
- Srtd = true; // Sorted positions can be calculated
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- if (k == Nval)
- To_LastVal = prev;
-
- if (X->Read(g, nv, NW, sizeof(int)))
- goto err;
-
- colp = To_Cols[k];
-
- if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- kcp = new(g) KXYCOL(this);
-
- if (kcp->Init(g, colp, nv[0], true, (int)nv[3]))
- goto err; // Error
-
- /*******************************************************************/
- /* Read the index values from the index file. */
- /*******************************************************************/
- if (k == 0 && Nblk) {
- if (kcp->MakeBlockArray(g, Nblk, 0))
- goto err;
-
- // Read block values
- if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen))
- goto err;
-
- } // endif Nblk
-
- // Read the entire (small) index
- if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen))
- goto err;
-
- if (nv[1]) {
- if (!kcp->MakeOffset(g, nv[1] - 1))
- goto err;
-
- // Read the offset array
- if (X->Read(g, kcp->Kof, nv[1], sizeof(int)))
- goto err;
-
- } // endif n[1]
-
- if (!kcp->Prefix)
- // Indicate that the key column value can be found from KXYCOL
- colp->SetKcol(kcp);
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- } // endfor k
-
- To_LastCol = prev;
-
- if (Mul && prev) {
- // Last key offset is the index offset
- kcp->Koff = Offset;
- kcp->Koff.Sub = true;
- } // endif Mul
-
- X->Close();
-
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
-
-err:
- Close();
- return true;
- } // end of Init
-
-#else // XMAP
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XINDEX::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* If sorting is required, this will be done later. */
- /*********************************************************************/
- const char *ftype;
- BYTE *mbase;
- char fn[_MAX_PATH];
- int *nv, k, n, id = -1;
- bool estim;
- PCOL colp;
- PXCOL prev = NULL, kcp = NULL;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- PDBUSER dup = PlgGetUser(g);
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Get the first key column. */
- /*********************************************************************/
- if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } else
- colp = To_Cols[0];
-
- switch (Tdbp->Ftype) {
- 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(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was save in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif SepIndex
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Get a view on the part of the index file containing this index. */
- /*********************************************************************/
- if (!(mbase = (BYTE*)X->FileView(g, fn)))
- goto err;
-
- if (id >= 0) {
- // Get offset from the header
- IOFF *noff = (IOFF*)mbase;
-
- // Position the memory base at the offset of this index
- mbase += noff[id].Low;
- } // endif id
-
- // Now start the mapping process.
- nv = (int*)mbase;
- mbase += NZ * sizeof(int);
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d %d %d %d\n",
- nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- // Not this index
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif nv
-
- if (nv[2]) {
- // Set the offset array memory block
- Offset.Memp = mbase;
- Offset.Size = nv[2] * sizeof(int);
- Offset.Sub = true;
- Mul = true;
- Ndif = nv[2] - 1;
- mbase += Offset.Size;
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
- Incr = nv[4];
- Nblk = nv[5];
- Sblk = nv[6];
-
- if (!Incr) {
- /*******************************************************************/
- /* Point to the storage that contains the file positions. */
- /*******************************************************************/
- Record.Size = Num_K * sizeof(int);
- Record.Memp = mbase;
- Record.Sub = true;
- mbase += Record.Size;
- } else
- Srtd = true; // Sorted positions can be calculated
-
- /*********************************************************************/
- /* Allocate the KXYCOL blocks used to store column values. */
- /*********************************************************************/
- for (k = 0; k < Nk; k++) {
- if (k == Nval)
- To_LastVal = prev;
-
- nv = (int*)mbase;
- mbase += (NW * sizeof(int));
-
- colp = To_Cols[k];
-
- if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- kcp = new(g) KXYCOL(this);
-
- if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
- goto err;
-
- if (!kcp->Prefix)
- // Indicate that the key column value can be found from KXYCOL
- colp->SetKcol(kcp);
-
- if (prev) {
- kcp->Previous = prev;
- prev->Next = kcp;
- } else
- To_KeyCol = kcp;
-
- prev = kcp;
- } // endfor k
-
- To_LastCol = prev;
-
- if (Mul && prev)
- // Last key offset is the index offset
- kcp->Koff = Offset;
-
- /*********************************************************************/
- /* No valid record read yet for secondary file. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
-
-err:
- Close();
- return true;
- } // end of Init
-#endif // XMAP
-
-/***********************************************************************/
-/* Get Ndif and Num_K from the index file. */
-/***********************************************************************/
-bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
- {
- char *ftype;
- char fn[_MAX_PATH];
- int n, nv[NZ], id = -1;
- bool estim = false;
- PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
-
- ndif = numk = 0;
-
- /*********************************************************************/
- /* Get the estimated table size. */
- /* Note: for fixed tables we must use cardinality to avoid the call */
- /* to MaxBlkSize that could reduce the cardinality value. */
- /*********************************************************************/
- if (Tdbp->Cardinality(NULL)) {
- // For DBF tables, Cardinality includes bad or soft deleted lines
- // that are not included in the index, and can be larger then the
- // index size.
- estim = (Tdbp->Ftype == RECFM_DBF);
- n = Tdbp->Cardinality(g); // n is exact table size
- } else {
- // Variable table not optimized
- estim = true; // n is an estimate of the size
- n = Tdbp->GetMaxSize(g);
- } // endif Cardinality
-
- if (n <= 0)
- return !(n == 0); // n < 0 error, n = 0 void table
-
- /*********************************************************************/
- /* Check the key part number. */
- /*********************************************************************/
- if (!Nk) {
- strcpy(g->Message, MSG(NO_KEY_COL));
- return true; // Error
- } // endif Nk
-
- switch (Tdbp->Ftype) {
- 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(INVALID_FTYPE), Tdbp->Ftype);
- return true;
- } // endswitch Ftype
-
- if (defp->SepIndex()) {
- // Index was saved in a separate file
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), Xdp->GetName());
- _makepath(fn, drive, direc, fname, ftype);
- } else {
- id = ID;
- strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
- } // endif sep
-
- PlugSetPath(fn, fn, Tdbp->GetPath());
-
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
-
- /*********************************************************************/
- /* Open the index file and check its validity. */
- /*********************************************************************/
- if (X->Open(g, fn, id, MODE_READ))
- goto err; // No saved values
-
- // Get offset from XDB file
-//if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
-// goto err;
-
- // Now start the reading process.
- if (X->Read(g, nv, NZ, sizeof(int)))
- goto err;
-
-#if defined(TRACE)
- printf("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
-#endif // TRACE
-
- // The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
- if (/*nv[0] != ID ||*/ nv[1] != Nk) {
- sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
-#if defined(TRACE)
- printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
-#endif // TRACE
- goto err;
- } // endif
-
- if (nv[2]) {
- Mul = true;
- Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
- } else {
- Mul = false;
- Ndif = nv[3];
- } // endif nv[2]
-
- if (nv[3] < n && estim)
- n = nv[3]; // n was just an evaluated max value
-
- if (nv[3] != n) {
- sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
- goto err;
- } // endif
-
- Num_K = nv[3];
-
- if (Nk > 1) {
- if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
- goto err;
-
- if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
- goto err;
-
- if (X->Read(g, nv, NW, sizeof(int)))
- goto err;
-
- PCOL colp = *To_Cols;
-
- if (nv[4] != colp->GetResultType() ||
- (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
- sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
- goto err; // Error
- } // endif GetKey
-
- Ndif = nv[0];
- } // endif Nk
-
- /*********************************************************************/
- /* Set size values. */
- /*********************************************************************/
- ndif = Ndif;
- numk = Num_K;
- return false;
-
-err:
- X->Close();
- return true;
- } // end of GetAllSizes
-
-/***********************************************************************/
-/* RANGE: Tell how many records exist for a given value, for an array */
-/* of values, or in a given value range. */
-/***********************************************************************/
-int XINDEX::Range(PGLOBAL g, int limit, bool incl)
- {
- int i, k, n = 0;
- PXOB *xp = To_Vals;
- PXCOL kp = To_KeyCol;
- OPVAL op = Op;
-
- switch (limit) {
- case 1: Op = (incl) ? OP_GE : OP_GT; break;
- case 2: Op = (incl) ? OP_GT : OP_GE; break;
- default: return 0;
- } // endswitch limit
-
- /*********************************************************************/
- /* Currently only range of constant values with an EQ operator is */
- /* implemented. Find the number of rows for each given values. */
- /*********************************************************************/
- if (xp[0]->GetType() == TYPE_CONST) {
- for (i = 0; kp; kp = kp->Next) {
- kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
- if (++i == Nval) break;
- } // endfor kp
-
- if ((k = FastFind(Nval)) < Num_K)
- n = k;
-// if (limit)
-// n = (Mul) ? k : kp->Val_K;
-// else
-// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
-
- } else {
- strcpy(g->Message, MSG(RANGE_NO_JOIN));
- n = -1; // Logical error
- } // endif'f Type
-
- Op = op;
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* Return the size of the group (equal values) of the current value. */
-/***********************************************************************/
-int XINDEX::GroupSize(void)
- {
-#if defined(_DEBUG)
- assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
-#endif // _DEBUG
-
- if (Nval == Nk)
- return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
- : 1;
-
-#if defined(_DEBUG)
- assert(To_LastVal);
-#endif // _DEBUG
-
- // Index whose only some columns are used
- int ck1, ck2;
-
- ck1 = To_LastVal->Val_K;
- ck2 = ck1 + 1;
-
-#if defined(_DEBUG)
- assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
-#endif // _DEBUG
-
- for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
- ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
- ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
- } // endfor kcp
-
- return ck2 - ck1;
- } // end of GroupSize
-
-/***********************************************************************/
-/* Find Cur_K and Val_K's of the next distinct value of the index. */
-/* Returns false if Ok, true if there are no more different values. */
-/***********************************************************************/
-bool XINDEX::NextValDif(void)
- {
- int curk;
- PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
-
- if (++kcp->Val_K < kcp->Ndf) {
- Cur_K = curk = kcp->Val_K;
-
- // (Cur_K return is currently not used by SQLGBX)
- for (PXCOL kp = kcp; kp; kp = kp->Next)
- Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
-
- } else
- return true;
-
- for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
- if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
- break; // all previous columns have same value
-
- curk = ++kcp->Val_K; // This is a break, get new column value
- } // endfor kcp
-
- return false;
- } // end of NextValDif
-
-/***********************************************************************/
-/* XINDEX: Find Cur_K and Val_K's of next index entry. */
-/* If eq is true next values must be equal to last ones up to Nval. */
-/* Returns false if Ok, true if there are no more (equal) values. */
-/***********************************************************************/
-bool XINDEX::NextVal(bool eq)
- {
- int n, neq = Nk + 1, curk;
- PXCOL kcp;
-
- if (Cur_K == Num_K)
- return true;
- else
- curk = ++Cur_K;
-
- for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
- if (kcp->Kof) {
- if (curk == kcp->Kof[kcp->Val_K + 1])
- neq = n;
-
- } else {
-#ifdef _DEBUG
- assert(curk == kcp->Val_K + 1);
-#endif // _DEBUG
- neq = n;
- } // endif Kof
-
-#ifdef _DEBUG
- assert(kcp->Val_K < kcp->Ndf);
-#endif // _DEBUG
-
- // If this is not a break...
- if (neq > n)
- break; // all previous columns have same value
-
- curk = ++kcp->Val_K; // This is a break, get new column value
- } // endfor kcp
-
- // Return true if no more values or, in case of "equal" values,
- // if the last used column value has changed
- return (Cur_K == Num_K || (eq && neq <= Nval));
- } // end of NextVal
-
-/***********************************************************************/
-/* XINDEX: Fetch a physical or logical record. */
-/***********************************************************************/
-int XINDEX::Fetch(PGLOBAL g)
- {
- int n;
- PXCOL kp;
-
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Table read through a sorted index. */
- /*********************************************************************/
- switch (Op) {
- case OP_NEXT: // Read next
- if (NextVal(false))
- return -1; // End of indexed file
-
- break;
- case OP_FIRST: // Read first
- for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = 0;
-
- Op = OP_NEXT;
- break;
- case OP_SAME: // Read next same
- // Logically the key values should be the same as before
-#if defined(TRACE)
- printf("looking for next same value\n");
-#endif // TRACE
-
- if (NextVal(true)) {
- Op = OP_EQ;
- return -2; // no more equal values
- } // endif NextVal
-
- break;
- case OP_NXTDIF: // Read next dif
-// while (!NextVal(true)) ;
-
-// if (Cur_K >= Num_K)
-// return -1; // End of indexed file
- if (NextValDif())
- return -1; // End of indexed file
-
- break;
- case OP_FSTDIF: // Read first diff
- for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
- kp->Val_K = 0;
-
- Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
- break;
- default: // Should be OP_EQ
-// if (Tbxp->Key_Rank < 0) {
- /***************************************************************/
- /* Look for the first key equal to the link column values */
- /* and return its rank whithin the index table. */
- /***************************************************************/
- for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
- if (kp->InitFind(g, To_Vals[n]))
- return -1; // No more constant values
-
- Nth++;
-
-#if defined(TRACE)
- printf("Fetch: Looking for new value\n");
-#endif // TRACE
- Cur_K = FastFind(Nval);
-
- if (Cur_K >= Num_K)
- /*************************************************************/
- /* Rank not whithin index table, signal record not found. */
- /*************************************************************/
- return -2;
-
- else if (Mul || Nval < Nk)
- Op = OP_SAME;
-
- } // endswitch Op
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- /*********************************************************************/
- /* Return the position of the required record. */
- /*********************************************************************/
- return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching record in a join using an */
-/* optimized algorithm based on dichotomie and optimized comparing. */
-/***********************************************************************/
-int XINDEX::FastFind(int nv)
- {
- register int curk, sup, inf, i= 0, k, n = 2;
- register PXCOL kp, kcp;
-
- assert((int)nv == Nval);
-
- if (Nblk && Op == OP_EQ) {
- // Look in block values to find in which block to search
- sup = Nblk;
- inf = -1;
-
- while (n && sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = To_KeyCol->CompBval(i);
-
- if (n < 0)
- sup = i;
- else
- inf = i;
-
- } // endwhile
-
- if (inf < 0)
- return Num_K;
-
-// i = inf;
- inf *= Sblk;
-
- if ((sup = inf + Sblk) > To_KeyCol->Ndf)
- sup = To_KeyCol->Ndf;
-
- inf--;
- } else {
- inf = -1;
- sup = To_KeyCol->Ndf;
- } // endif Nblk
-
- for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
- while (sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompVal(i);
-
- if (n < 0)
- sup = i;
- else if (n > 0)
- inf = i;
- else
- break;
-
- } // endwhile
-
- if (n) {
- if (Op != OP_EQ) {
- // Currently only OP_GT or OP_GE
- kcp->Val_K = curk = sup;
-
- // Check for value changes in previous key parts
- for (kp = kcp->Previous; kp; kp = kp->Previous)
- if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
- break;
- else
- curk = ++kp->Val_K;
-
- n = 0;
- } // endif Op
-
- break;
- } // endif n
-
- kcp->Val_K = i;
-
- if (++k == Nval) {
- if (Op == OP_GT) { // n is always 0
- curk = ++kcp->Val_K; // Increment value by 1
-
- // Check for value changes in previous key parts
- for (kp = kcp->Previous; kp; kp = kp->Previous)
- if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
- break; // Not changed
- else
- curk = ++kp->Val_K;
-
- } // endif Op
-
- break; // So kcp remains pointing the last tested block
- } // endif k
-
- if (kcp->Kof) {
- inf = kcp->Kof[i] - 1;
- sup = kcp->Kof[i + 1];
- } else {
- inf = i - 1;
- sup = i + 1;
- } // endif Kof
-
- } // endfor k, kcp
-
- if (n) {
- // Record not found
- for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
- kcp->Val_K = kcp->Ndf; // Not a valid value
-
- return Num_K;
- } // endif n
-
- for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
- kcp->Val_K = curk;
- curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
- } // endfor kcp
-
- return curk;
- } // end of FastFind
-
-/* -------------------------- XINDXS Class --------------------------- */
-
-/***********************************************************************/
-/* XINDXS public constructor. */
-/***********************************************************************/
-XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
- : XINDEX(tdbp, xdp, pxp, cp, xp)
- {
- Srtd = To_Cols[0]->GetOpt() < 0; // ?????
- } // end of XINDXS constructor
-
-/***********************************************************************/
-/* XINDXS compare routine for C Quick/Insertion sort. */
-/***********************************************************************/
-int XINDXS::Qcompare(int *i1, int *i2)
- {
-#ifdef DEBTRACE
- num_comp++;
-#endif
-
- return To_KeyCol->Compare(*i1, *i2);
- } // end of Qcompare
-
-/***********************************************************************/
-/* Range: Tell how many records exist for given value(s): */
-/* If limit=0 return range for these values. */
-/* If limit=1 return the start of range. */
-/* If limit=2 return the end of range. */
-/***********************************************************************/
-int XINDXS::Range(PGLOBAL g, int limit, bool incl)
- {
- int k, n = 0;
- PXOB xp = To_Vals[0];
- PXCOL kp = To_KeyCol;
- OPVAL op = Op;
-
- switch (limit) {
- case 1: Op = (incl) ? OP_GE : OP_GT; break;
- case 2: Op = (incl) ? OP_GT : OP_GE; break;
- default: Op = OP_EQ;
- } // endswitch limit
-
- /*********************************************************************/
- /* Currently only range of constant values with an EQ operator is */
- /* implemented. Find the number of rows for each given values. */
- /*********************************************************************/
- if (xp->GetType() == TYPE_CONST) {
- kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
- k = FastFind(Nval);
-
- if (k < Num_K || Op != OP_EQ)
- if (limit)
- n = (Mul) ? k : kp->Val_K;
- else
- n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
-
- } else {
- strcpy(g->Message, MSG(RANGE_NO_JOIN));
- n = -1; // Logical error
- } // endif'f Type
-
- Op = op;
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* Return the size of the group (equal values) of the current value. */
-/***********************************************************************/
-int XINDXS::GroupSize(void)
- {
-#if defined(_DEBUG)
- assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
-#endif // _DEBUG
- return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K]
- : 1;
- } // end of GroupSize
-
-/***********************************************************************/
-/* XINDXS: Find Cur_K and Val_K of next index value. */
-/* If b is true next value must be equal to last one. */
-/* Returns false if Ok, true if there are no more (equal) values. */
-/***********************************************************************/
-bool XINDXS::NextVal(bool eq)
- {
- bool rc;
-
- if (To_KeyCol->Val_K == Ndif)
- return true;
-
- if (Mul) {
- int limit = Pof[To_KeyCol->Val_K + 1];
-
-#ifdef _DEBUG
- assert(Cur_K < limit);
- assert(To_KeyCol->Val_K < Ndif);
-#endif // _DEBUG
-
- if (++Cur_K == limit) {
- To_KeyCol->Val_K++;
- rc = (eq || limit == Num_K);
- } else
- rc = false;
-
- } else
- rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
-
- return rc;
- } // end of NextVal
-
-/***********************************************************************/
-/* XINDXS: Fetch a physical or logical record. */
-/***********************************************************************/
-int XINDXS::Fetch(PGLOBAL g)
- {
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Table read through a sorted index. */
- /*********************************************************************/
- switch (Op) {
- case OP_NEXT: // Read next
- if (NextVal(false))
- return -1; // End of indexed file
-
- break;
- case OP_FIRST: // Read first
- To_KeyCol->Val_K = Cur_K = 0;
- Op = OP_NEXT;
- break;
- case OP_SAME: // Read next same
-#if defined(TRACE)
-// printf("looking for next same value\n");
-#endif // TRACE
-
- if (!Mul || NextVal(true)) {
- Op = OP_EQ;
- return -2; // No more equal values
- } // endif Mul
-
- break;
- case OP_NXTDIF: // Read next dif
- if (++To_KeyCol->Val_K == Ndif)
- return -1; // End of indexed file
-
- Cur_K = Pof[To_KeyCol->Val_K];
- break;
- case OP_FSTDIF: // Read first diff
- To_KeyCol->Val_K = Cur_K = 0;
- Op = (Mul) ? OP_NXTDIF : OP_NEXT;
- break;
- default: // Should OP_EQ
- /*****************************************************************/
- /* Look for the first key equal to the link column values */
- /* and return its rank whithin the index table. */
- /*****************************************************************/
- if (To_KeyCol->InitFind(g, To_Vals[0]))
- return -1; // No more constant values
- else
- Nth++;
-
-#if defined(TRACE)
- printf("Fetch: Looking for new value\n");
-#endif // TRACE
-
- Cur_K = FastFind(1);
-
- if (Cur_K >= Num_K)
- // Rank not whithin index table, signal record not found
- return -2;
- else if (Mul)
- Op = OP_SAME;
-
- } // endswitch Op
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- /*********************************************************************/
- /* Return the position of the required record. */
- /*********************************************************************/
- return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching indexed record using an */
-/* optimized algorithm based on dichotomie and optimized comparing. */
-/***********************************************************************/
-int XINDXS::FastFind(int nk)
- {
- register int sup, inf, i= 0, n = 2;
- register PXCOL kcp = To_KeyCol;
-
- if (Nblk && Op == OP_EQ) {
- // Look in block values to find in which block to search
- sup = Nblk;
- inf = -1;
-
- while (n && sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompBval(i);
-
- if (n < 0)
- sup = i;
- else
- inf = i;
-
- } // endwhile
-
- if (inf < 0)
- return Num_K;
-
-// i = inf;
- inf *= Sblk;
-
- if ((sup = inf + Sblk) > Ndif)
- sup = Ndif;
-
- inf--;
- } else {
- inf = -1;
- sup = Ndif;
- } // endif Nblk
-
- while (sup - inf > 1) {
- i = (inf + sup) >> 1;
-
- n = kcp->CompVal(i);
-
- if (n < 0)
- sup = i;
- else if (n > 0)
- inf = i;
- else
- break;
-
- } // endwhile
-
- if (!n && Op == OP_GT) {
- ++i;
- } else if (n && Op != OP_EQ) {
- // Currently only OP_GT or OP_GE
- i = sup;
- n = 0;
- } // endif sup
-
- kcp->Val_K = i; // Used by FillValue
- return ((n) ? Num_K : (Mul) ? Pof[i] : i);
- } // end of FastFind
-
-/* -------------------------- XLOAD Class --------------------------- */
-
-/***********************************************************************/
-/* XLOAD constructor. */
-/***********************************************************************/
-XLOAD::XLOAD(void)
- {
- Hfile = INVALID_HANDLE_VALUE;
-#if defined(WIN32) && defined(XMAP)
- ViewBase = NULL;
-#endif // WIN32 && XMAP
- NewOff.Val = 0LL;
-} // end of XLOAD constructor
-
-/***********************************************************************/
-/* Close the index huge file. */
-/***********************************************************************/
-void XLOAD::Close(void)
- {
- if (Hfile != INVALID_HANDLE_VALUE) {
- CloseFileHandle(Hfile);
- Hfile = INVALID_HANDLE_VALUE;
- } // endif Hfile
-
-#if defined(WIN32) && defined(XMAP)
- if (ViewBase) {
- if (!UnmapViewOfFile(ViewBase))
- printf("Error %d closing Viewmap\n", GetLastError());
-
- ViewBase = NULL;
- } // endif ViewBase
-#endif // WIN32 && XMAP
-
- } // end of Close
-
-/* --------------------------- XFILE Class --------------------------- */
-
-/***********************************************************************/
-/* XFILE constructor. */
-/***********************************************************************/
-XFILE::XFILE(void) : XLOAD()
- {
- Xfile = NULL;
-#if defined(XMAP) && !defined(WIN32)
- Mmp = NULL;
-#endif // XMAP && !WIN32
- } // end of XFILE constructor
-
-/***********************************************************************/
-/* Xopen function: opens a file using native API's. */
-/***********************************************************************/
-bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
- {
- char *pmod;
- bool rc;
- IOFF noff[MAX_INDX];
-
- /*********************************************************************/
- /* Open the index file according to mode. */
- /*********************************************************************/
- switch (mode) {
- case MODE_READ: pmod = "rb"; break;
- case MODE_WRITE: pmod = "wb"; break;
- case MODE_INSERT: pmod = "ab"; break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch mode
-
- if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
-#if defined(TRACE)
- printf("Open: %s\n", g->Message);
-#endif // TRACE
- return true;
- } // endif Xfile
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Position the cursor at end of file so ftell returns file size. */
- /*******************************************************************/
- if (fseek(Xfile, 0, SEEK_END)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
- NewOff.Low = (int)ftell(Xfile);
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
- fseek(Xfile, 0, SEEK_END);
- NewOff.Low = (int)ftell(Xfile);
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
- sprintf(g->Message, MSG(XFILE_READERR), errno);
- return true;
- } // endif MAX_INDX
-
- // Position the cursor at the offset of this index
- if (fseek(Xfile, noff[id].Low, SEEK_SET)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
- } // endif mode
-
- return false;
- } // end of Open
-
-/***********************************************************************/
-/* Move into an index file. */
-/***********************************************************************/
-bool XFILE::Seek(PGLOBAL g, int low, int high, int origin)
- {
-#if defined(_DEBUG)
- assert(high == 0);
-#endif // !_DEBUG
-
- if (fseek(Xfile, low, origin)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
- return true;
- } // endif
-
-//ftell(Xfile);
- return false;
- } // end of Seek
-
-/***********************************************************************/
-/* Read from the index file. */
-/***********************************************************************/
-bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
- {
- if (fread(buf, size, n, Xfile) != (size_t)n) {
- sprintf(g->Message, MSG(XFILE_READERR), errno);
- return true;
- } // endif size
-
- return false;
- } // end of Read
-
-/***********************************************************************/
-/* Write on index file, set rc and return the number of bytes written */
-/***********************************************************************/
-int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
- {
- int niw = (int)fwrite(buf, size, n, Xfile);
-
- if (niw != n) {
- sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
- rc = true;
- } // endif size
-
- return niw * size;
- } // end of Write
-
-/***********************************************************************/
-/* Update the file header and close the index file. */
-/***********************************************************************/
-void XFILE::Close(char *fn, int id)
- {
- if (id >= 0 && fn && Xfile) {
- fclose(Xfile);
-
- if ((Xfile = fopen(fn, "r+b")))
- if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
- fwrite(&NewOff, sizeof(int), 2, Xfile);
-
- } // endif id
-
- Close();
- } // end of Close
-
-/***********************************************************************/
-/* Close the index file. */
-/***********************************************************************/
-void XFILE::Close(void)
- {
- XLOAD::Close();
-
- if (Xfile) {
- fclose(Xfile);
- Xfile = NULL;
- } // endif Xfile
-
-#if defined(XMAP) && !defined(WIN32)
- if (Mmp) {
- CloseMemMap(Mmp->memory, Mmp->lenL);
- Mmp = NULL;
- } // endif Mmp
-#endif // XMAP
- } // end of Close
-
-#if defined(XMAP)
- /*********************************************************************/
- /* Map the entire index file. */
- /*********************************************************************/
-void *XFILE::FileView(PGLOBAL g, char *fn)
- {
- HANDLE h;
-
- Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
- h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
-
- if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
- if (!(*g->Message))
- strcpy(g->Message, MSG(FILE_MAP_ERR));
-
- CloseFileHandle(h); // Not used anymore
- return NULL; // No saved values
- } // endif h
-
- CloseFileHandle(h); // Not used anymore
- return Mmp->memory;
- } // end of FileView
-#endif // XMAP
-
-/* -------------------------- XHUGE Class --------------------------- */
-
-/***********************************************************************/
-/* Xopen function: opens a file using native API's. */
-/***********************************************************************/
-bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
- {
- IOFF noff[MAX_INDX];
-
- if (Hfile != INVALID_HANDLE_VALUE) {
- sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
- return true;
- } // endif
-
-#if defined(TRACE)
- printf( "Xopen: filename=%s mode=%d\n", filename, mode);
-#endif // TRACE
-
-#if defined(WIN32)
- LONG high = 0;
- DWORD rc, drc, access, share, creation;
-
- /*********************************************************************/
- /* 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_WRITE:
- access = GENERIC_WRITE;
- share = 0;
- creation = CREATE_ALWAYS;
- break;
- case MODE_INSERT:
- access = GENERIC_WRITE;
- share = 0;
- creation = OPEN_EXISTING;
- break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch
-
- 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);
- return true;
- } // endif Hfile
-
-#ifdef DEBTRACE
- fprintf(debug,
- " access=%p share=%p creation=%d handle=%p fn=%s\n",
- access, share, creation, Hfile, filename);
-#endif
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* In Insert mode we must position the cursor at end of file. */
- /*******************************************************************/
- rc = SetFilePointer(Hfile, 0, &high, FILE_END);
-
- if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
- sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
- CloseHandle(Hfile);
- Hfile = INVALID_HANDLE_VALUE;
- return true;
- } // endif
-
- NewOff.Low = (int)rc;
- NewOff.High = (int)high;
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
- NewOff.Low = (int)drc;
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
-
- if (!rc) {
- sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
- return true;
- } // endif rc
-
- // Position the cursor at the offset of this index
- rc = SetFilePointer(Hfile, noff[id].Low,
- (PLONG)&noff[id].High, FILE_BEGIN);
-
- if (rc == INVALID_SET_FILE_POINTER) {
- sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
- return true;
- } // endif
-
- } // endif Mode
-
-#else // UNIX
- int oflag = O_LARGEFILE; // Enable file size > 2G
- mode_t pmod = 0;
-
- /*********************************************************************/
- /* Create the file object according to access mode */
- /*********************************************************************/
- switch (mode) {
- case MODE_READ:
- oflag |= O_RDONLY;
- break;
- case MODE_WRITE:
- oflag |= O_WRONLY | O_CREAT | O_TRUNC;
- pmod = S_IREAD | S_IWRITE;
- break;
- case MODE_INSERT:
- oflag |= (O_WRONLY | O_APPEND);
- break;
- default:
- sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
- return true;
- } // endswitch
-
- Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
-
- if (Hfile == INVALID_HANDLE_VALUE) {
- /*rc = errno;*/
-#if defined(TRACE)
- printf("Open: %s\n", g->Message);
-#endif // TRACE
- return true;
- } // endif Hfile
-
-#if defined(TRACE)
- printf(" rc=%d oflag=%p mode=%d handle=%d fn=%s\n",
- rc, oflag, mode, Hfile, filename);
-#endif // TRACE
-
- if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Position the cursor at end of file so ftell returns file size. */
- /*******************************************************************/
- if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
- return true;
- } // endif
-
- } else if (mode == MODE_WRITE) {
- if (id >= 0) {
- // New not sep index file. Write the header.
- memset(noff, 0, sizeof(noff));
- NewOff.Low = write(Hfile, &noff, sizeof(noff));
- } // endif id
-
- } else if (mode == MODE_READ && id >= 0) {
- // Get offset from the header
- if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
- sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
- return true;
- } // endif MAX_INDX
-
- // Position the cursor at the offset of this index
- if (!lseek64(Hfile, noff[id].Val, SEEK_SET)) {
- sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
- return true;
- } // endif
-
- } // endif mode
-#endif // UNIX
-
- return false;
- } // end of Open
-
-/***********************************************************************/
-/* Go to position in a huge file. */
-/***********************************************************************/
-bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
- {
-#if defined(WIN32)
- LONG hi = high;
- DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
-
- if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
- sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
- return true;
- } // endif
-
-#else // UNIX
- off64_t pos = (off64_t)low
- + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
-
- if (lseek64(Hfile, pos, origin) < 0) {
- sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
-#if defined(TRACE)
- printf("lseek64 error %d\n", errno);
-#endif // TRACE
- return true;
- } // endif lseek64
-
-#if defined(TRACE)
- printf("Seek: low=%d high=%d\n", low, high);
-#endif // TRACE
-#endif // UNIX
-
- return false;
- } // end of Seek
-
-/***********************************************************************/
-/* Read from a huge index file. */
-/***********************************************************************/
-bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
- {
- bool rc = false;
-
-#if defined(WIN32)
- bool brc;
- DWORD nbr, count = (DWORD)(n * size);
-
- brc = ReadFile(Hfile, buf, count, &nbr, NULL);
-
- if (brc) {
- if (nbr != count) {
- strcpy(g->Message, MSG(EOF_INDEX_FILE));
- rc = true;
- } // endif nbr
-
- } else {
- char *buf[256];
- DWORD drc = GetLastError();
-
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)buf, sizeof(buf), NULL);
- sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
- rc = true;
- } // endif brc
-#else // UNIX
- ssize_t count = (ssize_t)(n * size);
-
-#if defined(TRACE)
- printf("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
-#endif // TRACE
-
- if (read(Hfile, buf, count) != count) {
- sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
-#if defined(TRACE)
- printf("read error %d\n", errno);
-#endif // TRACE
- rc = true;
- } // endif nbr
-#endif // UNIX
-
- return rc;
- } // end of Read
-
-/***********************************************************************/
-/* Write on a huge index file. */
-/***********************************************************************/
-int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
- {
-#if defined(WIN32)
- bool brc;
- DWORD nbw, count = (DWORD)n * (DWORD) size;
-
- brc = WriteFile(Hfile, buf, count, &nbw, NULL);
-
- if (!brc) {
- char msg[256];
- DWORD drc = GetLastError();
-
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)msg, sizeof(msg), NULL);
- sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
- rc = true;
- } // endif size
-
- return (int)nbw;
-#else // UNIX
- ssize_t nbw;
- size_t count = (size_t)n * (size_t)size;
-
- nbw = write(Hfile, buf, count);
-
- if (nbw != (signed)count) {
- sprintf(g->Message, MSG(WRITING_ERROR),
- "index file", strerror(errno));
- rc = true;
- } // endif nbw
-
- return (int)nbw;
-#endif // UNIX
- } // end of Write
-
-/***********************************************************************/
-/* Update the file header and close the index file. */
-/***********************************************************************/
-void XHUGE::Close(char *fn, int id)
- {
-#if defined(WIN32)
- if (id >= 0 && fn) {
- CloseFileHandle(Hfile);
- Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
- if (Hfile != INVALID_HANDLE_VALUE)
- if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
- != INVALID_SET_FILE_POINTER) {
- DWORD nbw;
-
- WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
- } // endif SetFilePointer
-
- } // endif id
-#else // !WIN32
- if (id >= 0 && fn) {
- fcntl(Hfile, F_SETFD, O_WRONLY);
-
- if (lseek(Hfile, id * sizeof(IOFF), SEEK_SET))
- write(Hfile, &NewOff, sizeof(IOFF));
-
- } // endif id
-#endif // !WIN32
-
- XLOAD::Close();
- } // end of Close
-
-#if defined(XMAP)
-/***********************************************************************/
-/* Don't know whether this is possible for huge files. */
-/***********************************************************************/
-void *XHUGE::FileView(PGLOBAL g, char *fn)
- {
- strcpy(g->Message, MSG(NO_PART_MAP));
- return NULL;
- } // end of FileView
-#endif // XMAP
-
-/* -------------------------- XXROW Class --------------------------- */
-
-/***********************************************************************/
-/* XXROW Public Constructor. */
-/***********************************************************************/
-XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
- {
- Tdbp = tdbp;
- Valp = NULL;
- } // end of XXROW constructor
-
-/***********************************************************************/
-/* XXROW Reset: re-initialize a Kindex block. */
-/***********************************************************************/
-void XXROW::Reset(void)
- {
-#if defined(_DEBUG)
- assert(Tdbp->GetLink()); // This a join index
-#endif // _DEBUG
- } // end of Reset
-
-/***********************************************************************/
-/* Init: Open and Initialize a Key Index. */
-/***********************************************************************/
-bool XXROW::Init(PGLOBAL g)
- {
- /*********************************************************************/
- /* Table will be accessed through an index table. */
- /* To_Link should not be NULL. */
- /*********************************************************************/
- if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
- return true;
-
- if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
- strcpy(g->Message, MSG(TYPE_MISMATCH));
- return true;
- } else
- Valp = (*Tdbp->GetLink())->GetValue();
-
- if ((Num_K = Tbxp->Cardinality(g)) < 0)
- return true; // Not a fixed file
-
- /*********************************************************************/
- /* The entire table is indexed, no need to construct the index. */
- /*********************************************************************/
- Cur_K = Num_K;
- return false;
- } // end of Init
-
-/***********************************************************************/
-/* RANGE: Tell how many record exist in a given value range. */
-/***********************************************************************/
-int XXROW::Range(PGLOBAL g, int limit, bool incl)
- {
- int n = Valp->GetIntValue();
-
- switch (limit) {
- case 1: n += ((incl) ? 0 : 1); break;
- case 2: n += ((incl) ? 1 : 0); break;
- default: n = 1;
- } // endswitch limit
-
- return n;
- } // end of Range
-
-/***********************************************************************/
-/* XXROW: Fetch a physical or logical record. */
-/***********************************************************************/
-int XXROW::Fetch(PGLOBAL g)
- {
- if (Num_K == 0)
- return -1; // means end of file
-
- /*********************************************************************/
- /* Look for a key equal to the link column of previous table, */
- /* and return its rank whithin the index table. */
- /*********************************************************************/
- Cur_K = FastFind(1);
-
- if (Cur_K >= Num_K)
- /*******************************************************************/
- /* Rank not whithin index table, signal record not found. */
- /*******************************************************************/
- return -2; // Means record not found
-
- /*********************************************************************/
- /* If rank is equal to stored rank, record is already there. */
- /*********************************************************************/
- if (Cur_K == Old_K)
- return -3; // Means record already there
- else
- Old_K = Cur_K; // Store rank of newly read record
-
- return Cur_K;
- } // end of Fetch
-
-/***********************************************************************/
-/* FastFind: Returns the index of matching record in a join. */
-/***********************************************************************/
-int XXROW::FastFind(int nk)
- {
- int n = Valp->GetIntValue();
-
- if (n < 0)
- return (Op == OP_EQ) ? (-1) : 0;
- else if (n > Num_K)
- return Num_K;
- else
- return (Op == OP_GT) ? n : (n - 1);
-
- } // end of FastFind
-
-/* ------------------------- KXYCOL Classes -------------------------- */
-
-/***********************************************************************/
-/* KXYCOL public constructor. */
-/***********************************************************************/
-KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
- To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
- {
- Next = NULL;
- Previous = NULL;
- Kxp = kp;
- Colp = NULL;
- IsSorted = false;
- Asc = true;
- Keys = Nmblk;
- Kblp = NULL;
- Bkeys = Nmblk;
- Blkp = NULL;
- Valp = NULL;
- Klen = 0;
- Kprec = 0;
- Type = TYPE_ERROR;
- Prefix = false;
- Koff = Nmblk;
- Val_K = 0;
- Ndf = 0;
- Mxs = 0;
- } // end of KXYCOL constructor
-
-/***********************************************************************/
-/* KXYCOL Init: initialize and allocate storage. */
-/* Key length kln can be smaller than column length for CHAR columns. */
-/***********************************************************************/
-bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
- {
- int len = colp->GetLength(), prec = colp->GetScale();
-
- // Currently no indexing on NULL columns
- if (colp->IsNullable()) {
- sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
- return true;
- } // endif nullable
-
- if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
- len = kln;
- Prefix = true;
- } // endif kln
-
-#ifdef DEBTRACE
- htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
- this, colp->GetName(), n, colp->GetResultType(), sm);
-#endif
-
- // Allocate the Value object used when moving items
- Type = colp->GetResultType();
-
- if (!(Valp = AllocateValue(g, Type, len, colp->GetScale(),
- colp->IsUnsigned())))
- return true;
-
- Klen = Valp->GetClen();
- Keys.Size = n * Klen;
-
- if (!PlgDBalloc(g, NULL, Keys)) {
- sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
- return true; // Error
- } // endif
-
- // Allocate the Valblock. The last parameter is to have rows filled
- // by blanks (if true) or keep the zero ending char (if false).
- // Currently we set it to true to be compatible with QRY blocks,
- // and the one before last is to enable length/type checking, set to
- // true if not a prefix key.
- Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true);
- Asc = sm; // Sort mode: Asc=true Desc=false
- Ndf = n;
-
- // Store this information to avoid sorting when already done
- if (Asc)
- IsSorted = colp->GetOpt() < 0;
-
-//SetNulls(colp->IsNullable()); for when null columns will be indexable
- return false;
- } // end of Init
-
-#if defined(XMAP)
-/***********************************************************************/
-/* KXYCOL MapInit: initialize and address storage. */
-/* Key length kln can be smaller than column length for CHAR columns. */
-/***********************************************************************/
-BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
- {
- int len = colp->GetLength(), prec = colp->GetPrecision();
-
- if (n[3] && colp->GetLength() > n[3]
- && colp->GetResultType() == TYPE_STRING) {
- len = n[3];
- Prefix = true;
- } // endif kln
-
- Type = colp->GetResultType();
-
-#ifdef DEBTRACE
- htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
- this, colp, Type, n[0], len, m);
-#endif
-
- // Allocate the Value object used when moving items
- Valp = AllocateValue(g, Type, len, prec, false, NULL);
- Klen = Valp->GetClen();
-
- if (n[2]) {
- Bkeys.Size = n[2] * Klen;
- Bkeys.Memp = m;
- Bkeys.Sub = true;
-
- // Allocate the Valblk containing initial block key values
- Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true);
- } // endif nb
-
- Keys.Size = n[0] * Klen;
- Keys.Memp = m + Bkeys.Size;
- Keys.Sub = true;
-
- // Allocate the Valblock. Last two parameters are to have rows filled
- // by blanks (if true) or keep the zero ending char (if false).
- // Currently we set it to true to be compatible with QRY blocks,
- // and last one to enable type checking (no conversion).
- Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, true, true);
-
- if (n[1]) {
- Koff.Size = n[1] * sizeof(int);
- Koff.Memp = m + Bkeys.Size + Keys.Size;
- Koff.Sub = true;
- } // endif n[1]
-
- Ndf = n[0];
- IsSorted = colp->GetOpt() < 0;
- return m + Bkeys.Size + Keys.Size + Koff.Size;
- } // end of MapInit
-#endif // XMAP
-
-/***********************************************************************/
-/* Allocate the offset block used by intermediate key columns. */
-/***********************************************************************/
-int *KXYCOL::MakeOffset(PGLOBAL g, int n)
- {
- if (!Kof) {
- // Calculate the initial size of the offset
- Koff.Size = (n + 1) * sizeof(int);
-
- // Allocate the required memory
- if (!PlgDBalloc(g, NULL, Koff)) {
- strcpy(g->Message, MSG(KEY_ALLOC_ERR));
- return NULL; // Error
- } // endif
-
- } else if (n) {
- // This is a reallocation call
- PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
- } else
- PlgDBfree(Koff);
-
- return (int*)Kof;
- } // end of MakeOffset
-
-/***********************************************************************/
-/* Make a front end array of key values that are the first value of */
-/* each blocks (of size n). This to reduce paging in FastFind. */
-/***********************************************************************/
-bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
- {
- int i, k;
-
- // Calculate the size of the block array in the index
- Bkeys.Size = nb * Klen;
-
- // Allocate the required memory
- if (!PlgDBalloc(g, NULL, Bkeys)) {
- sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
- return true; // Error
- } // endif
-
- // Allocate the Valblk used to contains initial block key values
- Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
-
- // Populate the array with values
- for (i = k = 0; i < nb; i++, k += size)
- Blkp->SetValue(Kblp, i, k);
-
- return false;
- } // end of MakeBlockArray
-
-/***********************************************************************/
-/* KXYCOL SetValue: read column value for nth array element. */
-/***********************************************************************/
-void KXYCOL::SetValue(PCOL colp, int i)
- {
-#if defined(_DEBUG)
- assert (Kblp != NULL);
-#endif
-
- Kblp->SetValue(colp->GetValue(), i);
- } // end of SetValue
-
-/***********************************************************************/
-/* InitFind: initialize finding the rank of column value in index. */
-/***********************************************************************/
-bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
- {
- if (xp->GetType() == TYPE_CONST) {
- if (Kxp->Nth)
- return true;
-
- Valp->SetValue_pval(xp->GetValue(), !Prefix);
- } else {
- xp->Reset();
- xp->Eval(g);
- Valp->SetValue_pval(xp->GetValue(), false);
-// Valp->SetValue_pval(xp->GetValue(), !Prefix);
- } // endif Type
-
- return false;
- } // end of InitFind
-
-/***********************************************************************/
-/* InitBinFind: initialize Value to the value pointed by vp. */
-/***********************************************************************/
-void KXYCOL::InitBinFind(void *vp)
- {
- Valp->SetBinValue(vp);
- } // end of InitBinFind
-
-/***********************************************************************/
-/* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
-/* already in storage in the corresponding KXYCOL. */
-/***********************************************************************/
-void KXYCOL::FillValue(PVAL valp)
- {
- valp->SetValue_pvblk(Kblp, Val_K);
-
- // Set null when applicable (NIY)
-//if (valp->GetNullable())
-// valp->SetNull(valp->IsZero());
-
- } // end of FillValue
-
-/***********************************************************************/
-/* KXYCOL: Compare routine for one numeric value. */
-/***********************************************************************/
-int KXYCOL::Compare(int i1, int i2)
- {
- // Do the actual comparison between values.
- register int k = Kblp->CompVal(i1, i2);
-
-#ifdef DEBUG2
- htrc("Compare done result=%d\n", k);
-#endif
-
- return (Asc) ? k : -k;
- } // end of Compare
-
-/***********************************************************************/
-/* KXYCOL: Compare the ith key to the stored Value. */
-/***********************************************************************/
-int KXYCOL::CompVal(int i)
- {
- // Do the actual comparison between numerical values.
-#ifdef DEBUG2
- register int k = (int)Kblp->CompVal(Valp, (int)i);
-
- htrc("Compare done result=%d\n", k);
- return k;
-#endif
- return Kblp->CompVal(Valp, i);
- } // end of CompVal
-
-/***********************************************************************/
-/* KXYCOL: Compare the key to the stored block value. */
-/***********************************************************************/
-int KXYCOL::CompBval(int i)
- {
- // Do the actual comparison between key values.
- return Blkp->CompVal(Valp, i);
- } // end of CompBval
-
-/***********************************************************************/
-/* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
-/***********************************************************************/
-void KXYCOL::ReAlloc(PGLOBAL g, int n)
- {
- PlgDBrealloc(g, NULL, Keys, n * Klen);
- Kblp->ReAlloc(To_Keys, n);
- Ndf = n;
- } // end of ReAlloc
-
-/***********************************************************************/
-/* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
-/***********************************************************************/
-void KXYCOL::FreeData(void)
- {
- PlgDBfree(Keys);
- Kblp = NULL;
- PlgDBfree(Bkeys);
- Blkp = NULL;
- PlgDBfree(Koff);
- Ndf = 0;
- } // end of FreeData
+/***************** Xindex C++ Class Xindex Code (.CPP) *****************/
+/* Name: XINDEX.CPP Version 2.8 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
+/* */
+/* This file contains the class XINDEX implementation code. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#else // !UNIX
+#include <io.h>
+#endif // !UNIX
+#include <fcntl.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* kindex.h is header containing the KINDEX class definition. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "osutil.h"
+#include "maputil.h"
+//nclude "filter.h"
+#include "tabcol.h"
+#include "xindex.h"
+#include "xobject.h"
+//nclude "scalfnc.h"
+//nclude "array.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+
+/***********************************************************************/
+/* Macro or external routine definition */
+/***********************************************************************/
+#define NZ 7
+#define NW 5
+#define MAX_INDX 10
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
+#endif
+
+/***********************************************************************/
+/* DB static external variables. */
+/***********************************************************************/
+extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
+
+/***********************************************************************/
+/* Last two parameters are true to enable type checking, and last one */
+/* to have rows filled by blanks to be compatible with QRY blocks. */
+/***********************************************************************/
+PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
+ bool check = true, bool blank = true, bool un = false);
+
+/***********************************************************************/
+/* Check whether we have to create/update permanent indexes. */
+/***********************************************************************/
+int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
+ {
+ int rc;
+ PTABLE tablep;
+ PTDBDOS tdbp;
+ PCATLG cat = PlgGetCatalog(g, true);
+
+ /*********************************************************************/
+ /* Open a new table in mode read and with only the keys columns. */
+ /*********************************************************************/
+ tablep = new(g) XTAB(name);
+
+ if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep)))
+ rc = RC_NF;
+ else if (!tdbp->GetDef()->Indexable()) {
+ sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
+ rc = RC_NF;
+ } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
+ rc = RC_OK; // No index
+
+ return rc;
+ } // end of PlgMakeIndex
+
+/* -------------------------- Class INDEXDEF ------------------------- */
+
+/***********************************************************************/
+/* INDEXDEF Constructor. */
+/***********************************************************************/
+INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
+ {
+//To_Def = NULL;
+ Next = NULL;
+ ToKeyParts = NULL;
+ Name = name;
+ Unique = uniq;
+ Invalid = false;
+ AutoInc = false;
+ Nparts = 0;
+ ID = n;
+//Offset = 0;
+//Offhigh = 0;
+//Size = 0;
+ MaxSame = 1;
+ } // end of INDEXDEF constructor
+
+/***********************************************************************/
+/* Set the max same values for each colum after making the index. */
+/***********************************************************************/
+void INDEXDEF::SetMxsame(PXINDEX x)
+ {
+ PKPDEF kdp;
+ PXCOL xcp;
+
+ for (kdp = ToKeyParts, xcp = x->To_KeyCol;
+ kdp && xcp; kdp = kdp->Next, xcp = xcp->Next)
+ kdp->Mxsame = xcp->Mxs;
+ } // end of SetMxsame
+
+/* -------------------------- Class KPARTDEF ------------------------- */
+
+/***********************************************************************/
+/* KPARTDEF Constructor. */
+/***********************************************************************/
+KPARTDEF::KPARTDEF(PSZ name, int n)
+ {
+ Next = NULL;
+ Name = name;
+ Mxsame = 0;
+ Ncol = n;
+ Klen = 0;
+ } // end of KPARTDEF constructor
+
+/* -------------------------- XXBASE Class --------------------------- */
+
+/***********************************************************************/
+/* XXBASE public constructor. */
+/***********************************************************************/
+XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
+ To_Rec((int*&)Record.Memp)
+ {
+ Tbxp = tbxp;
+ Record = Nmblk;
+ Cur_K = -1;
+ Old_K = -1;
+ Num_K = 0;
+ Ndif = 0;
+ Bot = Top = Inf = Sup = 0;
+ Op = OP_EQ;
+ To_KeyCol = NULL;
+ Mul = false;
+ Val_K = -1;
+ Nblk = Sblk = 0;
+ Thresh = 7;
+ ID = -1;
+ Nth = 0;
+ } // end of XXBASE constructor
+
+/***********************************************************************/
+/* Make file output of XINDEX contents. */
+/***********************************************************************/
+void XXBASE::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+ fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K);
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of XINDEX contents. */
+/***********************************************************************/
+void XXBASE::Print(PGLOBAL g, char *ps, uint z)
+ {
+ *ps = '\0';
+ strncat(ps, "Xindex", z);
+ } // end of Print
+
+/* -------------------------- XINDEX Class --------------------------- */
+
+/***********************************************************************/
+/* XINDEX public constructor. */
+/***********************************************************************/
+XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k)
+ : XXBASE(tdbp, !xdp->IsUnique())
+ {
+ Xdp = xdp;
+ ID = xdp->GetID();
+ Tdbp = tdbp;
+ X = pxp;
+ To_LastCol = NULL;
+ To_LastVal = NULL;
+ To_Cols = cp;
+ To_Vals = xp;
+ Mul = !xdp->IsUnique();
+ Srtd = false;
+ Nk = xdp->GetNparts();
+ Nval = (k) ? k : Nk;
+ Incr = 0;
+//Defoff = xdp->GetOffset();
+//Defhigh = xdp->GetOffhigh();
+//Size = xdp->GetSize();
+ MaxSame = xdp->GetMaxSame();
+ } // end of XINDEX constructor
+
+/***********************************************************************/
+/* XINDEX Reset: re-initialize a Xindex block. */
+/***********************************************************************/
+void XINDEX::Reset(void)
+ {
+ for (PXCOL kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = kp->Ndf;
+
+ Cur_K = Num_K;
+ Old_K = -1; // Needed to avoid not setting CurBlk for Update
+ Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST :
+ (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ;
+ Nth = 0;
+ } // end of Reset
+
+/***********************************************************************/
+/* XINDEX Close: terminate index and free all allocated data. */
+/* Do not reset other values that are used at return to make. */
+/***********************************************************************/
+void XINDEX::Close(void)
+ {
+ // Close file or view of file
+ X->Close();
+
+ // De-allocate data
+ PlgDBfree(Record);
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+
+ // De-allocate Key data
+ for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->FreeData();
+
+ // Column values cannot be retrieved from key anymore
+ for (int k = 0; k < Nk; k++)
+ To_Cols[k]->SetKcol(NULL);
+
+ } // end of Close
+
+/***********************************************************************/
+/* XINDEX compare routine for C Quick/Insertion sort. */
+/***********************************************************************/
+int XINDEX::Qcompare(int *i1, int *i2)
+ {
+ register int k;
+ register PXCOL kcp;
+
+ for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next)
+ if ((k = kcp->Compare(*i1, *i2)))
+ break;
+
+#ifdef DEBTRACE
+ num_comp++;
+#endif
+
+ return k;
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Make: Make and index on key column(s). */
+/***********************************************************************/
+bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
+ {
+ /*********************************************************************/
+ /* Table can be accessed through an index. */
+ /*********************************************************************/
+ int k, rc = RC_OK;
+ int *bof, i, j, n, ndf, nkey;
+ PKPDEF kdfp = Xdp->GetToKeyParts();
+ bool brc = true;
+ PCOL colp;
+ PXCOL kp, prev = NULL, kcp = NULL;
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ /*********************************************************************/
+ /* Allocate the storage that will contain the keys and the file */
+ /* positions corresponding to them. */
+ /*********************************************************************/
+ if ((n = Tdbp->GetMaxSize(g)) < 0)
+ return true;
+ else if (!n) {
+ Num_K = Ndif = 0;
+ MaxSame = 1;
+
+ // The if condition was suppressed because this may be an existing
+ // index that is now void because all table lines were deleted.
+// if (sxp)
+ goto nox; // Truncate eventually existing index file
+// else
+// return false;
+
+ } // endif n
+
+ // File position must be stored
+ Record.Size = n * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Record)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n);
+ goto err; // Error
+ } // endif
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ colp = To_Cols[k];
+
+ if (!kdfp) {
+ sprintf(g->Message, MSG(INT_COL_ERROR),
+ (colp) ? colp->GetName() : "???");
+ goto err; // Error
+ } // endif kdfp
+
+ kcp = new(g) KXYCOL(this);
+
+ if (kcp->Init(g, colp, n, true, kdfp->Klen))
+ goto err; // Error
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ kdfp = kdfp->Next;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ /*********************************************************************/
+ /* Get the starting information for progress. */
+ /*********************************************************************/
+ dup->Step = (char*)PlugSubAlloc(g, NULL, 128);
+ sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
+ dup->ProgMax = Tdbp->GetProgMax(g);
+ dup->ProgCur = 0;
+
+ /*********************************************************************/
+ /* Standard init: read the file and construct the index table. */
+ /* Note: reading will be sequential as To_Kindex is not set. */
+ /*********************************************************************/
+ for (i = nkey = 0; i < n && rc != RC_EF; i++) {
+#if defined(THREAD)
+ if (!dup->Step) {
+ strcpy(g->Message, MSG(QUERY_CANCELLED));
+ longjmp(g->jumper[g->jump_level], 99);
+ } // endif Step
+#endif // THREAD
+
+ /*******************************************************************/
+ /* Read a valid record from table file. */
+ /*******************************************************************/
+ rc = Tdbp->ReadDB(g);
+
+ // Update progress information
+ dup->ProgCur = Tdbp->GetProgCur();
+
+ // Check return code and do whatever must be done according to it
+ switch (rc) {
+ case RC_OK:
+ break;
+ case RC_EF:
+ goto end_of_file;
+ case RC_NF:
+ continue;
+ default:
+ sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
+ goto err;
+ } // endswitch rc
+
+ /*******************************************************************/
+ /* Get and Store the file position of the last read record for */
+ /* future direct access. */
+ /*******************************************************************/
+ To_Rec[nkey] = Tdbp->GetRecpos();
+
+ /*******************************************************************/
+ /* Get the keys and place them in the key blocks. */
+ /*******************************************************************/
+ for (k = 0, kcp = To_KeyCol;
+ k < Nk && kcp;
+ k++, kcp = kcp->Next) {
+ colp = To_Cols[k];
+ colp->Reset();
+
+ colp->ReadColumn(g);
+// if (colp->ReadColumn(g))
+// goto err;
+
+ kcp->SetValue(colp, nkey);
+ } // endfor k
+
+ nkey++; // A new valid key was found
+ } // endfor i
+
+ end_of_file:
+
+ // Update progress information
+ dup->ProgCur = Tdbp->GetProgMax(g);
+
+ /*********************************************************************/
+ /* Record the Index size and eventually resize memory allocation. */
+ /*********************************************************************/
+ if ((Num_K = nkey) < n) {
+ PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int));
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->ReAlloc(g, Num_K);
+
+ } // endif Num_K
+
+ /*********************************************************************/
+ /* Sort the index so we can use an optimized Find algorithm. */
+ /* Note: for a unique index we use the non conservative sort */
+ /* version because normally all index values are different. */
+ /* This was set at CSORT class construction. */
+ /* For all indexes, an offset array is made so we can check the */
+ /* uniqueness of unique indexes. */
+ /*********************************************************************/
+ Index.Size = Num_K * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Index)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
+ goto err; // Error
+ } // endif alloc
+
+ Offset.Size = (Num_K + 1) * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1);
+ goto err; // Error
+ } // endif alloc
+
+ // Call the sort program, it returns the number of distinct values
+ if ((Ndif = Qsort(g, Num_K)) < 0)
+ goto err; // Error during sort
+
+ // Check whether the unique index is unique indeed
+ if (!Mul)
+ if (Ndif < Num_K) {
+ strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
+ goto err;
+ } else
+ PlgDBfree(Offset); // Not used anymore
+
+ // Use the index to physically reorder the xindex
+ Srtd = Reorder(g);
+
+ if (Ndif < Num_K) {
+ // Resize the offset array
+ PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int));
+
+ // Initial value of MaxSame
+ MaxSame = Pof[1] - Pof[0];
+
+ // Resize the Key array by only keeping the distinct values
+ for (i = 1; i < Ndif; i++) {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Move(i, Pof[i]);
+
+ MaxSame = max(MaxSame, Pof[i + 1] - Pof[i]);
+ } // endfor i
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->ReAlloc(g, Ndif);
+
+ } else {
+ Mul = false; // Current index is unique
+ PlgDBfree(Offset); // Not used anymore
+ MaxSame = 1; // Reset it when remaking an index
+ } // endif Ndif
+
+ /*********************************************************************/
+ /* Now do the reduction of the index. Indeed a multi-column index */
+ /* can be used for only some of the first columns. For instance if */
+ /* an index is defined for column A, B, C PlugDB can use it for */
+ /* only the column A or the columns A, B. */
+ /* What we do here is to reduce the data so column A will contain */
+ /* only the sorted distinct values of A, B will contain data such */
+ /* as only distinct values of A,B are stored etc. */
+ /* This implies that for each column set an offset array is made */
+ /* except if the subset originally contains unique values. */
+ /*********************************************************************/
+ // Update progress information
+ dup->Step = STEP(REDUCE_INDEX);
+
+ ndf = Ndif;
+ To_LastCol->Mxs = MaxSame;
+
+ for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) {
+ if (!(bof = kcp->MakeOffset(g, ndf)))
+ goto err;
+ else
+ *bof = 0;
+
+ for (n = 0, i = j = 1; i < ndf; i++)
+ for (kp = kcp; kp; kp = kp->Previous)
+ if (kp->Compare(n, i)) {
+ // Values are not equal to last ones
+ bof[j++] = n = i;
+ break;
+ } // endif Compare
+
+ if (j < ndf) {
+ // Sub-index is multiple
+ bof[j] = ndf;
+ ndf = j; // New number of distinct values
+
+ // Resize the Key array by only keeping the distinct values
+ for (kp = kcp; kp; kp = kp->Previous) {
+ for (i = 1; i < ndf; i++)
+ kp->Move(i, bof[i]);
+
+ kp->ReAlloc(g, ndf);
+ } // endif kcp
+
+ // Resize the offset array
+ kcp->MakeOffset(g, ndf);
+
+ // Calculate the max same value for this column
+ kcp->Mxs = ColMaxSame(kcp);
+ } else {
+ // Current sub-index is unique
+ kcp->MakeOffset(g, 0); // The offset is not used anymore
+ kcp->Mxs = 1; // Unique
+ } // endif j
+
+ } // endfor kcp
+
+ /*********************************************************************/
+ /* For sorted columns and fixed record size, file position can be */
+ /* calculated, so the Record array can be discarted. */
+ /* Note: for Num_K = 1 any non null value is Ok. */
+ /*********************************************************************/
+ if (Srtd && Tdbp->Ftype != RECFM_VAR) {
+ Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
+ PlgDBfree(Record);
+ } // endif Srtd
+
+ /*********************************************************************/
+ /* Check whether a two-tier find algorithm can be implemented. */
+ /* It is currently implemented only for single key indexes. */
+ /*********************************************************************/
+ if (Nk == 1 && ndf >= 65536) {
+ // Implement a two-tier find algorithm
+ for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ;
+
+ Nblk = (ndf -1) / Sblk + 1;
+
+ if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk))
+ goto err; // Error
+
+ } // endif Num_K
+
+ nox:
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+
+ /*********************************************************************/
+ /* Save the index so it has not to be recalculated. */
+ /*********************************************************************/
+ if (!SaveIndex(g, sxp))
+ brc = false;
+
+ err:
+ // We don't need the index anymore
+ Close();
+
+ if (brc)
+ printf("%s\n", g->Message);
+
+ return brc;
+ } // end of Make
+
+/***********************************************************************/
+/* Return the max size of the intermediate column. */
+/***********************************************************************/
+int XINDEX::ColMaxSame(PXCOL kp)
+ {
+ int *kof, i, ck1, ck2, ckn = 1;
+ PXCOL kcp;
+
+ // Calculate the max same value for this column
+ for (i = 0; i < kp->Ndf; i++) {
+ ck1 = i;
+ ck2 = i + 1;
+
+ for (kcp = kp; kcp; kcp = kcp->Next) {
+ if (!(kof = (kcp->Next) ? kcp->Kof : Pof))
+ break;
+
+ ck1 = kof[ck1];
+ ck2 = kof[ck2];
+ } // endfor kcp
+
+ ckn = max(ckn, ck2 - ck1);
+ } // endfor i
+
+ return ckn;
+ } // end of ColMaxSame
+
+/***********************************************************************/
+/* Reorder: use the sort index to reorder the data in storage so */
+/* it will be physically sorted and sort index can be removed. */
+/***********************************************************************/
+bool XINDEX::Reorder(PGLOBAL g)
+ {
+ register int i, j, k, n;
+ bool sorted = true;
+ PXCOL kcp;
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ if (Num_K > 500000) {
+ // Update progress information
+ dup->Step = STEP(REORDER_INDEX);
+ dup->ProgMax = Num_K;
+ dup->ProgCur = 0;
+ } else
+ dup = NULL;
+
+ if (!Pex)
+ return Srtd;
+
+ for (i = 0; i < Num_K; i++) {
+ if (Pex[i] == Num_K) { // Already moved
+ continue;
+ } else if (Pex[i] == i) { // Already placed
+ if (dup)
+ dup->ProgCur++;
+
+ continue;
+ } // endif's Pex
+
+ sorted = false;
+
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Save(i);
+
+ n = To_Rec[i];
+
+ for (j = i;; j = k) {
+ k = Pex[j];
+ Pex[j] = Num_K; // Mark position as set
+
+ if (k == i) {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Restore(j);
+
+ To_Rec[j] = n;
+ break; // end of loop
+ } else {
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Move(j, k); // Move k to j
+
+ To_Rec[j] = To_Rec[k];
+ } // endif k
+
+ if (dup)
+ dup->ProgCur++;
+
+ } // endfor j
+
+ } // endfor i
+
+ // The index is not used anymore
+ PlgDBfree(Index);
+ return sorted;
+ } // end of Reorder
+
+/***********************************************************************/
+/* Save the index 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 XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
+ {
+ char *ftype;
+ char fn[_MAX_PATH];
+ int n[NZ], nof = (Mul) ? (Ndif + 1) : 0;
+ int id = -1, size = 0;
+ bool sep, rc = false;
+ PXCOL kcp = To_KeyCol;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+ PDBUSER dup = PlgGetUser(g);
+
+ dup->Step = STEP(SAVING_INDEX);
+ dup->ProgMax = 15 + 16 * Nk;
+ dup->ProgCur = 0;
+
+ switch (Tdbp->Ftype) {
+ 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(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) {
+ // Index is saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ sxp = NULL;
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+ if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) {
+ printf("%s\n", g->Message);
+ return true;
+ } // endif Open
+
+ if (!Ndif)
+ goto end; // Void index
+
+ /*********************************************************************/
+ /* Write the index values on the index file. */
+ /*********************************************************************/
+ n[0] = ID; // To check validity
+ n[1] = Nk; // The number of indexed columns
+ n[2] = nof; // The offset array size or 0
+ n[3] = Num_K; // The index size
+ n[4] = Incr; // Increment of record positions
+ n[5] = Nblk; n[6] = Sblk;
+
+#if defined(TRACE)
+ printf("Saving index %s\n", Xdp->GetName());
+ printf("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d\n",
+ ID, Nk, nof, Num_K, Incr, Nblk, Sblk);
+#endif // TRACE
+
+ size = X->Write(g, n, NZ, sizeof(int), rc);
+ dup->ProgCur = 1;
+
+ if (Mul) // Write the offset array
+ size += X->Write(g, Pof, nof, sizeof(int), rc);
+
+ dup->ProgCur = 5;
+
+ if (!Incr) // Write the record position array(s)
+ size += X->Write(g, To_Rec, Num_K, sizeof(int), rc);
+
+ dup->ProgCur = 15;
+
+ for (; kcp; kcp = kcp->Next) {
+ n[0] = kcp->Ndf; // Number of distinct sub-values
+ n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique
+ n[2] = (kcp == To_KeyCol) ? Nblk : 0;
+ n[3] = kcp->Klen; // To be checked later
+ n[4] = kcp->Type; // To be checked later
+
+ size += X->Write(g, n, NW, sizeof(int), rc);
+ dup->ProgCur += 1;
+
+ if (n[2])
+ size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
+
+ dup->ProgCur += 5;
+
+ size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
+ dup->ProgCur += 5;
+
+ if (n[1])
+ size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
+
+ dup->ProgCur += 5;
+ } // endfor kcp
+
+#if defined(TRACE)
+ printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size);
+#endif // TRACE
+
+ end:
+ X->Close(fn, id);
+ return rc;
+ } // end of SaveIndex
+
+#if !defined(XMAP)
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XINDEX::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* If sorting is required, this will be done later. */
+ /*********************************************************************/
+ char *ftype;
+ char fn[_MAX_PATH];
+ int k, n, nv[NZ], id = -1;
+ bool estim = false;
+ PCOL colp;
+ PXCOL prev = NULL, kcp = NULL;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Get the first key column. */
+ /*********************************************************************/
+ if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } else
+ colp = To_Cols[0];
+
+ switch (Tdbp->Ftype) {
+ 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(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Open the index file and check its validity. */
+ /*********************************************************************/
+ if (X->Open(g, fn, id, MODE_READ))
+ goto err; // No saved values
+
+ // Now start the reading process.
+ if (X->Read(g, nv, NZ, sizeof(int)))
+ goto err;
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d %d %d %d\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif
+
+ if (nv[2]) {
+ Mul = true;
+ Ndif = nv[2];
+
+ // Allocate the storage that will contain the offset array
+ Offset.Size = Ndif * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif);
+ goto err;
+ } // endif
+
+ if (X->Read(g, Pof, Ndif, sizeof(int)))
+ goto err;
+
+ Ndif--; // nv[2] is offset size, equal to Ndif + 1
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+ Incr = nv[4];
+ Nblk = nv[5];
+ Sblk = nv[6];
+
+ if (!Incr) {
+ /*******************************************************************/
+ /* Allocate the storage that will contain the file positions. */
+ /*******************************************************************/
+ Record.Size = Num_K * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Record)) {
+ sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K);
+ goto err;
+ } // endif
+
+ if (X->Read(g, To_Rec, Num_K, sizeof(int)))
+ goto err;
+
+ } else
+ Srtd = true; // Sorted positions can be calculated
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ if (k == Nval)
+ To_LastVal = prev;
+
+ if (X->Read(g, nv, NW, sizeof(int)))
+ goto err;
+
+ colp = To_Cols[k];
+
+ if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ kcp = new(g) KXYCOL(this);
+
+ if (kcp->Init(g, colp, nv[0], true, (int)nv[3]))
+ goto err; // Error
+
+ /*******************************************************************/
+ /* Read the index values from the index file. */
+ /*******************************************************************/
+ if (k == 0 && Nblk) {
+ if (kcp->MakeBlockArray(g, Nblk, 0))
+ goto err;
+
+ // Read block values
+ if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen))
+ goto err;
+
+ } // endif Nblk
+
+ // Read the entire (small) index
+ if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen))
+ goto err;
+
+ if (nv[1]) {
+ if (!kcp->MakeOffset(g, nv[1] - 1))
+ goto err;
+
+ // Read the offset array
+ if (X->Read(g, kcp->Kof, nv[1], sizeof(int)))
+ goto err;
+
+ } // endif n[1]
+
+ if (!kcp->Prefix)
+ // Indicate that the key column value can be found from KXYCOL
+ colp->SetKcol(kcp);
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ if (Mul && prev) {
+ // Last key offset is the index offset
+ kcp->Koff = Offset;
+ kcp->Koff.Sub = true;
+ } // endif Mul
+
+ X->Close();
+
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+
+err:
+ Close();
+ return true;
+ } // end of Init
+
+#else // XMAP
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XINDEX::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* If sorting is required, this will be done later. */
+ /*********************************************************************/
+ const char *ftype;
+ BYTE *mbase;
+ char fn[_MAX_PATH];
+ int *nv, k, n, id = -1;
+ bool estim;
+ PCOL colp;
+ PXCOL prev = NULL, kcp = NULL;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+ PDBUSER dup = PlgGetUser(g);
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Get the first key column. */
+ /*********************************************************************/
+ if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } else
+ colp = To_Cols[0];
+
+ switch (Tdbp->Ftype) {
+ 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(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was save in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif SepIndex
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Get a view on the part of the index file containing this index. */
+ /*********************************************************************/
+ if (!(mbase = (BYTE*)X->FileView(g, fn)))
+ goto err;
+
+ if (id >= 0) {
+ // Get offset from the header
+ IOFF *noff = (IOFF*)mbase;
+
+ // Position the memory base at the offset of this index
+ mbase += noff[id].Low;
+ } // endif id
+
+ // Now start the mapping process.
+ nv = (int*)mbase;
+ mbase += NZ * sizeof(int);
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d %d %d %d\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ // Not this index
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif nv
+
+ if (nv[2]) {
+ // Set the offset array memory block
+ Offset.Memp = mbase;
+ Offset.Size = nv[2] * sizeof(int);
+ Offset.Sub = true;
+ Mul = true;
+ Ndif = nv[2] - 1;
+ mbase += Offset.Size;
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+ Incr = nv[4];
+ Nblk = nv[5];
+ Sblk = nv[6];
+
+ if (!Incr) {
+ /*******************************************************************/
+ /* Point to the storage that contains the file positions. */
+ /*******************************************************************/
+ Record.Size = Num_K * sizeof(int);
+ Record.Memp = mbase;
+ Record.Sub = true;
+ mbase += Record.Size;
+ } else
+ Srtd = true; // Sorted positions can be calculated
+
+ /*********************************************************************/
+ /* Allocate the KXYCOL blocks used to store column values. */
+ /*********************************************************************/
+ for (k = 0; k < Nk; k++) {
+ if (k == Nval)
+ To_LastVal = prev;
+
+ nv = (int*)mbase;
+ mbase += (NW * sizeof(int));
+
+ colp = To_Cols[k];
+
+ if (nv[4] != colp->GetResultType() || !colp->GetValue() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ kcp = new(g) KXYCOL(this);
+
+ if (!(mbase = kcp->MapInit(g, colp, nv, mbase)))
+ goto err;
+
+ if (!kcp->Prefix)
+ // Indicate that the key column value can be found from KXYCOL
+ colp->SetKcol(kcp);
+
+ if (prev) {
+ kcp->Previous = prev;
+ prev->Next = kcp;
+ } else
+ To_KeyCol = kcp;
+
+ prev = kcp;
+ } // endfor k
+
+ To_LastCol = prev;
+
+ if (Mul && prev)
+ // Last key offset is the index offset
+ kcp->Koff = Offset;
+
+ /*********************************************************************/
+ /* No valid record read yet for secondary file. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+
+err:
+ Close();
+ return true;
+ } // end of Init
+#endif // XMAP
+
+/***********************************************************************/
+/* Get Ndif and Num_K from the index file. */
+/***********************************************************************/
+bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
+ {
+ char *ftype;
+ char fn[_MAX_PATH];
+ int n, nv[NZ], id = -1;
+ bool estim = false;
+ PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
+
+ ndif = numk = 0;
+
+ /*********************************************************************/
+ /* Get the estimated table size. */
+ /* Note: for fixed tables we must use cardinality to avoid the call */
+ /* to MaxBlkSize that could reduce the cardinality value. */
+ /*********************************************************************/
+ if (Tdbp->Cardinality(NULL)) {
+ // For DBF tables, Cardinality includes bad or soft deleted lines
+ // that are not included in the index, and can be larger then the
+ // index size.
+ estim = (Tdbp->Ftype == RECFM_DBF);
+ n = Tdbp->Cardinality(g); // n is exact table size
+ } else {
+ // Variable table not optimized
+ estim = true; // n is an estimate of the size
+ n = Tdbp->GetMaxSize(g);
+ } // endif Cardinality
+
+ if (n <= 0)
+ return !(n == 0); // n < 0 error, n = 0 void table
+
+ /*********************************************************************/
+ /* Check the key part number. */
+ /*********************************************************************/
+ if (!Nk) {
+ strcpy(g->Message, MSG(NO_KEY_COL));
+ return true; // Error
+ } // endif Nk
+
+ switch (Tdbp->Ftype) {
+ 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(INVALID_FTYPE), Tdbp->Ftype);
+ return true;
+ } // endswitch Ftype
+
+ if (defp->SepIndex()) {
+ // Index was saved in a separate file
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ _splitpath(defp->GetOfn(), drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), Xdp->GetName());
+ _makepath(fn, drive, direc, fname, ftype);
+ } else {
+ id = ID;
+ strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype);
+ } // endif sep
+
+ PlugSetPath(fn, fn, Tdbp->GetPath());
+
+#if defined(TRACE)
+ printf("Index %s file: %s\n", Xdp->GetName(), fn);
+#endif // TRACE
+
+ /*********************************************************************/
+ /* Open the index file and check its validity. */
+ /*********************************************************************/
+ if (X->Open(g, fn, id, MODE_READ))
+ goto err; // No saved values
+
+ // Get offset from XDB file
+//if (X->Seek(g, Defoff, Defhigh, SEEK_SET))
+// goto err;
+
+ // Now start the reading process.
+ if (X->Read(g, nv, NZ, sizeof(int)))
+ goto err;
+
+#if defined(TRACE)
+ printf("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
+#endif // TRACE
+
+ // The test on ID was suppressed because MariaDB can change an index ID
+ // when other indexes are added or deleted
+ if (/*nv[0] != ID ||*/ nv[1] != Nk) {
+ sprintf(g->Message, MSG(BAD_INDEX_FILE), fn);
+#if defined(TRACE)
+ printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+#endif // TRACE
+ goto err;
+ } // endif
+
+ if (nv[2]) {
+ Mul = true;
+ Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
+ } else {
+ Mul = false;
+ Ndif = nv[3];
+ } // endif nv[2]
+
+ if (nv[3] < n && estim)
+ n = nv[3]; // n was just an evaluated max value
+
+ if (nv[3] != n) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
+ goto err;
+ } // endif
+
+ Num_K = nv[3];
+
+ if (Nk > 1) {
+ if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
+ goto err;
+
+ if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR))
+ goto err;
+
+ if (X->Read(g, nv, NW, sizeof(int)))
+ goto err;
+
+ PCOL colp = *To_Cols;
+
+ if (nv[4] != colp->GetResultType() ||
+ (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) {
+ sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName());
+ goto err; // Error
+ } // endif GetKey
+
+ Ndif = nv[0];
+ } // endif Nk
+
+ /*********************************************************************/
+ /* Set size values. */
+ /*********************************************************************/
+ ndif = Ndif;
+ numk = Num_K;
+ return false;
+
+err:
+ X->Close();
+ return true;
+ } // end of GetAllSizes
+
+/***********************************************************************/
+/* RANGE: Tell how many records exist for a given value, for an array */
+/* of values, or in a given value range. */
+/***********************************************************************/
+int XINDEX::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int i, k, n = 0;
+ PXOB *xp = To_Vals;
+ PXCOL kp = To_KeyCol;
+ OPVAL op = Op;
+
+ switch (limit) {
+ case 1: Op = (incl) ? OP_GE : OP_GT; break;
+ case 2: Op = (incl) ? OP_GT : OP_GE; break;
+ default: return 0;
+ } // endswitch limit
+
+ /*********************************************************************/
+ /* Currently only range of constant values with an EQ operator is */
+ /* implemented. Find the number of rows for each given values. */
+ /*********************************************************************/
+ if (xp[0]->GetType() == TYPE_CONST) {
+ for (i = 0; kp; kp = kp->Next) {
+ kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix);
+ if (++i == Nval) break;
+ } // endfor kp
+
+ if ((k = FastFind(Nval)) < Num_K)
+ n = k;
+// if (limit)
+// n = (Mul) ? k : kp->Val_K;
+// else
+// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
+
+ } else {
+ strcpy(g->Message, MSG(RANGE_NO_JOIN));
+ n = -1; // Logical error
+ } // endif'f Type
+
+ Op = op;
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* Return the size of the group (equal values) of the current value. */
+/***********************************************************************/
+int XINDEX::GroupSize(void)
+ {
+#if defined(_DEBUG)
+ assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif);
+#endif // _DEBUG
+
+ if (Nval == Nk)
+ return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K]
+ : 1;
+
+#if defined(_DEBUG)
+ assert(To_LastVal);
+#endif // _DEBUG
+
+ // Index whose only some columns are used
+ int ck1, ck2;
+
+ ck1 = To_LastVal->Val_K;
+ ck2 = ck1 + 1;
+
+#if defined(_DEBUG)
+ assert(ck1 >= 0 && ck1 < To_LastVal->Ndf);
+#endif // _DEBUG
+
+ for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) {
+ ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1;
+ ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2;
+ } // endfor kcp
+
+ return ck2 - ck1;
+ } // end of GroupSize
+
+/***********************************************************************/
+/* Find Cur_K and Val_K's of the next distinct value of the index. */
+/* Returns false if Ok, true if there are no more different values. */
+/***********************************************************************/
+bool XINDEX::NextValDif(void)
+ {
+ int curk;
+ PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol;
+
+ if (++kcp->Val_K < kcp->Ndf) {
+ Cur_K = curk = kcp->Val_K;
+
+ // (Cur_K return is currently not used by SQLGBX)
+ for (PXCOL kp = kcp; kp; kp = kp->Next)
+ Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K;
+
+ } else
+ return true;
+
+ for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) {
+ if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1])
+ break; // all previous columns have same value
+
+ curk = ++kcp->Val_K; // This is a break, get new column value
+ } // endfor kcp
+
+ return false;
+ } // end of NextValDif
+
+/***********************************************************************/
+/* XINDEX: Find Cur_K and Val_K's of next index entry. */
+/* If eq is true next values must be equal to last ones up to Nval. */
+/* Returns false if Ok, true if there are no more (equal) values. */
+/***********************************************************************/
+bool XINDEX::NextVal(bool eq)
+ {
+ int n, neq = Nk + 1, curk;
+ PXCOL kcp;
+
+ if (Cur_K == Num_K)
+ return true;
+ else
+ curk = ++Cur_K;
+
+ for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) {
+ if (kcp->Kof) {
+ if (curk == kcp->Kof[kcp->Val_K + 1])
+ neq = n;
+
+ } else {
+#ifdef _DEBUG
+ assert(curk == kcp->Val_K + 1);
+#endif // _DEBUG
+ neq = n;
+ } // endif Kof
+
+#ifdef _DEBUG
+ assert(kcp->Val_K < kcp->Ndf);
+#endif // _DEBUG
+
+ // If this is not a break...
+ if (neq > n)
+ break; // all previous columns have same value
+
+ curk = ++kcp->Val_K; // This is a break, get new column value
+ } // endfor kcp
+
+ // Return true if no more values or, in case of "equal" values,
+ // if the last used column value has changed
+ return (Cur_K == Num_K || (eq && neq <= Nval));
+ } // end of NextVal
+
+/***********************************************************************/
+/* XINDEX: Fetch a physical or logical record. */
+/***********************************************************************/
+int XINDEX::Fetch(PGLOBAL g)
+ {
+ int n;
+ PXCOL kp;
+
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Table read through a sorted index. */
+ /*********************************************************************/
+ switch (Op) {
+ case OP_NEXT: // Read next
+ if (NextVal(false))
+ return -1; // End of indexed file
+
+ break;
+ case OP_FIRST: // Read first
+ for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = 0;
+
+ Op = OP_NEXT;
+ break;
+ case OP_SAME: // Read next same
+ // Logically the key values should be the same as before
+#if defined(TRACE)
+ printf("looking for next same value\n");
+#endif // TRACE
+
+ if (NextVal(true)) {
+ Op = OP_EQ;
+ return -2; // no more equal values
+ } // endif NextVal
+
+ break;
+ case OP_NXTDIF: // Read next dif
+// while (!NextVal(true)) ;
+
+// if (Cur_K >= Num_K)
+// return -1; // End of indexed file
+ if (NextValDif())
+ return -1; // End of indexed file
+
+ break;
+ case OP_FSTDIF: // Read first diff
+ for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next)
+ kp->Val_K = 0;
+
+ Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT;
+ break;
+ default: // Should be OP_EQ
+// if (Tbxp->Key_Rank < 0) {
+ /***************************************************************/
+ /* Look for the first key equal to the link column values */
+ /* and return its rank whithin the index table. */
+ /***************************************************************/
+ for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next)
+ if (kp->InitFind(g, To_Vals[n]))
+ return -1; // No more constant values
+
+ Nth++;
+
+#if defined(TRACE)
+ printf("Fetch: Looking for new value\n");
+#endif // TRACE
+ Cur_K = FastFind(Nval);
+
+ if (Cur_K >= Num_K)
+ /*************************************************************/
+ /* Rank not whithin index table, signal record not found. */
+ /*************************************************************/
+ return -2;
+
+ else if (Mul || Nval < Nk)
+ Op = OP_SAME;
+
+ } // endswitch Op
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ /*********************************************************************/
+ /* Return the position of the required record. */
+ /*********************************************************************/
+ return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching record in a join using an */
+/* optimized algorithm based on dichotomie and optimized comparing. */
+/***********************************************************************/
+int XINDEX::FastFind(int nv)
+ {
+ register int curk, sup, inf, i= 0, k, n = 2;
+ register PXCOL kp, kcp;
+
+ assert((int)nv == Nval);
+
+ if (Nblk && Op == OP_EQ) {
+ // Look in block values to find in which block to search
+ sup = Nblk;
+ inf = -1;
+
+ while (n && sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = To_KeyCol->CompBval(i);
+
+ if (n < 0)
+ sup = i;
+ else
+ inf = i;
+
+ } // endwhile
+
+ if (inf < 0)
+ return Num_K;
+
+// i = inf;
+ inf *= Sblk;
+
+ if ((sup = inf + Sblk) > To_KeyCol->Ndf)
+ sup = To_KeyCol->Ndf;
+
+ inf--;
+ } else {
+ inf = -1;
+ sup = To_KeyCol->Ndf;
+ } // endif Nblk
+
+ for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) {
+ while (sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompVal(i);
+
+ if (n < 0)
+ sup = i;
+ else if (n > 0)
+ inf = i;
+ else
+ break;
+
+ } // endwhile
+
+ if (n) {
+ if (Op != OP_EQ) {
+ // Currently only OP_GT or OP_GE
+ kcp->Val_K = curk = sup;
+
+ // Check for value changes in previous key parts
+ for (kp = kcp->Previous; kp; kp = kp->Previous)
+ if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
+ break;
+ else
+ curk = ++kp->Val_K;
+
+ n = 0;
+ } // endif Op
+
+ break;
+ } // endif n
+
+ kcp->Val_K = i;
+
+ if (++k == Nval) {
+ if (Op == OP_GT) { // n is always 0
+ curk = ++kcp->Val_K; // Increment value by 1
+
+ // Check for value changes in previous key parts
+ for (kp = kcp->Previous; kp; kp = kp->Previous)
+ if (kp->Kof && curk < kp->Kof[kp->Val_K + 1])
+ break; // Not changed
+ else
+ curk = ++kp->Val_K;
+
+ } // endif Op
+
+ break; // So kcp remains pointing the last tested block
+ } // endif k
+
+ if (kcp->Kof) {
+ inf = kcp->Kof[i] - 1;
+ sup = kcp->Kof[i + 1];
+ } else {
+ inf = i - 1;
+ sup = i + 1;
+ } // endif Kof
+
+ } // endfor k, kcp
+
+ if (n) {
+ // Record not found
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Val_K = kcp->Ndf; // Not a valid value
+
+ return Num_K;
+ } // endif n
+
+ for (curk = kcp->Val_K; kcp; kcp = kcp->Next) {
+ kcp->Val_K = curk;
+ curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K;
+ } // endfor kcp
+
+ return curk;
+ } // end of FastFind
+
+/* -------------------------- XINDXS Class --------------------------- */
+
+/***********************************************************************/
+/* XINDXS public constructor. */
+/***********************************************************************/
+XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
+ : XINDEX(tdbp, xdp, pxp, cp, xp)
+ {
+ Srtd = To_Cols[0]->GetOpt() == 2;
+ } // end of XINDXS constructor
+
+/***********************************************************************/
+/* XINDXS compare routine for C Quick/Insertion sort. */
+/***********************************************************************/
+int XINDXS::Qcompare(int *i1, int *i2)
+ {
+#ifdef DEBTRACE
+ num_comp++;
+#endif
+
+ return To_KeyCol->Compare(*i1, *i2);
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Range: Tell how many records exist for given value(s): */
+/* If limit=0 return range for these values. */
+/* If limit=1 return the start of range. */
+/* If limit=2 return the end of range. */
+/***********************************************************************/
+int XINDXS::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int k, n = 0;
+ PXOB xp = To_Vals[0];
+ PXCOL kp = To_KeyCol;
+ OPVAL op = Op;
+
+ switch (limit) {
+ case 1: Op = (incl) ? OP_GE : OP_GT; break;
+ case 2: Op = (incl) ? OP_GT : OP_GE; break;
+ default: Op = OP_EQ;
+ } // endswitch limit
+
+ /*********************************************************************/
+ /* Currently only range of constant values with an EQ operator is */
+ /* implemented. Find the number of rows for each given values. */
+ /*********************************************************************/
+ if (xp->GetType() == TYPE_CONST) {
+ kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix);
+ k = FastFind(Nval);
+
+ if (k < Num_K || Op != OP_EQ)
+ if (limit)
+ n = (Mul) ? k : kp->Val_K;
+ else
+ n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
+
+ } else {
+ strcpy(g->Message, MSG(RANGE_NO_JOIN));
+ n = -1; // Logical error
+ } // endif'f Type
+
+ Op = op;
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* Return the size of the group (equal values) of the current value. */
+/***********************************************************************/
+int XINDXS::GroupSize(void)
+ {
+#if defined(_DEBUG)
+ assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif);
+#endif // _DEBUG
+ return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K]
+ : 1;
+ } // end of GroupSize
+
+/***********************************************************************/
+/* XINDXS: Find Cur_K and Val_K of next index value. */
+/* If b is true next value must be equal to last one. */
+/* Returns false if Ok, true if there are no more (equal) values. */
+/***********************************************************************/
+bool XINDXS::NextVal(bool eq)
+ {
+ bool rc;
+
+ if (To_KeyCol->Val_K == Ndif)
+ return true;
+
+ if (Mul) {
+ int limit = Pof[To_KeyCol->Val_K + 1];
+
+#ifdef _DEBUG
+ assert(Cur_K < limit);
+ assert(To_KeyCol->Val_K < Ndif);
+#endif // _DEBUG
+
+ if (++Cur_K == limit) {
+ To_KeyCol->Val_K++;
+ rc = (eq || limit == Num_K);
+ } else
+ rc = false;
+
+ } else
+ rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq;
+
+ return rc;
+ } // end of NextVal
+
+/***********************************************************************/
+/* XINDXS: Fetch a physical or logical record. */
+/***********************************************************************/
+int XINDXS::Fetch(PGLOBAL g)
+ {
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Table read through a sorted index. */
+ /*********************************************************************/
+ switch (Op) {
+ case OP_NEXT: // Read next
+ if (NextVal(false))
+ return -1; // End of indexed file
+
+ break;
+ case OP_FIRST: // Read first
+ To_KeyCol->Val_K = Cur_K = 0;
+ Op = OP_NEXT;
+ break;
+ case OP_SAME: // Read next same
+#if defined(TRACE)
+// printf("looking for next same value\n");
+#endif // TRACE
+
+ if (!Mul || NextVal(true)) {
+ Op = OP_EQ;
+ return -2; // No more equal values
+ } // endif Mul
+
+ break;
+ case OP_NXTDIF: // Read next dif
+ if (++To_KeyCol->Val_K == Ndif)
+ return -1; // End of indexed file
+
+ Cur_K = Pof[To_KeyCol->Val_K];
+ break;
+ case OP_FSTDIF: // Read first diff
+ To_KeyCol->Val_K = Cur_K = 0;
+ Op = (Mul) ? OP_NXTDIF : OP_NEXT;
+ break;
+ default: // Should OP_EQ
+ /*****************************************************************/
+ /* Look for the first key equal to the link column values */
+ /* and return its rank whithin the index table. */
+ /*****************************************************************/
+ if (To_KeyCol->InitFind(g, To_Vals[0]))
+ return -1; // No more constant values
+ else
+ Nth++;
+
+#if defined(TRACE)
+ printf("Fetch: Looking for new value\n");
+#endif // TRACE
+
+ Cur_K = FastFind(1);
+
+ if (Cur_K >= Num_K)
+ // Rank not whithin index table, signal record not found
+ return -2;
+ else if (Mul)
+ Op = OP_SAME;
+
+ } // endswitch Op
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ /*********************************************************************/
+ /* Return the position of the required record. */
+ /*********************************************************************/
+ return (Incr) ? Cur_K * Incr : To_Rec[Cur_K];
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching indexed record using an */
+/* optimized algorithm based on dichotomie and optimized comparing. */
+/***********************************************************************/
+int XINDXS::FastFind(int nk)
+ {
+ register int sup, inf, i= 0, n = 2;
+ register PXCOL kcp = To_KeyCol;
+
+ if (Nblk && Op == OP_EQ) {
+ // Look in block values to find in which block to search
+ sup = Nblk;
+ inf = -1;
+
+ while (n && sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompBval(i);
+
+ if (n < 0)
+ sup = i;
+ else
+ inf = i;
+
+ } // endwhile
+
+ if (inf < 0)
+ return Num_K;
+
+// i = inf;
+ inf *= Sblk;
+
+ if ((sup = inf + Sblk) > Ndif)
+ sup = Ndif;
+
+ inf--;
+ } else {
+ inf = -1;
+ sup = Ndif;
+ } // endif Nblk
+
+ while (sup - inf > 1) {
+ i = (inf + sup) >> 1;
+
+ n = kcp->CompVal(i);
+
+ if (n < 0)
+ sup = i;
+ else if (n > 0)
+ inf = i;
+ else
+ break;
+
+ } // endwhile
+
+ if (!n && Op == OP_GT) {
+ ++i;
+ } else if (n && Op != OP_EQ) {
+ // Currently only OP_GT or OP_GE
+ i = sup;
+ n = 0;
+ } // endif sup
+
+ kcp->Val_K = i; // Used by FillValue
+ return ((n) ? Num_K : (Mul) ? Pof[i] : i);
+ } // end of FastFind
+
+/* -------------------------- XLOAD Class --------------------------- */
+
+/***********************************************************************/
+/* XLOAD constructor. */
+/***********************************************************************/
+XLOAD::XLOAD(void)
+ {
+ Hfile = INVALID_HANDLE_VALUE;
+#if defined(WIN32) && defined(XMAP)
+ ViewBase = NULL;
+#endif // WIN32 && XMAP
+ NewOff.Val = 0LL;
+} // end of XLOAD constructor
+
+/***********************************************************************/
+/* Close the index huge file. */
+/***********************************************************************/
+void XLOAD::Close(void)
+ {
+ if (Hfile != INVALID_HANDLE_VALUE) {
+ CloseFileHandle(Hfile);
+ Hfile = INVALID_HANDLE_VALUE;
+ } // endif Hfile
+
+#if defined(WIN32) && defined(XMAP)
+ if (ViewBase) {
+ if (!UnmapViewOfFile(ViewBase))
+ printf("Error %d closing Viewmap\n", GetLastError());
+
+ ViewBase = NULL;
+ } // endif ViewBase
+#endif // WIN32 && XMAP
+
+ } // end of Close
+
+/* --------------------------- XFILE Class --------------------------- */
+
+/***********************************************************************/
+/* XFILE constructor. */
+/***********************************************************************/
+XFILE::XFILE(void) : XLOAD()
+ {
+ Xfile = NULL;
+#if defined(XMAP) && !defined(WIN32)
+ Mmp = NULL;
+#endif // XMAP && !WIN32
+ } // end of XFILE constructor
+
+/***********************************************************************/
+/* Xopen function: opens a file using native API's. */
+/***********************************************************************/
+bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
+ {
+ char *pmod;
+ bool rc;
+ IOFF noff[MAX_INDX];
+
+ /*********************************************************************/
+ /* Open the index file according to mode. */
+ /*********************************************************************/
+ switch (mode) {
+ case MODE_READ: pmod = "rb"; break;
+ case MODE_WRITE: pmod = "wb"; break;
+ case MODE_INSERT: pmod = "ab"; break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch mode
+
+ if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) {
+#if defined(TRACE)
+ printf("Open: %s\n", g->Message);
+#endif // TRACE
+ return true;
+ } // endif Xfile
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Position the cursor at end of file so ftell returns file size. */
+ /*******************************************************************/
+ if (fseek(Xfile, 0, SEEK_END)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+ NewOff.Low = (int)ftell(Xfile);
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ Write(g, noff, sizeof(IOFF), MAX_INDX, rc);
+ fseek(Xfile, 0, SEEK_END);
+ NewOff.Low = (int)ftell(Xfile);
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) {
+ sprintf(g->Message, MSG(XFILE_READERR), errno);
+ return true;
+ } // endif MAX_INDX
+
+ // Position the cursor at the offset of this index
+ if (fseek(Xfile, noff[id].Low, SEEK_SET)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+ } // endif mode
+
+ return false;
+ } // end of Open
+
+/***********************************************************************/
+/* Move into an index file. */
+/***********************************************************************/
+bool XFILE::Seek(PGLOBAL g, int low, int high, int origin)
+ {
+#if defined(_DEBUG)
+ assert(high == 0);
+#endif // !_DEBUG
+
+ if (fseek(Xfile, low, origin)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
+ return true;
+ } // endif
+
+//ftell(Xfile);
+ return false;
+ } // end of Seek
+
+/***********************************************************************/
+/* Read from the index file. */
+/***********************************************************************/
+bool XFILE::Read(PGLOBAL g, void *buf, int n, int size)
+ {
+ if (fread(buf, size, n, Xfile) != (size_t)n) {
+ sprintf(g->Message, MSG(XFILE_READERR), errno);
+ return true;
+ } // endif size
+
+ return false;
+ } // end of Read
+
+/***********************************************************************/
+/* Write on index file, set rc and return the number of bytes written */
+/***********************************************************************/
+int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
+ {
+ int niw = (int)fwrite(buf, size, n, Xfile);
+
+ if (niw != n) {
+ sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno));
+ rc = true;
+ } // endif size
+
+ return niw * size;
+ } // end of Write
+
+/***********************************************************************/
+/* Update the file header and close the index file. */
+/***********************************************************************/
+void XFILE::Close(char *fn, int id)
+ {
+ if (id >= 0 && fn && Xfile) {
+ fclose(Xfile);
+
+ if ((Xfile = fopen(fn, "r+b")))
+ if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET))
+ fwrite(&NewOff, sizeof(int), 2, Xfile);
+
+ } // endif id
+
+ Close();
+ } // end of Close
+
+/***********************************************************************/
+/* Close the index file. */
+/***********************************************************************/
+void XFILE::Close(void)
+ {
+ XLOAD::Close();
+
+ if (Xfile) {
+ fclose(Xfile);
+ Xfile = NULL;
+ } // endif Xfile
+
+#if defined(XMAP) && !defined(WIN32)
+ if (Mmp) {
+ CloseMemMap(Mmp->memory, Mmp->lenL);
+ Mmp = NULL;
+ } // endif Mmp
+#endif // XMAP
+ } // end of Close
+
+#if defined(XMAP)
+ /*********************************************************************/
+ /* Map the entire index file. */
+ /*********************************************************************/
+void *XFILE::FileView(PGLOBAL g, char *fn)
+ {
+ HANDLE h;
+
+ Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP));
+ h = CreateFileMap(g, fn, Mmp, MODE_READ, false);
+
+ if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) {
+ if (!(*g->Message))
+ strcpy(g->Message, MSG(FILE_MAP_ERR));
+
+ CloseFileHandle(h); // Not used anymore
+ return NULL; // No saved values
+ } // endif h
+
+ CloseFileHandle(h); // Not used anymore
+ return Mmp->memory;
+ } // end of FileView
+#endif // XMAP
+
+/* -------------------------- XHUGE Class --------------------------- */
+
+/***********************************************************************/
+/* Xopen function: opens a file using native API's. */
+/***********************************************************************/
+bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
+ {
+ IOFF noff[MAX_INDX];
+
+ if (Hfile != INVALID_HANDLE_VALUE) {
+ sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
+ return true;
+ } // endif
+
+#if defined(TRACE)
+ printf( "Xopen: filename=%s mode=%d\n", filename, mode);
+#endif // TRACE
+
+#if defined(WIN32)
+ LONG high = 0;
+ DWORD rc, drc, access, share, creation;
+
+ /*********************************************************************/
+ /* 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_WRITE:
+ access = GENERIC_WRITE;
+ share = 0;
+ creation = CREATE_ALWAYS;
+ break;
+ case MODE_INSERT:
+ access = GENERIC_WRITE;
+ share = 0;
+ creation = OPEN_EXISTING;
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch
+
+ 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);
+ return true;
+ } // endif Hfile
+
+#ifdef DEBTRACE
+ fprintf(debug,
+ " access=%p share=%p creation=%d handle=%p fn=%s\n",
+ access, share, creation, Hfile, filename);
+#endif
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* In Insert mode we must position the cursor at end of file. */
+ /*******************************************************************/
+ rc = SetFilePointer(Hfile, 0, &high, FILE_END);
+
+ if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) {
+ sprintf(g->Message, MSG(ERROR_IN_SFP), drc);
+ CloseHandle(Hfile);
+ Hfile = INVALID_HANDLE_VALUE;
+ return true;
+ } // endif
+
+ NewOff.Low = (int)rc;
+ NewOff.High = (int)high;
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL);
+ NewOff.Low = (int)drc;
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL);
+
+ if (!rc) {
+ sprintf(g->Message, MSG(XFILE_READERR), GetLastError());
+ return true;
+ } // endif rc
+
+ // Position the cursor at the offset of this index
+ rc = SetFilePointer(Hfile, noff[id].Low,
+ (PLONG)&noff[id].High, FILE_BEGIN);
+
+ if (rc == INVALID_SET_FILE_POINTER) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer");
+ return true;
+ } // endif
+
+ } // endif Mode
+
+#else // UNIX
+ int oflag = O_LARGEFILE; // Enable file size > 2G
+ mode_t pmod = 0;
+
+ /*********************************************************************/
+ /* Create the file object according to access mode */
+ /*********************************************************************/
+ switch (mode) {
+ case MODE_READ:
+ oflag |= O_RDONLY;
+ break;
+ case MODE_WRITE:
+ oflag |= O_WRONLY | O_CREAT | O_TRUNC;
+ pmod = S_IREAD | S_IWRITE;
+ break;
+ case MODE_INSERT:
+ oflag |= (O_WRONLY | O_APPEND);
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode);
+ return true;
+ } // endswitch
+
+ Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod);
+
+ if (Hfile == INVALID_HANDLE_VALUE) {
+ /*rc = errno;*/
+#if defined(TRACE)
+ printf("Open: %s\n", g->Message);
+#endif // TRACE
+ return true;
+ } // endif Hfile
+
+#if defined(TRACE)
+ printf(" rc=%d oflag=%p mode=%d handle=%d fn=%s\n",
+ rc, oflag, mode, Hfile, filename);
+#endif // TRACE
+
+ if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Position the cursor at end of file so ftell returns file size. */
+ /*******************************************************************/
+ if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
+ return true;
+ } // endif
+
+ } else if (mode == MODE_WRITE) {
+ if (id >= 0) {
+ // New not sep index file. Write the header.
+ memset(noff, 0, sizeof(noff));
+ NewOff.Low = write(Hfile, &noff, sizeof(noff));
+ } // endif id
+
+ } else if (mode == MODE_READ && id >= 0) {
+ // Get offset from the header
+ if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) {
+ sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
+ return true;
+ } // endif MAX_INDX
+
+ // Position the cursor at the offset of this index
+ if (!lseek64(Hfile, noff[id].Val, SEEK_SET)) {
+ sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
+ return true;
+ } // endif
+
+ } // endif mode
+#endif // UNIX
+
+ return false;
+ } // end of Open
+
+/***********************************************************************/
+/* Go to position in a huge file. */
+/***********************************************************************/
+bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
+ {
+#if defined(WIN32)
+ LONG hi = high;
+ DWORD rc = SetFilePointer(Hfile, low, &hi, origin);
+
+ if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
+ sprintf(g->Message, MSG(FUNC_ERROR), "Xseek");
+ return true;
+ } // endif
+
+#else // UNIX
+ off64_t pos = (off64_t)low
+ + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000);
+
+ if (lseek64(Hfile, pos, origin) < 0) {
+ sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
+#if defined(TRACE)
+ printf("lseek64 error %d\n", errno);
+#endif // TRACE
+ return true;
+ } // endif lseek64
+
+#if defined(TRACE)
+ printf("Seek: low=%d high=%d\n", low, high);
+#endif // TRACE
+#endif // UNIX
+
+ return false;
+ } // end of Seek
+
+/***********************************************************************/
+/* Read from a huge index file. */
+/***********************************************************************/
+bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
+ {
+ bool rc = false;
+
+#if defined(WIN32)
+ bool brc;
+ DWORD nbr, count = (DWORD)(n * size);
+
+ brc = ReadFile(Hfile, buf, count, &nbr, NULL);
+
+ if (brc) {
+ if (nbr != count) {
+ strcpy(g->Message, MSG(EOF_INDEX_FILE));
+ rc = true;
+ } // endif nbr
+
+ } else {
+ char *buf[256];
+ DWORD drc = GetLastError();
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ sprintf(g->Message, MSG(READ_ERROR), "index file", buf);
+ rc = true;
+ } // endif brc
+#else // UNIX
+ ssize_t count = (ssize_t)(n * size);
+
+#if defined(TRACE)
+ printf("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count);
+#endif // TRACE
+
+ if (read(Hfile, buf, count) != count) {
+ sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno));
+#if defined(TRACE)
+ printf("read error %d\n", errno);
+#endif // TRACE
+ rc = true;
+ } // endif nbr
+#endif // UNIX
+
+ return rc;
+ } // end of Read
+
+/***********************************************************************/
+/* Write on a huge index file. */
+/***********************************************************************/
+int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
+ {
+#if defined(WIN32)
+ bool brc;
+ DWORD nbw, count = (DWORD)n * (DWORD) size;
+
+ brc = WriteFile(Hfile, buf, count, &nbw, NULL);
+
+ if (!brc) {
+ char msg[256];
+ DWORD drc = GetLastError();
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)msg, sizeof(msg), NULL);
+ sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg);
+ rc = true;
+ } // endif size
+
+ return (int)nbw;
+#else // UNIX
+ ssize_t nbw;
+ size_t count = (size_t)n * (size_t)size;
+
+ nbw = write(Hfile, buf, count);
+
+ if (nbw != (signed)count) {
+ sprintf(g->Message, MSG(WRITING_ERROR),
+ "index file", strerror(errno));
+ rc = true;
+ } // endif nbw
+
+ return (int)nbw;
+#endif // UNIX
+ } // end of Write
+
+/***********************************************************************/
+/* Update the file header and close the index file. */
+/***********************************************************************/
+void XHUGE::Close(char *fn, int id)
+ {
+#if defined(WIN32)
+ if (id >= 0 && fn) {
+ CloseFileHandle(Hfile);
+ Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (Hfile != INVALID_HANDLE_VALUE)
+ if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN)
+ != INVALID_SET_FILE_POINTER) {
+ DWORD nbw;
+
+ WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL);
+ } // endif SetFilePointer
+
+ } // endif id
+#else // !WIN32
+ if (id >= 0 && fn) {
+ fcntl(Hfile, F_SETFD, O_WRONLY);
+
+ if (lseek(Hfile, id * sizeof(IOFF), SEEK_SET))
+ write(Hfile, &NewOff, sizeof(IOFF));
+
+ } // endif id
+#endif // !WIN32
+
+ XLOAD::Close();
+ } // end of Close
+
+#if defined(XMAP)
+/***********************************************************************/
+/* Don't know whether this is possible for huge files. */
+/***********************************************************************/
+void *XHUGE::FileView(PGLOBAL g, char *fn)
+ {
+ strcpy(g->Message, MSG(NO_PART_MAP));
+ return NULL;
+ } // end of FileView
+#endif // XMAP
+
+/* -------------------------- XXROW Class --------------------------- */
+
+/***********************************************************************/
+/* XXROW Public Constructor. */
+/***********************************************************************/
+XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
+ {
+ Tdbp = tdbp;
+ Valp = NULL;
+ } // end of XXROW constructor
+
+/***********************************************************************/
+/* XXROW Reset: re-initialize a Kindex block. */
+/***********************************************************************/
+void XXROW::Reset(void)
+ {
+#if defined(_DEBUG)
+ assert(Tdbp->GetLink()); // This a join index
+#endif // _DEBUG
+ } // end of Reset
+
+/***********************************************************************/
+/* Init: Open and Initialize a Key Index. */
+/***********************************************************************/
+bool XXROW::Init(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Table will be accessed through an index table. */
+ /* To_Link should not be NULL. */
+ /*********************************************************************/
+ if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1)
+ return true;
+
+ if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) {
+ strcpy(g->Message, MSG(TYPE_MISMATCH));
+ return true;
+ } else
+ Valp = (*Tdbp->GetLink())->GetValue();
+
+ if ((Num_K = Tbxp->Cardinality(g)) < 0)
+ return true; // Not a fixed file
+
+ /*********************************************************************/
+ /* The entire table is indexed, no need to construct the index. */
+ /*********************************************************************/
+ Cur_K = Num_K;
+ return false;
+ } // end of Init
+
+/***********************************************************************/
+/* RANGE: Tell how many record exist in a given value range. */
+/***********************************************************************/
+int XXROW::Range(PGLOBAL g, int limit, bool incl)
+ {
+ int n = Valp->GetIntValue();
+
+ switch (limit) {
+ case 1: n += ((incl) ? 0 : 1); break;
+ case 2: n += ((incl) ? 1 : 0); break;
+ default: n = 1;
+ } // endswitch limit
+
+ return n;
+ } // end of Range
+
+/***********************************************************************/
+/* XXROW: Fetch a physical or logical record. */
+/***********************************************************************/
+int XXROW::Fetch(PGLOBAL g)
+ {
+ if (Num_K == 0)
+ return -1; // means end of file
+
+ /*********************************************************************/
+ /* Look for a key equal to the link column of previous table, */
+ /* and return its rank whithin the index table. */
+ /*********************************************************************/
+ Cur_K = FastFind(1);
+
+ if (Cur_K >= Num_K)
+ /*******************************************************************/
+ /* Rank not whithin index table, signal record not found. */
+ /*******************************************************************/
+ return -2; // Means record not found
+
+ /*********************************************************************/
+ /* If rank is equal to stored rank, record is already there. */
+ /*********************************************************************/
+ if (Cur_K == Old_K)
+ return -3; // Means record already there
+ else
+ Old_K = Cur_K; // Store rank of newly read record
+
+ return Cur_K;
+ } // end of Fetch
+
+/***********************************************************************/
+/* FastFind: Returns the index of matching record in a join. */
+/***********************************************************************/
+int XXROW::FastFind(int nk)
+ {
+ int n = Valp->GetIntValue();
+
+ if (n < 0)
+ return (Op == OP_EQ) ? (-1) : 0;
+ else if (n > Num_K)
+ return Num_K;
+ else
+ return (Op == OP_GT) ? n : (n - 1);
+
+ } // end of FastFind
+
+/* ------------------------- KXYCOL Classes -------------------------- */
+
+/***********************************************************************/
+/* KXYCOL public constructor. */
+/***********************************************************************/
+KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
+ To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
+ {
+ Next = NULL;
+ Previous = NULL;
+ Kxp = kp;
+ Colp = NULL;
+ IsSorted = false;
+ Asc = true;
+ Keys = Nmblk;
+ Kblp = NULL;
+ Bkeys = Nmblk;
+ Blkp = NULL;
+ Valp = NULL;
+ Klen = 0;
+ Kprec = 0;
+ Type = TYPE_ERROR;
+ Prefix = false;
+ Koff = Nmblk;
+ Val_K = 0;
+ Ndf = 0;
+ Mxs = 0;
+ } // end of KXYCOL constructor
+
+/***********************************************************************/
+/* KXYCOL Init: initialize and allocate storage. */
+/* Key length kln can be smaller than column length for CHAR columns. */
+/***********************************************************************/
+bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
+ {
+ int len = colp->GetLength(), prec = colp->GetScale();
+
+ // Currently no indexing on NULL columns
+ if (colp->IsNullable()) {
+ sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
+ return true;
+ } // endif nullable
+
+ if (kln && len > kln && colp->GetResultType() == TYPE_STRING) {
+ len = kln;
+ Prefix = true;
+ } // endif kln
+
+#ifdef DEBTRACE
+ htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n",
+ this, colp->GetName(), n, colp->GetResultType(), sm);
+#endif
+
+ // Allocate the Value object used when moving items
+ Type = colp->GetResultType();
+
+ if (!(Valp = AllocateValue(g, Type, len, colp->GetScale(),
+ colp->IsUnsigned())))
+ return true;
+
+ Klen = Valp->GetClen();
+ Keys.Size = n * Klen;
+
+ if (!PlgDBalloc(g, NULL, Keys)) {
+ sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n);
+ return true; // Error
+ } // endif
+
+ // Allocate the Valblock. The last parameter is to have rows filled
+ // by blanks (if true) or keep the zero ending char (if false).
+ // Currently we set it to true to be compatible with QRY blocks,
+ // and the one before last is to enable length/type checking, set to
+ // true if not a prefix key.
+ Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true);
+ Asc = sm; // Sort mode: Asc=true Desc=false
+ Ndf = n;
+
+ // Store this information to avoid sorting when already done
+ if (Asc)
+ IsSorted = colp->GetOpt() == 2;
+
+//SetNulls(colp->IsNullable()); for when null columns will be indexable
+ return false;
+ } // end of Init
+
+#if defined(XMAP)
+/***********************************************************************/
+/* KXYCOL MapInit: initialize and address storage. */
+/* Key length kln can be smaller than column length for CHAR columns. */
+/***********************************************************************/
+BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
+ {
+ int len = colp->GetLength(), prec = colp->GetPrecision();
+
+ if (n[3] && colp->GetLength() > n[3]
+ && colp->GetResultType() == TYPE_STRING) {
+ len = n[3];
+ Prefix = true;
+ } // endif kln
+
+ Type = colp->GetResultType();
+
+#ifdef DEBTRACE
+ htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
+ this, colp, Type, n[0], len, m);
+#endif
+
+ // Allocate the Value object used when moving items
+ Valp = AllocateValue(g, Type, len, prec, false, NULL);
+ Klen = Valp->GetClen();
+
+ if (n[2]) {
+ Bkeys.Size = n[2] * Klen;
+ Bkeys.Memp = m;
+ Bkeys.Sub = true;
+
+ // Allocate the Valblk containing initial block key values
+ Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true);
+ } // endif nb
+
+ Keys.Size = n[0] * Klen;
+ Keys.Memp = m + Bkeys.Size;
+ Keys.Sub = true;
+
+ // Allocate the Valblock. Last two parameters are to have rows filled
+ // by blanks (if true) or keep the zero ending char (if false).
+ // Currently we set it to true to be compatible with QRY blocks,
+ // and last one to enable type checking (no conversion).
+ Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, true, true);
+
+ if (n[1]) {
+ Koff.Size = n[1] * sizeof(int);
+ Koff.Memp = m + Bkeys.Size + Keys.Size;
+ Koff.Sub = true;
+ } // endif n[1]
+
+ Ndf = n[0];
+ IsSorted = colp->GetOpt() < 0;
+ return m + Bkeys.Size + Keys.Size + Koff.Size;
+ } // end of MapInit
+#endif // XMAP
+
+/***********************************************************************/
+/* Allocate the offset block used by intermediate key columns. */
+/***********************************************************************/
+int *KXYCOL::MakeOffset(PGLOBAL g, int n)
+ {
+ if (!Kof) {
+ // Calculate the initial size of the offset
+ Koff.Size = (n + 1) * sizeof(int);
+
+ // Allocate the required memory
+ if (!PlgDBalloc(g, NULL, Koff)) {
+ strcpy(g->Message, MSG(KEY_ALLOC_ERR));
+ return NULL; // Error
+ } // endif
+
+ } else if (n) {
+ // This is a reallocation call
+ PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int));
+ } else
+ PlgDBfree(Koff);
+
+ return (int*)Kof;
+ } // end of MakeOffset
+
+/***********************************************************************/
+/* Make a front end array of key values that are the first value of */
+/* each blocks (of size n). This to reduce paging in FastFind. */
+/***********************************************************************/
+bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size)
+ {
+ int i, k;
+
+ // Calculate the size of the block array in the index
+ Bkeys.Size = nb * Klen;
+
+ // Allocate the required memory
+ if (!PlgDBalloc(g, NULL, Bkeys)) {
+ sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb);
+ return true; // Error
+ } // endif
+
+ // Allocate the Valblk used to contains initial block key values
+ Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec);
+
+ // Populate the array with values
+ for (i = k = 0; i < nb; i++, k += size)
+ Blkp->SetValue(Kblp, i, k);
+
+ return false;
+ } // end of MakeBlockArray
+
+/***********************************************************************/
+/* KXYCOL SetValue: read column value for nth array element. */
+/***********************************************************************/
+void KXYCOL::SetValue(PCOL colp, int i)
+ {
+#if defined(_DEBUG)
+ assert (Kblp != NULL);
+#endif
+
+ Kblp->SetValue(colp->GetValue(), i);
+ } // end of SetValue
+
+/***********************************************************************/
+/* InitFind: initialize finding the rank of column value in index. */
+/***********************************************************************/
+bool KXYCOL::InitFind(PGLOBAL g, PXOB xp)
+ {
+ if (xp->GetType() == TYPE_CONST) {
+ if (Kxp->Nth)
+ return true;
+
+ Valp->SetValue_pval(xp->GetValue(), !Prefix);
+ } else {
+ xp->Reset();
+ xp->Eval(g);
+ Valp->SetValue_pval(xp->GetValue(), false);
+// Valp->SetValue_pval(xp->GetValue(), !Prefix);
+ } // endif Type
+
+ return false;
+ } // end of InitFind
+
+/***********************************************************************/
+/* InitBinFind: initialize Value to the value pointed by vp. */
+/***********************************************************************/
+void KXYCOL::InitBinFind(void *vp)
+ {
+ Valp->SetBinValue(vp);
+ } // end of InitBinFind
+
+/***********************************************************************/
+/* KXYCOL FillValue: called by COLBLK::Eval when a column value is */
+/* already in storage in the corresponding KXYCOL. */
+/***********************************************************************/
+void KXYCOL::FillValue(PVAL valp)
+ {
+ valp->SetValue_pvblk(Kblp, Val_K);
+
+ // Set null when applicable (NIY)
+//if (valp->GetNullable())
+// valp->SetNull(valp->IsZero());
+
+ } // end of FillValue
+
+/***********************************************************************/
+/* KXYCOL: Compare routine for one numeric value. */
+/***********************************************************************/
+int KXYCOL::Compare(int i1, int i2)
+ {
+ // Do the actual comparison between values.
+ register int k = Kblp->CompVal(i1, i2);
+
+#ifdef DEBUG2
+ htrc("Compare done result=%d\n", k);
+#endif
+
+ return (Asc) ? k : -k;
+ } // end of Compare
+
+/***********************************************************************/
+/* KXYCOL: Compare the ith key to the stored Value. */
+/***********************************************************************/
+int KXYCOL::CompVal(int i)
+ {
+ // Do the actual comparison between numerical values.
+#ifdef DEBUG2
+ register int k = (int)Kblp->CompVal(Valp, (int)i);
+
+ htrc("Compare done result=%d\n", k);
+ return k;
+#endif
+ return Kblp->CompVal(Valp, i);
+ } // end of CompVal
+
+/***********************************************************************/
+/* KXYCOL: Compare the key to the stored block value. */
+/***********************************************************************/
+int KXYCOL::CompBval(int i)
+ {
+ // Do the actual comparison between key values.
+ return Blkp->CompVal(Valp, i);
+ } // end of CompBval
+
+/***********************************************************************/
+/* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */
+/***********************************************************************/
+void KXYCOL::ReAlloc(PGLOBAL g, int n)
+ {
+ PlgDBrealloc(g, NULL, Keys, n * Klen);
+ Kblp->ReAlloc(To_Keys, n);
+ Ndf = n;
+ } // end of ReAlloc
+
+/***********************************************************************/
+/* KXYCOL FreeData: Free To_Keys if it is not suballocated. */
+/***********************************************************************/
+void KXYCOL::FreeData(void)
+ {
+ PlgDBfree(Keys);
+ Kblp = NULL;
+ PlgDBfree(Bkeys);
+ Blkp = NULL;
+ PlgDBfree(Koff);
+ Ndf = 0;
+ } // end of FreeData
diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h
index b7e597b9e6d..ac886673b68 100644
--- a/storage/connect/xindex.h
+++ b/storage/connect/xindex.h
@@ -98,12 +98,6 @@ class DllExport INDEXDEF : public BLOCK { /* Index description block */
void SetNParts(uint np) {Nparts = (signed)np;}
void SetMaxSame(int mxs) {MaxSame = mxs;}
void SetMxsame(PXINDEX x);
-//int GetOffset(void) {return Offset;}
-//void SetOffset(int off) {Offset = off;}
-//int GetOffhigh(void) {return Offhigh;}
-//void SetOffhigh(int hof) {Offhigh = hof;}
-//int GetSize(void) {return Size;}
-//void SetSize(int size) {Size = size;}
int GetMaxSame(void) {return MaxSame;}
bool Define(PGLOBAL g, void *memp, PTABDEF dfp, LPCSTR p);
PIXDEF GetIndexOf(PCOL colp, bool hd = false);
@@ -123,9 +117,6 @@ class DllExport INDEXDEF : public BLOCK { /* Index description block */
bool AutoInc; /* true if unique key in auto increment */
int Nparts; /* Number of key parts */
int ID; /* Index ID number */
-//int Offset; /* Offset in index file */
-//int Offhigh; /* Offset high in big index file */
-//int Size; /* Size of index file */
int MaxSame; /* Max number of same values */
}; // end of INDEXDEF
@@ -253,9 +244,6 @@ class DllExport XINDEX : public XXBASE {
virtual int GetCurPos(void) {return (Pex) ? Pex[Cur_K] : Cur_K;}
virtual void SetNval(int n) {Nval = n;}
int GetMaxSame(void) {return MaxSame;}
-// int GetDefoff(void) {return Defoff;}
-// int GetDefhigh(void) {return Defhigh;}
-// int GetSize(void) {return Size;}
// Methods
virtual void Reset(void);
@@ -288,9 +276,6 @@ class DllExport XINDEX : public XXBASE {
int Nk; // The number of indexed columns
int Nval; // The number of used columns
int Incr; // Increment of record position
-//int Defoff; // Offset of definition in index file
-//int Defhigh; // High order of offset big value
-//int Size; // Size of definition in index file
int MaxSame; // Max number of same values
}; // end of class XINDEX
diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp
index 4050fd520fa..cdc2ef9bf62 100644
--- a/storage/connect/xobject.cpp
+++ b/storage/connect/xobject.cpp
@@ -1,188 +1,186 @@
-/************ Xobject C++ Functions Source Code File (.CPP) ************/
-/* Name: XOBJECT.CPP Version 2.2 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */
-/* */
-/* This file contains base XOBJECT class functions. */
-/* Also here is the implementation of the CONSTANT class. */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include mariaDB header file. */
-/***********************************************************************/
-#include "my_global.h"
-
-/***********************************************************************/
-/* Include required application header files */
-/* global.h is header containing all global Plug declarations. */
-/* plgdbsem.h is header containing the DB applic. declarations. */
-/***********************************************************************/
-#include "global.h"
-#include "plgdbsem.h"
-#include "xobject.h"
-
-/***********************************************************************/
-/* Macro definitions. */
-/***********************************************************************/
-#if defined(_DEBUG) || defined(DEBTRACE)
-#define ASSERT(B) assert(B);
-#else
-#define ASSERT(B)
-#endif
-
-/***********************************************************************/
-/* The one and only needed void object. */
-/***********************************************************************/
-XVOID Xvoid;
-PXOB const pXVOID = &Xvoid; // Pointer used by other classes
-
-/* ------------------------- Class XOBJECT --------------------------- */
-
-/***********************************************************************/
-/* GetCharValue: returns the Result value as a char string. */
-/* Using GetCharValue provides no conversion from numeric types. */
-/***********************************************************************/
-PSZ XOBJECT::GetCharValue(void)
- {
- ASSERT(Value)
- return Value->GetCharValue();
- } // end of GetCharValue()
-
-/***********************************************************************/
-/* GetShortValue: returns the Result value as a short integer. */
-/***********************************************************************/
-short XOBJECT::GetShortValue(void)
- {
- ASSERT(Value)
- return Value->GetShortValue();
- } // end of GetShortValue
-
-/***********************************************************************/
-/* GetIntValue: returns the Result value as a int integer. */
-/***********************************************************************/
-int XOBJECT::GetIntValue(void)
- {
- ASSERT(Value)
- return Value->GetIntValue();
- } // end of GetIntValue
-
-/***********************************************************************/
-/* GetFloatValue: returns the Result value as a double float. */
-/***********************************************************************/
-double XOBJECT::GetFloatValue(void)
- {
- ASSERT(Value)
- return Value->GetFloatValue();
- } // end of GetFloatValue
-
-/* ------------------------- Class CONSTANT -------------------------- */
-
-/***********************************************************************/
-/* CONSTANT public constructor. */
-/***********************************************************************/
-CONSTANT::CONSTANT(PGLOBAL g, void *value, short type)
- {
- if (!(Value = AllocateValue(g, value, (int)type)))
- longjmp(g->jumper[g->jump_level], TYPE_CONST);
-
- Constant = true;
- } // end of CONSTANT constructor
-
-/***********************************************************************/
-/* CONSTANT public constructor. */
-/***********************************************************************/
-CONSTANT::CONSTANT(PGLOBAL g, int n)
- {
- if (!(Value = AllocateValue(g, &n, TYPE_INT)))
- longjmp(g->jumper[g->jump_level], TYPE_CONST);
-
- Constant = true;
- } // end of CONSTANT constructor
-
-/***********************************************************************/
-/* GetLengthEx: returns an evaluation of the constant string length. */
-/* Note: When converting from token to string, length has to be */
-/* specified but we need the domain length, not the value length. */
-/***********************************************************************/
-int CONSTANT::GetLengthEx(void)
- {
- return Value->GetValLen();
- } // end of GetLengthEx
-
-#if defined(BLK_INDX)
-/***********************************************************************/
-/* Convert a constant to the given type. */
-/***********************************************************************/
-void CONSTANT::Convert(PGLOBAL g, int newtype)
- {
- if (Value->GetType() != newtype)
- if (!(Value = AllocateValue(g, Value, newtype)))
- longjmp(g->jumper[g->jump_level], TYPE_CONST);
-
- } // end of Convert
-#endif // BLK_INDX
-
-/***********************************************************************/
-/* Compare: returns true if this object is equivalent to xp. */
-/***********************************************************************/
-bool CONSTANT::Compare(PXOB xp)
- {
- if (this == xp)
- return true;
- else if (xp->GetType() != TYPE_CONST)
- return false;
- else
- return Value->IsEqual(xp->GetValue(), true);
-
- } // end of Compare
-
-#if 0
-/***********************************************************************/
-/* Rephrase: temporary implementation used by PlugRephraseSQL. */
-/***********************************************************************/
-bool CONSTANT::Rephrase(PGLOBAL g, PSZ work)
- {
- switch (Value->GetType()) {
- case TYPE_STRING:
- sprintf(work + strlen(work), "'%s'", Value->GetCharValue());
- break;
- case TYPE_SHORT:
- sprintf(work + strlen(work), "%hd", Value->GetShortValue());
- break;
- case TYPE_INT:
- case TYPE_DATE:
- sprintf(work + strlen(work), "%d", Value->GetIntValue());
- break;
- case TYPE_DOUBLE:
- sprintf(work + strlen(work), "%lf", Value->GetFloatValue());
- break;
- case TYPE_BIGINT:
- sprintf(work + strlen(work), "%lld", Value->GetBigintValue());
- break;
- case TYPE_TINY:
- sprintf(work + strlen(work), "%d", Value->GetTinyValue());
- break;
- default:
- sprintf(g->Message, MSG(BAD_CONST_TYPE), Value->GetType());
- return false;
- } // endswitch
-
- return false;
- } // end of Rephrase
-#endif // 0
-
-/***********************************************************************/
-/* Make file output of a constant object. */
-/***********************************************************************/
-void CONSTANT::Print(PGLOBAL g, FILE *f, uint n)
- {
- Value->Print(g, f, n);
- } /* end of Print */
-
-/***********************************************************************/
-/* Make string output of a constant object. */
-/***********************************************************************/
-void CONSTANT::Print(PGLOBAL g, char *ps, uint z)
- {
- Value->Print(g, ps, z);
- } /* end of Print */
+/************ Xobject C++ Functions Source Code File (.CPP) ************/
+/* Name: XOBJECT.CPP Version 2.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* */
+/* This file contains base XOBJECT class functions. */
+/* Also here is the implementation of the CONSTANT class. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include mariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xobject.h"
+
+/***********************************************************************/
+/* Macro definitions. */
+/***********************************************************************/
+#if defined(_DEBUG) || defined(DEBTRACE)
+#define ASSERT(B) assert(B);
+#else
+#define ASSERT(B)
+#endif
+
+/***********************************************************************/
+/* The one and only needed void object. */
+/***********************************************************************/
+XVOID Xvoid;
+PXOB const pXVOID = &Xvoid; // Pointer used by other classes
+
+/* ------------------------- Class XOBJECT --------------------------- */
+
+/***********************************************************************/
+/* GetCharValue: returns the Result value as a char string. */
+/* Using GetCharValue provides no conversion from numeric types. */
+/***********************************************************************/
+PSZ XOBJECT::GetCharValue(void)
+ {
+ ASSERT(Value)
+ return Value->GetCharValue();
+ } // end of GetCharValue()
+
+/***********************************************************************/
+/* GetShortValue: returns the Result value as a short integer. */
+/***********************************************************************/
+short XOBJECT::GetShortValue(void)
+ {
+ ASSERT(Value)
+ return Value->GetShortValue();
+ } // end of GetShortValue
+
+/***********************************************************************/
+/* GetIntValue: returns the Result value as a int integer. */
+/***********************************************************************/
+int XOBJECT::GetIntValue(void)
+ {
+ ASSERT(Value)
+ return Value->GetIntValue();
+ } // end of GetIntValue
+
+/***********************************************************************/
+/* GetFloatValue: returns the Result value as a double float. */
+/***********************************************************************/
+double XOBJECT::GetFloatValue(void)
+ {
+ ASSERT(Value)
+ return Value->GetFloatValue();
+ } // end of GetFloatValue
+
+/* ------------------------- Class CONSTANT -------------------------- */
+
+/***********************************************************************/
+/* CONSTANT public constructor. */
+/***********************************************************************/
+CONSTANT::CONSTANT(PGLOBAL g, void *value, short type)
+ {
+ if (!(Value = AllocateValue(g, value, (int)type)))
+ longjmp(g->jumper[g->jump_level], TYPE_CONST);
+
+ Constant = true;
+ } // end of CONSTANT constructor
+
+/***********************************************************************/
+/* CONSTANT public constructor. */
+/***********************************************************************/
+CONSTANT::CONSTANT(PGLOBAL g, int n)
+ {
+ if (!(Value = AllocateValue(g, &n, TYPE_INT)))
+ longjmp(g->jumper[g->jump_level], TYPE_CONST);
+
+ Constant = true;
+ } // end of CONSTANT constructor
+
+/***********************************************************************/
+/* GetLengthEx: returns an evaluation of the constant string length. */
+/* Note: When converting from token to string, length has to be */
+/* specified but we need the domain length, not the value length. */
+/***********************************************************************/
+int CONSTANT::GetLengthEx(void)
+ {
+ return Value->GetValLen();
+ } // end of GetLengthEx
+
+/***********************************************************************/
+/* Convert a constant to the given type. */
+/***********************************************************************/
+void CONSTANT::Convert(PGLOBAL g, int newtype)
+ {
+ if (Value->GetType() != newtype)
+ if (!(Value = AllocateValue(g, Value, newtype)))
+ longjmp(g->jumper[g->jump_level], TYPE_CONST);
+
+ } // end of Convert
+
+/***********************************************************************/
+/* Compare: returns true if this object is equivalent to xp. */
+/***********************************************************************/
+bool CONSTANT::Compare(PXOB xp)
+ {
+ if (this == xp)
+ return true;
+ else if (xp->GetType() != TYPE_CONST)
+ return false;
+ else
+ return Value->IsEqual(xp->GetValue(), true);
+
+ } // end of Compare
+
+#if 0
+/***********************************************************************/
+/* Rephrase: temporary implementation used by PlugRephraseSQL. */
+/***********************************************************************/
+bool CONSTANT::Rephrase(PGLOBAL g, PSZ work)
+ {
+ switch (Value->GetType()) {
+ case TYPE_STRING:
+ sprintf(work + strlen(work), "'%s'", Value->GetCharValue());
+ break;
+ case TYPE_SHORT:
+ sprintf(work + strlen(work), "%hd", Value->GetShortValue());
+ break;
+ case TYPE_INT:
+ case TYPE_DATE:
+ sprintf(work + strlen(work), "%d", Value->GetIntValue());
+ break;
+ case TYPE_DOUBLE:
+ sprintf(work + strlen(work), "%lf", Value->GetFloatValue());
+ break;
+ case TYPE_BIGINT:
+ sprintf(work + strlen(work), "%lld", Value->GetBigintValue());
+ break;
+ case TYPE_TINY:
+ sprintf(work + strlen(work), "%d", Value->GetTinyValue());
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_CONST_TYPE), Value->GetType());
+ return false;
+ } // endswitch
+
+ return false;
+ } // end of Rephrase
+#endif // 0
+
+/***********************************************************************/
+/* Make file output of a constant object. */
+/***********************************************************************/
+void CONSTANT::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ Value->Print(g, f, n);
+ } /* end of Print */
+
+/***********************************************************************/
+/* Make string output of a constant object. */
+/***********************************************************************/
+void CONSTANT::Print(PGLOBAL g, char *ps, uint z)
+ {
+ Value->Print(g, ps, z);
+ } /* end of Print */
diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h
index 793a08ddb9e..1621b4e82ff 100644
--- a/storage/connect/xobject.h
+++ b/storage/connect/xobject.h
@@ -1,137 +1,119 @@
-/*************** Xobject H Declares Source Code File (.H) **************/
-/* Name: XOBJECT.H Version 2.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */
-/* */
-/* This file contains the XOBJECT and derived classes declares. */
-/***********************************************************************/
-
-#ifndef __XOBJECT__H
-#define __XOBJECT__H
-
-/***********************************************************************/
-/* Include required application header files */
-/* block.h is header containing Block global declarations. */
-/***********************************************************************/
-#include "block.h"
-#include "valblk.h" // includes value.h
-
-/***********************************************************************/
-/* Types used in some class definitions. */
-/***********************************************************************/
-//typedef struct _tabdesc *PTABD; // For friend setting
-
-/***********************************************************************/
-/* The pointer to the one and only needed void object. */
-/***********************************************************************/
-extern PXOB const pXVOID;
-
-/***********************************************************************/
-/* Class XOBJECT is the base class for all classes that can be used */
-/* in evaluation operations: FILTER, EXPRESSION, SCALF, FNC, COLBLK, */
-/* SELECT, FILTER as well as all the constant object types. */
-/***********************************************************************/
-class DllExport XOBJECT : public BLOCK {
- public:
- XOBJECT(void) {Value = NULL; Constant = false;}
-
- // Implementation
- PVAL GetValue(void) {return Value;}
- bool IsConstant(void) {return Constant;}
- virtual int GetType(void) {return TYPE_XOBJECT;}
- virtual int GetResultType(void) {return TYPE_VOID;}
- virtual int GetKey(void) {return 0;}
-#if defined(_DEBUG)
- virtual void SetKey(int k) {assert(false);}
-#else // !_DEBUG
- virtual void SetKey(int k) {} // Only defined for COLBLK
-#endif // !_DEBUG
- virtual int GetLength(void) = 0;
- virtual int GetLengthEx(void) = 0;
- virtual PSZ GetCharValue(void);
- virtual short GetShortValue(void);
- virtual int GetIntValue(void);
- virtual double GetFloatValue(void);
- virtual int GetScale(void) = 0;
-
- // Methods
- virtual void Reset(void) {}
- virtual bool Compare(PXOB) = 0;
- virtual bool Init(PGLOBAL) {return false;}
- virtual bool Eval(PGLOBAL) {return false;}
- virtual bool SetFormat(PGLOBAL, FORMAT&) = 0;
- virtual int CheckColumn(PGLOBAL, PSQL, PXOB &, int &) {return 0;}
- virtual int RefNum(PSQL) {return 0;}
- virtual void AddTdb(PSQL, PTDB *, int&) {}
- virtual PXOB SetSelect(PGLOBAL, PSQL, bool) {return this;}
- virtual PXOB CheckSubQuery(PGLOBAL, PSQL) {return this;}
- virtual bool CheckLocal(PTDB) {return true;}
- virtual int CheckSpcCol(PTDB, int) {return 2;}
- virtual bool CheckSort(PTDB) {return false;}
- virtual bool VerifyColumn(PTDB txp) {return false;}
- virtual bool VerifyTdb(PTDB& tdbp) {return false;}
- virtual bool IsColInside(PCOL colp) {return false;}
-
- protected:
- PVAL Value; // The current value of the object.
- bool Constant; // true for an object having a constant value.
- }; // end of class XOBJECT
-
-/***********************************************************************/
-/* Class XVOID: represent a void (null) object. */
-/* Used to represent a void parameter for count(*) or for a filter. */
-/***********************************************************************/
-class DllExport XVOID : public XOBJECT {
- public:
- XVOID(void) {Constant = true;}
-
- // Implementation
- virtual int GetType(void) {return TYPE_VOID;}
- virtual int GetLength(void) {return 0;}
- virtual int GetLengthEx(void) {return 0;}
- virtual PSZ GetCharValue(void) {return NULL;}
- virtual int GetIntValue(void) {return 0;}
- virtual double GetFloatValue(void) {return 0.0;}
- virtual int GetScale() {return 0;}
-
- // Methods
- virtual bool Compare(PXOB xp) {return xp->GetType() == TYPE_VOID;}
- virtual bool SetFormat(PGLOBAL, FORMAT&) {return true;}
- virtual int CheckSpcCol(PTDB, int) {return 0;}
- }; // end of class XVOID
-
-
-/***********************************************************************/
-/* Class CONSTANT: represents a constant XOBJECT of any value type. */
-/* Note that the CONSTANT class is a friend of the VALUE class; */
-/***********************************************************************/
-class DllExport CONSTANT : public XOBJECT {
- public:
- CONSTANT(PGLOBAL g, void *value, short type);
- CONSTANT(PGLOBAL g, int n);
- CONSTANT(PVAL valp) {Value = valp; Constant = true;}
-
- // Implementation
- virtual int GetType(void) {return TYPE_CONST;}
- virtual int GetResultType(void) {return Value->Type;}
- virtual int GetLength(void) {return Value->GetValLen();}
- virtual int GetScale() {return Value->GetValPrec();}
- virtual int GetLengthEx(void);
-
- // Methods
- virtual bool Compare(PXOB xp);
- virtual bool SetFormat(PGLOBAL g, FORMAT& fmt)
- {return Value->SetConstFormat(g, fmt);}
- virtual int CheckSpcCol(PTDB, int) {return 1;}
-#if defined(BLK_INDX)
- void Convert(PGLOBAL g, int newtype);
-#endif // BLK_INDX
-// bool Rephrase(PGLOBAL g, PSZ work);
- void SetValue(PVAL vp) {Value = vp;}
- virtual bool VerifyColumn(PTDB txp) {return true;}
- virtual bool VerifyTdb(PTDB& tdbp) {return true;}
- virtual void Print(PGLOBAL g, FILE *, uint);
- virtual void Print(PGLOBAL g, char *, uint);
- }; // end of class CONSTANT
-
-#endif
+/*************** Xobject H Declares Source Code File (.H) **************/
+/* Name: XOBJECT.H Version 2.4 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* */
+/* This file contains the XOBJECT and derived classes declares. */
+/***********************************************************************/
+
+#ifndef __XOBJECT__H
+#define __XOBJECT__H
+
+/***********************************************************************/
+/* Include required application header files */
+/* block.h is header containing Block global declarations. */
+/***********************************************************************/
+#include "block.h"
+#include "valblk.h" // includes value.h
+
+/***********************************************************************/
+/* Types used in some class definitions. */
+/***********************************************************************/
+//typedef struct _tabdesc *PTABD; // For friend setting
+
+/***********************************************************************/
+/* The pointer to the one and only needed void object. */
+/***********************************************************************/
+extern PXOB const pXVOID;
+
+/***********************************************************************/
+/* Class XOBJECT is the base class for all classes that can be used */
+/* in evaluation operations: FILTER, EXPRESSION, SCALF, FNC, COLBLK, */
+/* SELECT, FILTER as well as all the constant object types. */
+/***********************************************************************/
+class DllExport XOBJECT : public BLOCK {
+ public:
+ XOBJECT(void) {Value = NULL; Constant = false;}
+
+ // Implementation
+ PVAL GetValue(void) {return Value;}
+ bool IsConstant(void) {return Constant;}
+ virtual int GetType(void) {return TYPE_XOBJECT;}
+ virtual int GetResultType(void) {return TYPE_VOID;}
+ virtual int GetKey(void) {return 0;}
+#if defined(_DEBUG)
+ virtual void SetKey(int k) {assert(false);}
+#else // !_DEBUG
+ virtual void SetKey(int k) {} // Only defined for COLBLK
+#endif // !_DEBUG
+ virtual int GetLength(void) = 0;
+ virtual int GetLengthEx(void) = 0;
+ virtual PSZ GetCharValue(void);
+ virtual short GetShortValue(void);
+ virtual int GetIntValue(void);
+ virtual double GetFloatValue(void);
+ virtual int GetScale(void) = 0;
+
+ // Methods
+ virtual void Reset(void) {}
+ virtual bool Compare(PXOB) = 0;
+ virtual bool Init(PGLOBAL) {return false;}
+ virtual bool Eval(PGLOBAL) {return false;}
+ virtual bool SetFormat(PGLOBAL, FORMAT&) = 0;
+
+ protected:
+ PVAL Value; // The current value of the object.
+ bool Constant; // true for an object having a constant value.
+ }; // end of class XOBJECT
+
+/***********************************************************************/
+/* Class XVOID: represent a void (null) object. */
+/* Used to represent a void parameter for count(*) or for a filter. */
+/***********************************************************************/
+class DllExport XVOID : public XOBJECT {
+ public:
+ XVOID(void) {Constant = true;}
+
+ // Implementation
+ virtual int GetType(void) {return TYPE_VOID;}
+ virtual int GetLength(void) {return 0;}
+ virtual int GetLengthEx(void) {return 0;}
+ virtual PSZ GetCharValue(void) {return NULL;}
+ virtual int GetIntValue(void) {return 0;}
+ virtual double GetFloatValue(void) {return 0.0;}
+ virtual int GetScale() {return 0;}
+
+ // Methods
+ virtual bool Compare(PXOB xp) {return xp->GetType() == TYPE_VOID;}
+ virtual bool SetFormat(PGLOBAL, FORMAT&) {return true;}
+ }; // end of class XVOID
+
+
+/***********************************************************************/
+/* Class CONSTANT: represents a constant XOBJECT of any value type. */
+/* Note that the CONSTANT class is a friend of the VALUE class; */
+/***********************************************************************/
+class DllExport CONSTANT : public XOBJECT {
+ public:
+ CONSTANT(PGLOBAL g, void *value, short type);
+ CONSTANT(PGLOBAL g, int n);
+ CONSTANT(PVAL valp) {Value = valp; Constant = true;}
+
+ // Implementation
+ virtual int GetType(void) {return TYPE_CONST;}
+ virtual int GetResultType(void) {return Value->Type;}
+ virtual int GetLength(void) {return Value->GetValLen();}
+ virtual int GetScale() {return Value->GetValPrec();}
+ virtual int GetLengthEx(void);
+
+ // Methods
+ virtual bool Compare(PXOB xp);
+ virtual bool SetFormat(PGLOBAL g, FORMAT& fmt)
+ {return Value->SetConstFormat(g, fmt);}
+ void Convert(PGLOBAL g, int newtype);
+ void SetValue(PVAL vp) {Value = vp;}
+ virtual void Print(PGLOBAL g, FILE *, uint);
+ virtual void Print(PGLOBAL g, char *, uint);
+ }; // end of class CONSTANT
+
+#endif
diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h
index 79b343e8a6d..443f1e51a5b 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -1,267 +1,259 @@
-/**************** Table H Declares Source Code File (.H) ***************/
-/* Name: TABLE.H Version 2.3 */
-/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
-/* */
-/* This file contains the TBX, OPJOIN and TDB class definitions. */
-/***********************************************************************/
-#if !defined(TABLE_DEFINED)
-#define TABLE_DEFINED
-
-
-/***********************************************************************/
-/* Include required application header files */
-/* block.h is header containing Block global declarations. */
-/***********************************************************************/
-#include "assert.h"
-#include "block.h"
-#include "colblk.h"
-#include "m_ctype.h"
-
-typedef class CMD *PCMD;
-
-// Commands executed by XDBC and MYX tables
-class CMD : public BLOCK {
- public:
- // Constructor
- CMD(PGLOBAL g, char *cmd) {
- Cmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
- strcpy(Cmd, cmd); Next = NULL; }
-
- // Members
- PCMD Next;
- char *Cmd;
-}; // end of class CMD
-
-// Condition filter structure
-typedef struct _cond_filter {
- char *Body;
- OPVAL Op;
- PCMD Cmds;
-} CONDFIL, *PCFIL;
-
-typedef class TDBCAT *PTDBCAT;
-typedef class CATCOL *PCATCOL;
-
-/***********************************************************************/
-/* Definition of class TDB with all its method functions. */
-/***********************************************************************/
-class DllExport TDB: public BLOCK { // Table Descriptor Block.
- public:
- // Constructors
- TDB(PTABDEF tdp = NULL);
- TDB(PTDB tdbp);
-
- // Implementation
- static void SetTnum(int n) {Tnum = n;}
- inline PTDB GetOrig(void) {return To_Orig;}
- inline TUSE GetUse(void) {return Use;}
- inline PCFIL GetCondFil(void) {return To_CondFil;}
- inline LPCSTR GetName(void) {return Name;}
- inline PTABLE GetTable(void) {return To_Table;}
- inline PCOL GetColumns(void) {return Columns;}
- inline int GetDegree(void) {return Degree;}
- inline MODE GetMode(void) {return Mode;}
-#if defined(BLK_INDX)
- inline PFIL GetFilter(void) {return To_Filter;}
- inline void SetFilter(PFIL fp) {To_Filter = fp;}
-#endif // BLK_INDX
- inline void SetOrig(PTDB txp) {To_Orig = txp;}
- inline void SetUse(TUSE n) {Use = n;}
- inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;}
- inline void SetNext(PTDB tdbp) {Next = tdbp;}
- inline void SetName(LPCSTR name) {Name = name;}
- inline void SetTable(PTABLE tablep) {To_Table = tablep;}
- inline void SetColumns(PCOL colp) {Columns = colp;}
- inline void SetDegree(int degree) {Degree = degree;}
- inline void SetMode(MODE mode) {Mode = mode;}
-
- //Properties
- virtual AMT GetAmType(void) {return TYPE_AM_ERROR;}
- virtual int GetTdb_No(void) {return Tdb_No;}
- virtual PTDB GetNext(void) {return Next;}
- virtual PCATLG GetCat(void) {return NULL;}
-
- // Methods
- virtual bool IsSame(PTDB tp) {return tp == this;}
- virtual bool GetBlockValues(PGLOBAL g) {return false;}
- virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
- virtual int GetMaxSize(PGLOBAL) = 0;
- virtual int GetProgMax(PGLOBAL) = 0;
- virtual int GetProgCur(void) = 0;
- virtual int RowNumber(PGLOBAL g, bool b = false);
- virtual bool IsReadOnly(void) {return true;}
- virtual const CHARSET_INFO *data_charset() {return NULL;}
- virtual PTDB Duplicate(PGLOBAL g) {return NULL;}
- virtual PTDB CopyOne(PTABS t) {return this;}
- virtual PTDB Copy(PTABS t);
- virtual void PrintAM(FILE *f, char *m)
- {fprintf(f, "%s AM(%d)\n", m, GetAmType());}
- virtual void Print(PGLOBAL g, FILE *f, uint n);
- virtual void Print(PGLOBAL g, char *ps, uint z);
- virtual PSZ GetServer(void) = 0;
- virtual int GetBadLines(void) {return 0;}
-
- // Database pure virtual routines
- virtual PCOL ColDB(PGLOBAL g, PSZ name, int num) = 0;
- virtual void MarkDB(PGLOBAL, PTDB) = 0;
- virtual bool OpenDB(PGLOBAL) = 0;
- virtual int ReadDB(PGLOBAL) = 0;
- virtual int WriteDB(PGLOBAL) = 0;
- virtual int DeleteDB(PGLOBAL, int) = 0;
- virtual void CloseDB(PGLOBAL) = 0;
- virtual int CheckWrite(PGLOBAL g) {return 0;}
-
- // Database routines
- bool OpenTable(PGLOBAL g, PSQL sqlp, MODE mode);
- void CloseTable(PGLOBAL g);
-
- protected:
- // Members
- PTDB To_Orig; // Pointer to original if it is a copy
- TUSE Use;
-#if defined(BLK_INDX)
- PFIL To_Filter;
-#endif // BLK_INDX
- PCFIL To_CondFil; // To condition filter structure
- static int Tnum; // Used to generate Tdb_no's
- const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN
- PTDB Next; // Next in linearized queries
- PTABLE To_Table; // Points to the XTAB object
- LPCSTR Name; // Table name
- PCOL Columns; // Points to the first column of the table
- MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete
- int Degree; // Number of columns
- }; // end of class TDB
-
-/***********************************************************************/
-/* This is the base class for all query tables (except decode). */
-/***********************************************************************/
-class DllExport TDBASE : public TDB {
- friend class INDEXDEF;
- friend class XINDEX;
- friend class XINDXS;
- public:
- // Constructor
- TDBASE(PTABDEF tdp = NULL);
- TDBASE(PTDBASE tdbp);
-
- // Implementation
- inline int GetKnum(void) {return Knum;}
- inline PTABDEF GetDef(void) {return To_Def;}
- inline PKXBASE GetKindex(void) {return To_Kindex;}
- inline PCOL GetSetCols(void) {return To_SetCols;}
- inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
-
- // Properties
- void SetKindex(PKXBASE kxp);
- PCOL Key(int i) {return (To_Key_Col) ? To_Key_Col[i] : NULL;}
-
- // Methods
- virtual bool IsUsingTemp(PGLOBAL g) {return false;}
- virtual PCATLG GetCat(void);
- virtual PSZ GetPath(void);
- virtual void PrintAM(FILE *f, char *m);
- virtual RECFM GetFtype(void) {return RECFM_NAF;}
- virtual int GetAffectedRows(void) {return -1;}
- virtual int GetRecpos(void) = 0;
- virtual bool SetRecpos(PGLOBAL g, int recpos);
- virtual bool IsReadOnly(void) {return Read_Only;}
- virtual bool IsView(void) {return FALSE;}
- virtual CHARSET_INFO *data_charset(void);
- virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);}
- virtual int GetProgCur(void) {return GetRecpos();}
- virtual PSZ GetFile(PGLOBAL g) {return "Not a file";}
- virtual int GetRemote(void) {return 0;}
- virtual void SetFile(PGLOBAL g, PSZ fn) {}
- virtual void ResetDB(void) {}
- virtual void ResetSize(void) {MaxSize = -1;}
- virtual void RestoreNrec(void) {}
- virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
- virtual PSZ GetServer(void) {return "Current";}
-
- // Database routines
- virtual PCOL ColDB(PGLOBAL g, PSZ name, int num);
- virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int)
- {assert(false); return NULL;}
- virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
- virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
- virtual void MarkDB(PGLOBAL g, PTDB tdb2);
-
- protected:
- // Members
- PTABDEF To_Def; // Points to catalog description block
- PXOB *To_Link; // Points to column of previous relations
- PCOL *To_Key_Col; // Points to key columns in current file
- PKXBASE To_Kindex; // Points to table key index
- PCOL To_SetCols; // Points to updated columns
- int MaxSize; // Max size in number of lines
- int Knum; // Size of key arrays
- bool Read_Only; // True for read only tables
- const CHARSET_INFO *m_data_charset;
- }; // end of class TDBASE
-
-/***********************************************************************/
-/* The abstract base class declaration for the catalog tables. */
-/***********************************************************************/
-class DllExport TDBCAT : public TDBASE {
- friend class CATCOL;
- public:
- // Constructor
- TDBCAT(PTABDEF tdp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_CAT;}
-
- // Methods
- virtual int GetRecpos(void) {return N;}
- virtual int GetProgCur(void) {return N;}
- virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
- virtual bool SetRecpos(PGLOBAL g, int recpos);
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
-
- protected:
- // Specific routines
- virtual PQRYRES GetResult(PGLOBAL g) = 0;
- bool Initialize(PGLOBAL g);
- bool InitCol(PGLOBAL g);
-
- // Members
- PQRYRES Qrp;
- int N; // Row number
- bool Init;
- }; // end of class TDBCAT
-
-/***********************************************************************/
-/* Class CATCOL: ODBC info column. */
-/***********************************************************************/
-class DllExport CATCOL : public COLBLK {
- friend class TDBCAT;
- public:
- // Constructors
- CATCOL(PCOLDEF cdp, PTDB tdbp, int n);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_ODBC;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- CATCOL(void) {} // Default constructor not to be used
-
- // Members
- PTDBCAT Tdbp; // Points to ODBC table block
- PCOLRES Crp; // The column data array
- int Flag;
- }; // end of class CATCOL
-
-#endif // TABLE_DEFINED
+/**************** Table H Declares Source Code File (.H) ***************/
+/* Name: TABLE.H Version 2.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
+/* */
+/* This file contains the TBX, OPJOIN and TDB class definitions. */
+/***********************************************************************/
+#if !defined(TABLE_DEFINED)
+#define TABLE_DEFINED
+
+
+/***********************************************************************/
+/* Include required application header files */
+/* block.h is header containing Block global declarations. */
+/***********************************************************************/
+#include "assert.h"
+#include "block.h"
+#include "colblk.h"
+#include "m_ctype.h"
+
+typedef class CMD *PCMD;
+
+// Commands executed by XDBC and MYX tables
+class CMD : public BLOCK {
+ public:
+ // Constructor
+ CMD(PGLOBAL g, char *cmd) {
+ Cmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
+ strcpy(Cmd, cmd); Next = NULL; }
+
+ // Members
+ PCMD Next;
+ char *Cmd;
+}; // end of class CMD
+
+// Condition filter structure
+typedef struct _cond_filter {
+ char *Body;
+ OPVAL Op;
+ PCMD Cmds;
+} CONDFIL, *PCFIL;
+
+typedef class TDBCAT *PTDBCAT;
+typedef class CATCOL *PCATCOL;
+
+/***********************************************************************/
+/* Definition of class TDB with all its method functions. */
+/***********************************************************************/
+class DllExport TDB: public BLOCK { // Table Descriptor Block.
+ public:
+ // Constructors
+ TDB(PTABDEF tdp = NULL);
+ TDB(PTDB tdbp);
+
+ // Implementation
+ static void SetTnum(int n) {Tnum = n;}
+ inline PTDB GetOrig(void) {return To_Orig;}
+ inline TUSE GetUse(void) {return Use;}
+ inline PCFIL GetCondFil(void) {return To_CondFil;}
+ inline LPCSTR GetName(void) {return Name;}
+ inline PTABLE GetTable(void) {return To_Table;}
+ inline PCOL GetColumns(void) {return Columns;}
+ inline int GetDegree(void) {return Degree;}
+ inline MODE GetMode(void) {return Mode;}
+ inline PFIL GetFilter(void) {return To_Filter;}
+ inline void SetFilter(PFIL fp) {To_Filter = fp;}
+ inline void SetOrig(PTDB txp) {To_Orig = txp;}
+ inline void SetUse(TUSE n) {Use = n;}
+ inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;}
+ inline void SetNext(PTDB tdbp) {Next = tdbp;}
+ inline void SetName(LPCSTR name) {Name = name;}
+ inline void SetTable(PTABLE tablep) {To_Table = tablep;}
+ inline void SetColumns(PCOL colp) {Columns = colp;}
+ inline void SetDegree(int degree) {Degree = degree;}
+ inline void SetMode(MODE mode) {Mode = mode;}
+
+ //Properties
+ virtual AMT GetAmType(void) {return TYPE_AM_ERROR;}
+ virtual int GetTdb_No(void) {return Tdb_No;}
+ virtual PTDB GetNext(void) {return Next;}
+ virtual PCATLG GetCat(void) {return NULL;}
+
+ // Methods
+ virtual bool IsSame(PTDB tp) {return tp == this;}
+ virtual bool GetBlockValues(PGLOBAL g) {return false;}
+ virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;}
+ virtual int GetMaxSize(PGLOBAL) = 0;
+ virtual int GetProgMax(PGLOBAL) = 0;
+ virtual int GetProgCur(void) = 0;
+ virtual int RowNumber(PGLOBAL g, bool b = false);
+ virtual bool IsReadOnly(void) {return true;}
+ virtual const CHARSET_INFO *data_charset() {return NULL;}
+ virtual PTDB Duplicate(PGLOBAL g) {return NULL;}
+ virtual PTDB CopyOne(PTABS t) {return this;}
+ virtual PTDB Copy(PTABS t);
+ virtual void PrintAM(FILE *f, char *m)
+ {fprintf(f, "%s AM(%d)\n", m, GetAmType());}
+ virtual void Print(PGLOBAL g, FILE *f, uint n);
+ virtual void Print(PGLOBAL g, char *ps, uint z);
+ virtual PSZ GetServer(void) = 0;
+ virtual int GetBadLines(void) {return 0;}
+
+ // Database pure virtual routines
+ virtual PCOL ColDB(PGLOBAL g, PSZ name, int num) = 0;
+ virtual void MarkDB(PGLOBAL, PTDB) = 0;
+ virtual bool OpenDB(PGLOBAL) = 0;
+ virtual int ReadDB(PGLOBAL) = 0;
+ virtual int WriteDB(PGLOBAL) = 0;
+ virtual int DeleteDB(PGLOBAL, int) = 0;
+ virtual void CloseDB(PGLOBAL) = 0;
+ virtual int CheckWrite(PGLOBAL g) {return 0;}
+
+ protected:
+ // Members
+ PTDB To_Orig; // Pointer to original if it is a copy
+ TUSE Use;
+ PFIL To_Filter;
+ PCFIL To_CondFil; // To condition filter structure
+ static int Tnum; // Used to generate Tdb_no's
+ const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN
+ PTDB Next; // Next in linearized queries
+ PTABLE To_Table; // Points to the XTAB object
+ LPCSTR Name; // Table name
+ PCOL Columns; // Points to the first column of the table
+ MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete
+ int Degree; // Number of columns
+ }; // end of class TDB
+
+/***********************************************************************/
+/* This is the base class for all query tables (except decode). */
+/***********************************************************************/
+class DllExport TDBASE : public TDB {
+ friend class INDEXDEF;
+ friend class XINDEX;
+ friend class XINDXS;
+ public:
+ // Constructor
+ TDBASE(PTABDEF tdp = NULL);
+ TDBASE(PTDBASE tdbp);
+
+ // Implementation
+ inline int GetKnum(void) {return Knum;}
+ inline PTABDEF GetDef(void) {return To_Def;}
+ inline PKXBASE GetKindex(void) {return To_Kindex;}
+ inline PCOL GetSetCols(void) {return To_SetCols;}
+ inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
+
+ // Properties
+ void SetKindex(PKXBASE kxp);
+ PCOL Key(int i) {return (To_Key_Col) ? To_Key_Col[i] : NULL;}
+
+ // Methods
+ virtual bool IsUsingTemp(PGLOBAL g) {return false;}
+ virtual PCATLG GetCat(void);
+ virtual PSZ GetPath(void);
+ virtual void PrintAM(FILE *f, char *m);
+ virtual RECFM GetFtype(void) {return RECFM_NAF;}
+ virtual int GetAffectedRows(void) {return -1;}
+ virtual int GetRecpos(void) = 0;
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
+ virtual bool IsReadOnly(void) {return Read_Only;}
+ virtual bool IsView(void) {return FALSE;}
+ virtual CHARSET_INFO *data_charset(void);
+ virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);}
+ virtual int GetProgCur(void) {return GetRecpos();}
+ virtual PSZ GetFile(PGLOBAL g) {return "Not a file";}
+ virtual int GetRemote(void) {return 0;}
+ virtual void SetFile(PGLOBAL g, PSZ fn) {}
+ virtual void ResetDB(void) {}
+ virtual void ResetSize(void) {MaxSize = -1;}
+ virtual void RestoreNrec(void) {}
+ virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
+ virtual PSZ GetServer(void) {return "Current";}
+
+ // Database routines
+ virtual PCOL ColDB(PGLOBAL g, PSZ name, int num);
+ virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int)
+ {assert(false); return NULL;}
+ virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
+ virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
+ virtual void MarkDB(PGLOBAL g, PTDB tdb2);
+
+ protected:
+ // Members
+ PTABDEF To_Def; // Points to catalog description block
+ PXOB *To_Link; // Points to column of previous relations
+ PCOL *To_Key_Col; // Points to key columns in current file
+ PKXBASE To_Kindex; // Points to table key index
+ PCOL To_SetCols; // Points to updated columns
+ int MaxSize; // Max size in number of lines
+ int Knum; // Size of key arrays
+ bool Read_Only; // True for read only tables
+ const CHARSET_INFO *m_data_charset;
+ }; // end of class TDBASE
+
+/***********************************************************************/
+/* The abstract base class declaration for the catalog tables. */
+/***********************************************************************/
+class DllExport TDBCAT : public TDBASE {
+ friend class CATCOL;
+ public:
+ // Constructor
+ TDBCAT(PTABDEF tdp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_CAT;}
+
+ // Methods
+ virtual int GetRecpos(void) {return N;}
+ virtual int GetProgCur(void) {return N;}
+ virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+
+ protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g) = 0;
+ bool Initialize(PGLOBAL g);
+ bool InitCol(PGLOBAL g);
+
+ // Members
+ PQRYRES Qrp;
+ int N; // Row number
+ bool Init;
+ }; // end of class TDBCAT
+
+/***********************************************************************/
+/* Class CATCOL: ODBC info column. */
+/***********************************************************************/
+class DllExport CATCOL : public COLBLK {
+ friend class TDBCAT;
+ public:
+ // Constructors
+ CATCOL(PCOLDEF cdp, PTDB tdbp, int n);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_ODBC;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ CATCOL(void) {} // Default constructor not to be used
+
+ // Members
+ PTDBCAT Tdbp; // Points to ODBC table block
+ PCOLRES Crp; // The column data array
+ int Flag;
+ }; // end of class CATCOL
+
+#endif // TABLE_DEFINED