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 -#else // !WIN32 -#if defined(UNIX) -#include -#include -#else // !UNIX -#include -#endif // !UNIX -#include -#endif // !WIN32 - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* filamtxt.h is header containing the file AM classes declarations. */ -/* 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 +#else // !WIN32 +#if defined(UNIX) +#include +#include +#else // !UNIX +#include +#endif // !UNIX +#include +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/* 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 -#include -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif // __BORLANDC__ -//#include -#else // !WIN32 -#if defined(UNIX) || defined(UNIV_LINUX) -#include -#include -//#if !defined(sun) // Sun has the ftruncate fnc. -//#define USETEMP // Force copy mode for DELETE -//#endif // !sun -#else // !UNIX -#include -#endif // !UNIX -#include -#endif // !WIN32 - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* filamtxt.h is header containing the file AM classes declarations. */ -/***********************************************************************/ -#include "global.h" -#include "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 +#include +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include +#else // !WIN32 +#if defined(UNIX) || defined(UNIV_LINUX) +#include +#include +//#if !defined(sun) // Sun has the ftruncate fnc. +//#define USETEMP // Force copy mode for DELETE +//#endif // !sun +#else // !UNIX +#include +#endif // !UNIX +#include +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "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 -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif // __BORLAND__ -//#include -#include -#else // !WIN32 F -#if defined(UNIX) -#include -#include -#include -#include -#define NO_ERROR 0 -#else // !UNIX -#include -#endif // !UNIX -#include -#endif // !WIN32 - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* tabdos.h is header containing the TABDOS class declarations. */ -/***********************************************************************/ -#include "global.h" -#include "osutil.h" // Unuseful for WIN32 -#include "plgdbsem.h" -#include "valblk.h" -#include "filamfix.h" -#include "tabdos.h" -#include "tabvct.h" -#include "maputil.h" -#include "filamvct.h" - -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - -extern int num_read, num_there; // Statistics -static int num_write; -extern "C" int trace; - -#if defined(UNIX) -// Add dummy strerror (NGC) -char *strerror(int num); -#endif // UNIX - -/***********************************************************************/ -/* Header containing block info for not split VEC tables. */ -/* Block and last values can be calculated from NumRec and Nrec. */ -/* This is better than directly storing Block and Last because it */ -/* make possible to use the same file with tables having a different */ -/* block size value (Element -> Nrec) */ -/* Note: can be in a separate file if header=1 or a true header (2) */ -/***********************************************************************/ -typedef struct _vecheader { -//int Block; /* The number of used blocks */ -//int Last; /* The number of used records in last block */ - int MaxRec; /* Max number of records (True vector format)*/ - int NumRec; /* Number of valid records in the table */ - } VECHEADER; - -/***********************************************************************/ -/* Char VCT column blocks are right filled with blanks (blank = true) */ -/* Conversion of block values allowed conditionally for insert only. */ -/***********************************************************************/ -PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, - bool check = true, bool blank = true, bool un = false); - -/* -------------------------- Class VCTFAM --------------------------- */ - -/***********************************************************************/ -/* Implementation of the VCTFAM class. */ -/***********************************************************************/ -VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp) - { - Last = tdp->GetLast(); - MaxBlk = (tdp->GetEstimate() > 0) ? - ((tdp->GetEstimate() - 1) / Nrec + 1) : 0; - NewBlock = NULL; - AddBlock = false; - Split = false; - - if ((Header = (MaxBlk) ? tdp->Header : 0)) - Block = Last = -1; - - Bsize = Nrec; - CurNum = Nrec - 1; - Colfn = NULL; - Tempat = NULL; - Clens = NULL; - Deplac = NULL; - Isnum = NULL; - Ncol = 0; - } // end of VCTFAM standard constructor - -VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp) - { - MaxBlk = txfp->MaxBlk; - NewBlock = NULL; - AddBlock = false; - Split = txfp->Split; - Header = txfp->Header; - Bsize = txfp->Bsize; - Colfn = txfp->Colfn; - Tempat = txfp->Tempat; - Clens = txfp->Clens; - Deplac = txfp->Deplac; - Isnum = txfp->Isnum; - Ncol = txfp->Ncol; - } // end of VCTFAM copy constructor - -/***********************************************************************/ -/* Reset read/write position values. */ -/***********************************************************************/ -void VCTFAM::Reset(void) - { - FIXFAM::Reset(); - NewBlock = NULL; - AddBlock = false; - CurNum = Nrec - 1; - } // end of Reset - -/***********************************************************************/ -/* Get the Headlen, Block and Last info from the file header. */ -/***********************************************************************/ -int VCTFAM::GetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - int h, k, n; - VECHEADER vh; - - if (Header < 1 || Header > 3 || !MaxBlk) { - sprintf(g->Message, "Invalid header value %d", Header); - return -1; - } else - n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header == 2) - strcat(PlugRemoveType(filename, filename), ".blk"); - - if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1 - || !_filelength(h)) { - // Consider this is a void table - Last = Nrec; - Block = 0; - - if (h != -1) - close(h); - - return n; - } else if (Header == 3) - k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END); - - if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) { - sprintf(g->Message, "Error reading header file %s", filename); - n = -1; - } else if (MaxBlk * Nrec != vh.MaxRec) { - sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", - vh.MaxRec, MaxBlk, Nrec); - n = -1; - } else { - Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; - Last = (vh.NumRec + Nrec - 1) % Nrec + 1; - } // endif s - - close(h); - return n; - } // end of GetBlockInfo - -/***********************************************************************/ -/* Get the Headlen, Block and Last info from the file header. */ -/***********************************************************************/ -bool VCTFAM::SetBlockInfo(PGLOBAL g) - { - char filename[_MAX_PATH]; - bool rc = false; - size_t n; - VECHEADER vh; - FILE *s; - - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - if (Header != 2) { - if (Stream) { - s = Stream; - - if (Header == 1) - /*k =*/ fseek(s, 0, SEEK_SET); - - } else - s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b"); - - } else { // Header == 2 - strcat(PlugRemoveType(filename, filename), ".blk"); - s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb"); - } // endif Header - - if (!s) { - sprintf(g->Message, "Error opening header file %s", filename); - return true; - } else if (Header == 3) - /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END); - - vh.MaxRec = MaxBlk * Bsize; - vh.NumRec = (Block - 1) * Nrec + Last; - - if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) { - sprintf(g->Message, "Error writing header file %s", filename); - rc = true; - } // endif fread - - if (Header == 2 || !Stream) - fclose(s); - - return rc; - } // end of SetBlockInfo - -/***********************************************************************/ -/* Use BlockTest to reduce the table estimated size. */ -/***********************************************************************/ -int VCTFAM::MaxBlkSize(PGLOBAL g, int s) - { - int rc = RC_OK, savcur = CurBlk; - int size; - - // Roughly estimate the table size as the sum of blocks - // that can contain good rows - for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) -#if 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 +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLAND__ +//#include +#include +#else // !WIN32 F +#if defined(UNIX) +#include +#include +#include +#include +#define NO_ERROR 0 +#else // !UNIX +#include +#endif // !UNIX +#include +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "osutil.h" // Unuseful for WIN32 +#include "plgdbsem.h" +#include "valblk.h" +#include "filamfix.h" +#include "tabdos.h" +#include "tabvct.h" +#include "maputil.h" +#include "filamvct.h" + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +extern int num_read, num_there; // Statistics +static int num_write; +extern "C" int trace; + +#if defined(UNIX) +// Add dummy strerror (NGC) +char *strerror(int num); +#endif // UNIX + +/***********************************************************************/ +/* Header containing block info for not split VEC tables. */ +/* Block and last values can be calculated from NumRec and Nrec. */ +/* This is better than directly storing Block and Last because it */ +/* make possible to use the same file with tables having a different */ +/* block size value (Element -> Nrec) */ +/* Note: can be in a separate file if header=1 or a true header (2) */ +/***********************************************************************/ +typedef struct _vecheader { +//int Block; /* The number of used blocks */ +//int Last; /* The number of used records in last block */ + int MaxRec; /* Max number of records (True vector format)*/ + int NumRec; /* Number of valid records in the table */ + } VECHEADER; + +/***********************************************************************/ +/* Char VCT column blocks are right filled with blanks (blank = true) */ +/* Conversion of block values allowed conditionally for insert only. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, + bool check = true, bool blank = true, bool un = false); + +/* -------------------------- Class VCTFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCTFAM class. */ +/***********************************************************************/ +VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp) + { + Last = tdp->GetLast(); + MaxBlk = (tdp->GetEstimate() > 0) ? + ((tdp->GetEstimate() - 1) / Nrec + 1) : 0; + NewBlock = NULL; + AddBlock = false; + Split = false; + + if ((Header = (MaxBlk) ? tdp->Header : 0)) + Block = Last = -1; + + Bsize = Nrec; + CurNum = Nrec - 1; + Colfn = NULL; + Tempat = NULL; + Clens = NULL; + Deplac = NULL; + Isnum = NULL; + Ncol = 0; + } // end of VCTFAM standard constructor + +VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp) + { + MaxBlk = txfp->MaxBlk; + NewBlock = NULL; + AddBlock = false; + Split = txfp->Split; + Header = txfp->Header; + Bsize = txfp->Bsize; + Colfn = txfp->Colfn; + Tempat = txfp->Tempat; + Clens = txfp->Clens; + Deplac = txfp->Deplac; + Isnum = txfp->Isnum; + Ncol = txfp->Ncol; + } // end of VCTFAM copy constructor + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void VCTFAM::Reset(void) + { + FIXFAM::Reset(); + NewBlock = NULL; + AddBlock = false; + CurNum = Nrec - 1; + } // end of Reset + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int VCTFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int h, k, n; + VECHEADER vh; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + + if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1 + || !_filelength(h)) { + // Consider this is a void table + Last = Nrec; + Block = 0; + + if (h != -1) + close(h); + + return n; + } else if (Header == 3) + k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END); + + if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + } // endif s + + close(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +bool VCTFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc = false; + size_t n; + VECHEADER vh; + FILE *s; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Stream) { + s = Stream; + + if (Header == 1) + /*k =*/ fseek(s, 0, SEEK_SET); + + } else + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b"); + + } else { // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb"); + } // endif Header + + if (!s) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } else if (Header == 3) + /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || !Stream) + fclose(s); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/***********************************************************************/ +int VCTFAM::MaxBlkSize(PGLOBAL g, int s) + { + int rc = RC_OK, savcur = CurBlk; + int size; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + if ((rc = Tdbp->TestBlock(g)) == RC_OK) + size += (CurBlk == Block - 1) ? Last : Nrec; + else if (rc == RC_EF) + break; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/***********************************************************************/ +/* VCT Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int VCTFAM::Cardinality(PGLOBAL g) + { + if (!g) + return 1; + + if (Block < 0) + if (Split) { + // Separate column files and no pre setting of Block and Last + // This allows to see a table modified externally, but Block + // and Last must be set from the file cardinality. + // Only happens when called by sub classes. + char filename[_MAX_PATH]; + PSZ savfn = To_File; + int len, clen, card = -1; + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + // Use the first column file to calculate the cardinality + clen = cdp->GetClen(); + sprintf(filename, Colfn, 1); + To_File = filename; + len = GetFileLength(g); + To_File = savfn; + + if (len >= 0) { + if (!(len % clen)) + card = len / clen; // Fixed length file + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen); + + if (trace) + htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen); + + } else + card = 0; + + // Set number of blocks for later use + Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; + Last = (card + Nrec - 1) % Nrec + 1; + return card; + } else { + // Vector table having Block and Last info in a Header (file) + if ((Headlen = GetBlockInfo(g)) < 0) + return -1; // Error + + } // endif split + + return (int)((Block - 1) * Nrec + Last); + } // end of Cardinality + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int VCTFAM::GetRowID(void) + { + return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk + : (Block - 1) * Nrec + Last); + } // end of GetRowID + +/***********************************************************************/ +/* VCT Create an empty file for Vector formatted tables. */ +/***********************************************************************/ +bool VCTFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file: this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int h, n; + + PlugSetPath(filename, fn, Tdbp->GetPath()); +#if defined(WIN32) + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE); +#else // !WIN32 + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); +#endif // !WIN32 + + if (h == -1) + return true; + + n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) == -1) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; + } // end of MakeEmptyFile + +/***********************************************************************/ +/* VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VCTFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "rb" : "r+b"); + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + strcpy(opmode, "r+b"); // Required to update empty blocks + } else if (Last == Nrec) + strcpy(opmode, "ab"); + else + strcpy(opmode, "r+b"); // Required to update the last block + + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Use conventionnal input/output functions. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Stream + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fb = dbuserp->Openlist; // Keep track of File block + + if (!strcmp(opmode, "wb")) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + num_read = num_there = num_write = 0; + + // Allocate the table and column block buffer + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VCTFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); // Initialize inserting + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0, n = (MaxBlk) ? MaxBlk : 1; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec; + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCTFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Last block must be updated by new values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + } // endif Last + + // We are not currently using a temporary file for Insert + T_Stream = Stream; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* ReadBuffer: Read one line for a VCT file. */ +/***********************************************************************/ +int VCTFAM::ReadBuffer(PGLOBAL g) + { + int rc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (Placed) + Placed = false; + else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + next: + if (++CurBlk == Block) + return RC_EF; // End of file + + /*******************************************************************/ + /* Before reading a new block, check whether block optimizing */ + /* can be done, as well as for join as for local filtering. */ + /*******************************************************************/ + switch (Tdbp->TestBlock(g)) { + case RC_EF: + return RC_EF; + case RC_NF: + goto next; + } // endswitch rc + + num_there++; + } // endif CurNum + + if (OldBlk != CurBlk) { + if (mode == MODE_UPDATE) { + /*****************************************************************/ + /* Flush the eventually modified column buffers in old blocks */ + /* and read the blocks to modify attached to Set columns. */ + /*****************************************************************/ + if (MoveLines(g)) // For VECFAM + return RC_FX; + + for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) { + colp->WriteBlock(g); + colp->ReadBlock(g); + } // endfor colp + + } // endif mode + + OldBlk = CurBlk; // Last block actually read + } // endif oldblk + + if (trace) + htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK); + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VCTFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (!T_Stream) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + T_Stream = Stream; + + } // endif T_Stream + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // For VCT tables, future blocks must be added + char filename[_MAX_PATH]; + + // Close the file and reopen it in mode Insert + fclose(Stream); + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Stream + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if ((size_t)Nrec != + fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VCT access method. */ +/* Note: lines are moved directly in the files (ooops...) */ +/* Using temp file depends on the Check setting, false by default. */ +/***********************************************************************/ +int VCTFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + if (trace) + htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just the setting of future Spos and Tpos. */ + /*****************************************************************/ + T_Stream = Stream; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { + /*******************************************************************/ + /* Reposition the file pointer and set Spos. */ + /*******************************************************************/ +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* Update the Block and Last values. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + /***************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS ??) this is still to be */ + /* checked for compatibility with Text files and other OS's. */ + /***************************************************************/ + char filename[_MAX_PATH]; + int h; + + /*rc =*/ CleanUnusedSpace(g); // Clean last block + /*rc =*/ PlugCloseFile(g, To_Fb); + Stream = NULL; // For SetBlockInfo + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra blocks. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Headlen + Block * Blksize)) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } else + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool VCTFAM::OpenTempFile(PGLOBAL g) + { + char *opmode, tempname[_MAX_PATH]; + bool rc = false; + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (MaxBlk) { + if (MakeEmptyFile(g, tempname)) + return true; + + opmode = "r+b"; + } else + opmode = "wb"; + + if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + rc = true; + } else + To_Fbt = PlgGetUser(g)->Openlist; + + return rc; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, dep, off; + int n; + bool eof = (b) ? *b : false; + size_t req, len; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (size_t)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (size_t)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (MaxBlk) { + dep = Deplac[i]; + off = Spos * Clens[i]; + } else { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + dep = Deplac[i] + (Spos / Nrec) * Blksize; + off = (Spos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Stream); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp || MaxBlk) { + if (MaxBlk) { + dep = Deplac[i]; + off = Tpos * Clens[i]; + } else { + dep = Deplac[i] + (Tpos / Nrec) * Blksize; + off = (Tpos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(T_Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endif UseTemp + + if (trace) + htrc("after write pos=%d\n", ftell(Stream)); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, + // must be done here because T_Stream was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + // Write a new block in the temporary file + len = (size_t)Blksize; + + if (fwrite(NewBlock, 1, len, T_Stream) != len) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (Spos == Fpos) + eof = false; + + } // endif UseTemp + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a VCT or Vec table file. */ +/***********************************************************************/ +bool VCTFAM::CleanUnusedSpace(PGLOBAL g) + { + int i, dep; + int n; + size_t req, len; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); + + if (!(n = Nrec - Last)) + return false; + + dep = (Block - 1) * Blksize; + req = (size_t)n; + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + } else for (n = Fpos - Tpos; n > 0; n -= req) { + /*******************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* Note: this seems to work even column blocks have been made */ + /* with Blanks = true. Perhaps should it be set to false for VEC. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + memset(To_Buf, 0, Buflen); + + for (i = 0; i < Ncol; i++) { + if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + Tpos += (int)req; + } // endfor n + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && T_Stream) { + rc = RenameTempFile(g); + + if (Header) { + // Header must be set because it was not set in temp file + Stream = T_Stream = NULL; // For SetBlockInfo + rc = SetBlockInfo(g); + } // endif Header + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && T_Stream) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Stream = T_Stream = NULL; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (!(UseTemp && T_Stream)) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Stream = NULL; + } // end of CloseTableFile + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last) + { + bool rc = false; + + // Set Block and Last values for TDBVCT::MakeBlockValues + Block = block; + Last = last; + + if (!Split) { + if (!Header) { + // Update catalog values for Block and Last + PVCTDEF defp = (PVCTDEF)Tdbp->GetDef(); + LPCSTR name = Tdbp->GetName(); + PCATLG cat = PlgGetCatalog(g); + + defp->SetBlock(Block); + defp->SetLast(Last); + + if (!cat->SetIntCatInfo("Blocks", Block) || + !cat->SetIntCatInfo("Last", Last)) { + sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); + rc = true; + } // endif + + } else + rc = SetBlockInfo(g); + + } // endif Split + + Tdbp->ResetSize(); + return rc; + } // end of ResetTableSize + +/***********************************************************************/ +/* Rewind routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; +//rewind(Stream); will be placed by fseek + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // True vector format + len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk); + else // Blocked vector format + len = Nrec * (colp->Deplac + Lrecl * CurBlk); + + if (trace) + htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n", + len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (fseek(Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Stream); + + if (n != (size_t)Nrec) { + if (errno == NO_ERROR) + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File); + else + sprintf(g->Message, MSG(READ_ERROR), + To_File, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + len = Headlen + + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk); + else // Old VCT format + len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk); + + if (trace) + htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (fseek(T_Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), + (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } // endif + +#if defined(UNIX) + fflush(T_Stream); //NGC +#endif + +#ifdef _DEBUG + num_write++; +#endif + + return false; + } // end of WriteBlock + +/* -------------------------- Class VCMFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCMFAM class. */ +/***********************************************************************/ +VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Memory = NULL; + Memcol = NULL; + } // end of VCMFAM standard constructor + +VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp) + { + Memory = txfp->Memory; + Memcol = txfp->Memcol; + } // end of VCMFAM copy constructor + +/***********************************************************************/ +/* Mapped VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VCMFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + int len; + MODE mode = Tdbp->GetMode(); + PFBLOCK fp = NULL; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* We used the file name relative to recorded datapath. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as if it were */ + /* entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dbuserp->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memory = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* If required, delete the whole file if no filtering is implied. */ + /*******************************************************************/ + bool del; + HANDLE hFile; + MEMMAP mm; + MODE mapmode = mode; + + if (mode == MODE_INSERT) { + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Inserting will be like updating the file + mapmode = MODE_UPDATE; + } else { + strcpy(g->Message, "MAP Insert is for VEC Estimate tables only"); + return true; + } // endif MaxBlk + + } // endif mode + + del = mode == MODE_DELETE && !Tdbp->GetNext(); + + if (del) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + } // endif del + + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mapmode, del); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*******************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************/ + len = mm.lenL; + Memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + bool rc = ResetTableSize(g, 0, Nrec); + return (mapmode == MODE_UPDATE) ? true : rc; + } // endif len + + if (!Memory) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memory; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fb = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n", + fp, fp->Count, Memory, len); + + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/* Note: Memcol must be set for all columns because it can be used */ +/* for set columns in Update. Clens values are used only in Delete. */ +/***********************************************************************/ +bool VCMFAM::AllocateBuffer(PGLOBAL g) + { + int m, i = 0; + bool b = Tdbp->GetMode() == MODE_DELETE; + PVCTCOL cp; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + // Calculate the number of columns + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + // To store the start position of each column + Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*)); + m = (MaxBlk) ? MaxBlk : 1; + + // We will need all column sizes and type for Delete + if (b) { + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + } // endif b + + for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) { + if (b) { + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + } // endif b + + Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec; + } // endfor cdp + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + if (Tdbp->GetMode() == MODE_INSERT) + return InitInsert(g); + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCMFAM::InitInsert(PGLOBAL g) + { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + } // endif Last + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Initialize the column block pointer + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Data Base write routine for VMP access method. */ +/***********************************************************************/ +int VCMFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + // Mode Update being done in ReadDB we process here Insert mode only. + if (Tdbp->GetMode() == MODE_INSERT) { + if (CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing) { + CurBlk++; + CurNum = 0; + + // Re-initialize the column block pointer + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VCMFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if (Fpos > Spos) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) { + // Old VCT format, moving must respect block limits + char *ps, *pt; + int req, soff, toff; + + for (n = Fpos - Spos; n > 0; n -= req) { + soff = Spos % Nrec; + toff = Tpos % Nrec; + req = (size_t)min(n, Nrec - max(soff, toff)); + + for (i = 0; i < Ncol; i++) { + ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i]; + pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i]; + memmove(pt, ps, req * Clens[i]); + } // endfor i + + Tpos += req; + Spos += req; + } // endfor n + + } else { + // True vector format, all is simple... + n = Fpos - Spos; + + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m); + } // endfor i + + Tpos += n; + } // endif MaxBlk + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. Reset the Block and */ + /* Last values for TDBVCT::MakeBlockValues. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!MaxBlk) { + PFBLOCK fp = To_Fb; + + // Clean the unused part of the last block + m = (Block - 1) * Blksize; + n = Nrec - Last; + + for (i = 0; i < Ncol; i++) + memset(Memcol[i] + m + Last * Clens[i], + (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + // We must Unmap the view and use the saved file handle + // to put an EOF at the end of the last block of the file. + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + // Remove extra blocks + n = Block * Blksize; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } else + // True vector table, Table file size does not change. + // Just clean the unused part of the file. + for (n = Fpos - Tpos, i = 0; i < Ncol; i++) + memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]); + + // Reset Last and Block values in the catalog + PlugCloseFile(g, To_Fb); // in case of Header + ResetTableSize(g, Block, Last); + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VCMFAM::CloseTableFile(PGLOBAL g) + { + int wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (!Closing) { + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + } else + wrc = RC_FX; // Last write was in error + + PlugCloseFile(g, To_Fb); + + if (wrc != RC_FX) + /*rc =*/ ResetTableSize(g, Block, Last); + + } else if (mode != MODE_DELETE) + PlugCloseFile(g, To_Fb); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCMFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + char *mempos; + int i = colp->Index - 1; + int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl); + + /*********************************************************************/ + /* Calculate the start position of the column block to read. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n", + mempos, i, Nrec, colp->Clen, CurBlk); + + if (colp->GetStatus(BUF_MAPPED)) + colp->Blk->SetValPointer(mempos); + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: there is nothing to do because we are working directly into */ +/* the mapped file, except when checking for Update but in this case */ +/* we do not want to write back the modifications either. */ +/***********************************************************************/ +bool VCMFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { +#if defined(_DEBUG) + char *mempos; + int i = colp->Index - 1; + int n = Nrec * colp->Clen; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n", + Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk); + +#endif // _DEBUG + + return false; + } // end of WriteBlock + +/* -------------------------- Class VECFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VECFAM class. */ +/***********************************************************************/ +VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Streams = NULL; + To_Fbs = NULL; + To_Bufs = NULL; + Split = true; + Block = Last = -1; + InitUpdate = false; + } // end of VECFAM standard constructor + +VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp) + { + Streams = txfp->Streams; + To_Fbs = txfp->To_Fbs; + Clens = txfp->Clens; + To_Bufs = txfp->To_Bufs; + InitUpdate = txfp->InitUpdate; + } // end of VECFAM copy constructor + +/***********************************************************************/ +/* VEC Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VECFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4]; + int i; + bool b= false; + PCOLDEF cdp; + PVCTCOL cp; + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + /*********************************************************************/ + /* Call Cardinality to set Block and Last values in case it was not */ + /* already called (this happens indeed in test xmode) */ + /*********************************************************************/ + Cardinality(g); + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + break; + } // endif filter + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "r": "r+"); + break; + case MODE_INSERT: + strcpy(opmode, "ab"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern and set Ncol + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_INSERT || mode == MODE_DELETE) { + // All columns must be written or deleted + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (OpenColumnFile(g, opmode, i)) + return true; + + // Check for void table or missing columns + for (b = !Streams[0], i = 1; i < Ncol; i++) + if (b != !Streams[i]) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding to updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (OpenColumnFile(g, opmode, cp->Index - 1)) + return true; + + // Open in read only mode the used columns not already open + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Streams[cp->Index - 1]) + if (OpenColumnFile(g, "rb", cp->Index - 1)) + return true; + + // Check for void table or missing columns + for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp; + cp = (PVCTCOL)cp->Next) + if (!i++) + b = !Streams[cp->Index - 1]; + else if (b != !Streams[cp->Index - 1]) + return true; + + } // endif mode + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VECFAM::OpenColumnFile(PGLOBAL g, char *opmode, int i) + { + char filename[_MAX_PATH]; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (Tdbp->GetMode() == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Streams + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fbs[i] = dup->Openlist; // Keep track of File blocks + return false; + } // end of OpenColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VECFAM::AllocateBuffer(PGLOBAL g) + { + int i; + PVCTCOL cp; + PCOLDEF cdp; + PTDBVCT tdbp = (PTDBVCT)Tdbp; + MODE mode = tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)tdbp->GetDef(); + + if (mode != MODE_READ) { + // Allocate what is needed by all modes except Read + T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + // Give default values + for (i = 0; i < Ncol; i++) { + T_Streams[i] = Streams[i]; + Clens[i] = 0; + } // endfor i + + } // endif mode + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*)); + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]); + + if (cdp->GetType() == TYPE_STRING) + memset(To_Bufs[i], ' ', Nrec * Clens[i]); + else + memset(To_Bufs[i], 0, Nrec * Clens[i]); + + } // endfor cdp + + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1], + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines and make Temp + if (UseTemp) { + Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + strcpy(Tempat, Colfn); + PlugSetPath(Tempat, Tempat, Tdbp->GetPath()); + strcat(PlugRemoveType(Tempat, Tempat), ".t"); + T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + } // endif UseTemp + + if (UseTemp) + for (i = 0; i < Ncol; i++) { + T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL; + T_Fbs[i] = NULL; + } // endfor i + + if (mode == MODE_DELETE) { // All columns are moved + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + } else { // Mode Update, only some columns are updated + for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) { + i = cp->Index -1; + + if (UseTemp) + T_Streams[i] = NULL; // Mark the streams to open + + Clens[i] = cp->Clen; + Buflen = max(Buflen, cp->Clen); + } // endfor cp + + InitUpdate = true; // To be initialized + } // endif mode + + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec); + } // endif mode + + // Finally allocate column buffers for all modes + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } // endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VECFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + CurBlk = 0; + CurNum = 0; + AddBlock = true; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Reset buffer access according to indexing and to mode. */ +/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ +/***********************************************************************/ +void VECFAM::ResetBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* If access is random, performances can be much better when the */ + /* reads are done on only one row, except for small tables that can */ + /* be entirely read in one block. If the index is just used as a */ + /* bitmap filter, as for Update or Delete, reading will be */ + /* sequential and we better keep block reading. */ + /*********************************************************************/ + if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) { + Nrec = 1; // Better for random access + Rbuf = 0; + OldBlk = -2; // Has no meaning anymore + Block = Tdbp->Cardinality(g); // Blocks are one line now + Last = 1; // Probably unuseful + } // endif Mode + + } // end of ResetBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VECFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_INSERT) { + if (Closing || ++CurNum == Nrec) { + // Here we must add a new blocks to the files + int i; + size_t n = (size_t)CurNum; + + for (i = 0; i < Ncol; i++) + if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } else // Mode Update + // Writing updates being done in ReadDB we do initialization only. + if (InitUpdate) { + if (OpenTempFile(g)) + return RC_FX; + + InitUpdate = false; // Done + } // endif InitUpdate + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for split vertical access methods. */ +/* Note: lines are moved directly in the files (ooops...) */ +/***********************************************************************/ +int VECFAM::DeleteRecords(PGLOBAL g, int irc) + { + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This depends on the Check setting, false by default. */ + /*********************************************************************/ + if (trace) + htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = Cardinality(g); + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + // First line to delete + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary files, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Spos = Tpos = Fpos; + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + if (!UseTemp) { + /*****************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS??) this is still to be checked */ + /* for compatibility with other OS's. */ + /*****************************************************************/ + char filename[_MAX_PATH]; + int h; // File handle, return code + + for (int i = 0; i < Ncol; i++) { + sprintf(filename, Colfn, i + 1); + /*rc =*/ PlugCloseFile(g, To_Fbs[i]); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Tpos * Clens[i]))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Tpos * Clens[i])) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } // endfor i + + } else // UseTemp + // Ok, now delete old files and rename new temp files + if (RenameTempFile(g) == RC_FX) + return RC_FX; + + // Reset these values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open temporary files used while updating or deleting. */ +/* Note: the files not updated have been given a T_Stream value of 1. */ +/***********************************************************************/ +bool VECFAM::OpenTempFile(PGLOBAL g) + { + char tempname[_MAX_PATH]; + + for (int i = 0; i < Ncol; i++) + if (!T_Streams[i]) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + sprintf(tempname, Tempat, i+1); + + if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) { + if (trace) + htrc("%s\n", g->Message); + + return true; + } else + T_Fbs[i] = PlgGetUser(g)->Openlist; + + } else // This is a column that is not updated + T_Streams[i] = NULL; // For RenameTempFile + + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate updated lines before writing blocks. */ +/***********************************************************************/ +bool VECFAM::MoveLines(PGLOBAL g) + { + if (UseTemp && !InitUpdate) { // Don't do it in check pass + Fpos = OldBlk * Nrec; + + if (MoveIntermediateLines(g)) { + Closing = true; // ??? + return true; + } // endif UseTemp + +// Spos = Fpos + Nrec; + } // endif UseTemp + return false; + + } // end of MoveLines + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *bn) + { + int i; + int n; + bool b = false; + size_t req, len; + + for (n = Fpos - Spos; n > 0; n -= Nrec) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + if (!T_Streams[i]) + continue; // Non updated column + + if (!UseTemp || !b) + if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Streams[i]); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp) + if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (trace) + htrc("after write pos=%d\n", ftell(Streams[i])); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + b = true; + } // endfor n + + return false; + } // end of MoveIntermediate Lines + +/***********************************************************************/ +/* Delete the old files and rename the new temporary files. */ +/***********************************************************************/ +int VECFAM::RenameTempFile(PGLOBAL g) + { + char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH]; + int rc = RC_OK; + + // Close all files. + // This loop is necessary because, in case of join, + // the table files can have been open several times. + for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next) + rc = PlugCloseFile(g, fb); + + for (int i = 0; i < Ncol && rc == RC_OK; i++) { + if (!T_Fbs[i]) + continue; + + tempname = (char*)T_Fbs[i]->Fname; + sprintf(filename, Colfn, i+1); + PlugSetPath(filename, filename, Tdbp->GetPath()); + strcat(PlugRemoveType(filetemp, filename), ".ttt"); + remove(filetemp); // May still be there from previous error + + if (rename(filename, filetemp)) { // Save file for security + sprintf(g->Message, MSG(RENAME_ERROR), + filename, filetemp, strerror(errno)); + rc = RC_FX; + } else if (rename(tempname, filename)) { + sprintf(g->Message, MSG(RENAME_ERROR), + tempname, filename, strerror(errno)); + rc = rename(filetemp, filename); // Restore saved file + rc = RC_FX; + } else if (remove(filetemp)) { + sprintf(g->Message, MSG(REMOVE_ERROR), + filetemp, strerror(errno)); + rc = RC_INFO; // Acceptable + } // endif's + + } // endfor i + + return rc; + } // end of RenameTempFile + +/***********************************************************************/ +/* Data Base close routine for VEC access method. */ +/***********************************************************************/ +void VECFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last += (CurBlk * Nrec + CurNum -1); + Block += (Last / Nrec); + Last = Last % Nrec + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Block += CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) + rc = ResetTableSize(g, Block, Last); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (mode == MODE_UPDATE) { + if (UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = OldBlk * Nrec; + wrc = MoveIntermediateLines(g); +// Spos = Fpos + Nrec; + } // endif UseTemp + + // Write back to file any pending modifications + if (wrc == RC_OK) + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (wrc == RC_OK && UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = (Block - 1) * Nrec + Last; + wrc = MoveIntermediateLines(g); + } // endif UseTemp + + } // endif's mode + + if (UseTemp && !InitUpdate) { + // If they are errors, leave files unchanged + if (wrc == RC_OK) + rc = RenameTempFile(g); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (Streams) + for (int i = 0; i < Ncol; i++) + if (Streams[i]) { + rc = PlugCloseFile(g, To_Fbs[i]); + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif Streams + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + len = Nrec * colp->Clen * CurBlk; + i = colp->Index - 1; + + if (trace) + htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n", + len, i, Nrec, colp->Deplac, Lrecl, CurBlk); + + if (fseek(Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Streams[i]); + + if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) { + char fn[_MAX_PATH]; + + sprintf(fn, Colfn, colp->Index); +#if defined(WIN32) + if (feof(Streams[i])) +#else // !WIN32 + if (errno == NO_ERROR) +#endif // !WIN32 + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn); + else + sprintf(g->Message, MSG(READ_ERROR), + fn, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + len = Nrec * colp->Clen * colp->ColBlk; + i = colp->Index - 1; + + if (trace) + htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp) + if (fseek(T_Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum + : (colp->ColBlk == Block - 1) ? Last : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Streams[i])) { + char fn[_MAX_PATH]; + + sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index); + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } else + Spos = Fpos + n; + +#if defined(UNIX) + fflush(Streams[i]); //NGC +#endif + return false; + } // end of WriteBlock + +/* -------------------------- Class VMPFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VMPFAM class. */ +/***********************************************************************/ +VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp) + { + To_Fbs = NULL; + Split = true; + Block = Last = -1; + } // end of VMPFAM standard constructor + +VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp) + { + To_Fbs = txfp->To_Fbs; + } // end of VMPFAM copy constructor + +/***********************************************************************/ +/* VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VMPFAM::OpenTableFile(PGLOBAL g) + { + int i; + bool b; + MODE mode = Tdbp->GetMode(); + PCOLDEF cdp; + PVCTCOL cp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + if (mode == MODE_DELETE && !Tdbp->GetNext()) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + } else + Cardinality(g); // See comment in VECFAM::OpenTbleFile + + + /*********************************************************************/ + /* Prepare the filename pattern for column files and set Ncol. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Memcol[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_DELETE) { + // All columns are used in Delete mode + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (MapColumnFile(g, mode, i)) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1)) + return true; + + /*******************************************************************/ + /* Open other non already open used columns (except pseudos) */ + /*******************************************************************/ + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Memcol[cp->Index - 1]) + if (MapColumnFile(g, MODE_READ, cp->Index - 1)) + return true; + + } // endif mode + + /*********************************************************************/ + /* Check for void table or missing columns */ + /*********************************************************************/ + for (b = !Memcol[0], i = 1; i < Ncol; i++) + if (b != !Memcol[i]) + return true; + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i) + { + char filename[_MAX_PATH]; + int len; + HANDLE hFile; + MEMMAP mm; + PFBLOCK fp; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as */ + /* if it were entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dup->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping file, fp=%p\n", fp); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memcol[i] = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mode, DelRows); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*****************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*****************************************************************/ + len = mm.lenL; + Memcol[i] = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + ResetTableSize(g, 0, Nrec); + return false; + } // endif len + + if (!Memcol[i]) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dup->Openlist; + dup->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memcol[i]; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fbs[i] = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d\n", + fp, fp->Count, Memcol[i], len); + + return false; + } // end of MapColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/***********************************************************************/ +bool VMPFAM::AllocateBuffer(PGLOBAL g) + { + PVCTCOL cp; + + if (Tdbp->GetMode() == MODE_DELETE) { + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) + Clens[i] = cdp->GetClen(); + + } // endif mode + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VMPFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if ((n = Fpos - Spos) > 0) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n); + } // endif i + + Tpos += n; + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* We must firstly Unmap the view and use the saved file handle */ + /* to put an EOF at the end of the copied part of the file. */ + /*******************************************************************/ + PFBLOCK fp; + + for (i = 0; i < Ncol; i++) { + fp = To_Fbs[i]; + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ + n = Tpos * Clens[i]; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } // endfor i + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VMPFAM::CloseTableFile(PGLOBAL g) + { + if (Tdbp->GetMode() == MODE_DELETE) { + // Set Block and Nrec values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + ResetTableSize(g, Block, Last); + } else if (Tdbp->GetMode() == MODE_INSERT) + assert(false); + + for (int i = 0; i < Ncol; i++) + PlugCloseFile(g, To_Fbs[i]); + + } // end of CloseTableFile + +/* -------------------------- Class BGVFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the BGVFAM class. */ +/***********************************************************************/ +// Constructors +BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp) + { + Hfile = INVALID_HANDLE_VALUE; + Tfile = INVALID_HANDLE_VALUE; + BigDep = NULL; + } // end of BGVFAM constructor + +BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp) + { + Hfile = txfp->Hfile; + Tfile = txfp->Tfile; + BigDep= txfp->BigDep; + } // end of BGVFAM copy constructor + +/***********************************************************************/ +/* Set current position in a big file. */ +/***********************************************************************/ +bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b) + { +#if defined(WIN32) + char buf[256]; + DWORD drc, m = (b) ? FILE_END : FILE_BEGIN; + LARGE_INTEGER of; + + of.QuadPart = pos; + of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (drc = GetLastError()) != NO_ERROR) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(SFP_ERROR), buf); + return true; + } // endif +#else // !WIN32 + if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) { + sprintf(g->Message, MSG(ERROR_IN_LSK), errno); + return true; + } // endif +#endif // !WIN32 + + return false; + } // end of BigSeek + +/***********************************************************************/ +/* Read from a big file. */ +/***********************************************************************/ +bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbr, drc, len = (DWORD)req; + bool brc = ReadFile(h, inbuf, len, &nbr, NULL); + + if (trace) + htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr); + + if (!brc || nbr != len) { + char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_READ)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(READ_ERROR), To_File, buf); + + if (trace) + htrc("BIGREAD: %s\n", g->Message); + + rc = true; + } // endif brc || nbr +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbr = read(h, inbuf, len); + + if (nbr != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n", + nbr, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigRead + +/***********************************************************************/ +/* Write into a big file. */ +/***********************************************************************/ +bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbw, drc, len = (DWORD)req; + bool brc = WriteFile(h, inbuf, len, &nbw, NULL); + + if (trace) + htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw); + + if (!brc || nbw != len) { + char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_NUM)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, drc, g->Message); + + rc = true; + } // endif brc || nbw +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbw = write(h, inbuf, len); + + if (nbw != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigWrite + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int BGVFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int n; + VECHEADER vh; + HANDLE h; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + +#if defined(WIN32) + LARGE_INTEGER len; + + h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h != INVALID_HANDLE_VALUE) { + // Get the size of the file (can be greater than 4 GB) + len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart); + } // endif h + + if (h == INVALID_HANDLE_VALUE || !len.QuadPart) { +#else // !WIN32 + h = open64(filename, O_RDONLY, 0); + + if (h == INVALID_HANDLE_VALUE || !_filelength(h)) { +#endif // !WIN32 + // Consider this is a void table + if (trace) + htrc("Void table h=%d\n", h); + + Last = Nrec; + Block = 0; + + if (h != INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return n; + } else if (Header == 3) + /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + if (BigRead(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + + if (trace) + htrc("Block=%d Last=%d\n", Block, Last); + + } // endif's + + CloseFileHandle(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Set the MaxRec and NumRec info in the file header. */ +/***********************************************************************/ +bool BGVFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool b = false, rc = false; + VECHEADER vh; + HANDLE h = INVALID_HANDLE_VALUE; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Hfile != INVALID_HANDLE_VALUE) { + h = Hfile; + + if (Header == 1) + /*bk =*/ BigSeek(g, h, (BIGINT)0); + + } else + b = true; + + } else // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + + if (h == INVALID_HANDLE_VALUE) { +#if defined(WIN32) + DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING; + + h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + +#else // !WIN32 + int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC; + + h = open64(filename, oflag, 0); +#endif // !WIN32 + + if (h == INVALID_HANDLE_VALUE) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } // endif h + + } // endif h + + if (Header == 3) + /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if (BigWrite(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || Hfile == INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* VEC Create an empty file for new Vector formatted tables. */ +/***********************************************************************/ +bool BGVFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + PlugSetPath(filename, fn, Tdbp->GetPath()); + +#if defined(WIN32) + char *p; + DWORD rc; + bool brc; + LARGE_INTEGER of; + HANDLE h; + + h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) { + p = MSG(OPENING); + goto err; + } // endif h + + of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n", + of.QuadPart, n, MaxBlk, Blksize); + + of.LowPart = SetFilePointer(h, of.LowPart, + &of.HighPart, FILE_BEGIN); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) { + p = MSG(MAKING); + goto err; + } // endif + + brc = WriteFile(h, &c, 1, &rc, NULL); + + if (!brc || rc != 1) { + p = MSG(WRITING); + goto err; + } // endif + + CloseHandle(h); + return false; + + err: + rc = GetLastError(); + sprintf(g->Message, MSG(EMPTY_FILE), p, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + + if (h != INVALID_HANDLE_VALUE) + CloseHandle(h); + + return true; +#else // !WIN32 + int h; + BIGINT pos; + + h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); + + if (h == -1) + return true; + + pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n", + pos, n, MaxBlk, Blksize); + + if (lseek64(h, pos, SEEK_SET) < 0) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; +#endif // !WIN32 + } // end of MakeEmptyFile + +/***********************************************************************/ +/* Vopen function: opens a file using Windows or Unix API's. */ +/***********************************************************************/ +bool BGVFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool del = false; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) { + sprintf(g->Message, MSG(FILE_OPEN_YET), To_File); + return true; + } // endif + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (trace) + htrc("OpenTableFile: filename=%s mode=%d Last=%d\n", + filename, mode, Last); + +#if defined(WIN32) + DWORD access, creation, share = 0, rc = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + creation = OPEN_EXISTING; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + access = GENERIC_READ | GENERIC_WRITE; + } else if (Last == Nrec) + access = GENERIC_WRITE; + else + // Required to update the last block + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_ALWAYS; + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will stop the process by + // causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + del = true; + + // This will delete the whole file + access = GENERIC_READ | GENERIC_WRITE; + creation = TRUNCATE_EXISTING; + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + if ((UseTemp = Tdbp->IsUsingTemp(g))) + access = GENERIC_READ; + else + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_EXISTING; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + /*********************************************************************/ + /* Use specific Windows API functions. */ + /*********************************************************************/ + Hfile = CreateFile(filename, access, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + } // endif Hfile + + if (trace) + htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n", + rc, access, share, creation, Hfile, filename); + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode we must position the cursor at end of file. */ + /*******************************************************************/ + LARGE_INTEGER of; + + of.QuadPart = (BIGINT)0; + of.LowPart = SetFilePointer(Hfile, of.LowPart, + &of.HighPart, FILE_END); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (rc = GetLastError()) != NO_ERROR) { + sprintf(g->Message, MSG(ERROR_IN_SFP), rc); + CloseHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + } // endif + + } // endif Mode + +#else // UNIX + /*********************************************************************/ + /* The open() function has a transitional interface for 64-bit */ + /* file offsets. Note that using open64() is equivalent to using */ + /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */ + /*********************************************************************/ + int rc = 0; + int oflag; + mode_t pmd = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + oflag = O_RDONLY; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + oflag = O_RDWR; + } else if (Last == Nrec) + oflag = O_WRONLY | O_CREAT | O_APPEND; + else + // Required to update the last block + oflag = O_RDWR | O_CREAT | O_APPEND; + + pmd = S_IREAD | S_IWRITE; + break; + case MODE_DELETE: + // This is temporary until a partial delete is implemented + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + del = true; + + // This will delete the whole file and provoque ReadDB to + // return immediately. + oflag = O_RDWR | O_TRUNC; + strcpy(g->Message, MSG(NO_VCT_DELETE)); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + oflag = (UseTemp) ? O_RDONLY : O_RDWR; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + Hfile = open64(filename, oflag, pmd); // Enable file size > 2G + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + strcat(g->Message, strerror(errno)); + } // endif Hfile + + if (trace) + htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n", + rc, oflag, mode, Hfile, filename); +#endif // UNIX + + if (!rc) { + if (!To_Fb) { + To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fb->Fname = To_File; + To_Fb->Type = TYPE_FB_HANDLE; + To_Fb->Memory = NULL; + To_Fb->Length = 0; + To_Fb->File = NULL; + To_Fb->Next = dbuserp->Openlist; + dbuserp->Openlist = To_Fb; + } // endif To_Fb + + To_Fb->Count = 1; + To_Fb->Mode = mode; + To_Fb->Handle = Hfile; + + if (trace) + htrc("File %s is open in mode %d\n", filename, mode); + + if (del) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + /*********************************************************************/ + /* Allocate the table and column block buffers. */ + /*********************************************************************/ + return AllocateBuffer(g); + } else + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool BGVFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + if (!NewBlock) { + // Not reopening after inserting the last block + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + InitInsert(g); // Initialize inserting + + // Currently we don't use a temporary file for inserting + Tfile = Hfile; + } // endif NewBlock + + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + if (MaxBlk) + BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT)); + else + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + if (MaxBlk) + BigDep[i] = (BIGINT)Headlen + + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk; + else + Deplac[i] = cdp->GetPoff() * Nrec; + + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base write routine for huge VCT access method. */ +/***********************************************************************/ +int BGVFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (Tfile == INVALID_HANDLE_VALUE) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + Tfile = Hfile; + + } // endif Tfile + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for a Vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // Close the VCT file and reopen it in mode Insert +//#if defined(WIN32) //OB +// CloseHandle(Hfile); +//#else // UNIX +// close(Hfile); +//#endif // UNIX + CloseFileHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + To_Fb->Count = 0; + Last = Nrec; // Tested in OpenTableFile + + if (OpenTableFile(g)) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Vopen + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the VCT file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if (BigWrite(g, Hfile, NewBlock, Blksize)) + return RC_FX; + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for BGVFAM access method. */ +/***********************************************************************/ +int BGVFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + /*********************************************************************/ + /* There is an alternative here depending on UseTemp: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /*********************************************************************/ + if (trace) + htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Tfile = Hfile; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + if (Last < Nrec) // Clean last block + if (CleanUnusedSpace(g)) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(WIN32) + BIGINT pos = (BIGINT)Block * (BIGINT)Blksize; + + if (BigSeek(g, Hfile, pos)) + return RC_FX; + + if (!SetEndOfFile(Hfile)) { + DWORD drc = GetLastError(); + + sprintf(g->Message, MSG(SETEOF_ERROR), drc); + return RC_FX; + } // endif error +#else // !WIN32 + if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + return RC_FX; + } // endif +#endif // !WIN32 + } else // MaxBlk + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool BGVFAM::OpenTempFile(PGLOBAL g) + { + char *tempname; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (!MaxBlk) + remove(tempname); // Be sure it does not exist yet + else if (MakeEmptyFile(g, tempname)) + return true; + +#if defined(WIN32) + DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW; + + Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL, + access, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Tfile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)tempname, _MAX_PATH, NULL); + strcat(g->Message, tempname); + return true; + } // endif Tfile +#else // UNIX + int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC; + + Tfile = open64(tempname, oflag, S_IWRITE); + + if (Tfile == INVALID_HANDLE_VALUE) { + int rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); + strcat(g->Message, strerror(errno)); + return true; + } //endif Tfile +#endif // UNIX + + To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fbt->Fname = tempname; + To_Fbt->Type = TYPE_FB_HANDLE; + To_Fbt->Memory = NULL; + To_Fbt->Length = 0; + To_Fbt->File = NULL; + To_Fbt->Next = dup->Openlist; + To_Fbt->Count = 1; + To_Fbt->Mode = MODE_INSERT; + To_Fbt->Handle = Tfile; + dup->Openlist = To_Fbt; + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, n, req, dep; + bool eof = (b) ? *b : false; + BIGINT pos; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (DWORD)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (DWORD)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (!MaxBlk) { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i]) + + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize; + } else + pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i]; + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, To_Buf, req * Clens[i])) + return true; + + if (!UseTemp || MaxBlk) { + if (!MaxBlk) + pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i]) + + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize; + else + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endif UseTemp + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, must be + // done here because Tfile was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + if (BigWrite(g, Tfile, NewBlock, Blksize)) + return true; + + if (Spos == Fpos) + eof = false; + + } // endif Usetemp... + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a huge VCT or Vec table file. */ +/***********************************************************************/ +bool BGVFAM::CleanUnusedSpace(PGLOBAL g) + { + int i; + int n; + BIGINT pos, dep; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); // This case is handled in MoveIntermediateLines + + if (!(n = Nrec - Last)) + return false; + + dep = (BIGINT)((Block - 1) * Blksize); + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigWrite(g, Hfile, To_Buf, n * Clens[i])) + return true; + + } // endfor i + + } else { + int req; + + memset(To_Buf, 0, Buflen); + + for (n = Fpos - Tpos; n > 0; n -= req) { + /*****************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* This seems to work even column blocks have been made with */ + /* Blanks = true. Perhaps should it be set to false for VEC. */ + /*****************************************************************/ + req = min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endfor i + + Tpos += req; + } // endfor n + + } // endif MaxBlk + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for huge VEC access method. */ +/***********************************************************************/ +void BGVFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && Tfile) { + rc = RenameTempFile(g); + Hfile = Tfile = INVALID_HANDLE_VALUE; + + if (Header) + // Header must be set because it was not set in temp file + rc = SetBlockInfo(g); + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && Tfile) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (Hfile != INVALID_HANDLE_VALUE) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Hfile = INVALID_HANDLE_VALUE; + } // end of CloseDB + +/***********************************************************************/ +/* Rewind routine for huge VCT access method. */ +/***********************************************************************/ +void BGVFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; + +#if 0 // This is probably unuseful as the file is directly accessed +#if defined(WIN32) //OB + SetFilePointer(Hfile, 0, NULL, FILE_BEGIN); +#else // UNIX + lseek64(Hfile, 0, SEEK_SET); +#endif // UNIX +#endif // 0 + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)CurBlk); + + if (trace) + htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec)) + return true; + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)colp->ColBlk); + + if (trace) + htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (BigSeek(g, Tfile, pos)) + return true; + +//len = colp->Clen * Nrec; see comment in VCTFAM + len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec); + + if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len)) + return true; + + return false; + } // end of WriteBlock + +/* ----------------------- End of FilAMVct --------------------------- */ 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 -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif -//#include -#else // !WIN32 -#if defined(UNIX) -#include -#else // !UNIX -#include -#endif -#include -#endif // !WIN32 - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* tabdos.h is header containing the TABDOS class declarations. */ -/***********************************************************************/ -#include "global.h" -#include "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 +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include +#else // !WIN32 +#if defined(UNIX) +#include +#else // !UNIX +#include +#endif +#include +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "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 manipulation declares */ -#include /* C standard library */ -#include /* C language specific types */ -#include /* FOPEN_MAX declaration */ -#include /* time_t type declaration */ -#include /* 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 : "") -#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 manipulation declares */ +#include /* C standard library */ +#include /* C language specific types */ +#include /* FOPEN_MAX declaration */ +#include /* time_t type declaration */ +#include /* 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 : "") +#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:
./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:
- CREATE TABLE (...) 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!
- -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 -#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 -#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(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(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 : ""); - - 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 : "", - 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* arglist= cond_item->argument_list(); - List_iterator 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* arglist= cond_item->argument_list(); - List_iterator 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, - ¬_used_1, ¬_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(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:
./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:
+ CREATE TABLE
(...) 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!
+ -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 +#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 +#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(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(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 : ""); + + 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 : "", + 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* arglist= cond_item->argument_list(); + List_iterator 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* arglist= cond_item->argument_list(); + List_iterator 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, + ¬_used_1, ¬_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(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 -#else -#if defined(UNIX) || defined(UNIV_LINUX) -#include -#include -//#define __stdcall -#else -#include -#endif -#include -#endif - -#if defined(WIN) -#include -#endif -#include /* definitions of ERANGE ENOMEM */ -#if !defined(UNIX) && !defined(UNIV_LINUX) -#include /* 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 +#else +#if defined(UNIX) || defined(UNIV_LINUX) +#include +#include +//#define __stdcall +#else +#include +#endif +#include +#endif + +#if defined(WIN) +#include +#endif +#include /* definitions of ERANGE ENOMEM */ +#if !defined(UNIX) && !defined(UNIV_LINUX) +#include /* 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 @@ -3,31 +3,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. */ /***********************************************************************/ @@ -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 -#else -#include // 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 +#else +#include // 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 -#include // For testing only -#include -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif // __BORLANDC__ -//#include -#else // !WIN32 -#if defined(UNIX) -#include -#include -#else // !UNIX -#include -#endif // !UNIX -#include -#endif // !WIN32 - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* filamtxt.h is header containing the file AM classes declarations. */ -/***********************************************************************/ -#include "global.h" -#include "osutil.h" -#include "plgdbsem.h" -#include "catalog.h" -#include "mycat.h" -#include "xindex.h" -#include "filamap.h" -#include "filamfix.h" -#include "filamdbf.h" -#if defined(ZIP_SUPPORT) -#include "filamzip.h" -#endif // ZIP_SUPPORT -#include "tabdos.h" -#include "tabfix.h" -#include "tabmul.h" -#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 +#include // For testing only +#include +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include +#else // !WIN32 +#if defined(UNIX) +#include +#include +#else // !UNIX +#include +#endif // !UNIX +#include +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "osutil.h" +#include "plgdbsem.h" +#include "catalog.h" +#include "mycat.h" +#include "xindex.h" +#include "filamap.h" +#include "filamfix.h" +#include "filamdbf.h" +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT +#include "tabdos.h" +#include "tabfix.h" +#include "tabmul.h" +#include "array.h" +#include "blkfil.h" + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +int num_read, num_there, num_eq[2]; // Statistics +extern "C" int trace; + +/***********************************************************************/ +/* Size of optimize file header. */ +/***********************************************************************/ +#define NZ 4 + +/***********************************************************************/ +/* Min and Max blocks contains zero ended fields (blank = FALSE). */ +/* No conversion of block values (check = TRUE). */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len = 0, int prec = 0, + bool check = TRUE, bool blank = FALSE, bool un = FALSE); + +/* --------------------------- Class DOSDEF -------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +DOSDEF::DOSDEF(void) + { + Pseudo = 3; + Fn = NULL; + Ofn = NULL; + To_Indx = NULL; + Recfm = RECFM_VAR; + Mapped = false; + Padded = false; + Huge = false; + Accept = false; + Eof = false; + To_Pos = NULL; + Optimized = 0; + AllocBlks = 0; + Compressed = 0; + Lrecl = 0; + AvgLen = 0; + Block = 0; + Last = 0; + Blksize = 0; + Maxerr = 0; + ReadMode = 0; + Ending = 0; + } // end of DOSDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char buf[8]; + bool map = (am && (*am == 'M' || *am == 'm')); + LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F" + : (am && (*am == 'B' || *am == 'b')) ? "B" + : (am && !stricmp(am, "DBF")) ? "D" : "V"; + + Desc = Fn = Cat->GetStringCatInfo(g, "Filename", NULL); + Ofn = Cat->GetStringCatInfo(g, "Optname", Fn); + Cat->GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); + Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : + (toupper(*buf) == 'B') ? RECFM_BIN : + (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR; + Lrecl = Cat->GetIntCatInfo("Lrecl", 0); + + if (Recfm != RECFM_DBF) + Compressed = Cat->GetIntCatInfo("Compressed", 0); + + Mapped = Cat->GetBoolCatInfo("Mapped", map); + Block = Cat->GetIntCatInfo("Blocks", 0); + Last = Cat->GetIntCatInfo("Last", 0); + Ending = Cat->GetIntCatInfo("Ending", CRLF); + + if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) { + Huge = Cat->GetBoolCatInfo("Huge", Cat->GetDefHuge()); + Padded = Cat->GetBoolCatInfo("Padded", false); + Blksize = Cat->GetIntCatInfo("Blksize", 0); + Eof = (Cat->GetIntCatInfo("EOF", 0) != 0); + } else if (Recfm == RECFM_DBF) { + Maxerr = Cat->GetIntCatInfo("Maxerr", 0); + Accept = (Cat->GetIntCatInfo("Accept", 0) != 0); + ReadMode = Cat->GetIntCatInfo("Readmode", 0); + } else // (Recfm == RECFM_VAR) + AvgLen = Cat->GetIntCatInfo("Avglen", 0); + + // Ignore wrong Index definitions for catalog commands + return (Cat->GetIndexInfo(g, this) /*&& !Cat->GetCatFnc()*/); + } // end of DefineAM + +/***********************************************************************/ +/* Get the full path/name of the optization file. */ +/***********************************************************************/ +bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename) + { + char *ftype; + + switch (Recfm) { + case RECFM_VAR: ftype = ".dop"; break; + case RECFM_FIX: ftype = ".fop"; break; + case RECFM_BIN: ftype = ".bop"; break; + case RECFM_VCT: ftype = ".vop"; break; + case RECFM_DBF: ftype = ".dbp"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Recfm); + return TRUE; + } // endswitch Ftype + + PlugSetPath(filename, Ofn, GetPath()); + strcat(PlugRemoveType(filename, filename), ftype); + return FALSE; + } // end of GetOptFileName + +/***********************************************************************/ +/* After an optimize error occured, remove all set optimize values. */ +/***********************************************************************/ +void DOSDEF::RemoveOptValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + PCOLDEF cdp; + + // Delete settings of optimized columns + for (cdp = To_Cols; cdp; cdp = cdp->GetNext()) + if (cdp->GetOpt()) { + cdp->SetMin(NULL); + cdp->SetMax(NULL); + cdp->SetNdv(0); + cdp->SetNbm(0); + cdp->SetDval(NULL); + cdp->SetBmap(NULL); + } // endif Opt + + // Delete block position setting for not fixed tables + To_Pos = NULL; + AllocBlks = 0; + + // Delete any eventually ill formed non matching optimization file + if (!GetOptFileName(g, filename)) +#if defined(WIN32) + DeleteFile(filename); +#else // UNIX + remove(filename); +#endif // WIN32 + + Optimized = 0; + } // end of RemoveOptValues + +/***********************************************************************/ +/* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */ +/***********************************************************************/ +bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf) + { + char *ftype; + char filename[_MAX_PATH]; + bool sep, rc = false; + + if (!To_Indx) + return false; // No index + + // If true indexes are in separate files + sep = Cat->GetBoolCatInfo("SepIndex", false); + + if (!sep && pxdf) { + strcpy(g->Message, MSG(NO_RECOV_SPACE)); + return true; + } // endif sep + + switch (Recfm) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm); + return true; + } // endswitch Ftype + + /*********************************************************************/ + /* Check for existence of an index file. */ + /*********************************************************************/ + if (sep) { + // Indexes are save in separate files +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + for (; pxdf; pxdf = pxdf->GetNext()) { + _splitpath(Ofn, drive, direc, fname, NULL); + strcat(strcat(fname, "_"), pxdf->GetName()); + _makepath(filename, drive, direc, fname, ftype); + PlugSetPath(filename, filename, GetPath()); +#if defined(WIN32) + rc |= !DeleteFile(filename); +#else // UNIX + rc |= remove(filename); +#endif // UNIX + } // endfor pxdf + + } else { // !sep + // Drop all indexes, delete the common file + PlugSetPath(filename, Ofn, GetPath()); + strcat(PlugRemoveType(filename, filename), ftype); +#if defined(WIN32) + rc = !DeleteFile(filename); +#else // UNIX + rc = remove(filename); +#endif // UNIX + } // endif sep + + if (rc) + sprintf(g->Message, MSG(DEL_FILE_ERR), filename); + + return rc; // Return true if error + } // end of DeleteIndexFile + +/***********************************************************************/ +/* InvalidateIndex: mark all indexes as invalid. */ +/***********************************************************************/ +bool DOSDEF::InvalidateIndex(PGLOBAL g) + { + if (To_Indx) + for (PIXDEF xp = To_Indx; xp; xp = xp->Next) + xp->Invalid = true; + + return false; + } // end of InvalidateIndex + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) + { + // Mapping not used for insert + USETEMP tmp = PlgGetUser(g)->UseTemp; + bool map = Mapped && mode != MODE_INSERT && + !(tmp != TMP_NO && Recfm == RECFM_VAR + && mode == MODE_UPDATE) && + !(tmp == TMP_FORCE && + (mode == MODE_UPDATE || mode == MODE_DELETE)); + PTXF txfp; + PTDBASE tdbp; + + /*********************************************************************/ + /* Allocate table and file processing class of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + if (Recfm == RECFM_DBF) { + if (Catfunc == FNC_NO) { + if (map) + txfp = new(g) DBMFAM(this); + else + txfp = new(g) DBFFAM(this); + + tdbp = new(g) TDBFIX(this, txfp); + } else // Catfunc should be 'C' + tdbp = new(g) TDBDCL(this); + + } else if (Recfm != RECFM_VAR && Compressed < 2) { + if (Huge) + txfp = new(g) BGXFAM(this); + else if (map) + txfp = new(g) MPXFAM(this); + else if (Compressed) { +#if defined(ZIP_SUPPORT) + txfp = new(g) ZIXFAM(this); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else + txfp = new(g) FIXFAM(this); + + tdbp = new(g) TDBFIX(this, txfp); + } else { + if (Compressed) { +#if defined(ZIP_SUPPORT) + if (Compressed == 1) + txfp = new(g) ZIPFAM(this); + else + txfp = new(g) ZLBFAM(this); + +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else if (map) + txfp = new(g) MAPFAM(this); + else + txfp = new(g) DOSFAM(this); + + // Txfp must be set even for not multiple tables because + // it is needed when calling Cardinality in GetBlockValues. + tdbp = new(g) TDBDOS(this, txfp); + } // endif Recfm + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); + else + /*******************************************************************/ + /* For block tables, get eventually saved optimization values. */ + /*******************************************************************/ + if (tdbp->GetBlockValues(g)) { + PushWarning(g, tdbp); +// return NULL; // causes a crash when deleting index + } else if (Recfm == RECFM_VAR || Compressed > 1) { + if (IsOptimized()) { + if (map) { + txfp = new(g) MBKFAM(this); + } else if (Compressed) { +#if defined(ZIP_SUPPORT) + if (Compressed == 1) + txfp = new(g) ZBKFAM(this); + else { + txfp->SetBlkPos(To_Pos); + ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL); + } // endelse +#else + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif + } else + txfp = new(g) BLKFAM(this); + + ((PTDBDOS)tdbp)->SetTxfp(txfp); + } // endif Optimized + + } // endif Recfm + + return tdbp; + } // end of GetTable + +/* ------------------------ Class TDBDOS ----------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBDOS class. This is the common class that */ +/* contain all that is common between the TDBDOS and TDBMAP classes. */ +/***********************************************************************/ +TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp) + { + if ((Txfp = txfp)) + Txfp->SetTdbp(this); + + Lrecl = tdp->Lrecl; + AvgLen = tdp->AvgLen; + Ftype = tdp->Recfm; + To_Line = NULL; + Cardinal = -1; +//To_BlkIdx = NULL; + To_BlkFil = NULL; + SavFil = NULL; +//Xeval = 0; + Beval = 0; + } // end of TDBDOS standard constructor + +TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp) + { + Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp; + Lrecl = tdbp->Lrecl; + AvgLen = tdbp->AvgLen; + Ftype = tdbp->Ftype; + To_Line = tdbp->To_Line; + Cardinal = tdbp->Cardinal; +//To_BlkIdx = tdbp->To_BlkIdx; + To_BlkFil = tdbp->To_BlkFil; + SavFil = tdbp->SavFil; +//Xeval = tdbp->Xeval; + Beval = tdbp->Beval; + } // end of TDBDOS copy constructor + +// Method +PTDB TDBDOS::CopyOne(PTABS t) + { + PTDB tp; + PDOSCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBDOS(g, this); + + for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) { + cp2 = new(g) DOSCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate DOS column description block. */ +/***********************************************************************/ +PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) DOSCOL(g, cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* Print debug information. */ +/***********************************************************************/ +void TDBDOS::PrintAM(FILE *f, char *m) + { + fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); + + if (Txfp->To_File) + fprintf(f, "%s File: %s\n", m, Txfp->To_File); + + } // end of PrintAM + +/***********************************************************************/ +/* Remake the indexes after the table was modified. */ +/***********************************************************************/ +int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox) + { + int prc = RC_OK, rc = RC_OK; + + MaxSize = -1; // Size must be recalculated + Cardinal = -1; // as well as Cardinality + + PTXF xp = Txfp; + + To_Filter = NULL; // Disable filtering +//To_BlkIdx = NULL; // and index filtering + To_BlkFil = NULL; // and block filtering + + if (dop) { + Columns = NULL; // Not used anymore + + if (Txfp->Blocked) { + // MakeBlockValues must be executed in non blocked mode + // except for ZLIB access method. + if (Txfp->GetAmType() == TYPE_AM_MAP) { + Txfp = new(g) MAPFAM((PDOSDEF)To_Def); +#if defined(ZIP_SUPPORT) + } else if (Txfp->GetAmType() == TYPE_AM_ZIP) { + Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); + } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) { + Txfp->Reset(); + ((PZLBFAM)Txfp)->SetOptimized(FALSE); +#endif // ZIP_SUPPORT + } else // (Txfp->GetAmType() == TYPE_AM_BLK) + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + + Txfp->SetTdbp(this); + } else + Txfp->Reset(); + + Use = USE_READY; // So the table can be reopened + Mode = MODE_ANY; // Just to be clean + rc = MakeBlockValues(g); // Redo optimization + } // endif dop + + if (dox && (rc == RC_OK || rc == RC_INFO)) { + // Remake eventual indexes + if (Mode != MODE_UPDATE) + To_SetCols = NULL; // Only used on Update + + Columns = NULL; // Not used anymore + Txfp->Reset(); // New start + Use = USE_READY; // So the table can be reopened + Mode = MODE_READ; // New mode + prc = rc; + + if (!(PlgGetUser(g)->Check & CHK_OPT)) { + // After the table was modified the indexes + // are invalid and we should mark them as such... + rc = ((PDOSDEF)To_Def)->InvalidateIndex(g); + } else + // ... or we should remake them. + rc = MakeIndex(g, NULL, false); + + rc = (rc == RC_INFO) ? prc : rc; + } // endif dox + + return rc; + } // end of ResetTableOpt + +/***********************************************************************/ +/* Calculate the block sizes so block I/O can be used and also the */ +/* Min/Max values for clustered/sorted table columns. */ +/***********************************************************************/ +int TDBDOS::MakeBlockValues(PGLOBAL g) + { + int i, lg, nrec, rc; + int curnum, curblk, block, last, savndv, savnbm; + void *savmin, *savmax; + bool blocked, xdb2 = FALSE; +//POOLHEADER save; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)To_Def; + PDOSCOL colp = NULL; + PDBUSER dup = PlgGetUser(g); + PCATLG cat = defp->GetCat(); +//void *memp = cat->GetDescp(); + + if ((nrec = defp->GetElemt()) < 2) { + strcpy(g->Message, MSG(TABLE_NOT_OPT)); + return RC_INFO; // Not to be optimized + } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) { + // Suppress the opt file firstly if the table is void, + // secondly when it was modified with OPTIMIZATION unchecked + // because it is no more valid. + defp->RemoveOptValues(g); // Erase opt file + return RC_OK; // void table + } else if (MaxSize < 0) + return RC_FX; + + defp->SetOptimized(0); + + // Estimate the number of needed blocks + block = (int)((MaxSize + (int)nrec - 1) / (int)nrec); + + // We have to use local variables because Txfp->CurBlk is set + // to Rows+1 by unblocked variable length table access methods. + curblk = -1; + curnum = nrec - 1; + last = 0; + Txfp->Block = block; // This is useful mainly for + Txfp->CurBlk = curblk; // blocked tables (ZLBFAM), for + Txfp->CurNum = curnum; // others it is just to be clean. + + /*********************************************************************/ + /* Allocate the array of block starting positions. */ + /*********************************************************************/ +//if (memp) +// save = *(PPOOLHEADER)memp; + + Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int)); + + /*********************************************************************/ + /* Allocate the blocks for clustered columns. */ + /*********************************************************************/ + blocked = Txfp->Blocked; // Save + Txfp->Blocked = TRUE; // So column block can be allocated + + for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) { + lg = cdp->GetClen(); + + if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) { + cdp->SetXdb2(TRUE); + savndv = cdp->GetNdv(); + cdp->SetNdv(0); // Reset Dval number of values + xdb2 = TRUE; + savmax = cdp->GetDval(); + cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg)); + savnbm = cdp->GetNbm(); + cdp->SetNbm(0); // Prevent Bmap allocation +// savmin = cdp->GetBmap(); +// cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int))); + + if (trace) + htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n", + cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg); + + // colp will be initialized with proper Dval VALBLK + colp = (PDOSCOL)MakeCol(g, cdp, colp, i); + colp->InitValue(g); // Allocate column value buffer + cdp->SetNbm(savnbm); +// cdp->SetBmap(savmin); // Can be reused if the new size + cdp->SetDval(savmax); // is not greater than this one. + cdp->SetNdv(savndv); + } else { + cdp->SetXdb2(FALSE); // Maxbmp may have been reset + savmin = cdp->GetMin(); + savmax = cdp->GetMax(); + cdp->SetMin(PlugSubAlloc(g, NULL, block * lg)); + cdp->SetMax(PlugSubAlloc(g, NULL, block * lg)); + + if (trace) + htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n", + cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg); + + // colp will be initialized with proper opt VALBLK's + colp = (PDOSCOL)MakeCol(g, cdp, colp, i); + colp->InitValue(g); // Allocate column value buffer + cdp->SetMin(savmin); // Can be reused if the number + cdp->SetMax(savmax); // of blocks does not change. + } // endif Freq + + } // endif Clustered + +//if (!colp) +// return RC_INFO; + + Txfp->Blocked = blocked; + + /*********************************************************************/ + /* Now do calculate the optimization values. */ + /*********************************************************************/ + Mode = MODE_READ; + + if (OpenDB(g)) + return RC_FX; + + if (xdb2) { + /*********************************************************************/ + /* Retrieve the distinct values of XDB2 columns. */ + /*********************************************************************/ + if (GetDistinctColumnValues(g, nrec)) + return RC_FX; + + OpenDB(g); // Rewind the table file + } // endif xdb2 + +#if defined(PROG_INFO) + /*********************************************************************/ + /* Initialize progress information */ + /*********************************************************************/ + char *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name)); + + dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name); + dup->ProgMax = GetProgMax(g); + dup->ProgCur = 0; +#endif // SOCKET_MODE || THREAD + + /*********************************************************************/ + /* Make block starting pos and min/max values of cluster columns. */ + /*********************************************************************/ + while ((rc = ReadDB(g)) == RC_OK) { + if (blocked) { + // A blocked FAM class handles CurNum and CurBlk (ZLBFAM) + if (!Txfp->CurNum) + Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos(); + + } else { + if (++curnum >= nrec) { + if (++curblk >= block) { + strcpy(g->Message, MSG(BAD_BLK_ESTIM)); + goto err; + } else + curnum = 0; + + // Get block starting position + Txfp->BlkPos[curblk] = Txfp->GetPos(); + } // endif CurNum + + last = curnum + 1; // curnum is zero based + Txfp->CurBlk = curblk; // Used in COLDOS::SetMinMax + Txfp->CurNum = curnum; // Used in COLDOS::SetMinMax + } // endif blocked + + /*******************************************************************/ + /* Now calculate the min and max values for the cluster columns. */ + /*******************************************************************/ + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext()) + if (colp->Clustered == 2) { + if (colp->SetBitMap(g)) + goto err; + + } else + if (colp->SetMinMax(g)) + goto err; // Currently: column is not sorted + +#if defined(SOCKET_MODE) || defined(THREAD) +#if defined(SOCKET_MODE) + if (SendProgress(dup)) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + goto err; + } else +#elif defined(THREAD) + if (!dup->Step) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + goto err; + } else +#endif // THREAD + dup->ProgCur = GetProgCur(); +#endif // SOCKET_MODE || THREAD + + } // endwhile + + if (rc == RC_EF) { + Txfp->Nrec = nrec; + + if (blocked) { + Txfp->Block = Txfp->CurBlk + 1; + Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec; + } else { + Txfp->Block = curblk + 1; + Txfp->Last = last; + } // endif blocked + + // This is needed to be able to calculate the last block size + Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos(); + } else + goto err; + + /*********************************************************************/ + /* Save the optimization values for this table. */ + /*********************************************************************/ + if (!SaveBlockValues(g)) { + PCATLG cat = PlgGetCatalog(g); + + defp->Block = Txfp->Block; + defp->Last = Txfp->Last; + CloseDB(g); + cat->SetIntCatInfo("Blocks", Txfp->Block); + cat->SetIntCatInfo("Last", Txfp->Last); + return RC_OK; + } // endif SaveBlockValues + + err: + // Restore Desc memory suballocation +//if (memp) +// *(PPOOLHEADER)memp = save; + + defp->RemoveOptValues(g); + CloseDB(g); + return RC_FX; + } // end of MakeBlockValues + +/***********************************************************************/ +/* Save the block and Min/Max values for this table. */ +/* The problem here is to avoid name duplication, because more than */ +/* one data file can have the same name (but different types) and/or */ +/* the same data file can be used with different block sizes. This is */ +/* why we use Ofn that defaults to the file name but can be set to a */ +/* different name if necessary. */ +/***********************************************************************/ +bool TDBDOS::SaveBlockValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + int lg, n[NZ + 2]; + size_t nbk, ndv, nbm, block = Txfp->Block; + bool rc = FALSE; + FILE *opfile; + PDOSCOL colp; + PDOSDEF defp = (PDOSDEF)To_Def; + + if (defp->GetOptFileName(g, filename)) + return TRUE; + + if (!(opfile = fopen(filename, "wb"))) { + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "wb", (int)errno, filename); + strcat(strcat(g->Message, ": "), strerror(errno)); + + if (trace) + htrc("%s\n", g->Message); + + return TRUE; + } // endif opfile + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Write block starting positions into the opt file. */ + /*******************************************************************/ + block++; + lg = sizeof(int); + n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block; + + if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPTBLK_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + block--; // = Txfp->Block; + } // endif Ftype + + /*********************************************************************/ + /* Write the Min/Max values into the opt file. */ + /*********************************************************************/ + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) { + lg = colp->Value->GetClen(); + + // Now start the writing process + if (colp->Clustered == 2) { + // New XDB2 block optimization. Will be recognized when reading + // because the column index is negated. + ndv = colp->Ndv; nbm = colp->Nbm; + nbk = nbm * block; + n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; + n[4] = ndv; n[5] = nbm; + + if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) { + sprintf(g->Message, MSG(OPT_DVAL_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) { + sprintf(g->Message, MSG(OPT_BMAP_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + } else { + n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block; + + if (fwrite(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPT_MIN_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) { + sprintf(g->Message, MSG(OPT_MAX_WR_ERR), strerror(errno)); + rc = TRUE; + } // endif size + + } // endif Clustered + + } // endfor colp + + fclose(opfile); + return rc; + } // end of SaveBlockValues + +/***********************************************************************/ +/* Read the Min/Max values for this table. */ +/* The problem here is to avoid name duplication, because more than */ +/* one data file can have the same name (but different types) and/or */ +/* the same data file can be used with different block sizes. This is */ +/* why we use Ofn that defaults to the file name but can be set to a */ +/* different name if necessary. */ +/***********************************************************************/ +bool TDBDOS::GetBlockValues(PGLOBAL g) + { + char filename[_MAX_PATH]; + int i, lg, n[NZ]; + int nrec, block, last = 0, allocblk = 0; + int len; + bool newblk = FALSE; + size_t ndv, nbm, nbk, blk; + FILE *opfile; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)To_Def; + PCATLG cat = defp->GetCat(); + + if (defp->Optimized) + return FALSE; // Already done or to be redone + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Variable length file that can be read by block. */ + /*******************************************************************/ + block = defp->GetBlock(); + nrec = (defp->GetElemt()) ? defp->GetElemt() : 1; + last = defp->GetLast(); + + if (nrec > 1 && block) + len = (int)((block - 1) * nrec + last); + else + len = 0; + + if (!len) { + if (nrec > 1) { + // The table can be declared optimized if it is void. + // This is useful to handle Insert in optimized mode. + char filename[_MAX_PATH]; + int h; + int flen = -1; + + PlugSetPath(filename, defp->Fn, GetPath()); + h = open(filename, _O_RDONLY); + flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h); + defp->SetOptimized((flen) ? 0 : 1); + + if (h != -1) + close(h); + + } // endif nrec + + return FALSE; // Opt file does not exist yet + } else if (len < 0) + return TRUE; // Table error + + cdp = defp->GetCols(); + i = 1; + } else { + /*******************************************************************/ + /* Fixed length file. Opt file exists only for clustered columns. */ + /*******************************************************************/ + // Check for existence of clustered columns + for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) + break; + + if (!cdp) + return FALSE; // No optimization needed + + if ((len = Cardinality(g)) < 0) + return TRUE; // Table error + else if (!len) + return FALSE; // File does not exist yet + + block = Txfp->Block; // Was set in Cardinality + nrec = Txfp->Nrec; + } // endif Ftype + + if (defp->GetOptFileName(g, filename)) + return TRUE; + + if (!(opfile = fopen(filename, "rb"))) + return FALSE; // No saved values + +//if (memp) { +// save = *(PPOOLHEADER)memp; +// allocblk = defp->GetAllocBlks(); +// } // endif memp + + if (block > allocblk) + newblk = TRUE; // Current allocation is too small + + if (Ftype == RECFM_VAR || defp->Compressed == 2) { + /*******************************************************************/ + /* Read block starting positions from the opt file. */ + /*******************************************************************/ + blk = block + 1; + lg = sizeof(int); + + if (fread(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (n[0] != last || n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (newblk) + defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg); + + if (fread(defp->To_Pos, lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPTBLK_RD_ERR), strerror(errno)); + goto err; + } // endif size + + } // endif Ftype + + /*********************************************************************/ + /* Read the Min/Max values from the opt file. */ + /*********************************************************************/ + for (; cdp; cdp = cdp->GetNext(), i++) + if (cdp->GetOpt()) { + lg = cdp->GetClen(); + blk = block; + + // Now start the reading process. + if (fread(n, sizeof(int), NZ, opfile) != NZ) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (n[0] == -i) { + // Read the XDB2 opt values from the opt file + if (n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (fread(n, sizeof(int), 2, opfile) != 2) { + sprintf(g->Message, MSG(OPT_HEAD_RD_ERR), strerror(errno)); + goto err; + } // endif fread + + ndv = n[0]; nbm = n[1]; nbk = nbm * blk; + + if (cdp->GetNdv() < (int)ndv || !cdp->GetDval()) + cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg)); + + cdp->SetNdv((int)ndv); + + if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) { + sprintf(g->Message, MSG(OPT_DVAL_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap()) + cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int))); + + cdp->SetNbm((int)nbm); + + if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) { + sprintf(g->Message, MSG(OPT_BMAP_RD_ERR), strerror(errno)); + goto err; + } // endif size + + cdp->SetXdb2(TRUE); + } else { + // Read the Min/Max values from the opt file + if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), filename); + goto err; + } // endif + + if (newblk || !cdp->GetMin()) + cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg)); + + if (fread(cdp->GetMin(), lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPT_MIN_RD_ERR), strerror(errno)); + goto err; + } // endif size + + if (newblk || !cdp->GetMax()) + cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg)); + + if (fread(cdp->GetMax(), lg, blk, opfile) != blk) { + sprintf(g->Message, MSG(OPT_MAX_RD_ERR), strerror(errno)); + goto err; + } // endif size + + cdp->SetXdb2(FALSE); + } // endif n[0] (XDB2) + + } // endif Clustered + + defp->SetBlock(block); + + if (newblk) + defp->SetAllocBlks(block); + + defp->SetOptimized(1); + fclose(opfile); + MaxSize = -1; // Can be refined later + return FALSE; + + err: + defp->RemoveOptValues(g); + fclose(opfile); +//return TRUE; + // Ignore error if not in mode CHK_OPT + return (PlgGetUser(g)->Check & CHK_OPT) != 0; + } // end of GetBlockValues + +/***********************************************************************/ +/* This fonction is used while making XDB2 block optimization. */ +/* It constructs for each elligible columns, the sorted list of the */ +/* distinct values existing in the column. This function uses an */ +/* algorithm that permit to get several sets of distinct values by */ +/* reading the table only once, which cannot be done using a standard */ +/* SQL query. */ +/***********************************************************************/ +bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec) + { + char *p; + int rc, blk, n = 0; + PDOSCOL colp; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Initialize progress information */ + /*********************************************************************/ + p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name)); + dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name); + dup->ProgMax = GetProgMax(g); + dup->ProgCur = 0; + + while ((rc = ReadDB(g)) == RC_OK) { + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) + if (colp->Clustered == 2) + if (colp->AddDistinctValue(g)) + return TRUE; // Too many distinct values + +#if defined(SOCKET_MODE) + if (SendProgress(dup)) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + return TRUE; + } else +#elif defined(THREAD) + if (!dup->Step) { + strcpy(g->Message, MSG(OPT_CANCELLED)); + return TRUE; + } else +#endif // THREAD + dup->ProgCur = GetProgCur(); + + n++; + } // endwhile + + if (rc != RC_EF) + return TRUE; + + // Reset the number of table blocks +//nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value) + blk = (n + nrec - 1) / nrec; + Txfp->Block = blk; // Useful mainly for ZLBFAM ??? + + // Set Nbm, Bmap for XDB2 columns + for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) + if (colp->Clustered == 2) { +// colp->Cdp->SetNdv(colp->Ndv); + colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP; + colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk); + } // endif Clustered + + return FALSE; + } // end of GetDistinctColumnValues + +/***********************************************************************/ +/* Analyze the filter and construct the Block Evaluation Filter. */ +/* This is possible when a filter contains predicates implying a */ +/* column marked as "clustered" or "sorted" matched to a constant */ +/* argument. It is then possible by comparison against the smallest */ +/* and largest column values in each block to determine whether the */ +/* filter condition will be always true or always false for the block.*/ +/***********************************************************************/ +PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp) + { + bool blk = Txfp->Blocked; + + if (To_BlkFil) + return To_BlkFil; // Already done + else if (!filp) + return NULL; + else if (blk) { + if (Txfp->GetAmType() == TYPE_AM_DBF) + /*****************************************************************/ + /* If RowID is used in this query, block optimization cannot be */ + /* used because currently the file must be read sequentially. */ + /*****************************************************************/ + for (PCOL cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm()) + return NULL; + + } // endif blk + + int i, op = filp->GetOpc(), opm = filp->GetOpm(), n = 0; + bool cnv[2]; + PCOL colp; + PXOB arg[2] = {NULL,NULL}; + PBF *fp = NULL, bfp = NULL; + + switch (op) { + case OP_EQ: + case OP_NE: + case OP_GT: + case OP_GE: + case OP_LT: + case OP_LE: + if (! opm) { + for (i = 0; i < 2; i++) { + arg[i] = filp->Arg(i); + cnv[i] = filp->Conv(i); + } // endfor i + + bfp = CheckBlockFilari(g, arg, op, cnv); + break; + } // endif !opm + + // if opm, pass thru + case OP_IN: + if (filp->GetArgType(0) == TYPE_COLBLK && + filp->GetArgType(1) == TYPE_ARRAY) { + arg[0] = filp->Arg(0); + arg[1] = filp->Arg(1); + colp = (PCOL)arg[0]; + + if (colp->GetTo_Tdb() == this) { + // Block evaluation is possible for... + if (colp->GetAmType() == TYPE_AM_ROWID) { + // Special column ROWID and constant array, but + // currently we don't know how to retrieve a RowID + // from a DBF table that is not sequentially read. +// if (Txfp->GetAmType() != TYPE_AM_DBF || +// ((RIDBLK*)arg[0])->GetRnm()) + bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec); + + } else if (blk && Txfp->Nrec > 1 && colp->IsClustered()) + // Clustered column and constant array + if (colp->GetClustered() == 2) + bfp = new(g) BLKFILIN2(g, this, op, opm, arg); + else + bfp = new(g) BLKFILIN(g, this, op, opm, arg); + + } // endif this + +#if 0 + } else if (filp->GetArgType(0) == TYPE_SCALF && + filp->GetArgType(1) == TYPE_ARRAY) { + arg[0] = filp->Arg(0); + arg[1] = filp->Arg(1); + + if (((PSCALF)arg[0])->GetOp() == OP_ROW && + arg[1]->GetResultType() == TYPE_LIST) { + PARRAY par = (PARRAY)arg[1]; + LSTVAL *vlp = (LSTVAL*)par->GetValue(); + + ((SFROW*)arg[0])->GetParms(n); + + if (n != vlp->GetN()) + return NULL; + else + n = par->GetNval(); + + arg[1] = new(g) CONSTANT(vlp); + fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF)); + cnv[0] = cnv[1] = FALSE; + + if (op == OP_IN) + op = OP_EQ; + + for (i = 0; i < n; i++) { + par->GetNthValue(vlp, i); + + if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv))) + return NULL; + + } // endfor i + + bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n); + } // endif ROW +#endif // 0 + + } // endif Type + + break; + case OP_AND: + case OP_OR: + fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF)); + fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0))); + fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1))); + + if (fp[0] || fp[1]) + bfp = new(g) BLKFILLOG(this, op, fp, 2); + + break; + case OP_NOT: + fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF)); + + if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0))))) + bfp = new(g) BLKFILLOG(this, op, fp, 1); + + break; + case OP_LIKE: + default: + break; + } // endswitch op + + return bfp; + } // end of InitBlockFilter + +/***********************************************************************/ +/* Analyze the passed arguments and construct the Block Filter. */ +/***********************************************************************/ +PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv) + { +//int i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; +//bool conv = FALSE, xdb2 = FALSE, ok = FALSE, b[2]; +//PXOB *xarg1, *xarg2 = NULL, xp[2]; + int i, ctype = TYPE_ERROR, n = 0, type[2] = {0,0}; + bool conv = FALSE, xdb2 = FALSE, ok = FALSE; + PXOB *xarg2 = NULL, xp[2]; + PCOL colp; +//LSTVAL *vlp = NULL; +//SFROW *sfr[2]; + PBF *fp = NULL, bfp = NULL; + + for (i = 0; i < 2; i++) { + switch (arg[i]->GetType()) { + case TYPE_CONST: + type[i] = 1; + ctype = arg[i]->GetResultType(); + break; + case TYPE_COLBLK: + conv = cnv[i]; + colp = (PCOL)arg[i]; + + if (colp->GetTo_Tdb() == this) { + if (colp->GetAmType() == TYPE_AM_ROWID) { + // Currently we don't know how to retrieve a RowID + // from a DBF table that is not sequentially read. +// if (Txfp->GetAmType() != TYPE_AM_DBF || +// ((RIDBLK*)arg[i])->GetRnm()) + type[i] = 5; + + } else if (Txfp->Blocked && Txfp->Nrec > 1 && + colp->IsClustered()) { + type[i] = 2; + xdb2 = colp->GetClustered() == 2; + } // endif Clustered + + } else if (colp->GetColUse(U_CORREL)) { + // This is a column pointing to the outer query of a + // correlated subquery, it has a constant value during + // each execution of the subquery. + type[i] = 1; + ctype = arg[i]->GetResultType(); + } // endif this + + break; +// case TYPE_SCALF: +// if (((PSCALF)arg[i])->GetOp() == OP_ROW) { +// sfr[i] = (SFROW*)arg[i]; +// type[i] = 7; +// } // endif Op + +// break; + default: + break; + } // endswitch ArgType + + if (!type[i]) + break; + + n += type[i]; + } // endfor i + + if (n == 3 || n == 6) { + if (conv) { + // The constant has not the good type and will not match + // the block min/max values. What we can do here is either + // abort with an error message or simply not do the block + // optimization (as column values can be converted when + // evaluating the filter.) Currently we prefer aborting + // because the user may count on the performance enhancing + // and silently not doing it is probably worse than just + // telling him to fix his query. + sprintf(g->Message, "Block opt: %s", MSG(VALTYPE_NOMATCH)); + longjmp(g->jumper[g->jump_level], 99); + } // endif Conv + + if (type[0] == 1) { + // Make it always as Column-op-Value + *xp = arg[0]; + arg[0] = arg[1]; + arg[1] = *xp; + + switch (op) { + case OP_GT: op = OP_LT; break; + case OP_GE: op = OP_LE; break; + case OP_LT: op = OP_GT; break; + case OP_LE: op = OP_GE; break; + } // endswitch op + + } // endif + +#if defined(_DEBUG) +// assert(arg[0]->GetResultType() == ctype); +#endif + + if (n == 3) { + if (xdb2) { + if (((PDOSCOL)arg[0])->GetNbm() == 1) + bfp = new(g) BLKFILAR2(g, this, op, arg); + else // Multiple bitmap made of several ULONG's + bfp = new(g) BLKFILMR2(g, this, op, arg); + } else + bfp = new(g) BLKFILARI(g, this, op, arg); + + } else // n = 6 + bfp = new(g) BLKSPCARI(this, op, arg, Txfp->Nrec); + +#if 0 + } else if (n == 8 || n == 14) { + if (n == 8 && ctype != TYPE_LIST) { + // Should never happen + strcpy(g->Message, "Block opt: bad constant"); + longjmp(g->jumper[g->jump_level], 99); + } // endif Conv + + if (type[0] == 1) { + // Make it always as Column-op-Value + sfr[0] = sfr[1]; + arg[1] = arg[0]; + + switch (op) { + case OP_GT: op = OP_LT; break; + case OP_GE: op = OP_LE; break; + case OP_LT: op = OP_GT; break; + case OP_LE: op = OP_GE; break; + } // endswitch op + + } // endif + + xarg1 = sfr[0]->GetParms(n1); + + if (n == 8) { + vlp = (LSTVAL*)arg[1]->GetValue(); + n2 = vlp->GetN(); + xp[1] = new(g) CONSTANT((PVAL)NULL); + } else + xarg2 = sfr[1]->GetParms(n2); + + if (n1 != n2) + return NULL; // Should we flag an error ? + + fp = (PBF*)PlugSubAlloc(g, NULL, n1 * sizeof(PBF)); + + for (i = 0; i < n1; i++) { + xp[0] = xarg1[i]; + + if (n == 8) + ((CONSTANT*)xp[1])->SetValue(vlp->GetSubVal(i)); + else + xp[1] = xarg2[i]; + + b[0] = b[1] = (xp[0]->GetResultType() != xp[1]->GetResultType()); + ok |= ((fp[i] = CheckBlockFilari(g, xp, op, b)) != NULL); + } // endfor i + + if (ok) + bfp = new(g) BLKFILLOG(this, OP_AND, fp, n1); +#endif // 0 + + } // endif n + + return bfp; + } // end of CheckBlockFilari + +/***********************************************************************/ +/* ResetBlkFil: reset the block filter and restore filtering. */ +/***********************************************************************/ +void TDBDOS::ResetBlockFilter(PGLOBAL g) + { + if (!To_BlkFil) + return; + + To_BlkFil->Reset(g); + + if (SavFil && !To_Filter) { + // Restore filter if it was disabled by optimization + To_Filter = SavFil; + SavFil = NULL; + } // endif + + Beval = 0; + } // end of ResetBlockFilter + +/***********************************************************************/ +/* Block optimization: evaluate the block index filter against */ +/* the min and max values of this block and return: */ +/* RC_OK: if some records in the block can meet filter criteria. */ +/* RC_NF: if no record in the block can meet filter criteria. */ +/* RC_EF: if no record in the remaining file can meet filter criteria.*/ +/* In addition, temporarily supress filtering if all the records in */ +/* the block meet filter criteria. */ +/***********************************************************************/ +int TDBDOS::TestBlock(PGLOBAL g) + { + int rc = RC_OK; + + if (To_BlkFil && Beval != 2) { + // Check for block filtering evaluation + if (Beval == 1) { + // Filter was removed for last block, restore it + To_Filter = SavFil; + SavFil = NULL; + } // endif Beval + + // Check for valid records in new block + switch (Beval = To_BlkFil->BlockEval(g)) { + case -2: // No more valid values in file + rc = RC_EF; + break; + case -1: // No valid values in block + rc = RC_NF; + break; + case 1: // All block values are valid + case 2: // All subsequent file values are Ok + // Before suppressing the filter for the block(s) it is + // necessary to reset the filtered columns to NOT_READ + // so their new values are retrieved by the SELECT list. + if (To_Filter) // Can be NULL when externally called (XDB) + To_Filter->Reset(); + + SavFil = To_Filter; + To_Filter = NULL; // So remove filter + } // endswitch Beval + + if (trace) + htrc("BF Eval Beval=%d\n", Beval); + + } // endif To_BlkFil + + return rc; + } // end of TestBlock + +/***********************************************************************/ +/* Check whether we have to create/update permanent indexes. */ +/***********************************************************************/ +int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add) + { + int k, n; + bool fixed, doit, sep, b = (pxdf != NULL); + PCOL *keycols, colp; + PIXDEF xdp, sxp = NULL; + PKPDEF kdp; + PDOSDEF dfp; +//PCOLDEF cdp; + PXINDEX x; + PXLOAD pxp; + PCATLG cat = PlgGetCatalog(g); + + Mode = MODE_READ; + Use = USE_READY; + dfp = (PDOSDEF)To_Def; + fixed = Cardinality(g) >= 0; + + // Are we are called from CreateTable or CreateIndex? + if (pxdf) { + if (!add && dfp->GetIndx()) { + strcpy(g->Message, MSG(INDX_EXIST_YET)); + return RC_FX; + } // endif To_Indx + + if (add && dfp->GetIndx()) { + for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext()) + if (!stricmp(sxp->GetName(), pxdf->GetName())) { + sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name); + return RC_FX; + } else if (!sxp->GetNext()) + break; + + sxp->SetNext(pxdf); +// first = false; + } else + dfp->SetIndx(pxdf); + +// pxdf->SetDef(dfp); + } else if (!(pxdf = dfp->GetIndx())) + return RC_INFO; // No index to make + + // Allocate all columns that will be used by indexes. + // This must be done before opening the table so specific + // column initialization can be done ( in particular by TDBVCT) + for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext()) + for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { + if (!(colp = ColDB(g, kdp->GetName(), 0))) { + sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name); + goto err; + } else if (colp->GetResultType() == TYPE_DECIM) { + sprintf(g->Message, "Decimal columns are not indexable yet"); + goto err; + } // endif Type + + colp->InitValue(g); + n = max(n, xdp->GetNparts()); + } // endfor kdp + + keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL)); + sep = cat->GetBoolCatInfo("SepIndex", false); + + /*********************************************************************/ + /* Construct and save the defined indexes. */ + /*********************************************************************/ + for (xdp = pxdf; xdp; xdp = xdp->GetNext()) + if (!OpenDB(g)) { + if (xdp->IsAuto() && fixed) + // Auto increment key and fixed file: use an XXROW index + continue; // XXROW index doesn't need to be made + + // On Update, redo only indexes that are modified + doit = !To_SetCols; + n = 0; + + if (sxp) + xdp->SetID(sxp->GetID() + 1); + + for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { + // Check whether this column was updated + for (colp = To_SetCols; !doit && colp; colp = colp->GetNext()) + if (!stricmp(kdp->GetName(), colp->GetName())) + doit = true; + + keycols[n++] = ColDB(g, kdp->GetName(), 0); + } // endfor kdp + + // If no indexed columns were updated, don't remake the index + // if indexes are in separate files. + if (!doit && sep) + continue; + + k = xdp->GetNparts(); + + // Make the index and save it + if (dfp->Huge) + pxp = new(g) XHUGE; + else + pxp = new(g) XFILE; + + if (k == 1) // Simple index + x = new(g) XINDXS(this, xdp, pxp, keycols); + else // Multi-Column index + x = new(g) XINDEX(this, xdp, pxp, keycols); + + if (!x->Make(g, sxp)) { + // Retreive define values from the index + xdp->SetMaxSame(x->GetMaxSame()); +// xdp->SetSize(x->GetSize()); + + // store KXYCOL Mxs in KPARTDEF Mxsame + xdp->SetMxsame(x); + +#if defined(TRACE) + printf("Make done...\n"); +#endif // TRACE + +// if (x->GetSize() > 0) + sxp = xdp; + + xdp->SetInvalid(false); + } else + goto err; + + } else + return RC_INFO; // Error or Physical table does not exist + + if (Use == USE_OPEN) + CloseDB(g); + + return RC_OK; + +err: + if (sxp) + sxp->SetNext(NULL); + else + dfp->SetIndx(NULL); + + return RC_FX; + } // end of MakeIndex + +/***********************************************************************/ +/* DOS GetProgMax: get the max value for progress information. */ +/***********************************************************************/ +int TDBDOS::GetProgMax(PGLOBAL g) + { + return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g); + } // end of GetProgMax + +/***********************************************************************/ +/* DOS GetProgCur: get the current value for progress information. */ +/***********************************************************************/ +int TDBDOS::GetProgCur(void) + { + return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos(); + } // end of GetProgCur + +/***********************************************************************/ +/* RowNumber: return the ordinal number of the current row. */ +/***********************************************************************/ +int TDBDOS::RowNumber(PGLOBAL g, bool b) + { + if (To_Kindex) { + /*******************************************************************/ + /* Don't know how to retrieve RowID from file address. */ + /*******************************************************************/ + sprintf(g->Message, MSG(NO_ROWID_FOR_AM), + GetAmName(g, Txfp->GetAmType())); + return 0; + } else + return Txfp->GetRowID(); + + } // end of RowNumber + +/***********************************************************************/ +/* DOS Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int TDBDOS::Cardinality(PGLOBAL g) + { + if (!g) + return Txfp->Cardinality(g); + + if (Cardinal < 0) + Cardinal = Txfp->Cardinality(g); + + return Cardinal; + } // end of Cardinality + +/***********************************************************************/ +/* DOS GetMaxSize: returns file size estimate in number of lines. */ +/* This function covers variable record length files. */ +/***********************************************************************/ +int TDBDOS::GetMaxSize(PGLOBAL g) + { + if (MaxSize >= 0) + return MaxSize; + + if (!Cardinality(NULL)) { + int len = GetFileLength(g); + + if (len >= 0) { + if (trace) + htrc("Estimating lines len=%d ending=%d\n", + len, ((PDOSDEF)To_Def)->Ending); + + /*****************************************************************/ + /* Estimate the number of lines in the table (if not known) by */ + /* dividing the file length by the minimum line length assuming */ + /* only the last column can be of variable length. This will be */ + /* a ceiling estimate (as last column is never totally absent). */ + /*****************************************************************/ + int rec = ((PDOSDEF)To_Def)->Ending; // +2: CRLF +1: LF + + if (AvgLen <= 0) // No given average estimate + rec += EstimatedLength(g); + else // A lower estimate was given for the average record length + rec += (int)AvgLen; + + if (trace) + htrc(" Filen=%d min_rec=%d\n", len, rec); + + MaxSize = (len + rec - 1) / rec; + + if (trace) + htrc(" Estimated max_K=%d\n", MaxSize); + + } // endif len + + } else + MaxSize = Cardinality(g); + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* DOS EstimatedLength. Returns an estimated minimum line length. */ +/***********************************************************************/ +int TDBDOS::EstimatedLength(PGLOBAL g) + { + int dep = 0; + PCOLDEF cdp = To_Def->GetCols(); + + if (!cdp->GetNext()) { + // One column table, we are going to return a ridiculous + // result if we set dep to 1 + dep = 1 + cdp->GetLong() / 20; // Why 20 ????? + } else for (; cdp; cdp = cdp->GetNext()) + dep = max(dep, cdp->GetOffset()); + + return (int)dep; + } // end of Estimated Length + +/***********************************************************************/ +/* DOS tables favor the use temporary files for Update. */ +/***********************************************************************/ +bool TDBDOS::IsUsingTemp(PGLOBAL g) + { + USETEMP usetemp = PlgGetUser(g)->UseTemp; + + return (usetemp == TMP_YES || usetemp == TMP_FORCE || + (usetemp == TMP_AUTO && Mode == MODE_UPDATE)); + } // end of IsUsingTemp + +/***********************************************************************/ +/* DOS Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBDOS::OpenDB(PGLOBAL g) + { + if (trace) + htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n", + this, Tdb_No, Use, Mode); + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + if (!To_Kindex) { + Txfp->Rewind(); // see comment in Work.log + + if (SkipHeader(g)) + return TRUE; + + } else + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + ResetBlockFilter(g); + return false; + } // endif use + + if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS) { + // Delete all lines. Not handled in MAP or block mode + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + Txfp->SetTdbp(this); + } else if (Txfp->Blocked && (Mode == MODE_DELETE || + (Mode == MODE_UPDATE && PlgGetUser(g)->UseTemp != TMP_NO))) { + /*******************************************************************/ + /* Delete is not currently handled in block mode neither Update */ + /* when using a temporary file. */ + /*******************************************************************/ + if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE) + Txfp = new(g) MAPFAM((PDOSDEF)To_Def); +#if defined(ZIP_SUPPORT) + else if (Txfp->GetAmType() == TYPE_AM_ZIP) + Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); +#endif // ZIP_SUPPORT + else if (Txfp->GetAmType() != TYPE_AM_DOS) + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + + Txfp->SetTdbp(this); + } // endif Mode + + /*********************************************************************/ + /* Open according to logical input/output mode required. */ + /* Use conventionnal input/output functions. */ + /* Treat files as binary in Delete mode (for line moving) */ + /*********************************************************************/ + if (Txfp->OpenTableFile(g)) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Allocate the block filter tree if evaluation is possible. */ + /*********************************************************************/ + To_BlkFil = InitBlockFilter(g, To_Filter); + + /*********************************************************************/ + /* Allocate the line buffer plus a null character. */ + /*********************************************************************/ + To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1); + + if (Mode == MODE_INSERT) { + // Spaces between fields must be filled with blanks + memset(To_Line, ' ', Lrecl); + To_Line[Lrecl] = '\0'; + } else + memset(To_Line, 0, Lrecl + 1); + + if (trace) + htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line); + + if (SkipHeader(g)) // When called from CSV/FMT files + return true; + + /*********************************************************************/ + /* Reset statistics values. */ + /*********************************************************************/ + num_read = num_there = num_eq[0] = num_eq[1] = 0; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for DOS access method. */ +/***********************************************************************/ +int TDBDOS::ReadDB(PGLOBAL g) + { + if (trace > 1) + htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n", + GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line); + + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + num_there++; + return RC_OK; + default: + /***************************************************************/ + /* Set the file position according to record to read. */ + /***************************************************************/ + if (SetRecpos(g, recpos)) + return RC_FX; + + if (trace > 1) + htrc("File position is now %d\n", GetRecpos()); + + if (Mode == MODE_READ) + /*************************************************************/ + /* Defer physical reading until one column setting needs it */ + /* as it can be a big saving on joins where no other column */ + /* than the keys are used, so reading is unnecessary. */ + /*************************************************************/ + if (Txfp->DeferReading()) + return RC_OK; + + } // endswitch recpos + + } // endif To_Kindex + + if (trace > 1) + htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line); + + /*********************************************************************/ + /* Now start the reading process. */ + /*********************************************************************/ + return ReadBuffer(g); + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for DOS access method. */ +/***********************************************************************/ +int TDBDOS::WriteDB(PGLOBAL g) + { + if (trace > 1) + htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode); + + if (!Ftype && (Mode == MODE_INSERT || Txfp->GetUseTemp())) { + char *p; + + /*******************************************************************/ + /* Suppress trailing blanks. */ + /* Also suppress eventual null from last line. */ + /*******************************************************************/ + for (p = To_Line + Lrecl -1; p >= To_Line; p--) + if (*p && *p != ' ') + break; + + *(++p) = '\0'; + } // endif Mode + + if (trace > 1) + htrc("Write: line is='%s'\n", To_Line); + + // Now start the writing process + return Txfp->WriteBuffer(g); + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for DOS (and FIX) access method. */ +/* RC_FX means delete all. Nothing to do here (was done at open). */ +/***********************************************************************/ +int TDBDOS::DeleteDB(PGLOBAL g, int irc) + { + return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc); + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for DOS access method. */ +/***********************************************************************/ +void TDBDOS::CloseDB(PGLOBAL g) + { + if (To_Kindex) { + To_Kindex->Close(); + To_Kindex = NULL; + } // endif + + Txfp->CloseTableFile(g); + } // end of CloseDB + +// ------------------------ DOSCOL functions ---------------------------- + +/***********************************************************************/ +/* DOSCOL public constructor (also called by MAPCOL). */ +/***********************************************************************/ +DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am) + : COLBLK(cdp, tp, i) + { + char *p; + int prec = Format.Prec; + PTXF txfp = ((PTDBDOS)tp)->Txfp; + + assert(cdp); + + if (cp) { + Next = cp->GetNext(); + cp->SetNext(this); + } else { + Next = tp->GetColumns(); + tp->SetColumns(this); + } // endif cprec + + // Set additional Dos access method information for column. + Deplac = cdp->GetOffset(); + Long = cdp->GetLong(); + To_Val = NULL; + Clustered = 0; + Sorted = 0; + Ndv = 0; // Currently used only for XDB2 + Nbm = 0; // Currently used only for XDB2 + Min = NULL; + Max = NULL; + Bmap = NULL; + Dval = NULL; + Buf = NULL; + + if (txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) { + int nblk = txfp->GetBlock(); + + Clustered = (cdp->GetXdb2()) ? 2 : 1; + Sorted = (cdp->GetOpt() > 1) ? 1 : 0; // Currently ascending only + + if (Clustered == 1) { + Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec); + Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec); + } else { // Clustered == 2 + // Ndv is the number of distinct values in Dval. Ndv and Nbm + // may be 0 when optimizing because Ndval is not filled yet, + // but the size of the passed Dval memory block is Ok. + Ndv = cdp->GetNdv(); + Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec); + + // Bmap cannot be allocated when optimizing, we must know Nbm first + if ((Nbm = cdp->GetNbm())) + Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk); + + } // endif Clustered + + } // endif Opt + + OldVal = NULL; // Currently used only in MinMax + Ldz = false; + Nod = false; + Dcm = -1; + p = cdp->GetFmt(); + + if (p && IsTypeNum(Buf_Type)) { + // Formatted numeric value + for (; p && *p && isalpha(*p); p++) + switch (toupper(*p)) { + case 'Z': // Have leading zeros + Ldz = true; + break; + case 'N': // Have no decimal point + Nod = true; + break; + } // endswitch p + + // Set number of decimal digits + Dcm = (*p) ? atoi(p) : GetScale(); + } // endif fmt + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of DOSCOL constructor + +/***********************************************************************/ +/* DOSCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Deplac = col1->Deplac; + Long = col1->Long; + To_Val = col1->To_Val; + Ldz = col1->Ldz; + Nod = col1->Nod; + Dcm = col1->Dcm; + OldVal = col1->OldVal; + Buf = col1->Buf; + Clustered = col1->Clustered; + Sorted = col1->Sorted; + Min = col1->Min; + Max = col1->Max; + Bmap = col1->Bmap; + Dval = col1->Dval; + Ndv = col1->Ndv; + Nbm = col1->Nbm; + } // end of DOSCOL copy constructor + +/***********************************************************************/ +/* VarSize: This function tells UpdateDB whether or not the block */ +/* optimization file must be redone if this column is updated, even */ +/* it is not sorted or clustered. This applies to the last column of */ +/* a variable length table that is blocked, because if it is updated */ +/* using a temporary file, the block size may be modified. */ +/***********************************************************************/ +bool DOSCOL::VarSize(void) + { + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + PTXF txfp = tdbp->Txfp; + + if (Cdp && !Cdp->GetNext() // Must be the last column + && tdbp->Ftype == RECFM_VAR // of a DOS variable length + && txfp->Blocked // blocked table + && txfp->GetUseTemp()) // using a temporary file. + return true; + else + return false; + + } // end VarSize + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_DOUBLE) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetScale()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Allocate the buffer used in WriteColumn for numeric columns + if (IsTypeNum(Buf_Type)) + Buf = (char*)PlugSubAlloc(g, NULL, max(32, Long + Dcm + 1)); + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the last line */ +/* read from the corresponding table, extract from it the field */ +/* corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void DOSCOL::ReadColumn(PGLOBAL g) + { + char *p = NULL; + int i, rc; + int field; + double dval; + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + + if (trace > 1) + htrc( + "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n", + Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type); + + /*********************************************************************/ + /* If physical reading of the line was deferred, do it now. */ + /*********************************************************************/ + if (!tdbp->IsRead()) + if ((rc = tdbp->ReadBuffer(g)) != RC_OK) { + if (rc == RC_EF) + sprintf(g->Message, MSG(INV_DEF_READ), rc); + + longjmp(g->jumper[g->jump_level], 11); + } // endif + + p = tdbp->To_Line + Deplac; + field = Long; + + switch (tdbp->Ftype) { + case RECFM_VAR: + /*****************************************************************/ + /* For a variable length file, check if the field exists. */ + /*****************************************************************/ + if (strlen(tdbp->To_Line) < (unsigned)Deplac) + field = 0; + + case RECFM_FIX: // Fixed length text file + case RECFM_DBF: // Fixed length DBase file + if (Nod) switch (Buf_Type) { + case TYPE_INT: + case TYPE_SHORT: + case TYPE_TINY: + case TYPE_BIGINT: + if (Value->SetValue_char(p, field - Dcm)) { + sprintf(g->Message, "Out of range value for column %s at row %d", + Name, tdbp->RowNumber(g)); + PushWarning(g, tdbp); + } // endif SetValue_char + + break; + case TYPE_DOUBLE: + Value->SetValue_char(p, field); + dval = Value->GetFloatValue(); + + for (i = 0; i < Dcm; i++) + dval /= 10.0; + + Value->SetValue(dval); + break; + default: + Value->SetValue_char(p, field); + break; + } // endswitch Buf_Type + + else + if (Value->SetValue_char(p, field)) { + sprintf(g->Message, "Out of range value for column %s at row %d", + Name, tdbp->RowNumber(g)); + PushWarning(g, tdbp); + } // endif SetValue_char + + break; + default: + sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype); + longjmp(g->jumper[g->jump_level], 34); + } // endswitch Ftype + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void DOSCOL::WriteColumn(PGLOBAL g) + { + char *p, *p2, fmt[32]; + int i, k, len, field; + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + + if (trace > 1) + htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, tdbp->GetTdb_No(), ColUse, Status); + + p = tdbp->To_Line + Deplac; + + if (trace > 1) + htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long); + + field = Long; + 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 -#include -#include -#include -#if defined(__BORLANDC__) -#define __MFC_COMPAT__ // To define min/max as macro -#endif -//#include -#include "osutil.h" -#else -#if defined(UNIX) -#include -#include -#include "osutil.h" -#else -#include -#endif -#include -#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 +#include +#include +#include +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include +#include "osutil.h" +#else +#if defined(UNIX) +#include +#include +#include "osutil.h" +#else +#include +#endif +#include +#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 -#else // !WIN32 -//#include -//#include -#include -#include -#include -#include "osutil.h" -//#include -//#include -#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 +#else // !WIN32 +//#include +//#include +#include +#include +#include +#include "osutil.h" +//#include +//#include +#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 -#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 +#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 -# pragma comment(lib, "wbemuuid.lib") -#include -using namespace std; -#include - -/***********************************************************************/ -/* 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 +# pragma comment(lib, "wbemuuid.lib") +#include +using namespace std; +#include + +/***********************************************************************/ +/* 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::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::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 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 { - 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 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 { + 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 -#else // !WIN32 -#include -#endif // !WIN32 - -#include - -#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)value); - break; - case TYPE_SHORT: - valp = new(g) TYPVAL(*(short*)value, TYPE_SHORT); - break; - case TYPE_INT: - valp = new(g) TYPVAL(*(int*)value, TYPE_INT); - break; - case TYPE_BIGINT: - valp = new(g) TYPVAL(*(longlong*)value, TYPE_BIGINT); - break; - case TYPE_DOUBLE: - valp = new(g) TYPVAL(*(double *)value, TYPE_DOUBLE, 2); - break; - case TYPE_TINY: - valp = new(g) TYPVAL(*(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(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)0, TYPE_INT, 0, true); - else - valp = new(g) TYPVAL((int)0, TYPE_INT); - - break; - case TYPE_BIGINT: - if (uns) - valp = new(g) TYPVAL((ulonglong)0, TYPE_BIGINT, 0, true); - else - valp = new(g) TYPVAL((longlong)0, TYPE_BIGINT); - - break; - case TYPE_SHORT: - if (uns) - valp = new(g) TYPVAL((ushort)0, TYPE_SHORT, 0, true); - else - valp = new(g) TYPVAL((short)0, TYPE_SHORT); - - break; - case TYPE_DOUBLE: - valp = new(g) TYPVAL(0.0, TYPE_DOUBLE, prec); - break; - case TYPE_TINY: - if (uns) - valp = new(g) TYPVAL((uchar)0, TYPE_TINY, 0, true); - else - valp = new(g) TYPVAL((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(g, p, valp->GetValLen(), valp->GetValPrec()); - break; - case TYPE_SHORT: - if (un) - valp = new(g) TYPVAL(valp->GetUShortValue(), - TYPE_SHORT, 0, true); - else - valp = new(g) TYPVAL(valp->GetShortValue(), TYPE_SHORT); - - break; - case TYPE_INT: - if (un) - valp = new(g) TYPVAL(valp->GetUIntValue(), TYPE_INT, 0, true); - else - valp = new(g) TYPVAL(valp->GetIntValue(), TYPE_INT); - - break; - case TYPE_BIGINT: - if (un) - valp = new(g) TYPVAL(valp->GetUBigintValue(), - TYPE_BIGINT, 0, true); - else - valp = new(g) TYPVAL(valp->GetBigintValue(), TYPE_BIGINT); - - break; - case TYPE_DATE: - valp = new(g) DTVAL(g, valp->GetIntValue()); - break; - case TYPE_DOUBLE: - valp = new(g) TYPVAL(valp->GetFloatValue(), TYPE_DOUBLE, - valp->GetValPrec()); - break; - case TYPE_TINY: - if (un) - valp = new(g) TYPVAL(valp->GetUTinyValue(), - TYPE_TINY, 0, true); - else - valp = new(g) TYPVAL(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 -TYPVAL::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 -ulonglong TYPVAL::MaxVal(void) {DBUG_ASSERT(false); return 0;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return INT_MAX16;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return UINT_MAX16;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return INT_MAX32;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return UINT_MAX32;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return INT_MAX8;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return UINT_MAX8;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return INT_MAX64;} - -template <> -ulonglong TYPVAL::MaxVal(void) {return ULONGLONG_MAX;} - -/***********************************************************************/ -/* TYPVAL GetValLen: returns the print length of the typed object. */ -/***********************************************************************/ -template -int TYPVAL::GetValLen(void) - { - char c[32]; - - return sprintf(c, Fmt, Tval); - } // end of GetValLen - -template <> -int TYPVAL::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 -bool TYPVAL::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::GetTypedValue(PVAL valp) - {return valp->GetShortValue();} - -template <> -ushort TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetUShortValue();} - -template <> -int TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetIntValue();} - -template <> -uint TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetUIntValue();} - -template <> -longlong TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetBigintValue();} - -template <> -ulonglong TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetUBigintValue();} - -template <> -double TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetFloatValue();} - -template <> -char TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetTinyValue();} - -template <> -uchar TYPVAL::GetTypedValue(PVAL valp) - {return valp->GetUTinyValue();} - -/***********************************************************************/ -/* TYPVAL SetValue: convert chars extracted from a line to TYPE value.*/ -/***********************************************************************/ -template -bool TYPVAL::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::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 -void TYPVAL::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 -void TYPVAL::SetValue_pvblk(PVBLK blk, int n) - { - Tval = GetTypedValue(blk, n); - Null = false; - } // end of SetValue - -template <> -int TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetIntValue(n);} - -template <> -uint TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetUIntValue(n);} - -template <> -short TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetShortValue(n);} - -template <> -ushort TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetUShortValue(n);} - -template <> -longlong TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetBigintValue(n);} - -template <> -ulonglong TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetUBigintValue(n);} - -template <> -double TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetFloatValue(n);} - -template <> -char TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetTinyValue(n);} - -template <> -uchar TYPVAL::GetTypedValue(PVBLK blk, int n) - {return blk->GetUTinyValue(n);} - -/***********************************************************************/ -/* TYPVAL SetBinValue: with bytes extracted from a line. */ -/***********************************************************************/ -template -void TYPVAL::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 -bool TYPVAL::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 -char *TYPVAL::ShowValue(char *buf, int len) - { - sprintf(buf, Xfmt, len, Tval); - return buf; - } // end of ShowValue - -template <> -char *TYPVAL::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 -char *TYPVAL::GetCharString(char *p) - { - sprintf(p, Fmt, Tval); - return p; - } // end of GetCharString - -template <> -char *TYPVAL::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 -char *TYPVAL::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 -char *TYPVAL::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 -char *TYPVAL::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 -char *TYPVAL::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 -char *TYPVAL::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 -bool TYPVAL::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 -int TYPVAL::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 -bool TYPVAL::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 -bool TYPVAL::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 -void TYPVAL::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\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 -void TYPVAL::Print(PGLOBAL g, char *ps, uint z) - { - if (Null) - strcpy(ps, ""); - else - sprintf(ps, Fmt, Tval); - - } /* end of Print */ - -/* -------------------------- Class STRING --------------------------- */ - -/***********************************************************************/ -/* STRING public constructor from a constant string. */ -/***********************************************************************/ -TYPVAL::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::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::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::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::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::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::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::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::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::GetUBigintValue(void) - { - return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true); - } // end of GetUBigintValue - -/***********************************************************************/ -/* STRING SetValue: copy the value of another Value object. */ -/***********************************************************************/ -bool TYPVAL::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::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::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::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::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::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::SetValue(short i) - { - SetValue((int)i); - Null = false; - } // end of SetValue - -/***********************************************************************/ -/* STRING SetValue: get the character representation of a ushort int. */ -/***********************************************************************/ -void TYPVAL::SetValue(ushort i) - { - SetValue((uint)i); - Null = false; - } // end of SetValue - -/***********************************************************************/ -/* STRING SetValue: get the character representation of a big integer.*/ -/***********************************************************************/ -void TYPVAL::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::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::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::SetValue(char c) - { - SetValue((int)c); - Null = false; - } // end of SetValue - -/***********************************************************************/ -/* STRING SetValue: get the character representation of a tiny int. */ -/***********************************************************************/ -void TYPVAL::SetValue(uchar c) - { - SetValue((uint)c); - Null = false; - } // end of SetValue - -/***********************************************************************/ -/* STRING SetBinValue: fill string with chars extracted from a line. */ -/***********************************************************************/ -void TYPVAL::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::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::ShowValue(char *buf, int len) - { - return Strp; - } // end of ShowValue - -/***********************************************************************/ -/* STRING GetCharString: get string representation of a char value. */ -/***********************************************************************/ -char *TYPVAL::GetCharString(char *p) - { - return Strp; - } // end of GetCharString - -/***********************************************************************/ -/* STRING compare value with another Value. */ -/***********************************************************************/ -bool TYPVAL::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::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::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::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(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(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)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(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, <ime, (my_time_t) *timep); - TIME_to_localtime(tm, <ime); - 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(<ime, ptm); - ltime.time_type= MYSQL_TIMESTAMP_DATETIME; - uint error_code; - time_t t= TIME_to_timestamp(current_thd, <ime, &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::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::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::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 +#else // !WIN32 +#include +#endif // !WIN32 + +#include + +#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)value); + break; + case TYPE_SHORT: + valp = new(g) TYPVAL(*(short*)value, TYPE_SHORT); + break; + case TYPE_INT: + valp = new(g) TYPVAL(*(int*)value, TYPE_INT); + break; + case TYPE_BIGINT: + valp = new(g) TYPVAL(*(longlong*)value, TYPE_BIGINT); + break; + case TYPE_DOUBLE: + valp = new(g) TYPVAL(*(double *)value, TYPE_DOUBLE, 2); + break; + case TYPE_TINY: + valp = new(g) TYPVAL(*(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(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)0, TYPE_INT, 0, true); + else + valp = new(g) TYPVAL((int)0, TYPE_INT); + + break; + case TYPE_BIGINT: + if (uns) + valp = new(g) TYPVAL((ulonglong)0, TYPE_BIGINT, 0, true); + else + valp = new(g) TYPVAL((longlong)0, TYPE_BIGINT); + + break; + case TYPE_SHORT: + if (uns) + valp = new(g) TYPVAL((ushort)0, TYPE_SHORT, 0, true); + else + valp = new(g) TYPVAL((short)0, TYPE_SHORT); + + break; + case TYPE_DOUBLE: + valp = new(g) TYPVAL(0.0, TYPE_DOUBLE, prec); + break; + case TYPE_TINY: + if (uns) + valp = new(g) TYPVAL((uchar)0, TYPE_TINY, 0, true); + else + valp = new(g) TYPVAL((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(g, p, valp->GetValLen(), valp->GetValPrec()); + break; + case TYPE_SHORT: + if (un) + valp = new(g) TYPVAL(valp->GetUShortValue(), + TYPE_SHORT, 0, true); + else + valp = new(g) TYPVAL(valp->GetShortValue(), TYPE_SHORT); + + break; + case TYPE_INT: + if (un) + valp = new(g) TYPVAL(valp->GetUIntValue(), TYPE_INT, 0, true); + else + valp = new(g) TYPVAL(valp->GetIntValue(), TYPE_INT); + + break; + case TYPE_BIGINT: + if (un) + valp = new(g) TYPVAL(valp->GetUBigintValue(), + TYPE_BIGINT, 0, true); + else + valp = new(g) TYPVAL(valp->GetBigintValue(), TYPE_BIGINT); + + break; + case TYPE_DATE: + valp = new(g) DTVAL(g, valp->GetIntValue()); + break; + case TYPE_DOUBLE: + valp = new(g) TYPVAL(valp->GetFloatValue(), TYPE_DOUBLE, + valp->GetValPrec()); + break; + case TYPE_TINY: + if (un) + valp = new(g) TYPVAL(valp->GetUTinyValue(), + TYPE_TINY, 0, true); + else + valp = new(g) TYPVAL(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 +TYPVAL::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 +ulonglong TYPVAL::MaxVal(void) {DBUG_ASSERT(false); return 0;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return INT_MAX16;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return UINT_MAX16;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return INT_MAX32;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return UINT_MAX32;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return INT_MAX8;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return UINT_MAX8;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return INT_MAX64;} + +template <> +ulonglong TYPVAL::MaxVal(void) {return ULONGLONG_MAX;} + +/***********************************************************************/ +/* TYPVAL GetValLen: returns the print length of the typed object. */ +/***********************************************************************/ +template +int TYPVAL::GetValLen(void) + { + char c[32]; + + return sprintf(c, Fmt, Tval); + } // end of GetValLen + +template <> +int TYPVAL::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 +bool TYPVAL::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::GetTypedValue(PVAL valp) + {return valp->GetShortValue();} + +template <> +ushort TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetUShortValue();} + +template <> +int TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetIntValue();} + +template <> +uint TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetUIntValue();} + +template <> +longlong TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetBigintValue();} + +template <> +ulonglong TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetUBigintValue();} + +template <> +double TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetFloatValue();} + +template <> +char TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetTinyValue();} + +template <> +uchar TYPVAL::GetTypedValue(PVAL valp) + {return valp->GetUTinyValue();} + +/***********************************************************************/ +/* TYPVAL SetValue: convert chars extracted from a line to TYPE value.*/ +/***********************************************************************/ +template +bool TYPVAL::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::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 +void TYPVAL::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 +void TYPVAL::SetValue_pvblk(PVBLK blk, int n) + { + Tval = GetTypedValue(blk, n); + Null = false; + } // end of SetValue + +template <> +int TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetIntValue(n);} + +template <> +uint TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetUIntValue(n);} + +template <> +short TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetShortValue(n);} + +template <> +ushort TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetUShortValue(n);} + +template <> +longlong TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetBigintValue(n);} + +template <> +ulonglong TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetUBigintValue(n);} + +template <> +double TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetFloatValue(n);} + +template <> +char TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetTinyValue(n);} + +template <> +uchar TYPVAL::GetTypedValue(PVBLK blk, int n) + {return blk->GetUTinyValue(n);} + +/***********************************************************************/ +/* TYPVAL SetBinValue: with bytes extracted from a line. */ +/***********************************************************************/ +template +void TYPVAL::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 +bool TYPVAL::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 +char *TYPVAL::ShowValue(char *buf, int len) + { + sprintf(buf, Xfmt, len, Tval); + return buf; + } // end of ShowValue + +template <> +char *TYPVAL::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 +char *TYPVAL::GetCharString(char *p) + { + sprintf(p, Fmt, Tval); + return p; + } // end of GetCharString + +template <> +char *TYPVAL::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 +char *TYPVAL::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 +char *TYPVAL::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 +char *TYPVAL::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 +char *TYPVAL::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 +char *TYPVAL::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 +bool TYPVAL::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 +int TYPVAL::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 +bool TYPVAL::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 +bool TYPVAL::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 +void TYPVAL::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\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 +void TYPVAL::Print(PGLOBAL g, char *ps, uint z) + { + if (Null) + strcpy(ps, ""); + else + sprintf(ps, Fmt, Tval); + + } /* end of Print */ + +/* -------------------------- Class STRING --------------------------- */ + +/***********************************************************************/ +/* STRING public constructor from a constant string. */ +/***********************************************************************/ +TYPVAL::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::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::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::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::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::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::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::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::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::GetUBigintValue(void) + { + return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true); + } // end of GetUBigintValue + +/***********************************************************************/ +/* STRING SetValue: copy the value of another Value object. */ +/***********************************************************************/ +bool TYPVAL::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::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::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::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::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::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::SetValue(short i) + { + SetValue((int)i); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a ushort int. */ +/***********************************************************************/ +void TYPVAL::SetValue(ushort i) + { + SetValue((uint)i); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a big integer.*/ +/***********************************************************************/ +void TYPVAL::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::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::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::SetValue(char c) + { + SetValue((int)c); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a tiny int. */ +/***********************************************************************/ +void TYPVAL::SetValue(uchar c) + { + SetValue((uint)c); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetBinValue: fill string with chars extracted from a line. */ +/***********************************************************************/ +void TYPVAL::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::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::ShowValue(char *buf, int len) + { + return Strp; + } // end of ShowValue + +/***********************************************************************/ +/* STRING GetCharString: get string representation of a char value. */ +/***********************************************************************/ +char *TYPVAL::GetCharString(char *p) + { + return Strp; + } // end of GetCharString + +/***********************************************************************/ +/* STRING compare value with another Value. */ +/***********************************************************************/ +bool TYPVAL::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::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::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::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(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(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)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(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, <ime, (my_time_t) *timep); + TIME_to_localtime(tm, <ime); + 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(<ime, ptm); + ltime.time_type= MYSQL_TIMESTAMP_DATETIME; + uint error_code; + time_t t= TIME_to_timestamp(current_thd, <ime, &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::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::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::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 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: 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 - -/***********************************************************************/ -/* Specific DECIMAL class. */ -/***********************************************************************/ -class DllExport DECVAL: public TYPVAL { - 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 { - 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() {} - - // 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 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: 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 + +/***********************************************************************/ +/* Specific DECIMAL class. */ +/***********************************************************************/ +class DllExport DECVAL: public TYPVAL { + 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 { + 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() {} + + // 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 -#include -#include -//#include -#else // !WIN32 -#if defined(UNIX) -#include -#include -#include -#include -#else // !UNIX -#include -#endif // !UNIX -#include -#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 +#include +#include +//#include +#else // !WIN32 +#if defined(UNIX) +#include +#include +#include +#include +#else // !UNIX +#include +#endif // !UNIX +#include +#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