summaryrefslogtreecommitdiff
path: root/storage/connect
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2014-10-15 12:59:13 +0200
committerSergei Golubchik <serg@mariadb.org>2014-10-15 12:59:13 +0200
commitf62c12b405ba7ec80b8e2490856b83c6f5899211 (patch)
tree010605c7f145da6ea6ac14b39abc4cf700d619b1 /storage/connect
parentf947f73b2b6d2bd246b81a9038224d2a85777520 (diff)
parentf1afc003eefe0aafd3e070c7453d9e029d8445a8 (diff)
downloadmariadb-git-f62c12b405ba7ec80b8e2490856b83c6f5899211.tar.gz
Merge 10.0.14 into 10.1
Diffstat (limited to 'storage/connect')
-rw-r--r--storage/connect/CMakeLists.txt26
-rw-r--r--storage/connect/array.cpp1173
-rw-r--r--storage/connect/array.h130
-rw-r--r--storage/connect/blkfil.cpp1080
-rw-r--r--storage/connect/blkfil.h295
-rw-r--r--storage/connect/catalog.h14
-rw-r--r--storage/connect/checklvl.h7
-rw-r--r--storage/connect/colblk.cpp795
-rw-r--r--storage/connect/colblk.h45
-rw-r--r--storage/connect/connect.cc249
-rw-r--r--storage/connect/connect.h16
-rw-r--r--storage/connect/filamap.cpp198
-rw-r--r--storage/connect/filamap.h24
-rw-r--r--storage/connect/filamdbf.cpp73
-rw-r--r--storage/connect/filamdbf.h13
-rw-r--r--storage/connect/filamfix.cpp403
-rw-r--r--storage/connect/filamfix.h45
-rw-r--r--storage/connect/filamtxt.cpp569
-rw-r--r--storage/connect/filamtxt.h44
-rwxr-xr-xstorage/connect/filamvct.cpp385
-rw-r--r--storage/connect/filamvct.h32
-rw-r--r--storage/connect/filamzip.cpp693
-rw-r--r--storage/connect/filamzip.h337
-rw-r--r--storage/connect/filter.cpp1733
-rw-r--r--storage/connect/filter.h178
-rw-r--r--storage/connect/global.h8
-rw-r--r--storage/connect/ha_connect.cc1384
-rw-r--r--storage/connect/ha_connect.h74
-rw-r--r--storage/connect/macutil.cpp2
-rw-r--r--storage/connect/maputil.cpp14
-rw-r--r--storage/connect/mycat.cc9
-rw-r--r--storage/connect/mycat.h6
-rw-r--r--storage/connect/myconn.cpp43
-rw-r--r--storage/connect/myconn.h1
-rw-r--r--storage/connect/mysql-test/connect/r/alter.result25
-rw-r--r--storage/connect/mysql-test/connect/r/alter_xml.result4
-rw-r--r--storage/connect/mysql-test/connect/r/mysql.result2
-rw-r--r--storage/connect/mysql-test/connect/r/occur.result2
-rw-r--r--storage/connect/mysql-test/connect/r/part_file.result346
-rw-r--r--storage/connect/mysql-test/connect/r/part_table.result197
-rw-r--r--storage/connect/mysql-test/connect/r/pivot.result2
-rw-r--r--storage/connect/mysql-test/connect/r/temporary.result6
-rw-r--r--storage/connect/mysql-test/connect/r/updelx.result2570
-rw-r--r--storage/connect/mysql-test/connect/r/xml.result2
-rw-r--r--storage/connect/mysql-test/connect/t/alter.test6
-rw-r--r--storage/connect/mysql-test/connect/t/part_file.test166
-rw-r--r--storage/connect/mysql-test/connect/t/part_table.test92
-rw-r--r--storage/connect/mysql-test/connect/t/temporary.test13
-rw-r--r--storage/connect/mysql-test/connect/t/updelx.inc25
-rw-r--r--storage/connect/mysql-test/connect/t/updelx.test96
-rw-r--r--storage/connect/plgdbsem.h33
-rw-r--r--storage/connect/plgdbutl.cpp89
-rw-r--r--storage/connect/plugutil.c19
-rw-r--r--storage/connect/reldef.cpp61
-rw-r--r--storage/connect/reldef.h40
-rw-r--r--storage/connect/tabcol.h2
-rw-r--r--storage/connect/tabdos.cpp1718
-rw-r--r--storage/connect/tabdos.h63
-rw-r--r--storage/connect/tabfix.cpp87
-rw-r--r--storage/connect/tabfix.h7
-rw-r--r--storage/connect/tabfmt.cpp140
-rw-r--r--storage/connect/tabfmt.h20
-rw-r--r--storage/connect/table.cpp67
-rw-r--r--storage/connect/tabmac.h1
-rwxr-xr-xstorage/connect/tabmul.cpp4
-rw-r--r--storage/connect/tabmysql.cpp126
-rw-r--r--storage/connect/tabmysql.h5
-rw-r--r--storage/connect/tabodbc.cpp94
-rw-r--r--storage/connect/tabodbc.h5
-rw-r--r--storage/connect/tabpivot.h1
-rw-r--r--storage/connect/tabsys.cpp65
-rw-r--r--storage/connect/tabsys.h363
-rw-r--r--storage/connect/tabtbl.cpp33
-rw-r--r--storage/connect/tabtbl.h1
-rw-r--r--storage/connect/tabutil.cpp153
-rw-r--r--storage/connect/tabutil.h21
-rw-r--r--storage/connect/tabvct.cpp39
-rw-r--r--storage/connect/tabvct.h11
-rw-r--r--storage/connect/tabwmi.h301
-rw-r--r--storage/connect/tabxml.cpp6
-rw-r--r--storage/connect/tabxml.h1
-rw-r--r--storage/connect/user_connect.cc11
-rw-r--r--storage/connect/valblk.cpp151
-rw-r--r--storage/connect/valblk.h73
-rw-r--r--storage/connect/value.cpp304
-rw-r--r--storage/connect/value.h19
-rwxr-xr-xstorage/connect/xindex.cpp480
-rw-r--r--storage/connect/xindex.h13
-rw-r--r--storage/connect/xobject.cpp361
-rw-r--r--storage/connect/xobject.h237
-rw-r--r--storage/connect/xtable.h34
91 files changed, 16129 insertions, 2782 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index 41956e5f398..0252091ef80 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -19,20 +19,20 @@ SET(CONNECT_PLUGIN_DYNAMIC "connect")
SET(CONNECT_SOURCES
ha_connect.cc connect.cc user_connect.cc mycat.cc
fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h
-csort.cpp maputil.cpp plgdbutl.cpp
-colblk.cpp reldef.cpp tabcol.cpp table.cpp
-filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamvct.cpp
-tabdos.cpp tabfix.cpp tabfmt.cpp tabmul.cpp tabsys.cpp tabvct.cpp
+array.cpp blkfil.cpp colblk.cpp csort.cpp
+filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamvct.cpp filamzip.cpp
+filter.cpp maputil.cpp myutil.cpp plgdbutl.cpp reldef.cpp tabcol.cpp
+tabdos.cpp tabfix.cpp tabfmt.cpp table.cpp tabmul.cpp taboccur.cpp
+tabpivot.cpp tabsys.cpp tabtbl.cpp tabutil.cpp tabvct.cpp tabxcl.cpp
valblk.cpp value.cpp xindex.cpp xobject.cpp
-filamzip.cpp tabtbl.cpp myutil.cpp
-tabutil.cpp tabxcl.cpp taboccur.cpp tabpivot.cpp
-block.h catalog.h checklvl.h colblk.h connect.h csort.h engmsg.h
-filamap.h filamdbf.h filamfix.h filamtxt.h filamvct.h filamzip.h
-global.h ha_connect.h inihandl.h maputil.h msgid.h mycat.h myutil.h os.h
-osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h resource.h tabcol.h
-tabdos.h tabfix.h tabfmt.h tabmul.h tabsys.h tabtbl.h tabvct.h
-user_connect.h valblk.h value.h xindex.h xobject.h xtable.h
-tabutil.h tabxcl.h taboccur.h tabpivot.h)
+
+array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
+engmsg.h filamap.h filamdbf.h filamfix.h filamtxt.h filamvct.h filamzip.h
+filter.h global.h ha_connect.h inihandl.h maputil.h msgid.h mycat.h myutil.h
+os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h resource.h tabcol.h
+tabdos.h tabfix.h tabfmt.h tabmul.h taboccur.h tabpivot.h tabsys.h
+tabtbl.h tabutil.h tabvct.h tabxcl.h user_connect.h valblk.h value.h
+xindex.h xobject.h xtable.h)
#
# Definitions that are shared for all OSes
diff --git a/storage/connect/array.cpp b/storage/connect/array.cpp
new file mode 100644
index 00000000000..9815fbb6be6
--- /dev/null
+++ b/storage/connect/array.cpp
@@ -0,0 +1,1173 @@
+/************* Array C++ Functions Source Code File (.CPP) *************/
+/* Name: ARRAY.CPP Version 2.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* This file contains the XOBJECT derived class ARRAY functions. */
+/* ARRAY is used for elaborate type of processing, such as sorting */
+/* and dichotomic search (Find). This new version does not use sub */
+/* classes anymore for the different types but relies entirely on the */
+/* functionalities provided by the VALUE and VALBLK classes. */
+/* Currently the only supported types are STRING, SHORT, int, DATE, */
+/* TOKEN, DOUBLE, and Compressed Strings. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#include "sql_class.h"
+//#include "sql_time.h"
+
+#if defined(WIN32)
+//#include <windows.h>
+#else // !WIN32
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* xobject.h is header containing XOBJECT derived classes declares. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xtable.h"
+#include "array.h"
+//#include "select.h"
+//#include "query.h"
+//#include "token.h"
+
+/***********************************************************************/
+/* Macro definitions. */
+/***********************************************************************/
+#if defined(_DEBUG)
+#define ASSERT(B) assert(B);
+#else
+#define ASSERT(B)
+#endif
+
+/***********************************************************************/
+/* Static variables. */
+/***********************************************************************/
+extern "C" int trace;
+
+/***********************************************************************/
+/* DB static external variables. */
+/***********************************************************************/
+extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
+
+/***********************************************************************/
+/* External functions. */
+/***********************************************************************/
+BYTE OpBmp(PGLOBAL g, OPVAL opc);
+void EncodeValue(int *lp, char *strp, int n);
+PARRAY MakeValueArray(PGLOBAL g, PPARM pp); // avoid gcc warning
+
+/***********************************************************************/
+/* MakeValueArray: Makes a value array from a value list. */
+/***********************************************************************/
+PARRAY MakeValueArray(PGLOBAL g, PPARM pp)
+ {
+ int n, valtyp = 0;
+ size_t len = 0;
+ PARRAY par;
+ PPARM parmp;
+
+ if (!pp)
+ return NULL;
+
+ /*********************************************************************/
+ /* New version with values coming in a list. */
+ /*********************************************************************/
+ if ((valtyp = pp->Type) != TYPE_STRING)
+ len = 1;
+
+ if (trace)
+ htrc("valtyp=%d len=%d\n", valtyp, len);
+
+ /*********************************************************************/
+ /* Firstly check the list and count the number of values in it. */
+ /*********************************************************************/
+ for (n = 0, parmp = pp; parmp; n++, parmp = parmp->Next)
+ if (parmp->Type != valtyp) {
+ sprintf(g->Message, MSG(BAD_PARAM_TYPE), "MakeValueArray", parmp->Type);
+ return NULL;
+ } else if (valtyp == TYPE_STRING)
+ len = MY_MAX(len, strlen((char*)parmp->Value));
+
+ /*********************************************************************/
+ /* Make an array object with one block of the proper size. */
+ /*********************************************************************/
+ par = new(g) ARRAY(g, valtyp, n, (int)len);
+
+ if (par->GetResultType() == TYPE_ERROR)
+ return NULL; // Memory allocation error in ARRAY
+
+ /*********************************************************************/
+ /* All is right now, fill the array block. */
+ /*********************************************************************/
+ for (parmp = pp; parmp; parmp = parmp->Next)
+ switch (valtyp) {
+ case TYPE_STRING:
+ par->AddValue(g, (PSZ)parmp->Value);
+ break;
+ case TYPE_SHORT:
+ par->AddValue(g, *(short*)parmp->Value);
+ break;
+ case TYPE_INT:
+ par->AddValue(g, *(int*)parmp->Value);
+ break;
+ case TYPE_DOUBLE:
+ par->AddValue(g, *(double*)parmp->Value);
+ break;
+ case TYPE_PCHAR:
+ par->AddValue(g, parmp->Value);
+ break;
+ case TYPE_VOID:
+ // Integer stored inside pp->Value
+ par->AddValue(g, (int)parmp->Value);
+ break;
+ } // endswitch valtyp
+
+ /*********************************************************************/
+ /* Send back resulting array. */
+ /*********************************************************************/
+ return par;
+ } // end of MakeValueArray
+
+/* -------------------------- Class ARRAY ---------------------------- */
+
+/***********************************************************************/
+/* ARRAY public constructor. */
+/***********************************************************************/
+ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec)
+ : CSORT(FALSE)
+ {
+ Nval = 0;
+ Ndif = 0;
+ Bot = 0;
+ Top = 0;
+ Size = size;
+ Type = type;
+ Xsize = -1;
+ Len = 1;
+
+ switch (type) {
+ case TYPE_STRING:
+ Len = length;
+ case TYPE_SHORT:
+ case TYPE_INT:
+ case TYPE_DOUBLE:
+ case TYPE_PCHAR:
+ Type = type;
+ break;
+ case TYPE_VOID:
+ Type = TYPE_INT;
+ break;
+#if 0
+ case TYPE_TOKEN:
+ break;
+ case TYPE_LIST:
+ Len = 0;
+ prec = length;
+ break;
+#endif // 0
+ default: // This is illegal an causes an ill formed array building
+ sprintf(g->Message, MSG(BAD_ARRAY_TYPE), type);
+ Type = TYPE_ERROR;
+ return;
+ } // endswitch type
+
+ Valblk = new(g) MBVALS;
+
+ if (!(Vblp = Valblk->Allocate(g, Type, Len, prec, Size)))
+ Type = TYPE_ERROR;
+ else if (!Valblk->GetMemp() && Type != TYPE_LIST)
+ // The error message was built by PlgDBalloc
+ Type = TYPE_ERROR;
+ else if (type != TYPE_PCHAR)
+ Value = AllocateValue(g, type, Len, prec, NULL);
+
+ Constant = TRUE;
+ } // end of ARRAY constructor
+
+#if 0
+/***********************************************************************/
+/* ARRAY public constructor from a QUERY. */
+/***********************************************************************/
+ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE)
+ {
+ Type = qryp->GetColType(0);
+ Nval = qryp->GetNblin();
+ Ndif = 0;
+ Bot = 0;
+ Top = 0;
+ Size = Nval;
+ Xsize = -1;
+ Len = qryp->GetColLength(0);
+ X = Inf = Sup = 0;
+ Correlated = FALSE;
+
+ switch (Type) {
+ case TYPE_STRING:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ case TYPE_DATE:
+ case TYPE_DOUBLE:
+// case TYPE_TOKEN:
+// case TYPE_LIST:
+// Valblk = qryp->GetCol(0)->Result;
+// Vblp = qryp->GetColBlk(0);
+// Value = qryp->GetColValue(0);
+// break;
+ default: // This is illegal an causes an ill formed array building
+ sprintf(g->Message, MSG(BAD_ARRAY_TYPE), Type);
+ Type = TYPE_ERROR;
+ } // endswitch type
+
+ if (!Valblk || (!Valblk->GetMemp() && Type != TYPE_LIST))
+ // The error message was built by ???
+ Type = TYPE_ERROR;
+
+ Constant = TRUE;
+ } // end of ARRAY constructor
+
+/***********************************************************************/
+/* ARRAY constructor from a TYPE_LIST subarray. */
+/***********************************************************************/
+ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(FALSE)
+ {
+ int prec;
+ LSTBLK *lp;
+
+ if (par->Type != TYPE_LIST) {
+ Type = TYPE_ERROR;
+ return;
+ } // endif Type
+
+ lp = (LSTBLK*)par->Vblp;
+
+ Nval = par->Nval;
+ Ndif = 0;
+ Bot = 0;
+ Top = 0;
+ Size = par->Size;
+ Xsize = -1;
+
+ Valblk = lp->Mbvk[k];
+ Vblp = Valblk->Vblk;
+ Type = Vblp->GetType();
+ Len = (Type == TYPE_STRING) ? Vblp->GetVlen() : 0;
+ prec = (Type == TYPE_FLOAT) ? 2 : 0;
+ Value = AllocateValue(g, Type, Len, prec, NULL);
+ Constant = TRUE;
+ } // end of ARRAY constructor
+
+/***********************************************************************/
+/* Empty: reset the array for a new use (correlated queries). */
+/* Note: this is temporary as correlated queries will not use arrays */
+/* anymore with future optimized algorithms. */
+/***********************************************************************/
+void ARRAY::Empty(void)
+ {
+ assert(Correlated);
+ Nval = Ndif = 0;
+ Bot = Top = X = Inf = Sup = 0;
+ } // end of Empty
+#endif // 0
+
+/***********************************************************************/
+/* Add a string element to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, PSZ strp)
+ {
+ if (Type != TYPE_STRING) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "CHAR");
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding string(%d): '%s'\n", Nval, strp);
+
+//Value->SetValue_psz(strp);
+//Vblp->SetValue(valp, Nval++);
+ Vblp->SetValue(strp, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add a char pointer element to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, void *p)
+ {
+ if (Type != TYPE_PCHAR) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "PCHAR");
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding pointer(%d): %p\n", Nval, p);
+
+ Vblp->SetValue((PSZ)p, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add a short integer element to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, short n)
+ {
+ if (Type != TYPE_SHORT) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "SHORT");
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding SHORT(%d): %hd\n", Nval, n);
+
+//Value->SetValue(n);
+//Vblp->SetValue(valp, Nval++);
+ Vblp->SetValue(n, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add an integer element to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, int n)
+ {
+ if (Type != TYPE_INT) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "INTEGER");
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding int(%d): %d\n", Nval, n);
+
+//Value->SetValue(n);
+//Vblp->SetValue(valp, Nval++);
+ Vblp->SetValue(n, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add a double float element to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, double d)
+ {
+ if (Type != TYPE_DOUBLE) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "DOUBLE");
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding float(%d): %lf\n", Nval, d);
+
+ Value->SetValue(d);
+ Vblp->SetValue(Value, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add the value of a XOBJECT block to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, PXOB xp)
+ {
+ if (Type != xp->GetResultType()) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE),
+ GetTypeName(xp->GetResultType()), GetTypeName(Type));
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding (%d) from xp=%p\n", Nval, xp);
+
+//AddValue(xp->GetValue());
+ Vblp->SetValue(xp->GetValue(), Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Add a value to an array. */
+/***********************************************************************/
+bool ARRAY::AddValue(PGLOBAL g, PVAL vp)
+ {
+ if (Type != vp->GetType()) {
+ sprintf(g->Message, MSG(ADD_BAD_TYPE),
+ GetTypeName(vp->GetType()), GetTypeName(Type));
+ return TRUE;
+ } // endif Type
+
+ if (trace)
+ htrc(" adding (%d) from vp=%p\n", Nval, vp);
+
+ Vblp->SetValue(vp, Nval++);
+ return FALSE;
+ } // end of AddValue
+
+/***********************************************************************/
+/* Retrieve the nth value of the array. */
+/***********************************************************************/
+void ARRAY::GetNthValue(PVAL valp, int n)
+ {
+ valp->SetValue_pvblk(Vblp, n);
+ } // end of GetNthValue
+
+#if 0
+/***********************************************************************/
+/* Retrieve the nth subvalue of a list array. */
+/***********************************************************************/
+bool ARRAY::GetSubValue(PGLOBAL g, PVAL valp, int *kp)
+ {
+ PVBLK vblp;
+
+ if (Type != TYPE_LIST) {
+ sprintf(g->Message, MSG(NO_SUB_VAL), Type);
+ return TRUE;
+ } // endif Type
+
+ vblp = ((LSTBLK*)Vblp)->Mbvk[kp[0]]->Vblk;
+ valp->SetValue_pvblk(vblp, kp[1]);
+ return FALSE;
+ } // end of GetSubValue
+#endif // 0
+
+/***********************************************************************/
+/* Return the nth value of an integer array. */
+/***********************************************************************/
+int ARRAY::GetIntValue(int n)
+ {
+ assert (Type == TYPE_INT);
+ return Vblp->GetIntValue(n);
+ } // end of GetIntValue
+
+/***********************************************************************/
+/* Return the nth value of a STRING array. */
+/***********************************************************************/
+char *ARRAY::GetStringValue(int n)
+ {
+ assert (Type == TYPE_STRING || Type == TYPE_PCHAR);
+ return Vblp->GetCharValue(n);
+ } // end of GetStringValue
+
+/***********************************************************************/
+/* Find whether a value is in an array. */
+/* Provide a conversion limited to the Value limitation. */
+/***********************************************************************/
+bool ARRAY::Find(PVAL valp)
+ {
+ register int n;
+ PVAL vp;
+
+ if (Type != valp->GetType()) {
+ Value->SetValue_pval(valp);
+ vp = Value;
+ } else
+ vp = valp;
+
+ Inf = Bot, Sup = Top;
+
+ while (Sup - Inf > 1) {
+ X = (Inf + Sup) >> 1;
+ n = Vblp->CompVal(vp, X);
+
+ if (n < 0)
+ Sup = X;
+ else if (n > 0)
+ Inf = X;
+ else
+ return TRUE;
+
+ } // endwhile
+
+ return FALSE;
+ } // end of Find
+
+/***********************************************************************/
+/* ARRAY: Compare routine for a list of values. */
+/***********************************************************************/
+BYTE ARRAY::Vcompare(PVAL vp, int n)
+ {
+ Value->SetValue_pvblk(Vblp, n);
+ return vp->TestValue(Value);
+ } // end of Vcompare
+
+/***********************************************************************/
+/* Test a filter condition on an array depending on operator and mod. */
+/* Modificator values are 1: ANY (or SOME) and 2: ALL. */
+/***********************************************************************/
+bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm)
+ {
+ int i;
+ PVAL vp;
+ BYTE bt = OpBmp(g, opc);
+ int top = Nval - 1;
+
+ if (top < 0) // Array is empty
+ // Return TRUE for ALL because it means that there are no item that
+ // does not verify the condition, which is true indeed.
+ // Return FALSE for ANY because TRUE means that there is at least
+ // one item that verifies the condition, which is false.
+ return opm == 2;
+
+ if (valp) {
+ if (Type != valp->GetType()) {
+ Value->SetValue_pval(valp);
+ vp = Value;
+ } else
+ vp = valp;
+
+ } else if (opc != OP_EXIST) {
+ sprintf(g->Message, MSG(MISSING_ARG), opc);
+ longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
+ } else // OP_EXIST
+ return Nval > 0;
+
+ if (opc == OP_IN || (opc == OP_EQ && opm == 1))
+ return Find(vp);
+ else if (opc == OP_NE && opm == 2)
+ return !Find(vp);
+ else if (opc == OP_EQ && opm == 2)
+ return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : FALSE;
+ else if (opc == OP_NE && opm == 1)
+ return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : TRUE;
+
+ if (Type != TYPE_LIST) {
+ if (opc == OP_GT || opc == OP_GE)
+ return !(Vcompare(vp, (opm == 1) ? 0 : top) & bt);
+ else
+ return !(Vcompare(vp, (opm == 2) ? 0 : top) & bt);
+
+ } // endif Type
+
+ // Case of TYPE_LIST
+ if (opm == 2) {
+ for (i = 0; i < Nval; i++)
+ if (Vcompare(vp, i) & bt)
+ return FALSE;
+
+ return TRUE;
+ } else { // opm == 1
+ for (i = 0; i < Nval; i++)
+ if (!(Vcompare(vp, i) & bt))
+ return TRUE;
+
+ return FALSE;
+ } // endif opm
+
+ } // end of FilTest
+
+/***********************************************************************/
+/* Test whether this array can be converted to TYPE_SHORT. */
+/* Must be called after the array is sorted. */
+/***********************************************************************/
+bool ARRAY::CanBeShort(void)
+ {
+ int* To_Val = (int*)Valblk->GetMemp();
+
+ if (Type != TYPE_INT || !Ndif)
+ return FALSE;
+
+ // Because the array is sorted, this is true if all the array
+ // int values are in the range of SHORT values
+ return (To_Val[0] >= -32768 && To_Val[Nval-1] < 32768);
+ } // end of CanBeShort
+
+/***********************************************************************/
+/* Convert an array to new numeric type k. */
+/* Note: conversion is always made in ascending order from STRING to */
+/* short to int to double so no precision is lost in the conversion. */
+/* One exception is converting from int to short compatible arrays. */
+/***********************************************************************/
+int ARRAY::Convert(PGLOBAL g, int k, PVAL vp)
+ {
+ int i;
+ bool b = FALSE;
+ PMBV ovblk = Valblk;
+ PVBLK ovblp = Vblp;
+
+ Type = k; // k is the new type
+ Valblk = new(g) MBVALS;
+
+ switch (Type) {
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ case TYPE_DATE:
+ Len = 1;
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_CONV_TYPE), Type);
+ return TYPE_ERROR;
+ } // endswitch k
+
+ Size = Nval;
+ Nval = 0;
+ Vblp = Valblk->Allocate(g, Type, Len, 0, Size);
+
+ if (!Valblk->GetMemp())
+ // The error message was built by PlgDBalloc
+ return TYPE_ERROR;
+ else
+ Value = AllocateValue(g, Type, Len, 0, NULL);
+
+ /*********************************************************************/
+ /* Converting STRING to DATE can be done according to date format. */
+ /*********************************************************************/
+ if (Type == TYPE_DATE && ovblp->GetType() == TYPE_STRING && vp)
+ if (((DTVAL*)Value)->SetFormat(g, vp))
+ return TYPE_ERROR;
+ else
+ b = TRUE; // Sort the new array on date internal values
+
+ /*********************************************************************/
+ /* Do the actual conversion. */
+ /*********************************************************************/
+ for (i = 0; i < Size; i++) {
+ Value->SetValue_pvblk(ovblp, i);
+
+ if (AddValue(g, Value))
+ return TYPE_ERROR;
+
+ } // endfor i
+
+ /*********************************************************************/
+ /* For sorted arrays, get the initial find values. */
+ /*********************************************************************/
+ if (b)
+ Sort(g);
+
+ ovblk->Free();
+ return Type;
+ } // end of Convert
+
+/***********************************************************************/
+/* ARRAY Save: save value at i (used while rordering). */
+/***********************************************************************/
+void ARRAY::Save(int i)
+ {
+ Value->SetValue_pvblk(Vblp, i);
+ } // end of Save
+
+/***********************************************************************/
+/* ARRAY Restore: restore value to j (used while rordering). */
+/***********************************************************************/
+void ARRAY::Restore(int j)
+ {
+ Vblp->SetValue(Value, j);
+ } // end of Restore
+
+/***********************************************************************/
+/* ARRAY Move: move value from k to j (used while rordering). */
+/***********************************************************************/
+void ARRAY::Move(int j, int k)
+ {
+ Vblp->Move(k, j); // VALBLK does the opposite !!!
+ } // end of Move
+
+/***********************************************************************/
+/* ARRAY: Compare routine for one LIST value (ascending only). */
+/***********************************************************************/
+int ARRAY::Qcompare(int *i1, int *i2)
+ {
+ return Vblp->CompVal(*i1, *i2);
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Mainly meant to set the character arrays case sensitiveness. */
+/***********************************************************************/
+void ARRAY::SetPrecision(PGLOBAL g, int p)
+ {
+ if (Vblp == NULL) {
+ strcpy(g->Message, MSG(PREC_VBLP_NULL));
+ longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
+ } // endif Vblp
+
+ bool was = Vblp->IsCi();
+
+ if (was && !p) {
+ strcpy(g->Message, MSG(BAD_SET_CASE));
+ longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
+ } // endif Vblp
+
+ if (was || !p)
+ return;
+ else
+ Vblp->SetPrec(p);
+
+ if (!was && Type == TYPE_STRING)
+ // Must be resorted to eliminate duplicate strings
+ if (Sort(g))
+ longjmp(g->jumper[g->jump_level], TYPE_ARRAY);
+
+ } // end of SetPrecision
+
+/***********************************************************************/
+/* Sort and eliminate distinct values from an array. */
+/* Note: this is done by making a sorted index on distinct values. */
+/* Returns FALSE if Ok or TRUE in case of error. */
+/***********************************************************************/
+bool ARRAY::Sort(PGLOBAL g)
+ {
+ int i, j, k;
+
+ // This is to avoid multiply allocating for correlated subqueries
+ if (Nval > Xsize) {
+ if (Xsize >= 0) {
+ // Was already allocated
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+ } // endif Xsize
+
+ // Prepare non conservative sort with offet values
+ Index.Size = Nval * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Index))
+ goto error;
+
+ Offset.Size = (Nval + 1) * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset))
+ goto error;
+
+ Xsize = Nval;
+ } // endif Nval
+
+ // Call the sort program, it returns the number of distinct values
+ Ndif = Qsort(g, Nval);
+
+ if (Ndif < 0)
+ goto error;
+
+ // Use the sort index to reorder the data in storage so it will
+ // be physically sorted and Index can be removed.
+ for (i = 0; i < Nval; i++) {
+ if (Pex[i] == i || Pex[i] == Nval)
+ // Already placed or already moved
+ continue;
+
+ Save(i);
+
+ for (j = i;; j = k) {
+ k = Pex[j];
+ Pex[j] = Nval; // Mark position as set
+
+ if (k == i) {
+ Restore(j);
+ break; // end of loop
+ } else
+ Move(j, k);
+
+ } // endfor j
+
+ } // endfor i
+
+ // Reduce the size of the To_Val array if Ndif < Nval
+ if (Ndif < Nval) {
+ for (i = 1; i < Ndif; i++)
+ if (i != Pof[i])
+ break;
+
+ for (; i < Ndif; i++)
+ Move(i, Pof[i]);
+
+ Nval = Ndif;
+ } // endif ndif
+
+//if (!Correlated) {
+ if (Size > Nval) {
+ Size = Nval;
+ Valblk->ReAllocate(g, Size);
+ } // endif Size
+
+ // Index and Offset are not used anymore
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+ Xsize = -1;
+// } // endif Correlated
+
+ Bot = -1; // For non optimized search
+ Top = Ndif; // Find searches the whole array.
+ return FALSE;
+
+ error:
+ Nval = Ndif = 0;
+ Valblk->Free();
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+ return TRUE;
+ } // end of Sort
+
+/***********************************************************************/
+/* Sort and return the sort index. */
+/* Note: This is meant if the array contains unique values. */
+/* Returns Index.Memp if Ok or NULL in case of error. */
+/***********************************************************************/
+void *ARRAY::GetSortIndex(PGLOBAL g)
+ {
+ // Prepare non conservative sort with offet values
+ Index.Size = Nval * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Index))
+ goto error;
+
+ Offset.Size = (Nval + 1) * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset))
+ goto error;
+
+ // Call the sort program, it returns the number of distinct values
+ Ndif = Qsort(g, Nval);
+
+ if (Ndif < 0)
+ goto error;
+
+ if (Ndif < Nval)
+ goto error;
+
+ PlgDBfree(Offset);
+ return Index.Memp;
+
+ error:
+ Nval = Ndif = 0;
+ Valblk->Free();
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+ return NULL;
+ } // end of GetSortIndex
+
+/***********************************************************************/
+/* Block filter testing for IN operator on Column/Array operands. */
+/* Here we call Find that returns TRUE if the value is in the array */
+/* with X equal to the index of the found value in the array, or */
+/* FALSE if the value is not in the array with Inf and Sup being the */
+/* indexes of the array values that are immediately below and over */
+/* the not found value. This enables to restrict the array to the */
+/* values that are between the min and max block values and to return */
+/* the indication of whether the Find will be always true, always not */
+/* true or other. */
+/***********************************************************************/
+int ARRAY::BlockTest(PGLOBAL g, int opc, int opm,
+ void *minp, void *maxp, bool s)
+ {
+ bool bin, bax, pin, pax, veq, all = (opm == 2);
+
+ if (Ndif == 0) // Array is empty
+ // Return TRUE for ALL because it means that there are no item that
+ // does not verify the condition, which is true indeed.
+ // Return FALSE for ANY because TRUE means that there is at least
+ // one item that verifies the condition, which is false.
+ return (all) ? 2 : -2;
+ else if (opc == OP_EQ && all && Ndif > 1)
+ return -2;
+ else if (opc == OP_NE && !all && Ndif > 1)
+ return 2;
+// else if (Ndif == 1)
+// all = FALSE;
+
+ // veq is true when all values in the block are equal
+ switch (Type) {
+ case TYPE_STRING: veq = (Vblp->IsCi())
+ ? !stricmp((char*)minp, (char*)maxp)
+ : !strcmp((char*)minp, (char*)maxp); break;
+ case TYPE_SHORT: veq = *(short*)minp == *(short*)maxp; break;
+ case TYPE_INT: veq = *(int*)minp == *(int*)maxp; break;
+ case TYPE_DOUBLE: veq = *(double*)minp == *(double*)maxp; break;
+ default: veq = FALSE; // Error ?
+ } // endswitch type
+
+ if (!s)
+ Bot = -1;
+
+ Top = Ndif; // Reset Top at top of list
+ Value->SetBinValue(maxp);
+ Top = (bax = Find(Value)) ? X + 1 : Sup;
+
+ if (bax) {
+ if (opc == OP_EQ)
+ return (veq) ? 1 : 0;
+ else if (opc == OP_NE)
+ return (veq) ? -1 : 0;
+
+ if (X == 0) switch (opc) {
+ // Max value is equal to min list value
+ case OP_LE: return 1; break;
+ case OP_LT: return (veq) ? -1 : 0; break;
+ case OP_GE: return (veq) ? 1 : 0; break;
+ case OP_GT: return -1; break;
+ } // endswitch opc
+
+ pax = (opc == OP_GE) ? (X < Ndif - 1) : TRUE;
+ } else if (Inf == Bot) {
+ // Max value is smaller than min list value
+ return (opc == OP_LT || opc == OP_LE || opc == OP_NE) ? 1 : -1;
+ } else
+ pax = (Sup < Ndif); // True if max value is inside the list value
+
+ if (!veq) {
+ Value->SetBinValue(minp);
+ bin = Find(Value);
+ } else
+ bin = bax;
+
+ Bot = (bin) ? X - 1 : Inf;
+
+ if (bin) {
+ if (opc == OP_EQ || opc == OP_NE)
+ return 0;
+
+ if (X == Ndif - 1) switch (opc) {
+ case OP_GE: return (s) ? 2 : 1; break;
+ case OP_GT: return (veq) ? -1 : 0; break;
+ case OP_LE: return (veq) ? 1 : 0; break;
+ case OP_LT: return (s) ? -2 : -1; break;
+ } // endswitch opc
+
+ pin = (opc == OP_LE) ? (X > 0) : TRUE;
+ } else if (Sup == Ndif) {
+ // Min value is greater than max list value
+ if (opc == OP_GT || opc == OP_GE || opc == OP_NE)
+ return (s) ? 2 : 1;
+ else
+ return (s) ? -2 : -1;
+
+ } else
+ pin = (Inf >= 0); // True if min value is inside the list value
+
+ if (Top - Bot <= 1) {
+ // No list item between min and max value
+#if defined(_DEBUG)
+ assert (!bin && !bax);
+#endif
+ switch (opc) {
+ case OP_EQ: return -1; break;
+ case OP_NE: return 1; break;
+ default: return (all) ? -1 : 1; break;
+ } // endswitch opc
+
+ } // endif
+
+#if defined(_DEBUG)
+ assert (Ndif > 1); // if Ndif = 1 we should have returned already
+#endif
+
+ // At this point, if there are no logical errors in the algorithm,
+ // the only possible overlaps between the array and the block are:
+ // Array: +-------+ +-------+ +-------+ +-----+
+ // Block: +-----+ +---+ +------+ +--------+
+ // TRUE: pax pin pax pin
+ if (all) switch (opc) {
+ case OP_GT:
+ case OP_GE: return (pax) ? -1 : 0; break;
+ case OP_LT:
+ case OP_LE: return (pin) ? -1 : 0; break;
+ } // endswitch opc
+
+ return 0;
+ } // end of BlockTest
+
+/***********************************************************************/
+/* MakeArrayList: Makes a value list from an SQL IN array (in work). */
+/***********************************************************************/
+PSZ ARRAY::MakeArrayList(PGLOBAL g)
+ {
+ char *p, *tp;
+ int i;
+ size_t z, len = 2;
+
+ if (Type == TYPE_LIST)
+ return "(?" "?" "?)"; // To be implemented
+
+ z = MY_MAX(24, GetTypeSize(Type, Len) + 4);
+ tp = (char*)PlugSubAlloc(g, NULL, z);
+
+ for (i = 0; i < Nval; i++) {
+ Value->SetValue_pvblk(Vblp, i);
+ Value->Print(g, tp, z);
+ len += strlen(tp);
+ } // enfor i
+
+ if (trace)
+ htrc("Arraylist: len=%d\n", len);
+
+ p = (char *)PlugSubAlloc(g, NULL, len);
+ strcpy(p, "(");
+
+ for (i = 0; i < Nval;) {
+ Value->SetValue_pvblk(Vblp, i);
+ Value->Print(g, tp, z);
+ strcat(p, tp);
+ strcat(p, (++i == Nval) ? ")" : ",");
+ } // enfor i
+
+ if (trace)
+ htrc("Arraylist: newlen=%d\n", strlen(p));
+
+ return p;
+ } // end of MakeArrayList
+
+/***********************************************************************/
+/* Make file output of ARRAY contents. */
+/***********************************************************************/
+void ARRAY::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+ int lim = MY_MIN(Nval,10);
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+ fprintf(f, "%sARRAY: type=%d\n", m, Type);
+ memset(m, ' ', n + 2); // Make margin string
+ m[n] = '\0';
+
+ if (Type != TYPE_LIST) {
+ fprintf(f, "%sblock=%p numval=%d\n", m, Valblk->GetMemp(), Nval);
+
+ if (Vblp)
+ for (int i = 0; i < lim; i++) {
+ Value->SetValue_pvblk(Vblp, i);
+ Value->Print(g, f, n+4);
+ } // endfor i
+
+ } else
+ fprintf(f, "%sVALLST: numval=%d\n", m, Nval);
+
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of ARRAY contents. */
+/***********************************************************************/
+void ARRAY::Print(PGLOBAL g, char *ps, uint z)
+ {
+ if (z < 16)
+ return;
+
+ sprintf(ps, "ARRAY: type=%d\n", Type);
+ // More to be implemented later
+ } // end of Print
+
+/* -------------------------- Class MULAR ---------------------------- */
+
+/***********************************************************************/
+/* MULAR public constructor. */
+/***********************************************************************/
+MULAR::MULAR(PGLOBAL g, int n) : CSORT(FALSE)
+ {
+ Narray = n;
+ Pars = (PARRAY*)PlugSubAlloc(g, NULL, n * sizeof(PARRAY));
+ } // end of MULAR constructor
+
+/***********************************************************************/
+/* MULAR: Compare routine multiple arrays. */
+/***********************************************************************/
+int MULAR::Qcompare(int *i1, int *i2)
+ {
+ register int i, n = 0;
+
+ for (i = 0; i < Narray; i++)
+ if ((n = Pars[i]->Qcompare(i1, i2)))
+ break;
+
+ return n;
+ } // end of Qcompare
+
+/***********************************************************************/
+/* Sort and eliminate distinct values from multiple arrays. */
+/* Note: this is done by making a sorted index on distinct values. */
+/* Returns FALSE if Ok or TRUE in case of error. */
+/***********************************************************************/
+bool MULAR::Sort(PGLOBAL g)
+ {
+ int i, j, k, n, nval, ndif;
+
+ // All arrays must have the same number of values
+ nval = Pars[0]->Nval;
+
+ for (n = 1; n < Narray; n++)
+ if (Pars[n]->Nval != nval) {
+ strcpy(g->Message, MSG(BAD_ARRAY_VAL));
+ return TRUE;
+ } // endif nval
+
+ // Prepare non conservative sort with offet values
+ Index.Size = nval * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Index))
+ goto error;
+
+ Offset.Size = (nval + 1) * sizeof(int);
+
+ if (!PlgDBalloc(g, NULL, Offset))
+ goto error;
+
+ // Call the sort program, it returns the number of distinct values
+ ndif = Qsort(g, nval);
+
+ if (ndif < 0)
+ goto error;
+
+ // Use the sort index to reorder the data in storage so it will
+ // be physically sorted and Index can be removed.
+ for (i = 0; i < nval; i++) {
+ if (Pex[i] == i || Pex[i] == nval)
+ // Already placed or already moved
+ continue;
+
+ for (n = 0; n < Narray; n++)
+ Pars[n]->Save(i);
+
+ for (j = i;; j = k) {
+ k = Pex[j];
+ Pex[j] = nval; // Mark position as set
+
+ if (k == i) {
+ for (n = 0; n < Narray; n++)
+ Pars[n]->Restore(j);
+
+ break; // end of loop
+ } else
+ for (n = 0; n < Narray; n++)
+ Pars[n]->Move(j, k);
+
+ } // endfor j
+
+ } // endfor i
+
+ // Reduce the size of the To_Val array if ndif < nval
+ if (ndif < nval) {
+ for (i = 1; i < ndif; i++)
+ if (i != Pof[i])
+ break;
+
+ for (; i < ndif; i++)
+ for (n = 0; n < Narray; n++)
+ Pars[n]->Move(i, Pof[i]);
+
+ for (n = 0; n < Narray; n++) {
+ Pars[n]->Nval = ndif;
+ Pars[n]->Size = ndif;
+ Pars[n]->Valblk->ReAllocate(g, ndif);
+ } // endfor n
+
+ } // endif ndif
+
+ // Index and Offset are not used anymore
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+
+ for (n = 0; n < Narray; n++) {
+ Pars[n]->Bot = -1; // For non optimized search
+ Pars[n]->Top = ndif; // Find searches the whole array.
+ } // endfor n
+
+ return FALSE;
+
+ error:
+ PlgDBfree(Index);
+ PlgDBfree(Offset);
+ return TRUE;
+ } // end of Sort
diff --git a/storage/connect/array.h b/storage/connect/array.h
new file mode 100644
index 00000000000..4a818414e9c
--- /dev/null
+++ b/storage/connect/array.h
@@ -0,0 +1,130 @@
+/**************** Array H Declares Source Code File (.H) ***************/
+/* Name: ARRAY.H Version 3.1 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* This file contains the ARRAY and VALBASE derived classes declares. */
+/***********************************************************************/
+#ifndef __ARRAY_H
+#define __ARRAY_H
+
+
+/***********************************************************************/
+/* Include required application header files */
+/***********************************************************************/
+#include "xobject.h"
+#include "valblk.h"
+#include "csort.h"
+
+typedef class ARRAY *PARRAY;
+
+/***********************************************************************/
+/* Definition of class ARRAY with all its method functions. */
+/* Note: This is not a general array class that could be defined as */
+/* a template class, but rather a specific object containing a list */
+/* of values to be processed by the filter IN operator. */
+/* In addition it must act as a metaclass by being able to give back */
+/* the type of values it contains. */
+/* It must also be able to convert itself from some type to another. */
+/***********************************************************************/
+class DllExport ARRAY : public XOBJECT, public CSORT { // Array descblock
+ friend class MULAR;
+//friend class VALLST;
+//friend class SFROW;
+ public:
+ // Constructors
+ ARRAY(PGLOBAL g, int type, int size, int len = 1, int prec = 0);
+//ARRAY(PGLOBAL g, PQUERY qryp);
+//ARRAY(PGLOBAL g, PARRAY par, int k);
+
+ // Implementation
+ virtual int GetType(void) {return TYPE_ARRAY;}
+ virtual int GetResultType(void) {return Type;}
+ virtual int GetLength(void) {return Len;}
+ virtual int GetLengthEx(void) {return Len;}
+ virtual int GetScale() {return 0;}
+ int GetNval(void) {return Nval;}
+ int GetSize(void) {return Size;}
+// PVAL GetValp(void) {return Valp;}
+ void SetType(int atype) {Type = atype;}
+// void SetCorrel(bool b) {Correlated = b;}
+
+ // Methods
+ virtual void Reset(void) {Bot = -1;}
+ 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 void Print(PGLOBAL g, FILE *f, uint n);
+ virtual void Print(PGLOBAL g, char *ps, uint z);
+// void Empty(void);
+ void SetPrecision(PGLOBAL g, int p);
+ bool AddValue(PGLOBAL g, PSZ sp);
+ bool AddValue(PGLOBAL g, void *p);
+ bool AddValue(PGLOBAL g, short n);
+ bool AddValue(PGLOBAL g, int n);
+ bool AddValue(PGLOBAL g, double f);
+ bool AddValue(PGLOBAL g, PXOB xp);
+ bool AddValue(PGLOBAL g, PVAL vp);
+ void GetNthValue(PVAL valp, int n);
+ int GetIntValue(int n);
+ char *GetStringValue(int n);
+ BYTE Vcompare(PVAL vp, int n);
+ void Save(int);
+ void Restore(int);
+ void Move(int, int);
+ bool Sort(PGLOBAL g);
+ void *GetSortIndex(PGLOBAL g);
+ bool Find(PVAL valp);
+ bool FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm);
+ int Convert(PGLOBAL g, int k, PVAL vp = NULL);
+ int BlockTest(PGLOBAL g, int opc, int opm,
+ void *minp, void *maxp, bool s);
+ PSZ MakeArrayList(PGLOBAL g);
+ bool CanBeShort(void);
+ bool GetSubValue(PGLOBAL g, PVAL valp, int *kp);
+
+ protected:
+ // Members
+ PMBV Valblk; // To the MBVALS class
+ PVBLK Vblp; // To Valblock of the data array
+//PVAL Valp; // The value used for Save and Restore is Value
+ int Size; // Size of value array
+ int Nval; // Total number of items in array
+ int Ndif; // Total number of distinct items in array
+ int Xsize; // Size of Index (used for correlated arrays)
+ int Type; // Type of individual values in the array
+ int Len; // Length of character string
+ int Bot; // Bottom of research index
+ int Top; // Top of research index
+ int X, Inf, Sup; // Used for block optimization
+//bool Correlated; // -----------> Temporary
+ }; // end of class ARRAY
+
+/***********************************************************************/
+/* Definition of class MULAR with all its method functions. */
+/* This class is used when constructing the arrays of constants used */
+/* for indexing. Its only purpose is to provide a way to sort, reduce */
+/* and reorder the arrays of multicolumn indexes as one block. Indeed */
+/* sorting the arrays independantly would break the correspondance of */
+/* column values. */
+/***********************************************************************/
+class MULAR : public CSORT, public BLOCK { // No need to be an XOBJECT
+ public:
+ // Constructor
+ MULAR(PGLOBAL g, int n);
+
+ // Implementation
+ void SetPars(PARRAY par, int i) {Pars[i] = par;}
+
+ // Methods
+ virtual int Qcompare(int *i1, int *i2); // Sort compare routine
+ bool Sort(PGLOBAL g);
+
+ protected:
+ // Members
+ int Narray; // The number of sub-arrays
+ PARRAY *Pars; // To the block of real arrays
+ }; // end of class ARRAY
+
+#endif // __ARRAY_H
diff --git a/storage/connect/blkfil.cpp b/storage/connect/blkfil.cpp
new file mode 100644
index 00000000000..c1099261cef
--- /dev/null
+++ b/storage/connect/blkfil.cpp
@@ -0,0 +1,1080 @@
+/************* BlkFil C++ Program Source Code File (.CPP) **************/
+/* PROGRAM NAME: BLKFIL */
+/* ------------- */
+/* Version 2.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program is the implementation of block indexing classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#include "sql_class.h"
+//#include "sql_time.h"
+
+#if defined(WIN32)
+//#include <windows.h>
+#else // !WIN32
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include application header files: */
+/***********************************************************************/
+#include "global.h" // global declarations
+#include "plgdbsem.h" // DB application declarations
+#include "xindex.h" // Key Index class declarations
+#include "filamtxt.h" // File access method dcls
+#include "tabdos.h" // TDBDOS and DOSCOL class dcls
+#include "array.h" // ARRAY classes dcls
+#include "blkfil.h" // Block Filter classes dcls
+
+/***********************************************************************/
+/* Static variables. */
+/***********************************************************************/
+extern "C" int trace;
+
+/* ------------------------ Class BLOCKFILTER ------------------------ */
+
+/***********************************************************************/
+/* BLOCKFILTER constructor. */
+/***********************************************************************/
+BLOCKFILTER::BLOCKFILTER(PTDBDOS tdbp, int op)
+ {
+ Tdbp = tdbp;
+ Correl = FALSE;
+ Opc = op;
+ Opm = 0;
+ Result = 0;
+ } // end of BLOCKFILTER constructor
+
+/***********************************************************************/
+/* Make file output of BLOCKFILTER contents. */
+/***********************************************************************/
+void BLOCKFILTER::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+
+ fprintf(f, "%sBLOCKFILTER: at %p opc=%d opm=%d result=%d\n",
+ m, this, Opc, Opm, Result);
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of BLOCKFILTER contents. */
+/***********************************************************************/
+void BLOCKFILTER::Print(PGLOBAL g, char *ps, uint z)
+ {
+ strncat(ps, "BlockFilter(s)", z);
+ } // end of Print
+
+
+/* ---------------------- Class BLKFILLOG ---------------------------- */
+
+/***********************************************************************/
+/* BLKFILLOG constructor. */
+/***********************************************************************/
+BLKFILLOG::BLKFILLOG(PTDBDOS tdbp, int op, PBF *bfp, int n)
+ : BLOCKFILTER(tdbp, op)
+ {
+ N = n;
+ Fil = bfp;
+
+ for (int i = 0; i < N; i++)
+ if (Fil[i])
+ Correl |= Fil[i]->Correl;
+
+ } // end of BLKFILLOG constructor
+
+/***********************************************************************/
+/* Reset: this function is used only to check the existence of a */
+/* BLKFILIN block and have it reset its Bot value for sorted columns. */
+/***********************************************************************/
+void BLKFILLOG::Reset(PGLOBAL g)
+ {
+ for (int i = 0; i < N; i++)
+ if (Fil[i])
+ Fil[i]->Reset(g);
+
+ } // end of Reset
+
+/***********************************************************************/
+/* This function is used for block filter evaluation. We use here a */
+/* fuzzy logic between the values returned by evaluation blocks: */
+/* -2: the condition will be always false for the rest of the file. */
+/* -1: the condition will be false for the whole group. */
+/* 0: the condition may be true for some of the group values. */
+/* 1: the condition will be true for the whole group. */
+/* 2: the condition will be always true for the rest of the file. */
+/***********************************************************************/
+int BLKFILLOG::BlockEval(PGLOBAL g)
+ {
+ int i, rc;
+
+ for (i = 0; i < N; i++) {
+ // 0: Means some block filter value may be True
+ rc = (Fil[i]) ? Fil[i]->BlockEval(g) : 0;
+
+ if (!i)
+ Result = (Opc == OP_NOT) ? -rc : rc;
+ else switch (Opc) {
+ case OP_AND:
+ Result = MY_MIN(Result, rc);
+ break;
+ case OP_OR:
+ Result = MY_MAX(Result, rc);
+ break;
+ default:
+ // Should never happen
+ Result = 0;
+ return Result;
+ } // endswitch Opc
+
+ } // endfor i
+
+ return Result;
+ } // end of BlockEval
+
+/* ---------------------- Class BLKFILARI----------------------------- */
+
+/***********************************************************************/
+/* BLKFILARI constructor. */
+/***********************************************************************/
+BLKFILARI::BLKFILARI(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp)
+ : BLOCKFILTER(tdbp, op)
+ {
+ Colp = (PDOSCOL)xp[0];
+
+ if (xp[1]->GetType() == TYPE_COLBLK) {
+ Cpx = (PCOL)xp[1]; // Subquery pseudo constant column
+ Correl = TRUE;
+ } else
+ Cpx = NULL;
+
+ Sorted = Colp->IsSorted() > 0;
+
+ // Don't remember why this was changed. Anyway it is no good for
+ // correlated subqueries because the Value must reflect changes
+ if (Cpx)
+ Valp = xp[1]->GetValue();
+ else
+ Valp = AllocateValue(g, xp[1]->GetValue());
+
+ } // end of BLKFILARI constructor
+
+/***********************************************************************/
+/* Reset: re-eval the constant value in the case of pseudo constant */
+/* column use in a correlated subquery. */
+/***********************************************************************/
+void BLKFILARI::Reset(PGLOBAL g)
+ {
+ if (Cpx) {
+ Cpx->Reset();
+ Cpx->Eval(g);
+ MakeValueBitmap(); // Does nothing for class BLKFILARI
+ } // endif Cpx
+
+ } // end of Reset
+
+/***********************************************************************/
+/* Evaluate block filter for arithmetic operators. */
+/***********************************************************************/
+int BLKFILARI::BlockEval(PGLOBAL g)
+ {
+ int mincmp, maxcmp, n;
+
+#if defined(_DEBUG)
+ assert (Colp->IsClustered());
+#endif
+
+ n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ mincmp = Colp->GetMin()->CompVal(Valp, n);
+ maxcmp = Colp->GetMax()->CompVal(Valp, n);
+
+ switch (Opc) {
+ case OP_EQ:
+ case OP_NE:
+ if (mincmp < 0) // Means minval > Val
+ Result = (Sorted) ? -2 : -1;
+ else if (maxcmp > 0) // Means maxval < Val
+ Result = -1;
+ else if (!mincmp && !maxcmp) // minval = maxval = val
+ Result = 1;
+ else
+ Result = 0;
+
+ break;
+ case OP_GT:
+ case OP_LE:
+ if (mincmp < 0) // minval > Val
+ Result = (Sorted) ? 2 : 1;
+ else if (maxcmp < 0) // maxval > Val
+ Result = 0;
+ else // maxval <= Val
+ Result = -1;
+
+ break;
+ case OP_GE:
+ case OP_LT:
+ if (mincmp <= 0) // minval >= Val
+ Result = (Sorted) ? 2 : 1;
+ else if (maxcmp <= 0) // Maxval >= Val
+ Result = 0;
+ else // Maxval < Val
+ Result = -1;
+
+ break;
+ } // endswitch Opc
+
+ switch (Opc) {
+ case OP_NE:
+ case OP_LE:
+ case OP_LT:
+ Result = -Result;
+ break;
+ } // endswitch Opc
+
+ if (trace)
+ htrc("BlockEval: op=%d n=%d rc=%d\n", Opc, n, Result);
+
+ return Result;
+ } // end of BlockEval
+
+/* ---------------------- Class BLKFILAR2----------------------------- */
+
+/***********************************************************************/
+/* BLKFILAR2 constructor. */
+/***********************************************************************/
+BLKFILAR2::BLKFILAR2(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp)
+ : BLKFILARI(g, tdbp, op, xp)
+ {
+ MakeValueBitmap();
+ } // end of BLKFILAR2 constructor
+
+/***********************************************************************/
+/* MakeValueBitmap: Set the constant value bit map. It can be void */
+/* if the constant value is not in the column distinct values list. */
+/***********************************************************************/
+void BLKFILAR2::MakeValueBitmap(void)
+ {
+ int i; // ndv = Colp->GetNdv();
+ bool found = FALSE;
+ PVBLK dval = Colp->GetDval();
+
+ assert(dval);
+
+ /*********************************************************************/
+ /* Here we cannot use Find because we must get the index */
+ /* of where to put the value if it is not found in the array. */
+ /* This is needed by operators other than OP_EQ or OP_NE. */
+ /*********************************************************************/
+ found = dval->Locate(Valp, i);
+
+ /*********************************************************************/
+ /* Set the constant value bitmap. The bitmaps are really matching */
+ /* the OP_EQ, OP_LE, and OP_LT operator but are also used for the */
+ /* other operators for which the Result will be inverted. */
+ /* The reason the bitmaps are not directly complemented for them is */
+ /* to be able to test easily the cases of sorted columns with Bxp, */
+ /* and the case of a void bitmap, which happens if the constant */
+ /* value is not in the column distinct values list. */
+ /*********************************************************************/
+ if (found) {
+ Bmp = 1 << i; // Bit of the found value
+ Bxp = Bmp - 1; // All smaller values
+
+ if (Opc != OP_LT && Opc != OP_GE)
+ Bxp |= Bmp; // Found value must be included
+
+ } else {
+ Bmp = 0;
+ Bxp = (1 << i) - 1;
+ } // endif found
+
+ if (!(Opc == OP_EQ || Opc == OP_NE))
+ Bmp = Bxp;
+
+ } // end of MakeValueBitmap
+
+/***********************************************************************/
+/* Evaluate XDB2 block filter for arithmetic operators. */
+/***********************************************************************/
+int BLKFILAR2::BlockEval(PGLOBAL g)
+ {
+#if defined(_DEBUG)
+ assert (Colp->IsClustered());
+#endif
+
+ int n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ uint bkmp = *(uint*)Colp->GetBmap()->GetValPtr(n);
+ uint bres = Bmp & bkmp;
+
+ // Set result as if Opc were OP_EQ, OP_LT, or OP_LE
+ if (!bres) {
+ if (!Bmp)
+ Result = -2; // No good block in the table file
+ else if (!Sorted)
+ Result = -1; // No good values in this block
+ else // Sorted column, test for no more good blocks in file
+ Result = (Bxp & bkmp) ? -1 : -2;
+
+ } else
+ // Test whether all block values are good or only some ones
+ Result = (bres == bkmp) ? 1 : 0;
+
+ // For OP_NE, OP_GE, and OP_GT the result must be inverted.
+ switch (Opc) {
+ case OP_NE:
+ case OP_GE:
+ case OP_GT:
+ Result = -Result;
+ break;
+ } // endswitch Opc
+
+ if (trace)
+ htrc("BlockEval2: op=%d n=%d rc=%d\n", Opc, n, Result);
+
+ return Result;
+ } // end of BlockEval
+
+/* ---------------------- Class BLKFILMR2----------------------------- */
+
+/***********************************************************************/
+/* BLKFILMR2 constructor. */
+/***********************************************************************/
+BLKFILMR2::BLKFILMR2(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp)
+ : BLKFILARI(g, tdbp, op, xp)
+ {
+ Nbm = Colp->GetNbm();
+ Bmp = (uint*)PlugSubAlloc(g, NULL, Nbm * sizeof(uint));
+ Bxp = (uint*)PlugSubAlloc(g, NULL, Nbm * sizeof(uint));
+ MakeValueBitmap();
+ } // end of BLKFILMR2 constructor
+
+/***********************************************************************/
+/* MakeValueBitmap: Set the constant value bit map. It can be void */
+/* if the constant value is not in the column distinct values list. */
+/***********************************************************************/
+void BLKFILMR2::MakeValueBitmap(void)
+ {
+ int i; // ndv = Colp->GetNdv();
+ bool found = FALSE, noteq = !(Opc == OP_EQ || Opc == OP_NE);
+ PVBLK dval = Colp->GetDval();
+
+ assert(dval);
+
+ for (i = 0; i < Nbm; i++)
+ Bmp[i] = Bxp[i] = 0;
+
+ /*********************************************************************/
+ /* Here we cannot use Find because we must get the index */
+ /* of where to put the value if it is not found in the array. */
+ /* This is needed by operators other than OP_EQ or OP_NE. */
+ /*********************************************************************/
+ found = dval->Locate(Valp, i);
+
+ /*********************************************************************/
+ /* For bitmaps larger than a ULONG, we must know where Bmp and Bxp */
+ /* are positioned in the ULONG bit map block array. */
+ /*********************************************************************/
+ N = i / MAXBMP;
+ i %= MAXBMP;
+
+ /*********************************************************************/
+ /* Set the constant value bitmaps. The bitmaps are really matching */
+ /* the OP_EQ, OP_LE, and OP_LT operator but are also used for the */
+ /* other operators for which the Result will be inverted. */
+ /* The reason the bitmaps are not directly complemented for them is */
+ /* to be able to easily test the cases of sorted columns with Bxp, */
+ /* and the case of a void bitmap, which happens if the constant */
+ /* value is not in the column distinct values list. */
+ /*********************************************************************/
+ if (found) {
+ Bmp[N] = 1 << i;
+ Bxp[N] = Bmp[N] - 1;
+
+ if (Opc != OP_LT && Opc != OP_GE)
+ Bxp[N] |= Bmp[N]; // Found value must be included
+
+ } else
+ Bxp[N] = (1 << i) - 1;
+
+ if (noteq)
+ Bmp[N] = Bxp[N];
+
+ Void = !Bmp[N]; // There are no good values in the file
+
+ for (i = 0; i < N; i++) {
+ Bxp[i] = ~0;
+
+ if (noteq)
+ Bmp[i] = Bxp[i];
+
+ Void = Void && !Bmp[i];
+ } // endfor i
+
+ if (!Bmp[N] && !Bxp[N])
+ N--;
+
+ } // end of MakeValueBitmap
+
+/***********************************************************************/
+/* Evaluate XDB2 block filter for arithmetic operators. */
+/***********************************************************************/
+int BLKFILMR2::BlockEval(PGLOBAL g)
+ {
+#if defined(_DEBUG)
+ assert (Colp->IsClustered());
+#endif
+
+ int i, n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ bool fnd = FALSE, all = TRUE, gt = TRUE;
+ uint bres;
+ uint *bkmp = (uint*)Colp->GetBmap()->GetValPtr(n * Nbm);
+
+ // Set result as if Opc were OP_EQ, OP_LT, or OP_LE
+ for (i = 0; i < Nbm; i++)
+ if (i <= N) {
+ if ((bres = Bmp[i] & bkmp[i]))
+ fnd = TRUE; // Some good value(s) found in the block
+
+ if (bres != bkmp[i])
+ all = FALSE; // Not all block values are good
+
+ if (Bxp[i] & bkmp[i])
+ gt = FALSE; // Not all block values are > good value(s)
+
+ } else if (bkmp[i]) {
+ all = FALSE;
+ break;
+ } // endif's
+
+ if (!fnd) {
+ if (Void || (gt && Sorted))
+ Result = -2; // No (more) good block in file
+ else
+ Result = -1; // No good values in this block
+
+ } else
+ Result = (all) ? 1 : 0; // All block values are good
+
+ // For OP_NE, OP_GE, and OP_GT the result must be inverted.
+ switch (Opc) {
+ case OP_NE:
+ case OP_GE:
+ case OP_GT:
+ Result = -Result;
+ break;
+ } // endswitch Opc
+
+ if (trace)
+ htrc("BlockEval2: op=%d n=%d rc=%d\n", Opc, n, Result);
+
+ return Result;
+ } // end of BlockEval
+
+/***********************************************************************/
+/* BLKSPCARI constructor. */
+/***********************************************************************/
+BLKSPCARI::BLKSPCARI(PTDBDOS tdbp, int op, PXOB *xp, int bsize)
+ : BLOCKFILTER(tdbp, op)
+ {
+ if (xp[1]->GetType() == TYPE_COLBLK) {
+ Cpx = (PCOL)xp[1]; // Subquery pseudo constant column
+ Correl = TRUE;
+ } else
+ Cpx = NULL;
+
+ Valp = xp[1]->GetValue();
+ Val = (int)xp[1]->GetValue()->GetIntValue();
+ Bsize = bsize;
+ } // end of BLKFILARI constructor
+
+/***********************************************************************/
+/* Reset: re-eval the constant value in the case of pseudo constant */
+/* column use in a correlated subquery. */
+/***********************************************************************/
+void BLKSPCARI::Reset(PGLOBAL g)
+ {
+ if (Cpx) {
+ Cpx->Reset();
+ Cpx->Eval(g);
+ Val = (int)Valp->GetIntValue();
+ } // endif Cpx
+
+ } // end of Reset
+
+/***********************************************************************/
+/* Evaluate block filter for arithmetic operators (ROWID) */
+/***********************************************************************/
+int BLKSPCARI::BlockEval(PGLOBAL g)
+ {
+ int mincmp, maxcmp, n, m;
+
+ n = Tdbp->GetCurBlk();
+ m = n * Bsize + 1; // Minimum Rowid value for this block
+ mincmp = (Val > m) ? 1 : (Val < m) ? (-1) : 0;
+ m = (n + 1) * Bsize; // Maximum Rowid value for this block
+ maxcmp = (Val > m) ? 1 : (Val < m) ? (-1) : 0;
+
+ switch (Opc) {
+ case OP_EQ:
+ case OP_NE:
+ if (mincmp < 0) // Means minval > Val
+ Result = -2; // Always sorted
+ else if (maxcmp > 0) // Means maxval < Val
+ Result = -1;
+ else if (!mincmp && !maxcmp) // minval = maxval = val
+ Result = 1;
+ else
+ Result = 0;
+
+ break;
+ case OP_GT:
+ case OP_LE:
+ if (mincmp < 0) // minval > Val
+ Result = 2; // Always sorted
+ else if (maxcmp < 0) // maxval > Val
+ Result = 0;
+ else // maxval <= Val
+ Result = -1;
+
+ break;
+ case OP_GE:
+ case OP_LT:
+ if (mincmp <= 0) // minval >= Val
+ Result = 2; // Always sorted
+ else if (maxcmp <= 0) // Maxval >= Val
+ Result = 0;
+ else // Maxval < Val
+ Result = -1;
+
+ break;
+ } // endswitch Opc
+
+ switch (Opc) {
+ case OP_NE:
+ case OP_LE:
+ case OP_LT:
+ Result = -Result;
+ break;
+ } // endswitch Opc
+
+ if (trace)
+ htrc("BlockEval: op=%d n=%d rc=%d\n", Opc, n, Result);
+
+ return Result;
+ } // end of BlockEval
+
+/* ------------------------ Class BLKFILIN --------------------------- */
+
+/***********************************************************************/
+/* BLKFILIN constructor. */
+/***********************************************************************/
+BLKFILIN::BLKFILIN(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp)
+ : BLOCKFILTER(tdbp, op)
+ {
+ if (op == OP_IN) {
+ Opc = OP_EQ;
+ Opm = 1;
+ } else {
+ Opc = op;
+ Opm = opm;
+ } // endif op
+
+ Colp = (PDOSCOL)xp[0];
+ Arap = (PARRAY)xp[1];
+ Type = Arap->GetResultType();
+
+ if (Colp->GetResultType() != Type) {
+ sprintf(g->Message, "BLKFILIN: %s", MSG(VALTYPE_NOMATCH));
+ longjmp(g->jumper[g->jump_level], 99);
+ } else if (Colp->GetValue()->IsCi())
+ Arap->SetPrecision(g, 1); // Case insensitive
+
+ Sorted = Colp->IsSorted() > 0;
+ } // end of BLKFILIN constructor
+
+/***********************************************************************/
+/* Reset: have the sorted array reset its Bot value to -1 (bottom). */
+/***********************************************************************/
+void BLKFILIN::Reset(PGLOBAL g)
+ {
+ Arap->Reset();
+// MakeValueBitmap(); // Does nothing for class BLKFILIN
+ } // end of Reset
+
+/***********************************************************************/
+/* Evaluate block filter for a IN operator on a constant array. */
+/* Note: here we need to use the GetValPtrEx function to get a zero */
+/* ended string in case of string argument. This is because the ARRAY */
+/* can have a different width than the char column. */
+/***********************************************************************/
+int BLKFILIN::BlockEval(PGLOBAL g)
+ {
+ int n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ void *minp = Colp->GetMin()->GetValPtrEx(n);
+ void *maxp = Colp->GetMax()->GetValPtrEx(n);
+
+ Result = Arap->BlockTest(g, Opc, Opm, minp, maxp, Sorted);
+ return Result;
+ } // end of BlockEval
+
+/* ------------------------ Class BLKFILIN2 -------------------------- */
+
+/***********************************************************************/
+/* BLKFILIN2 constructor. */
+/* New version that takes care of all operators and modificators. */
+/* It is also ready to handle the case of correlated sub-selects. */
+/***********************************************************************/
+BLKFILIN2::BLKFILIN2(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp)
+ : BLKFILIN(g, tdbp, op, opm, xp)
+ {
+ Nbm = Colp->GetNbm();
+ Valp = AllocateValue(g, Colp->GetValue());
+ Invert = (Opc == OP_NE || Opc == OP_GE || Opc ==OP_GT);
+ Bmp = (uint*)PlugSubAlloc(g, NULL, Nbm * sizeof(uint));
+ Bxp = (uint*)PlugSubAlloc(g, NULL, Nbm * sizeof(uint));
+ MakeValueBitmap();
+ } // end of BLKFILIN2 constructor
+
+/***********************************************************************/
+/* MakeValueBitmap: Set the constant values bit map. It can be void */
+/* if the constant values are not in the column distinct values list. */
+/* The bitmaps are prepared for the EQ, LE, and LT operators and */
+/* takes care of the ALL and ANY modificators. If the operators are */
+/* NE, GE, or GT the modificator is inverted and the result will be. */
+/***********************************************************************/
+void BLKFILIN2::MakeValueBitmap(void)
+ {
+ int i, k, n, ndv = Colp->GetNdv();
+ bool found, noteq = !(Opc == OP_EQ || Opc == OP_NE);
+ bool all = (!Invert) ? (Opm == 2) : (Opm != 2);
+ uint btp;
+ PVBLK dval = Colp->GetDval();
+
+ N = -1;
+
+ // Take care of special cases
+ if (!(n = Arap->GetNval())) {
+ // Return TRUE for ALL because it means that there are no item that
+ // does not verify the condition, which is true indeed.
+ // Return FALSE for ANY because TRUE means that there is at least
+ // one item that verifies the condition, which is false.
+ Result = (Opm == 2) ? 2 : -2;
+ return;
+ } else if (!noteq && all && n > 1) {
+ // An item cannot be equal to all different values
+ // or an item is always unequal to any different values
+ Result = (Opc == OP_EQ) ? -2 : 2;
+ return;
+ } // endif's
+
+ for (i = 0; i < Nbm; i++)
+ Bmp[i] = Bxp[i] = 0;
+
+ for (k = 0; k < n; k++) {
+ Arap->GetNthValue(Valp, k);
+ found = dval->Locate(Valp, i);
+ N = i / MAXBMP;
+ btp = 1 << (i % MAXBMP);
+
+ if (found)
+ Bmp[N] |= btp;
+
+ // For LT and LE if ALL the condition applies to the smallest item
+ // if ANY it applies to the largest item. In the case of EQ we come
+ // here only if ANY or if n == 1, so it does applies to the largest.
+ if ((!k && all) || (k == n - 1 && !all)) {
+ Bxp[N] = btp - 1;
+
+ if (found && Opc != OP_LT && Opc != OP_GE)
+ Bxp[N] |= btp; // Found value must be included
+
+ } // endif k, opm
+
+ } // endfor k
+
+ if (noteq)
+ Bmp[N] = Bxp[N];
+
+ Void = !Bmp[N]; // There are no good values in the file
+
+ for (i = 0; i < N; i++) {
+ Bxp[i] = ~0;
+
+ if (noteq) {
+ Bmp[i] = Bxp[i];
+ Void = FALSE;
+ } // endif noteq
+
+ } // endfor i
+
+ if (!Bmp[N] && !Bxp[N]) {
+ if (--N < 0)
+ // All array values are smaller than block values
+ Result = (Invert) ? 2 : -2;
+
+ } else if (N == Nbm - 1 && (signed)Bmp[N] == (1 << (ndv % MAXBMP)) - 1) {
+ // Condition will be always TRUE or FALSE for the whole file
+ Result = (Invert) ? -2 : 2;
+ N = -1;
+ } // endif's
+
+ } // end of MakeValueBitmap
+
+/***********************************************************************/
+/* Evaluate block filter for set operators on a constant array. */
+/* Note: here we need to use the GetValPtrEx function to get a zero */
+/* ended string in case of string argument. This is because the ARRAY */
+/* can have a different width than the char column. */
+/***********************************************************************/
+int BLKFILIN2::BlockEval(PGLOBAL g)
+ {
+ if (N < 0)
+ return Result; // Was set in MakeValueBitmap
+
+ int i, n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ bool fnd = FALSE, all = TRUE, gt = TRUE;
+ uint bres;
+ uint *bkmp = (uint*)Colp->GetBmap()->GetValPtr(n * Nbm);
+
+ // Set result as if Opc were OP_EQ, OP_LT, or OP_LE
+ // The difference between ALL or ANY was handled in MakeValueBitmap
+ for (i = 0; i < Nbm; i++)
+ if (i <= N) {
+ if ((bres = Bmp[i] & bkmp[i]))
+ fnd = TRUE;
+
+ if (bres != bkmp[i])
+ all = FALSE;
+
+ if (Bxp[i] & bkmp[i])
+ gt = FALSE;
+
+ } else if (bkmp[i]) {
+ all = FALSE;
+ break;
+ } // endif's
+
+ if (!fnd) {
+ if (Void || (Sorted && gt))
+ Result = -2; // No more good block in file
+ else
+ Result = -1; // No good values in this block
+
+ } else if (all)
+ Result = 1; // All block values are good
+ else
+ Result = 0; // Block contains some good values
+
+ // For OP_NE, OP_GE, and OP_GT the result must be inverted.
+ switch (Opc) {
+ case OP_NE:
+ case OP_GE:
+ case OP_GT:
+ Result = -Result;
+ break;
+ } // endswitch Opc
+
+ return Result;
+ } // end of BlockEval
+
+#if 0
+/***********************************************************************/
+/* BLKFILIN2 constructor. */
+/***********************************************************************/
+BLKFILIN2::BLKFILIN2(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp)
+ : BLKFILIN(g, tdbp, op, opm, xp)
+ {
+ // Currently, bitmap matching is only implemented for the IN operator
+ if (!(Bitmap = (op == OP_IN || (op == OP_EQ && opm != 2)))) {
+ Nbm = Colp->GetNbm();
+ N = 0;
+ return; // Revert to standard minmax method
+ } // endif minmax
+
+ int i, n;
+ ULONG btp;
+ PVAL valp = AllocateValue(g, Colp->GetValue());
+ PVBLK dval = Colp->GetDval();
+
+ Nbm = Colp->GetNbm();
+ N = -1;
+ Bmp = (PULONG)PlugSubAlloc(g, NULL, Nbm * sizeof(ULONG));
+ Bxp = (PULONG)PlugSubAlloc(g, NULL, Nbm * sizeof(ULONG));
+
+ for (i = 0; i < Nbm; i++)
+ Bmp[i] = Bxp[i] = 0;
+
+ for (n = 0; n < Arap->GetNval(); n++) {
+ Arap->GetNthValue(valp, n);
+
+ if ((i = dval->Find(valp)) >= 0)
+ Bmp[i / MAXBMP] |= 1 << (i % MAXBMP);
+
+ } // endfor n
+
+ for (i = Nbm - 1; i >= 0; i--)
+ if (Bmp[i]) {
+ for (btp = Bmp[i]; btp; btp >>= 1)
+ Bxp[i] |= btp;
+
+ for (N = i--; i >= 0; i--)
+ Bxp[i] = ~0;
+
+ break;
+ } // endif Bmp
+
+ } // end of BLKFILIN2 constructor
+
+/***********************************************************************/
+/* Evaluate block filter for a IN operator on a constant array. */
+/* Note: here we need to use the GetValPtrEx function to get a zero */
+/* ended string in case of string argument. This is because the ARRAY */
+/* can have a different width than the char column. */
+/***********************************************************************/
+int BLKFILIN2::BlockEval(PGLOBAL g)
+ {
+ if (N < 0)
+ return -2; // IN list contains no good values
+
+ int i, n = ((PTDBDOS)Colp->GetTo_Tdb())->GetCurBlk();
+ bool fnd = FALSE, all = TRUE, gt = TRUE;
+ ULONG bres;
+ PULONG bkmp = (PULONG)Colp->GetBmap()->GetValPtr(n * Nbm);
+
+ if (Bitmap) {
+ // For IN operator use the bitmap method
+ for (i = 0; i < Nbm; i++)
+ if (i <= N) {
+ if ((bres = Bmp[i] & bkmp[i]))
+ fnd = TRUE;
+
+ if (bres != bkmp[i])
+ all = FALSE;
+
+ if (Bxp[i] & bkmp[i])
+ gt = FALSE;
+
+ } else if (bkmp[i]) {
+ all = FALSE;
+ break;
+ } // endif's
+
+ if (!fnd) {
+ if (Sorted && gt)
+ Result = -2; // No more good block in file
+ else
+ Result = -1; // No good values in this block
+
+ } else if (all)
+ Result = 1; // All block values are good
+ else
+ Result = 0; // Block contains some good values
+
+ } else {
+ // For other than IN operators, revert to standard minmax method
+ int n = 0, ndv = Colp->GetNdv();
+ void *minp = NULL;
+ void *maxp = NULL;
+ ULONG btp;
+ PVBLK dval = Colp->GetDval();
+
+ for (i = 0; i < Nbm; i++)
+ for (btp = 1; btp && n < ndv; btp <<= 1, n++)
+ if (btp & bkmp[i]) {
+ if (!minp)
+ minp = dval->GetValPtrEx(n);
+
+ maxp = dval->GetValPtrEx(n);
+ } // endif btp
+
+ Result = Arap->BlockTest(g, Opc, Opm, minp, maxp, Colp->IsSorted());
+ } // endif Bitmap
+
+ return Result;
+ } // end of BlockEval
+#endif // 0
+
+/* ------------------------ Class BLKSPCIN --------------------------- */
+
+/***********************************************************************/
+/* BLKSPCIN constructor. */
+/***********************************************************************/
+BLKSPCIN::BLKSPCIN(PGLOBAL g, PTDBDOS tdbp, int op, int opm,
+ PXOB *xp, int bsize)
+ : BLOCKFILTER(tdbp, op)
+ {
+ if (op == OP_IN) {
+ Opc = OP_EQ;
+ Opm = 1;
+ } else
+ Opm = opm;
+
+ Arap = (PARRAY)xp[1];
+#if defined(_DEBUG)
+ assert (Opm);
+ assert (Arap->GetResultType() == TYPE_INT);
+#endif
+ Bsize = bsize;
+ } // end of BLKSPCIN constructor
+
+/***********************************************************************/
+/* Reset: have the sorted array reset its Bot value to -1 (bottom). */
+/***********************************************************************/
+void BLKSPCIN::Reset(PGLOBAL g)
+ {
+ Arap->Reset();
+ } // end of Reset
+
+/***********************************************************************/
+/* Evaluate block filter for a IN operator on a constant array. */
+/***********************************************************************/
+int BLKSPCIN::BlockEval(PGLOBAL g)
+ {
+ int n = Tdbp->GetCurBlk();
+ int minrow = n * Bsize + 1; // Minimum Rowid value for this block
+ int maxrow = (n + 1) * Bsize; // Maximum Rowid value for this block
+
+ Result = Arap->BlockTest(g, Opc, Opm, &minrow, &maxrow, TRUE);
+ return Result;
+ } // end of BlockEval
+
+/* ------------------------------------------------------------------- */
+
+#if 0
+/***********************************************************************/
+/* Implementation of the BLOCKINDEX class. */
+/***********************************************************************/
+BLOCKINDEX::BLOCKINDEX(PBX nx, PDOSCOL cp, PKXBASE kp)
+ {
+ Next = nx;
+ Tdbp = (cp) ? (PTDBDOS)cp->GetTo_Tdb() : NULL;
+ Colp = cp;
+ Kxp = kp;
+ Type = (cp) ? cp->GetResultType() : TYPE_ERROR;
+ Sorted = (cp) ? cp->IsSorted() > 0 : FALSE;
+ Result = 0;
+ } // end of BLOCKINDEX constructor
+
+/***********************************************************************/
+/* Reset Bot and Top values of optimized Kindex blocks. */
+/***********************************************************************/
+void BLOCKINDEX::Reset(void)
+ {
+ if (Next)
+ Next->Reset();
+
+ Kxp->Reset();
+ } // end of Reset
+
+/***********************************************************************/
+/* Evaluate block indexing test. */
+/***********************************************************************/
+int BLOCKINDEX::BlockEval(PGLOBAL g)
+ {
+#if defined(_DEBUG)
+ assert (Tdbp && Colp);
+#endif
+ int n = Tdbp->GetCurBlk();
+ void *minp = Colp->GetMin()->GetValPtr(n);
+ void *maxp = Colp->GetMax()->GetValPtr(n);
+
+ Result = Kxp->BlockTest(g, minp, maxp, Type, Sorted);
+ return Result;
+ } // end of BlockEval
+
+/***********************************************************************/
+/* Make file output of BLOCKINDEX contents. */
+/***********************************************************************/
+void BLOCKINDEX::Print(PGLOBAL g, FILE *f, UINT n)
+ {
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+
+ fprintf(f, "%sBLOCKINDEX: at %p next=%p col=%s kxp=%p result=%d\n",
+ m, this, Next, (Colp) ? Colp->GetName() : "Rowid", Kxp, Result);
+
+ if (Next)
+ Next->Print(g, f, n);
+
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of BLOCKINDEX contents. */
+/***********************************************************************/
+void BLOCKINDEX::Print(PGLOBAL g, char *ps, UINT z)
+ {
+ strncat(ps, "BlockIndex(es)", z);
+ } // end of Print
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the BLOCKINDX2 class. */
+/***********************************************************************/
+BLOCKINDX2::BLOCKINDX2(PBX nx, PDOSCOL cp, PKXBASE kp)
+ : BLOCKINDEX(nx, cp, kp)
+ {
+ Nbm = Colp->GetNbm();
+ Dval = Colp->GetDval();
+ Bmap = Colp->GetBmap();
+#if defined(_DEBUG)
+ assert(Dval && Bmap);
+#endif
+ } // end of BLOCKINDX2 constructor
+
+/***********************************************************************/
+/* Evaluate block indexing test. */
+/***********************************************************************/
+int BLOCKINDX2::BlockEval(PGLOBAL g)
+ {
+ int n = Tdbp->GetCurBlk();
+ PUINT bmp = (PUINT)Bmap->GetValPtr(n * Nbm);
+
+ Result = Kxp->BlockTst2(g, Dval, bmp, Nbm, Type, Sorted);
+ return Result;
+ } // end of BlockEval
+
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the BLKSPCINDX class. */
+/***********************************************************************/
+BLKSPCINDX::BLKSPCINDX(PBX nx, PTDBDOS tp, PKXBASE kp, int bsize)
+ : BLOCKINDEX(nx, NULL, kp)
+ {
+ Tdbp = tp;
+ Bsize = bsize;
+ Type = TYPE_INT;
+ Sorted = TRUE;
+ } // end of BLKSPCINDX constructor
+
+/***********************************************************************/
+/* Evaluate block indexing test. */
+/***********************************************************************/
+int BLKSPCINDX::BlockEval(PGLOBAL g)
+ {
+ int n = Tdbp->GetCurBlk();
+ int minrow = n * Bsize + 1; // Minimum Rowid value for this block
+ int maxrow = (n + 1) * Bsize; // Maximum Rowid value for this block
+
+ Result = Kxp->BlockTest(g, &minrow, &maxrow, TYPE_INT, TRUE);
+ return Result;
+ } // end of BlockEval
+#endif // 0
diff --git a/storage/connect/blkfil.h b/storage/connect/blkfil.h
new file mode 100644
index 00000000000..00b00139042
--- /dev/null
+++ b/storage/connect/blkfil.h
@@ -0,0 +1,295 @@
+/*************** BlkFil H Declares Source Code File (.H) ***************/
+/* Name: BLKFIL.H Version 2.1 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2010 */
+/* */
+/* This file contains the block optimization related classes declares */
+/***********************************************************************/
+#ifndef __BLKFIL__
+#define __BLKFIL__
+
+typedef class BLOCKFILTER *PBF;
+typedef class BLOCKINDEX *PBX;
+
+/***********************************************************************/
+/* Definition of class BLOCKFILTER. */
+/***********************************************************************/
+class DllExport BLOCKFILTER : public BLOCK { /* Block Filter */
+ friend class BLKFILLOG;
+ public:
+ // Constructors
+ BLOCKFILTER(PTDBDOS tdbp, int op);
+
+ // Implementation
+ int GetResult(void) {return Result;}
+ bool Correlated(void) {return Correl;}
+
+ // Methods
+ virtual void Reset(PGLOBAL) = 0;
+ virtual int BlockEval(PGLOBAL) = 0;
+ virtual void Print(PGLOBAL g, FILE *f, uint n);
+ virtual void Print(PGLOBAL g, char *ps, uint z);
+
+ protected:
+ BLOCKFILTER(void) {} // Standard constructor not to be used
+
+ // Members
+ PTDBDOS Tdbp; // Owner TDB
+ bool Correl; // TRUE for correlated subqueries
+ int Opc; // Comparison operator
+ int Opm; // Operator modificator
+ int Result; // Result from evaluation
+ }; // end of class BLOCKFILTER
+
+/***********************************************************************/
+/* Definition of class BLKFILLOG (with Op=OP_AND,OP_OR, or OP_NOT) */
+/***********************************************************************/
+class DllExport BLKFILLOG : public BLOCKFILTER { /* Logical Op Block Filter */
+ public:
+ // Constructors
+ BLKFILLOG(PTDBDOS tdbp, int op, PBF *bfp, int n);
+
+ // Methods
+ virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+
+ protected:
+ BLKFILLOG(void) {} // Standard constructor not to be used
+
+ // Members
+ PBF *Fil; // Points to Block filter args
+ int N;
+ }; // end of class BLKFILLOG
+
+/***********************************************************************/
+/* Definition of class BLKFILARI (with Op=OP_EQ,NE,GT,GE,LT, or LE) */
+/***********************************************************************/
+class DllExport BLKFILARI : public BLOCKFILTER { /* Arithm. Op Block Filter */
+ public:
+ // Constructors
+ BLKFILARI(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp);
+
+ // Methods
+ virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+ virtual void MakeValueBitmap(void) {}
+
+ protected:
+ BLKFILARI(void) {} // Standard constructor not to be used
+
+ // Members
+ PDOSCOL Colp; // Points to column argument
+ PCOL Cpx; // Point to subquery "constant" column
+ PVAL Valp; // Points to constant argument Value
+ bool Sorted; // True if the column is sorted
+ }; // end of class BLKFILARI
+
+/***********************************************************************/
+/* Definition of class BLKFILAR2 (with Op=OP_EQ,NE,GT,GE,LT, or LE) */
+/***********************************************************************/
+class DllExport BLKFILAR2 : public BLKFILARI { /* Arithm. Op Block Filter */
+ public:
+ // Constructors
+ BLKFILAR2(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp);
+
+ // Methods
+ virtual int BlockEval(PGLOBAL g);
+ virtual void MakeValueBitmap(void);
+
+ protected:
+ BLKFILAR2(void) {} // Standard constructor not to be used
+
+ // Members
+ uint Bmp; // The value bitmap used to test blocks
+ uint Bxp; // Bitmap used when Opc = OP_EQ
+ }; // end of class BLKFILAR2
+
+/***********************************************************************/
+/* Definition of class BLKFILAR2 (with Op=OP_EQ,NE,GT,GE,LT, or LE) */
+/* To be used when the bitmap is an array of ULONG bitmaps; */
+/***********************************************************************/
+class DllExport BLKFILMR2 : public BLKFILARI { /* Arithm. Op Block Filter */
+ public:
+ // Constructors
+ BLKFILMR2(PGLOBAL g, PTDBDOS tdbp, int op, PXOB *xp);
+
+ // Methods
+ virtual int BlockEval(PGLOBAL g);
+ virtual void MakeValueBitmap(void);
+
+ protected:
+ BLKFILMR2(void) {} // Standard constructor not to be used
+
+ // Members
+ int Nbm; // The number of ULONG bitmaps
+ int N; // The position of the leftmost ULONG
+ bool Void; // True if all file blocks can be skipped
+ uint *Bmp; // The values bitmaps used to test blocks
+ uint *Bxp; // Bit of values <= max value
+ }; // end of class BLKFILMR2
+
+/***********************************************************************/
+/* Definition of class BLKSPCARI (with Op=OP_EQ,NE,GT,GE,LT, or LE) */
+/***********************************************************************/
+class DllExport BLKSPCARI : public BLOCKFILTER { /* Arithm. Op Block Filter */
+ public:
+ // Constructors
+ BLKSPCARI(PTDBDOS tdbp, int op, PXOB *xp, int bsize);
+
+ // Methods
+ virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+
+ protected:
+ BLKSPCARI(void) {} // Standard constructor not to be used
+
+ // Members
+ PCOL Cpx; // Point to subquery "constant" column
+ PVAL Valp; // Points to constant argument Value
+ int Val; // Constant argument Value
+ int Bsize; // Table block size
+ }; // end of class BLKSPCARI
+
+/***********************************************************************/
+/* Definition of class BLKFILIN (with Op=OP_IN) */
+/***********************************************************************/
+class DllExport BLKFILIN : public BLOCKFILTER { // With array arguments.
+ public:
+ // Constructors
+ BLKFILIN(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp);
+
+ // Methods
+ virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+ virtual void MakeValueBitmap(void) {}
+
+ protected:
+ // Member
+ PDOSCOL Colp; // Points to column argument
+ PARRAY Arap; // Points to array argument
+ bool Sorted; // True if the column is sorted
+ int Type; // Type of array elements
+ }; // end of class BLKFILIN
+
+/***********************************************************************/
+/* Definition of class BLKFILIN2 (with Op=OP_IN) */
+/***********************************************************************/
+class DllExport BLKFILIN2 : public BLKFILIN { // With array arguments.
+ public:
+ // Constructors
+ BLKFILIN2(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp);
+
+ // Methods
+//virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+ virtual void MakeValueBitmap(void);
+
+ protected:
+ // Member
+ int Nbm; // The number of ULONG bitmaps
+ int N; // The position of the leftmost ULONG
+//bool Bitmap; // True for IN operator (temporary)
+ bool Void; // True if all file blocks can be skipped
+ bool Invert; // True when Result must be inverted
+ uint *Bmp; // The values bitmaps used to test blocks
+ uint *Bxp; // Bit of values <= max value
+ PVAL Valp; // Used while building the bitmaps
+ }; // end of class BLKFILIN2
+
+/***********************************************************************/
+/* Definition of class BLKSPCIN (with Op=OP_IN) Special column */
+/***********************************************************************/
+class DllExport BLKSPCIN : public BLOCKFILTER { // With array arguments.
+ public:
+ // Constructors
+ BLKSPCIN(PGLOBAL g, PTDBDOS tdbp, int op, int opm, PXOB *xp, int bsize);
+
+ // Methods
+ virtual void Reset(PGLOBAL g);
+ virtual int BlockEval(PGLOBAL g);
+
+ protected:
+ // Member
+ PARRAY Arap; // Points to array argument
+ int Bsize; // Table block size
+ }; // end of class BLKSPCIN
+
+// ---------------- Class used in block indexing testing ----------------
+
+#if 0
+/***********************************************************************/
+/* Definition of class BLOCKINDEX. */
+/* Used to test the indexing to joined tables when the foreign key is */
+/* a clustered or sorted column. If the table is joined to several */
+/* tables, blocks will be chained together. */
+/***********************************************************************/
+class DllExport BLOCKINDEX : public BLOCK { /* Indexing Test Block */
+ public:
+ // Constructors
+ BLOCKINDEX(PBX nx, PDOSCOL cp, PKXBASE kp);
+
+ // Implementation
+ PBX GetNext(void) {return Next;}
+
+ // Methods
+ void Reset(void);
+ virtual int BlockEval(PGLOBAL);
+ virtual void Print(PGLOBAL g, FILE *f, UINT n);
+ virtual void Print(PGLOBAL g, char *ps, UINT z);
+
+ protected:
+ BLOCKINDEX(void) {} // Standard constructor not to be used
+
+ // Members
+ PBX Next; // To next Index Block
+ PTDBDOS Tdbp; // To table description block
+ PDOSCOL Colp; // Clustered foreign key
+ PKXBASE Kxp; // To Kindex of joined table
+ bool Sorted; // TRUE if column is sorted
+ int Type; // Col/Index type
+ int Result; // Result from evaluation
+ }; // end of class BLOCKINDEX
+
+/***********************************************************************/
+/* Definition of class BLOCKINDX2. (XDB2) */
+/***********************************************************************/
+class DllExport BLOCKINDX2 : public BLOCKINDEX { /* Indexing Test Block */
+ public:
+ // Constructors
+ BLOCKINDX2(PBX nx, PDOSCOL cp, PKXBASE kp);
+
+ // Methods
+ virtual int BlockEval(PGLOBAL);
+
+ protected:
+ BLOCKINDX2(void) {} // Standard constructor not to be used
+
+ // Members
+ int Nbm; // The number of ULONG bitmaps
+ PVBLK Dval; // Array of column distinct values
+ PVBLK Bmap; // Array of block bitmap values
+ }; // end of class BLOCKINDX2
+
+/***********************************************************************/
+/* Definition of class BLKSPCINDX. */
+/* Used to test the indexing to joined tables when the foreign key is */
+/* the ROWID special column. If the table is joined to several */
+/* tables, blocks will be chained together. */
+/***********************************************************************/
+class DllExport BLKSPCINDX : public BLOCKINDEX { /* Indexing Test Block */
+ public:
+ // Constructors
+ BLKSPCINDX(PBX nx, PTDBDOS tp, PKXBASE kp, int bsize);
+
+ // Methods
+ virtual int BlockEval(PGLOBAL);
+
+ protected:
+ BLKSPCINDX(void) {} // Standard constructor not to be used
+
+ // Members
+ int Bsize; // Table block size
+ }; // end of class BLOCKINDEX
+#endif // 0
+
+#endif // __BLKFIL__
diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h
index 6e6cf86fc87..5baab294737 100644
--- a/storage/connect/catalog.h
+++ b/storage/connect/catalog.h
@@ -1,7 +1,7 @@
/*************** Catalog H Declares Source Code File (.H) **************/
-/* Name: CATALOG.H Version 3.2 */
+/* Name: CATALOG.H Version 3.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2000-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2000-2014 */
/* */
/* This file contains the CATALOG PlugDB classes definitions. */
/***********************************************************************/
@@ -43,6 +43,8 @@ typedef struct _colinfo {
int Key;
int Precision;
int Scale;
+ int Opt;
+ int Freq;
char *Remark;
char *Datefmt;
char *Fieldfmt;
@@ -66,11 +68,11 @@ class DllExport CATALOG {
bool GetDefHuge(void) {return DefHuge;}
void SetDefHuge(bool b) {DefHuge = b;}
char *GetCbuf(void) {return Cbuf;}
- char *GetDataPath(void) {return (char*)DataPath;}
+//char *GetDataPath(void) {return (char*)DataPath;}
// Methods
virtual void Reset(void) {}
- virtual void SetDataPath(PGLOBAL g, const char *path) {}
+//virtual void SetDataPath(PGLOBAL g, const char *path) {}
virtual bool CheckName(PGLOBAL g, char *name) {return true;}
virtual bool ClearName(PGLOBAL g, PSZ name) {return true;}
virtual PRELDEF MakeOneTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;}
@@ -82,7 +84,7 @@ class DllExport CATALOG {
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,
+ 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[]) {}
@@ -104,7 +106,7 @@ class DllExport CATALOG {
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 */
+//LPCSTR DataPath; /* Is the Path of DB data dir */
}; // end of class CATALOG
#endif // __CATALOG__H
diff --git a/storage/connect/checklvl.h b/storage/connect/checklvl.h
index 5505534678d..d1e37afbc93 100644
--- a/storage/connect/checklvl.h
+++ b/storage/connect/checklvl.h
@@ -34,9 +34,10 @@ enum XMOD {XMOD_EXECUTE = 0, /* DOS execution mode */
/***********************************************************************/
/* Following definitions indicate the use of a temporay file. */
/***********************************************************************/
-enum USETEMP {TMP_AUTO = 0, /* Best choice */
- TMP_NO = 1, /* Never */
+enum USETEMP {TMP_NO = 0, /* Never */
+ TMP_AUTO = 1, /* Best choice */
TMP_YES = 2, /* Always */
- TMP_FORCE = 3}; /* Forced for MAP tables */
+ TMP_FORCE = 3, /* Forced for MAP tables */
+ TMP_TEST = 4}; /* Testing value */
#endif // _CHKLVL_DEFINED_
diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp
index ffa29bb9821..81ab1ad7245 100644
--- a/storage/connect/colblk.cpp
+++ b/storage/connect/colblk.cpp
@@ -1,376 +1,419 @@
-/************* 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"
-
-extern "C" int trace;
-
-/***********************************************************************/
-/* 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;
- Long = cdp->Long;
- Precision = cdp->Precision;
- 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));
- Long = 0;
- Precision = 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;
-
- if (trace > 1)
- htrc(" copying COLBLK %s from %p to %p\n", Name, col1, this);
-
- 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;
-
- if (trace > 1)
- htrc("COLBLK: %p format=%c(%d,%d)\n",
- this, *fmt.Type, fmt.Length, fmt.Prec);
-
- 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)
- {
- if (trace > 1)
- htrc("Col Eval: %s status=%.4X\n", Name, Status);
-
- 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);
-
- if (trace > 1)
- htrc(" colp=%p type=%d value=%p coluse=%.4X status=%.4X\n",
- this, Buf_Type, Value, ColUse, Status);
-
- 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"
+
+extern "C" int trace;
+
+/***********************************************************************/
+/* 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;
+
+ if (trace > 1)
+ htrc(" copying COLBLK %s from %p to %p\n", Name, col1, this);
+
+ 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;
+
+ if (trace > 1)
+ htrc("COLBLK: %p format=%c(%d,%d)\n",
+ this, *fmt.Type, fmt.Length, fmt.Prec);
+
+ 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)
+ {
+ if (trace > 1)
+ htrc("Col Eval: %s status=%.4X\n", Name, Status);
+
+ 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);
+
+ if (trace > 1)
+ htrc(" colp=%p type=%d value=%p coluse=%.4X status=%.4X\n",
+ this, Buf_Type, Value, ColUse, Status);
+
+ 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, OPVAL op) : SPCBLK(cp), Op(op)
+ {
+//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());
+
+ if (Op != OP_XX) {
+ char buff[_MAX_PATH];
+
+ Value->SetValue_psz(ExtractFromPath(g, buff, filename, Op));
+ } else
+ 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
+
+/***********************************************************************/
+/* PRTBLK constructor for the PARTID special column. */
+/***********************************************************************/
+PRTBLK::PRTBLK(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 = true; // TODO: check whether this is true indeed
+ Pname = NULL;
+ } // end of PRTBLK constructor
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to return the partition ID. */
+/***********************************************************************/
+void PRTBLK::ReadColumn(PGLOBAL g)
+ {
+ if (Pname == NULL) {
+ char *p;
+ PTDBASE tdbp = (PTDBASE)To_Tdb;
+
+ Pname = tdbp->GetDef()->GetStringCatInfo(g, "partname", "?");
+
+ p = strrchr(Pname, '#');
+ Value->SetValue_psz((p) ? p + 1 : Pname);
+ } // endif Pname
+
+ } // 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 a340ee4450a..5e8dc77ff69 100644
--- a/storage/connect/colblk.h
+++ b/storage/connect/colblk.h
@@ -36,10 +36,13 @@ class DllExport COLBLK : public XOBJECT {
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);}
@@ -48,17 +51,18 @@ class DllExport COLBLK : public XOBJECT {
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;}
bool IsVirtual(void) {return Cdp->IsVirtual();}
+ bool IsNullable(void) {return Nullable;}
void SetNullable(bool b) {Nullable = b;}
-
- // Methods
+
+ // Methods
virtual void Reset(void);
virtual bool Compare(PXOB xp);
virtual bool SetFormat(PGLOBAL, FORMAT&);
@@ -70,6 +74,7 @@ class DllExport COLBLK : public XOBJECT {
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:
@@ -82,9 +87,11 @@ class DllExport COLBLK : public XOBJECT {
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
@@ -100,7 +107,7 @@ class DllExport SPCBLK : public COLBLK {
// Implementation
virtual int GetAmType(void) = 0;
- virtual bool GetRnm(void) {return false;}
+ virtual bool GetRnm(void) {return false;}
// Methods
virtual bool IsSpecial(void) {return true;}
@@ -122,7 +129,7 @@ class DllExport RIDBLK : public SPCBLK {
// Implementation
virtual int GetAmType(void) {return TYPE_AM_ROWID;}
- virtual bool GetRnm(void) {return Rnm;}
+ virtual bool GetRnm(void) {return Rnm;}
// Methods
virtual void ReadColumn(PGLOBAL g);
@@ -137,7 +144,7 @@ class DllExport RIDBLK : public SPCBLK {
class DllExport FIDBLK : public SPCBLK {
public:
// Constructor
- FIDBLK(PCOLUMN cp);
+ FIDBLK(PCOLUMN cp, OPVAL op);
// Implementation
virtual int GetAmType(void) {return TYPE_AM_FILID;}
@@ -147,7 +154,8 @@ class DllExport FIDBLK : public SPCBLK {
virtual void ReadColumn(PGLOBAL g);
protected:
- PSZ Fn; // The current To_File of the table
+ PSZ Fn; // The current To_File of the table
+ OPVAL Op; // The file part operator
}; // end of class FIDBLK
/***********************************************************************/
@@ -174,6 +182,29 @@ class DllExport TIDBLK : public SPCBLK {
}; // end of class TIDBLK
/***********************************************************************/
+/* Class PRTBLK: PARTID special column descriptor. */
+/***********************************************************************/
+class DllExport PRTBLK : public SPCBLK {
+ public:
+ // Constructor
+ PRTBLK(PCOLUMN cp);
+
+ // Implementation
+ virtual int GetAmType(void) {return TYPE_AM_PRTID;}
+
+ // Methods
+ virtual void Reset(void) {} // This is a pseudo constant column
+ virtual void ReadColumn(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ PRTBLK(void) {}
+
+ // Members
+ PSZ Pname; // The current partition name
+ }; // end of class PRTBLK
+
+/***********************************************************************/
/* Class SIDBLK: SERVID special column descriptor. */
/***********************************************************************/
class DllExport SIDBLK : public SPCBLK {
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index 2d8aeb8b5f4..381e437f9ec 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -57,7 +57,7 @@ extern "C" int trace;
/* Routines called internally by semantic routines. */
/***********************************************************************/
void CntEndDB(PGLOBAL);
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp);
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr= false);
/***********************************************************************/
/* MySQL routines called externally by semantic routines. */
@@ -122,9 +122,12 @@ bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
(dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL,
handler);
+ // Set the database path for this table
+ handler->SetDataPath(g, pathname);
+
if (dbuserp->Catalog) {
// ((MYCAT *)dbuserp->Catalog)->SetHandler(handler); done later
- ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
+// ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
return false; // Nothing else to do
} // endif Catalog
@@ -141,8 +144,8 @@ bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
if (!(dbuserp->Catalog= new MYCAT(handler)))
return true;
- ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
- dbuserp->UseTemp= TMP_YES; // Must use temporary file
+//((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname);
+//dbuserp->UseTemp= TMP_AUTO;
/*********************************************************************/
/* All is correct. */
@@ -167,7 +170,13 @@ bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
if (tdbp) {
b= tdbp->GetFtype() != RECFM_NAF;
info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0;
- info->records= (unsigned)tdbp->GetMaxSize(g);
+
+ if (!b || info->data_file_length)
+ info->records= (unsigned)tdbp->Cardinality(g);
+// info->records= (unsigned)tdbp->GetMaxSize(g);
+ else
+ info->records= 0;
+
// info->mean_rec_length= tdbp->GetLrecl();
info->mean_rec_length= 0;
info->data_file_name= (b) ? tdbp->GetFile(g) : NULL;
@@ -343,12 +352,12 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
//tdbp->SetMode(mode);
- if (del && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) {
+ 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);
+// PDOSDEF ddp= new(g) DOSDEF;
+// PTDB tp= new(g) TDBDOS(ddp, NULL);
+ tdbp->SetNext((PTDB)1);
dup->Check &= ~CHK_DELETE;
} // endif del
@@ -387,7 +396,7 @@ bool CntRewindTable(PGLOBAL g, PTDB tdbp)
/***********************************************************************/
/* Evaluate all columns after a record is read. */
/***********************************************************************/
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp)
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr)
{
RCODE rc= RC_OK;
PCOL colp;
@@ -415,7 +424,7 @@ RCODE EvalColumns(PGLOBAL g, PTDB tdbp)
colp->Reset();
// Virtual columns are computed by MariaDB
- if (!colp->GetColUse(U_VIRTUAL))
+ if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
if (colp->Eval(g))
rc= RC_FX;
@@ -439,8 +448,8 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
// 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);
+// for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
+// colp->SetKcol(NULL);
((PTDBASE)tdbp)->ResetKindex(g, NULL);
} // endif index
@@ -456,7 +465,12 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
goto err;
} // endif rc
- while ((rc= (RCODE)tdbp->ReadDB(g)) == RC_NF) ;
+ do {
+ if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK)
+ if (!ApplyFilter(g, tdbp->GetFilter()))
+ rc= RC_NF;
+
+ } while (rc == RC_NF);
err:
g->jump_level--;
@@ -468,7 +482,7 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
/***********************************************************************/
RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
{
- RCODE rc;
+ RCODE rc;
PCOL colp;
PTDBASE tp= (PTDBASE)tdbp;
@@ -492,11 +506,12 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
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);
+ if (tp->IsIndexed())
+ // Index values must be sorted before updating
+ rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, true);
+ else
+ // Return result code from write operation
+ rc= (RCODE)tdbp->WriteDB(g);
err:
g->jump_level--;
@@ -506,7 +521,7 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
/***********************************************************************/
/* UpdateRow: Update a row into a table. */
/***********************************************************************/
-RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
+RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
{
if (!tdbp || tdbp->GetMode() != MODE_UPDATE)
return RC_FX;
@@ -520,38 +535,70 @@ RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
/***********************************************************************/
RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
{
- RCODE rc;
+ RCODE rc;
+ PTDBASE tp= (PTDBASE)tdbp;
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);
+ if (all) {
+ if (((PTDBASE)tdbp)->GetDef()->Indexable())
+ ((PTDBDOS)tdbp)->Cardinal= 0;
+
+ // Note: if all, this call will be done when closing the table
+ rc= (RCODE)tdbp->DeleteDB(g, RC_FX);
+//} else if (tp->GetKindex() && !tp->GetKindex()->IsSorted() &&
+// tp->Txfp->GetAmType() != TYPE_AM_DBF) {
+ } else if(tp->IsIndexed()) {
+ // Index values must be sorted before updating
+ rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, false);
+ } else // Return result code from delete operation
+ rc= (RCODE)tdbp->DeleteDB(g, RC_OK);
+
return rc;
} // end of CntDeleteRow
/***********************************************************************/
/* CLOSETAB: Close a table. */
/***********************************************************************/
-int CntCloseTable(PGLOBAL g, PTDB tdbp)
+int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
{
int rc= RC_OK;
- TDBDOX *tbxp= NULL;
+ TDBASE *tbxp= (PTDBASE)tdbp;
- if (!tdbp || tdbp->GetUse() != USE_OPEN)
+ if (!tdbp)
return rc; // Nothing to do
+ else if (tdbp->GetUse() != USE_OPEN) {
+ if (tdbp->GetAmType() == TYPE_AM_XML)
+ tdbp->CloseDB(g); // Opened by GetMaxSize
+
+ return rc;
+ } // endif !USE_OPEN
if (trace)
- printf("CntCloseTable: tdbp=%p mode=%d\n", tdbp, tdbp->GetMode());
+ printf("CntCloseTable: tdbp=%p mode=%d nox=%d abort=%d\n",
+ tdbp, tdbp->GetMode(), nox, abort);
+
+ if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN) {
+ if (tbxp->IsIndexed())
+ rc= ((PTDBDOS)tdbp)->GetTxfp()->DeleteSortedRows(g);
- if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN)
- rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
+ if (!rc)
+ rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
+
+ } else if (tbxp->GetMode() == MODE_UPDATE && tbxp->IsIndexed())
+ rc= ((PTDBDOX)tdbp)->Txfp->UpdateSortedRows(g);
+
+ switch(rc) {
+ case RC_FX:
+ abort= true;
+ break;
+ case RC_INFO:
+ PushWarning(g, tbxp);
+ break;
+ } // endswitch rc
// Prepare error return
if (g->jump_level == MAX_JUMP) {
@@ -561,14 +608,16 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp)
} // endif
if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
+ rc= RC_FX;
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->SetAbort(abort);
tdbp->CloseDB(g);
-
+ tdbp->SetAbort(false);
g->jump_level--;
if (trace > 1)
@@ -577,17 +626,17 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp)
//if (!((PTDBDOX)tdbp)->GetModified())
// return 0;
- if (tdbp->GetMode() == MODE_READ || tdbp->GetMode() == MODE_ANY)
+ if (nox || tdbp->GetMode() == MODE_READ || tdbp->GetMode() == MODE_ANY)
return 0;
if (trace > 1)
- printf("About to reset indexes\n");
+ printf("About to reset opt\n");
// Make all the eventual indexes
tbxp= (TDBDOX*)tdbp;
tbxp->ResetKindex(g, NULL);
- tbxp->To_Key_Col= NULL;
- rc= tbxp->ResetTableOpt(g, ((PTDBASE)tdbp)->GetDef()->Indexable() == 1);
+ tbxp->SetKey_Col(NULL);
+ rc= tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1);
err:
if (trace > 1)
@@ -601,17 +650,10 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp)
/* 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 CntIndexInit(PGLOBAL g, PTDB ptdb, int id, bool sorted)
{
- int k;
- PCOL colp;
- PVAL valp;
- PKXBASE xp;
- PXLOAD pxp;
PIXDEF xdp;
- XKPDEF *kdp;
PTDBDOX tdbp;
- PCOLDEF cdp;
DOXDEF *dfp;
if (!ptdb)
@@ -650,63 +692,27 @@ int CntIndexInit(PGLOBAL g, PTDB ptdb, int 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))
+#if 0
+ if (xdp->IsDynamic()) {
+ // This is a dynamically created index (KINDEX)
+ // It should not be created now, if called by index range
+ tdbp->SetXdp(xdp);
+ return (xdp->IsUnique()) ? 1 : 2;
+ } // endif dynamic
+#endif // 0
+
+ // Static indexes must be initialized now for records_in_range
+ if (tdbp->InitialyzeIndex(g, xdp, sorted))
return 0;
- tdbp->To_Kindex= xp;
- return (xp->IsMul()) ? 2 : 1;
+ return (tdbp->To_Kindex->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)
+ const void *key, int len, bool mrr)
{
char *kp= (char*)key;
int n, x;
@@ -737,18 +743,29 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
// 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 (!tdbp->To_Xdp) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
+ return RC_FX;
+#if 0
+ } // endif !To_Xdp
+ // Now it's time to make the dynamic index
+ if (tdbp->InitialyzeIndex(g, NULL, false)) {
+ sprintf(g->Message, "Fail to make dynamic index %s",
+ tdbp->To_Xdp->GetName());
+ return RC_FX;
+ } // endif MakeDynamicIndex
+#endif // 0
+ } // endif !To_Kindex
+
+ 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()) {
@@ -774,7 +791,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
valp->SetBinValue((void*)kp);
kp+= valp->GetClen();
-
+
if (len == kp - (char*)key) {
n++;
break;
@@ -793,7 +810,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
rnd:
if ((rc= (RCODE)ptdb->ReadDB(g)) == RC_OK)
- rc= EvalColumns(g, ptdb);
+ rc= EvalColumns(g, ptdb, mrr);
return rc;
} // end of CntIndexRead
@@ -828,28 +845,32 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
} 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;
+ if (!tdbp->To_Kindex || !tdbp->To_Link) {
+ if (!tdbp->To_Xdp) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->Name);
+ DBUG_PRINT("Range", ("%s", g->Message));
+ return -1;
+ } else // Dynamic index
+ return tdbp->To_Xdp->GetMaxSame(); // TODO a better estimate
+
} 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;
+ 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()) {
@@ -862,7 +883,7 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
if (rcb) {
if (tdbp->RowNumber(g))
- sprintf(g->Message,
+ sprintf(g->Message,
"Out of range value for column %s at row %d",
colp->GetName(), tdbp->RowNumber(g));
else
@@ -881,7 +902,7 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
} // endif trace
p+= valp->GetClen();
-
+
if (len[i] == (unsigned)(p - kp)) {
n++;
break;
diff --git a/storage/connect/connect.h b/storage/connect/connect.h
index 4c9cee46daf..fd8b7e9442f 100644
--- a/storage/connect/connect.h
+++ b/storage/connect/connect.h
@@ -33,10 +33,10 @@ bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname);
PTDB CntGetTDB(PGLOBAL g, const char *name, MODE xmod, PHC);
bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE, char *, char *, bool, PHC);
bool CntRewindTable(PGLOBAL g, PTDB tdbp);
-int CntCloseTable(PGLOBAL g, PTDB tdbp);
-int CntIndexInit(PGLOBAL g, PTDB tdbp, int id);
+int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort);
+int CntIndexInit(PGLOBAL g, PTDB tdbp, int id, bool sorted);
RCODE CntReadNext(PGLOBAL g, PTDB tdbp);
-RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const void *k, int n);
+RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const void *k, int n, bool mrr);
RCODE CntWriteRow(PGLOBAL g, PTDB tdbp);
RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp);
RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all);
@@ -50,7 +50,7 @@ PGLOBAL CntExit(PGLOBAL g);
/* These classes purpose is chiefly to access protected items! */
/***********************************************************************/
class DOXDEF: public DOSDEF {
- friend int CntIndexInit(PGLOBAL, PTDB, int);
+ friend int CntIndexInit(PGLOBAL, PTDB, int, bool);
}; // end of class DOXDEF
/***********************************************************************/
@@ -58,9 +58,9 @@ class DOXDEF: public DOSDEF {
/***********************************************************************/
class TDBDOX: public TDBDOS {
friend int MakeIndex(PGLOBAL, PTDB, PIXDEF);
- friend int CntCloseTable(PGLOBAL, PTDB);
- friend int CntIndexInit(PGLOBAL, PTDB, int);
- friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const void*, int);
+ friend int CntCloseTable(PGLOBAL, PTDB, bool, bool);
+ friend int CntIndexInit(PGLOBAL, PTDB, int, bool);
+ friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const void*, int, bool);
friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool);
friend int CntIndexRange(PGLOBAL, PTDB, const uchar**, uint*,
bool*, key_part_map*);
@@ -70,7 +70,7 @@ class TDBDOX: public TDBDOS {
class XKPDEF: public KPARTDEF {
friend class TDBDOX;
friend class ha_connect;
- friend int CntIndexInit(PGLOBAL, PTDB, int);
+ friend int CntIndexInit(PGLOBAL, PTDB, int, bool);
public:
XKPDEF(const char *name, int n) : KPARTDEF((PSZ)name, n) {}
}; // end of class XKPDEF
diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp
index 1723ee4ac27..c0ca40f4c01 100644
--- a/storage/connect/filamap.cpp
+++ b/storage/connect/filamap.cpp
@@ -1,11 +1,11 @@
/*********** File AM Map C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMAP */
/* ------------- */
-/* Version 1.4 */
+/* Version 1.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -89,7 +89,7 @@ int MAPFAM::GetFileLength(PGLOBAL g)
{
int len;
- len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g);
+ len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g);
if (trace)
htrc("Mapped file length=%d\n", len);
@@ -129,7 +129,7 @@ bool MAPFAM::OpenTableFile(PGLOBAL g)
&& fp->Count && fp->Mode == mode)
break;
- if (trace)
+ if (trace)
htrc("Mapping file, fp=%p\n", fp);
} else
@@ -286,6 +286,16 @@ bool MAPFAM::RecordPos(PGLOBAL g)
} // end of RecordPos
/***********************************************************************/
+/* Initialize Fpos and Mempos for indexed DELETE. */
+/***********************************************************************/
+int MAPFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ Fpos = Memory + fpos;
+ Mempos = Memory + spos;
+ return RC_OK;
+ } // end of InitDelete
+
+/***********************************************************************/
/* Skip one record in file. */
/***********************************************************************/
int MAPFAM::SkipRecord(PGLOBAL g, bool header)
@@ -322,8 +332,27 @@ int MAPFAM::ReadBuffer(PGLOBAL g)
/*******************************************************************/
/* Record file position in case of UPDATE or DELETE. */
/*******************************************************************/
- Fpos = Mempos;
- CurBlk = (int)Rows++;
+ 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;
@@ -380,13 +409,13 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
} // endif irc
- if (Tpos == Spos)
+ if (Tpos == Spos) {
/*******************************************************************/
- /* First line to delete. Move of eventual preceding lines is */
+ /* 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) {
+ Tpos = Spos = Fpos;
+ } else if ((n = Fpos - Spos) > 0) {
/*******************************************************************/
/* Non consecutive line to delete. Move intermediate lines. */
/*******************************************************************/
@@ -396,7 +425,7 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
if (trace)
htrc("move %d bytes\n", n);
- } // endif n
+ } // endif n
if (irc == RC_OK) {
Spos = Mempos; // New start position
@@ -415,40 +444,46 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
CloseMemMap(fp->Memory, (size_t)fp->Length);
fp->Count = 0; // Avoid doing it twice
- /*******************************************************************/
- /* Remove extra records. */
- /*******************************************************************/
- n = Tpos - Memory;
+ if (!Abort) {
+ /*****************************************************************/
+ /* Remove extra records. */
+ /*****************************************************************/
+ n = Tpos - Memory;
#if defined(WIN32)
- DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
+ 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 (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 (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
+ 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
+ if (ftruncate(fp->Handle, (off_t)n)) {
+ sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
+ close(fp->Handle);
+ return RC_FX;
+ } // endif
+
+#endif // UNIX
+ } // endif Abort
- close(fp->Handle);
+#if defined(WIN32)
+ CloseHandle(fp->Handle);
+#else // UNIX
+ close(fp->Handle);
#endif // UNIX
} // endif irc
@@ -458,10 +493,10 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
/***********************************************************************/
/* Table file close routine for MAP access method. */
/***********************************************************************/
-void MAPFAM::CloseTableFile(PGLOBAL g)
+void MAPFAM::CloseTableFile(PGLOBAL g, bool abort)
{
PlugCloseFile(g, To_Fb);
- To_Fb = NULL; // To get correct file size in Cardinality
+ To_Fb = NULL; // To get correct file size in Cardinality
if (trace)
htrc("MAP Close: closing %s count=%d\n",
@@ -488,7 +523,7 @@ MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp)
Block = tdp->GetBlock();
Last = tdp->GetLast();
Nrec = tdp->GetElemt();
- BlkPos = NULL;
+ BlkPos = tdp->GetTo_Pos();
CurNum = Nrec;
} // end of MBKFAM standard constructor
@@ -508,9 +543,7 @@ void MBKFAM::Reset(void)
/***********************************************************************/
int MBKFAM::Cardinality(PGLOBAL g)
{
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
} // end of Cardinality
/***********************************************************************/
@@ -534,8 +567,49 @@ int MBKFAM::GetRowID(void)
/***********************************************************************/
int MBKFAM::ReadBuffer(PGLOBAL g)
{
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
+ 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
/***********************************************************************/
@@ -607,6 +681,16 @@ bool MPXFAM::SetPos(PGLOBAL g, int pos)
} // end of SetPos
/***********************************************************************/
+/* Initialize CurBlk, CurNum, Mempos and Fpos for indexed DELETE. */
+/***********************************************************************/
+int MPXFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ Fpos = Memory + Headlen + fpos * Lrecl;
+ Mempos = Fpos + Lrecl;
+ return RC_OK;
+ } // end of InitDelete
+
+/***********************************************************************/
/* ReadBuffer: Read one line for a mapped Fix file. */
/***********************************************************************/
int MPXFAM::ReadBuffer(PGLOBAL g)
@@ -618,17 +702,29 @@ int MPXFAM::ReadBuffer(PGLOBAL g)
Placed = false;
} else if (Mempos >= Top) { // Are we at the end of the memory
return RC_EF;
- } else if (++CurNum < Nrec) {
+ } else if (++CurNum < Nrec) {
Fpos = Mempos;
- } else {
+ } else {
/*******************************************************************/
/* New block. */
/*******************************************************************/
- CurNum = 0;
+ 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
- if (++CurBlk >= Block)
- return RC_EF;
-
Fpos = Mempos = Headlen + Memory + CurBlk * Blksize;
} // endif's
diff --git a/storage/connect/filamap.h b/storage/connect/filamap.h
index 7f71b90a18f..1d85fa36155 100644
--- a/storage/connect/filamap.h
+++ b/storage/connect/filamap.h
@@ -1,7 +1,7 @@
/*************** FilAMap H Declares Source Code File (.H) **************/
-/* Name: FILAMAP.H Version 1.2 */
+/* Name: FILAMAP.H Version 1.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* This file contains the MAP file access method classes declares. */
/***********************************************************************/
@@ -33,23 +33,26 @@ class DllExport MAPFAM : public TXTFAM {
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 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 int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
protected:
+ virtual int InitDelete(PGLOBAL g, int fpos, int spos);
+
// Members
char *Memory; // Pointer on file mapping view.
char *Mempos; // Position of next data to read
- char *Fpos; // Position of last read record
+ 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
@@ -71,6 +74,8 @@ class DllExport MBKFAM : public MAPFAM {
// 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);
@@ -96,12 +101,17 @@ class DllExport MPXFAM : public MBKFAM {
// Methods
virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);}
- virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int MaxBlkSize(PGLOBAL g, int s)
+ {return TXTFAM::MaxBlkSize(g, s);}
+ virtual bool SetPos(PGLOBAL g, int recpos);
+ virtual int GetNextPos(void) {return (int)Fpos + Nrec;}
virtual bool DeferReading(void) {return false;}
virtual int ReadBuffer(PGLOBAL g);
virtual int WriteBuffer(PGLOBAL g);
protected:
+ virtual int InitDelete(PGLOBAL g, int fpos, int spos);
+
// No additional members
}; // end of class MPXFAM
diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp
index 7ca98eeff55..a214ab8acf2 100644
--- a/storage/connect/filamdbf.cpp
+++ b/storage/connect/filamdbf.cpp
@@ -1,11 +1,11 @@
/*********** File AM Dbf C++ Program Source Code File (.CPP) ****************/
/* PROGRAM NAME: FILAMDBF */
/* ------------- */
-/* Version 1.6 */
+/* Version 1.7 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -176,7 +176,7 @@ static int dbfhead(PGLOBAL g, FILE *file, PSZ fn, DBFHEADER *buf)
/* DBFColumns: constructs the result blocks containing the description */
/* of all the columns of a DBF file that will be retrieved by #GetData. */
/****************************************************************************/
-PQRYRES DBFColumns(PGLOBAL g, const char *fn, BOOL info)
+PQRYRES DBFColumns(PGLOBAL g, char *dp, const char *fn, bool info)
{
int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
TYPE_INT, TYPE_INT, TYPE_SHORT};
@@ -186,7 +186,7 @@ PQRYRES DBFColumns(PGLOBAL g, const char *fn, BOOL info)
char buf[2], filename[_MAX_PATH];
int ncol = sizeof(buftyp) / sizeof(int);
int rc, type, len, field, fields;
- BOOL bad;
+ bool bad;
DBFHEADER mainhead;
DESCRIPTOR thisfield;
FILE *infile = NULL;
@@ -205,7 +205,7 @@ PQRYRES DBFColumns(PGLOBAL g, const char *fn, BOOL info)
/************************************************************************/
/* Open the input file. */
/************************************************************************/
- PlugSetPath(filename, fn, PlgGetDataPath(g));
+ PlugSetPath(filename, fn, dp);
if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "rb")))
return NULL;
@@ -668,12 +668,9 @@ void DBFFAM::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. */
+ /* be entirely read in one block. */
/*********************************************************************/
- if (Tdbp->GetKindex() && Tdbp->GetMode() == MODE_READ &&
- ReadBlks != 1) {
+ if (Tdbp->GetKindex() && ReadBlks != 1) {
Nrec = 1; // Better for random access
Rbuf = 0;
Blksize = Lrecl;
@@ -753,6 +750,36 @@ bool DBFFAM::CopyHeader(PGLOBAL g)
return rc;
} // end of CopyHeader
+#if 0 // Not useful when UseTemp is false.
+/***********************************************************************/
+/* Mark the line to delete with '*' (soft delete). */
+/* NOTE: this is not ready for UseTemp. */
+/***********************************************************************/
+int DBFFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ int rc = RC_FX;
+ size_t lrecl = (size_t)Lrecl;
+
+ if (Nrec != 1)
+ strcpy(g->Message, "Cannot delete in block mode");
+ else if (fseek(Stream, Headlen + fpos * Lrecl, SEEK_SET))
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ else if (fread(To_Buf, 1, lrecl, Stream) != lrecl)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+ else
+ *To_Buf = '*';
+
+ if (fseek(Stream, Headlen + fpos * Lrecl, SEEK_SET))
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ else if (fwrite(To_Buf, 1, lrecl, Stream) != lrecl)
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ else
+ rc = RC_NF; // Ok, Nothing else to do
+
+ return rc;
+ } // end of InitDelete
+#endif // 0
+
/***********************************************************************/
/* Data Base delete line routine for DBF access methods. */
/* Deleted lines are just flagged in the first buffer character. */
@@ -791,11 +818,13 @@ void DBFFAM::Rewind(void)
/***********************************************************************/
/* Table file close routine for DBF access method. */
/***********************************************************************/
-void DBFFAM::CloseTableFile(PGLOBAL g)
+void DBFFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = RC_OK, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
// Closing is True if last Write was in error
if (mode == MODE_INSERT && CurNum && !Closing) {
// Some more inserted lines remain to be written
@@ -806,21 +835,21 @@ void DBFFAM::CloseTableFile(PGLOBAL g)
if (Modif && !Closing) {
// Last updated block remains to be written
Closing = true;
- wrc = ReadBuffer(g);
+ wrc = WriteModifiedBlock(g);
} // endif Modif
if (UseTemp && T_Stream && wrc == RC_OK) {
- // Copy any remaining lines
- bool b;
-
- Fpos = Tdbp->Cardinality(g);
-
- if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) {
- // Delete the old file and rename the new temp file.
- RenameTempFile(g);
- goto fin;
- } // endif rc
+ if (!Abort) {
+ // Copy any remaining lines
+ bool b;
+
+ Fpos = Tdbp->Cardinality(g);
+ Abort = MoveIntermediateLines(g, &b) != RC_OK;
+ } // endif Abort
+ // Delete the old file and rename the new temp file.
+ RenameTempFile(g);
+ goto fin;
} // endif UseTemp
} // endif's mode
diff --git a/storage/connect/filamdbf.h b/storage/connect/filamdbf.h
index b85b9fc47fe..da84d7685a8 100644
--- a/storage/connect/filamdbf.h
+++ b/storage/connect/filamdbf.h
@@ -1,7 +1,7 @@
/***************** FilAmDbf H Declares Source Code File (.H) ****************/
-/* Name: filamdbf.h Version 1.3 */
+/* Name: filamdbf.h Version 1.4 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* This file contains the DBF file access method classes declares. */
/****************************************************************************/
@@ -19,7 +19,7 @@ typedef class DBMFAM *PDBMFAM;
/****************************************************************************/
/* Functions used externally. */
/****************************************************************************/
-PQRYRES DBFColumns(PGLOBAL g, const char *fn, BOOL info);
+PQRYRES DBFColumns(PGLOBAL g, char *dp, const char *fn, bool info);
/****************************************************************************/
/* This is the base class for dBASE file access methods. */
@@ -40,8 +40,8 @@ class DllExport DBFBASE {
// Members
int Records; /* records in the file */
bool Accept; /* true if bad lines are accepted */
- int Nerr; /* Number of bad records */
- int Maxerr; /* Maximum number of bad records */
+ int Nerr; /* Number of bad records */
+ int Maxerr; /* Maximum number of bad records */
int ReadMode; /* 1: ALL 2: DEL 0: NOT DEL */
}; // end of class DBFBASE
@@ -67,11 +67,12 @@ class DllExport DBFFAM : public FIXFAM, public DBFBASE {
virtual void ResetBuffer(PGLOBAL g);
virtual int ReadBuffer(PGLOBAL g);
virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
protected:
virtual bool CopyHeader(PGLOBAL g);
+//virtual int InitDelete(PGLOBAL g, int fpos, int spos);
// Members
}; // end of class DBFFAM
diff --git a/storage/connect/filamfix.cpp b/storage/connect/filamfix.cpp
index 9338ae322db..1fa72d52746 100644
--- a/storage/connect/filamfix.cpp
+++ b/storage/connect/filamfix.cpp
@@ -1,7 +1,7 @@
/*********** File AM Fix C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMFIX */
/* ------------- */
-/* Version 1.5 */
+/* Version 1.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
@@ -80,6 +80,37 @@ FIXFAM::FIXFAM(PFIXFAM txfp) : BLKFAM(txfp)
} // end of FIXFAM copy constructor
/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool FIXFAM::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
+
+/***********************************************************************/
+/* Initialize CurBlk and CurNum for indexed DELETE. */
+/***********************************************************************/
+int FIXFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ CurBlk = fpos / Nrec;
+ CurNum = fpos % Nrec;
+ return RC_OK;
+ } // end of InitDelete
+
+/***********************************************************************/
/* Allocate the block buffer for the table. */
/***********************************************************************/
bool FIXFAM::AllocateBuffer(PGLOBAL g)
@@ -128,12 +159,9 @@ void FIXFAM::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. */
+ /* be entirely read in one block. */
/*********************************************************************/
- if (Tdbp->GetMode() == MODE_READ && ReadBlks != 1 && !Padded &&
- Tdbp->GetKindex() /*&& Tdbp->GetKindex()->IsRandom()*/) {
+ if (Tdbp->GetKindex() && ReadBlks != 1 && !Padded) {
Nrec = 1; // Better for random access
Rbuf = 0;
Blksize = Lrecl;
@@ -144,79 +172,93 @@ void FIXFAM::ResetBuffer(PGLOBAL g)
} // end of ResetBuffer
/***********************************************************************/
+/* WriteModifiedBlock: Used when updating. */
+/***********************************************************************/
+int FIXFAM::WriteModifiedBlock(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* The old block was modified in Update mode. */
+ /* In Update mode we simply rewrite the old block on itself. */
+ /*********************************************************************/
+ int rc = RC_OK;
+ bool moved = false;
+
+ // Using temp copy any intermediate lines.
+ if (UseTemp && MoveIntermediateLines(g, &moved))
+ rc = RC_FX;
+
+ // Fpos is last position, Headlen is DBF file header length
+ else if (!moved && fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ rc = RC_FX;
+ } else if (fwrite(To_Buf, Lrecl, Rbuf, T_Stream) != (size_t)Rbuf) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ rc = RC_FX;
+ } else
+ Spos = Fpos + Nrec; // + Rbuf ???
+
+ if (Closing || rc != RC_OK) { // Error or called from CloseDB
+ Closing = true; // To tell CloseDB about error
+ return rc;
+ } // endif Closing
+
+ // NOTE: Next line was added to avoid a very strange fread bug.
+ // When the fseek is not executed (even the file has the good
+ // pointer position) the next read can happen anywhere in the file.
+ OldBlk = CurBlk; // This will force fseek to be executed
+ Modif = 0;
+ return rc;
+ } // end of WriteModifiedBlock
+
+/***********************************************************************/
/* ReadBuffer: Read one line for a FIX file. */
/***********************************************************************/
int FIXFAM::ReadBuffer(PGLOBAL g)
{
int n, rc = RC_OK;
- if (!Closing) {
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Tdbp->SetLine(To_Buf + CurNum * Lrecl);
+ Placed = false;
+ } else if (++CurNum < Rbuf) {
+ Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
+ return RC_OK;
+ } else if (Rbuf < Nrec && CurBlk != -1) {
+ return RC_EF;
+ } else {
/*******************************************************************/
- /* Sequential reading when Placed is not true. */
+ /* New block. */
/*******************************************************************/
- if (Placed) {
- Tdbp->SetLine(To_Buf + CurNum * Lrecl);
- Placed = false;
- } else if (++CurNum < Rbuf) {
- Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
- return RC_OK;
- } else if (Rbuf < Nrec && CurBlk != -1) {
- return RC_EF;
- } else {
- /*****************************************************************/
- /* New block. */
- /*****************************************************************/
- CurNum = 0;
- Tdbp->SetLine(To_Buf);
-
- if (++CurBlk >= Block)
- return RC_EF;
-
- } // endif's
-
- if (OldBlk == CurBlk) {
- IsRead = true; // Was read indeed
- return RC_OK; // Block is already there
- } // endif OldBlk
+ CurNum = 0;
+ Tdbp->SetLine(To_Buf);
- } // endif !Closing
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
- if (Modif) {
/*******************************************************************/
- /* The old block was modified in Update mode. */
- /* In Update mode we simply rewrite the old block on itself. */
+ /* Before reading a new block, check whether block indexing */
+ /* can be done, as well as for join as for local filtering. */
/*******************************************************************/
- bool moved = false;
+ switch (Tdbp->TestBlock(g)) {
+ case RC_EF:
+ return RC_EF;
+ case RC_NF:
+ goto next;
+ } // endswitch rc
+ } // endif's
- if (UseTemp) // Copy any intermediate lines.
- if (MoveIntermediateLines(g, &moved))
- rc = RC_FX;
+ if (OldBlk == CurBlk) {
+ IsRead = true; // Was read indeed
+ return RC_OK; // Block is already there
+ } // endif OldBlk
- if (rc == RC_OK) {
- // Fpos is last position, Headlen is DBF file header length
- if (!moved && fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- rc = RC_FX;
- } else if (fwrite(To_Buf, Lrecl, Rbuf, T_Stream) != (size_t)Rbuf) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- rc = RC_FX;
- } // endif fwrite
-
- Spos = Fpos + Nrec; // + Rbuf ???
- } // endif rc
-
- if (Closing || rc != RC_OK) { // Error or called from CloseDB
- Closing = true; // To tell CloseDB about error
- return rc;
- } // endif Closing
-
- // NOTE: Next line was added to avoid a very strange fread bug.
- // When the fseek is not executed (even the file has the good
- // pointer position) the next read can happen anywhere in the file.
- OldBlk = CurBlk; // This will force fseek to be executed
- Modif = 0;
-// Spos = Fpos + Nrec; done above
- } // endif Mode
+ // Write modified block in mode UPDATE
+ if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
+ return rc;
// This could be done only for new block. However note that FPOS
// is used as block position when updating and as line position
@@ -234,8 +276,6 @@ int FIXFAM::ReadBuffer(PGLOBAL g)
if (trace > 1)
htrc("File position is now %d\n", ftell(Stream));
-//long tell = ftell(Stream); not used
-
if (Padded)
n = fread(To_Buf, (size_t)Blksize, 1, Stream);
else
@@ -304,19 +344,23 @@ int FIXFAM::WriteBuffer(PGLOBAL g)
} else { // Mode == MODE_UPDATE
// T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
- {
- if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
+ if (!T_Stream) {
+ if (UseTemp) {
if (OpenTempFile(g))
return RC_FX;
-
- if (CopyHeader(g)) // For DBF tables
+ else if (CopyHeader(g)) // For DBF tables
return RC_FX;
} else
T_Stream = Stream;
- }
- Modif++; // Modified line in Update mode
+
+ } // endif T_Stream
+
+ if (Nrec > 1)
+ Modif++; // Modified line in blocked mode
+ else if (WriteModifiedBlock(g)) // Indexed update
+ return RC_FX;
+
} // endif Mode
return RC_OK;
@@ -516,36 +560,37 @@ bool FIXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
/***********************************************************************/
/* Table file close routine for FIX access method. */
/***********************************************************************/
-void FIXFAM::CloseTableFile(PGLOBAL g)
+void FIXFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = RC_OK, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
// Closing is True if last Write was in error
if (mode == MODE_INSERT && CurNum && !Closing) {
// Some more inserted lines remain to be written
Rbuf = CurNum--;
-// Closing = true;
wrc = WriteBuffer(g);
} else if (mode == MODE_UPDATE) {
if (Modif && !Closing) {
// Last updated block remains to be written
- Closing = true;
- wrc = ReadBuffer(g);
+ Closing = true; // ???
+ wrc = WriteModifiedBlock(g);
} // endif Modif
if (UseTemp && T_Stream && wrc == RC_OK) {
- // Copy any remaining lines
- bool b;
-
- Fpos = Tdbp->Cardinality(g);
-
- if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) {
- // Delete the old file and rename the new temp file.
- RenameTempFile(g);
- goto fin;
- } // endif rc
-
+ if (!Abort) {
+ // Copy any remaining lines
+ bool b;
+
+ Fpos = Tdbp->Cardinality(g);
+ Abort = MoveIntermediateLines(g, &b) != RC_OK;
+ } // endif Abort
+
+ // Delete the old file and rename the new temp file.
+ RenameTempFile(g);
+ goto fin;
} // endif UseTemp
} // endif's mode
@@ -602,7 +647,9 @@ bool BGXFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, int org)
} // endif
#else // !WIN32
if (lseek64(h, pos, org) < 0) {
- sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
+// sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
+ sprintf(g->Message, "lseek64: %s", strerror(errno));
+ printf("%s\n", g->Message);
return true;
} // endif
#endif // !WIN32
@@ -804,7 +851,7 @@ bool BGXFAM::OpenTableFile(PGLOBAL g)
#else // UNIX
int rc = 0;
int oflag = O_LARGEFILE; // Enable file size > 2G
- mode_t tmode = 0;
+ mode_t tmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
/*********************************************************************/
/* Create the file object according to access mode */
@@ -829,7 +876,7 @@ bool BGXFAM::OpenTableFile(PGLOBAL g)
break;
case MODE_INSERT:
oflag |= (O_WRONLY | O_CREAT | O_APPEND);
- tmode = S_IREAD | S_IWRITE;
+ // tmode = S_IREAD | S_IWRITE;
break;
default:
sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
@@ -988,73 +1035,92 @@ int BGXFAM::Cardinality(PGLOBAL g)
} // end of Cardinality
/***********************************************************************/
+/* WriteModifiedBlock: Used when updating. */
+/***********************************************************************/
+int BGXFAM::WriteModifiedBlock(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* The old block was modified in Update mode. */
+ /* In Update mode we simply rewrite the old block on itself. */
+ /*********************************************************************/
+ int rc = RC_OK;
+ bool moved = false;
+
+ if (UseTemp) // Copy any intermediate lines.
+ if (MoveIntermediateLines(g, &moved))
+ rc = RC_FX;
+
+ if (rc == RC_OK) {
+ // Set file position to OldBlk position (Fpos)
+ if (!moved && BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
+ rc = RC_FX;
+ else if (BigWrite(g, Tfile, To_Buf, Lrecl * Rbuf))
+ rc = RC_FX;
+
+ Spos = Fpos + Nrec; // + Rbuf ???
+ } // endif rc
+
+ if (Closing || rc != RC_OK) // Error or called from CloseDB
+ return rc;
+
+ // NOTE: Next line was added to avoid a very strange fread bug.
+ // When the fseek is not executed (even the file has the good
+ // pointer position) the next read can happen anywhere in the file.
+ OldBlk = CurBlk; // This will force fseek to be executed
+ Modif = 0;
+ return rc;
+ } // end of WriteModifiedBlock
+
+/***********************************************************************/
/* ReadBuffer: Read Nrec lines for a big fixed/binary file. */
/***********************************************************************/
int BGXFAM::ReadBuffer(PGLOBAL g)
{
int nbr, rc = RC_OK;
- if (!Closing) {
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Tdbp->SetLine(To_Buf + CurNum * Lrecl);
+ Placed = false;
+ } else if (++CurNum < Rbuf) {
+ Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
+ return RC_OK;
+ } else if (Rbuf < Nrec && CurBlk != -1) {
+ return RC_EF;
+ } else {
/*******************************************************************/
- /* Sequential reading when Placed is not true. */
+ /* New block. */
/*******************************************************************/
- if (Placed) {
- Tdbp->SetLine(To_Buf + CurNum * Lrecl);
- Placed = false;
- } else if (++CurNum < Rbuf) {
- Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
- return RC_OK;
- } else if (Rbuf < Nrec && CurBlk != -1) {
+ CurNum = 0;
+ Tdbp->SetLine(To_Buf);
+
+ next:
+ if (++CurBlk >= Block)
return RC_EF;
- } else {
- /*****************************************************************/
- /* New block. */
- /*****************************************************************/
- CurNum = 0;
- Tdbp->SetLine(To_Buf);
- if (++CurBlk >= Block)
+ /*******************************************************************/
+ /* 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
+ } // endif's
- if (OldBlk == CurBlk) {
- IsRead = true; // Was read indeed
- return RC_OK; // Block is already there
- } // endif OldBlk
+ if (OldBlk == CurBlk) {
+ IsRead = true; // Was read indeed
+ return RC_OK; // Block is already there
+ } // endif OldBlk
- } // endif !Closing
-
- if (Modif) {
- /*******************************************************************/
- /* The old block was modified in Update mode. */
- /* In Update mode we simply rewrite the old block on itself. */
- /*******************************************************************/
- bool moved = false;
-
- if (UseTemp) // Copy any intermediate lines.
- if (MoveIntermediateLines(g, &moved))
- rc = RC_FX;
-
- if (rc == RC_OK) {
- // Set file position to OldBlk position (Fpos)
- if (!moved && BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
- rc = RC_FX;
- else if (BigWrite(g, Tfile, To_Buf, Lrecl * Rbuf))
- rc = RC_FX;
-
- Spos = Fpos + Nrec; // + Rbuf ???
- } // endif rc
-
- if (Closing || rc != RC_OK) // Error or called from CloseDB
- return rc;
-
- // NOTE: Next line was added to avoid a very strange fread bug.
- // When the fseek is not executed (even the file has the good
- // pointer position) the next read can happen anywhere in the file.
- OldBlk = CurBlk; // This will force fseek to be executed
- Modif = 0;
- } // endif Mode
+ // Write modified block in mode UPDATE
+ if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
+ return rc;
Fpos = CurBlk * Nrec;
@@ -1116,16 +1182,21 @@ int BGXFAM::WriteBuffer(PGLOBAL g)
} else { // Mode == MODE_UPDATE
// Tfile is the temporary file or the table file handle itself
- if (Tfile == INVALID_HANDLE_VALUE)
- {
+ if (Tfile == INVALID_HANDLE_VALUE) {
if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
if (OpenTempFile(g))
return RC_FX;
} else
Tfile = Hfile;
- }
- Modif++; // Modified line in Update mode
+
+ } // endif Tfile
+
+ if (Nrec > 1)
+ Modif++; // Modified line in blocked mode
+ else if (WriteModifiedBlock(g)) // Indexed update
+ return RC_FX;
+
} // endif Mode
return RC_OK;
@@ -1210,14 +1281,10 @@ int BGXFAM::DeleteRecords(PGLOBAL g, int irc)
if (trace > 1)
htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
- } else {
+ } else if (irc != RC_OK) {
/*******************************************************************/
/* Last call after EOF has been reached. */
/*******************************************************************/
- char filename[_MAX_PATH];
-
- PlugSetPath(filename, To_File, Tdbp->GetPath());
-
if (UseTemp) {
/*****************************************************************/
/* Ok, now delete old file and rename new temp file. */
@@ -1352,11 +1419,13 @@ bool BGXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
/***********************************************************************/
/* Data Base close routine for BIGFIX access method. */
/***********************************************************************/
-void BGXFAM::CloseTableFile(PGLOBAL g)
+void BGXFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = RC_OK, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
// Closing is True if last Write was in error
if (mode == MODE_INSERT && CurNum && !Closing) {
// Some more inserted lines remain to be written
@@ -1366,21 +1435,21 @@ void BGXFAM::CloseTableFile(PGLOBAL g)
if (Modif && !Closing) {
// Last updated block remains to be written
Closing = true;
- wrc = ReadBuffer(g);
+ wrc = WriteModifiedBlock(g);
} // endif Modif
if (UseTemp && Tfile && wrc == RC_OK) {
- // Copy any remaining lines
- bool b;
-
- Fpos = Tdbp->Cardinality(g);
-
- if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) {
- // Delete the old file and rename the new temp file.
- RenameTempFile(g);
- goto fin;
- } // endif rc
-
+ if (!Abort) {
+ // Copy any remaining lines
+ bool b;
+
+ Fpos = Tdbp->Cardinality(g);
+ Abort = MoveIntermediateLines(g, &b) != RC_OK;
+ } // endif Abort
+
+ // Delete the old file and rename the new temp file.
+ RenameTempFile(g);
+ goto fin;
} // endif UseTemp
} // endif's mode
diff --git a/storage/connect/filamfix.h b/storage/connect/filamfix.h
index 6f9e6ef3b45..a99a36af232 100644
--- a/storage/connect/filamfix.h
+++ b/storage/connect/filamfix.h
@@ -1,7 +1,7 @@
/************** FilAMFix H Declares Source Code File (.H) **************/
-/* Name: FILAMFIX.H Version 1.2 */
+/* Name: FILAMFIX.H Version 1.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005 - 2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2005 - 2014 */
/* */
/* This file contains the FIX file access method classes declares. */
/***********************************************************************/
@@ -25,22 +25,28 @@ class DllExport FIXFAM : public BLKFAM {
FIXFAM(PFIXFAM txfp);
// Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_FIX;}
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) FIXFAM(this);}
+ 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 SetPos(PGLOBAL g, int recpos);
+ virtual int GetNextPos(void) {return Fpos + 1;}
virtual bool AllocateBuffer(PGLOBAL g);
virtual void ResetBuffer(PGLOBAL g);
+ virtual int WriteModifiedBlock(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 CloseTableFile(PGLOBAL g, bool abort);
protected:
virtual bool CopyHeader(PGLOBAL g) {return false;}
virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
+ virtual int InitDelete(PGLOBAL g, int fpos, int spos);
// No additional members
}; // end of class FIXFAM
@@ -58,25 +64,26 @@ class BGXFAM : public FIXFAM {
BGXFAM(PBGXFAM txfp);
// Implementation
- virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) BGXFAM(this);}
+ 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);
+ virtual int Cardinality(PGLOBAL g);
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int WriteModifiedBlock(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
+ 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);
+ int BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req);
+ bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos
+ , int org = FILE_BEGIN);
// Members
HANDLE Hfile; // Handle(descriptor) to big file
diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp
index e4e9130dc86..dfd5a6638cf 100644
--- a/storage/connect/filamtxt.cpp
+++ b/storage/connect/filamtxt.cpp
@@ -1,11 +1,11 @@
/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMTXT */
/* ------------- */
-/* Version 1.4 */
+/* Version 1.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -58,6 +58,11 @@
extern int num_read, num_there, num_eq[2]; // Statistics
extern "C" int trace;
+/***********************************************************************/
+/* Routine called externally by TXTFAM SortedRows functions. */
+/***********************************************************************/
+PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
+
/* --------------------------- Class TXTFAM -------------------------- */
/***********************************************************************/
@@ -75,6 +80,12 @@ TXTFAM::TXTFAM(PDOSDEF tdp)
To_Buf = NULL;
DelBuf = NULL;
BlkPos = NULL;
+ To_Pos = NULL;
+ To_Sos = NULL;
+ To_Upd = NULL;
+ Posar = NULL;
+ Sosar = NULL;
+ Updar = NULL;
BlkLen = 0;
Buflen = 0;
Dbflen = 0;
@@ -91,10 +102,12 @@ TXTFAM::TXTFAM(PDOSDEF tdp)
Rbuf = 0;
Modif = 0;
Blksize = 0;
+ Fpos = Spos = Tpos = 0;
Padded = false;
Eof = tdp->Eof;
Ending = tdp->Ending;
- CrLf = (char*)(Ending == 2 ? "\r\n" : "\n");
+ Abort = false;
+ CrLf = (char*)(Ending == 1 ? "\n" : "\r\n");
} // end of TXTFAM standard constructor
TXTFAM::TXTFAM(PTXF txfp)
@@ -109,6 +122,12 @@ TXTFAM::TXTFAM(PTXF txfp)
To_Buf = txfp->To_Buf;
DelBuf = txfp->DelBuf;
BlkPos = txfp->BlkPos;
+ To_Pos = txfp->To_Pos;
+ To_Sos = txfp->To_Sos;
+ To_Upd = txfp->To_Upd;
+ Posar = txfp->Posar;
+ Sosar = txfp->Sosar;
+ Updar = txfp->Updar;
BlkLen = txfp->BlkLen;
Buflen = txfp->Buflen;
Dbflen = txfp->Dbflen;
@@ -125,9 +144,14 @@ TXTFAM::TXTFAM(PTXF txfp)
Rbuf = txfp->Rbuf;
Modif = txfp->Modif;
Blksize = txfp->Blksize;
+ Fpos = txfp->Fpos;
+ Spos = txfp->Spos;
+ Tpos = txfp->Tpos;
Padded = txfp->Padded;
Eof = txfp->Eof;
Ending = txfp->Ending;
+ Abort = txfp->Abort;
+ CrLf = txfp->CrLf;
} // end of TXTFAM copy constructor
/***********************************************************************/
@@ -151,9 +175,9 @@ void TXTFAM::Reset(void)
/***********************************************************************/
int TXTFAM::GetFileLength(PGLOBAL g)
{
- char filename[_MAX_PATH];
- int h;
- int len;
+ 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);
@@ -165,13 +189,13 @@ int TXTFAM::GetFileLength(PGLOBAL g)
if (errno != ENOENT) {
if (trace)
htrc("%s\n", g->Message);
+
len = -1;
- }
- else
- {
+ } else {
len = 0; // File does not exist yet
g->Message[0]= '\0';
- }
+ } // endif errno
+
} else {
if ((len = _filelength(h)) < 0)
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
@@ -214,7 +238,7 @@ int TXTFAM::Cardinality(PGLOBAL g)
} // endif Padded
if (trace)
- htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
+ htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
card, len, Lrecl);
} else
@@ -228,6 +252,199 @@ int TXTFAM::Cardinality(PGLOBAL g)
} // 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
+
+/***********************************************************************/
+/* AddListValue: Used when doing indexed update or delete. */
+/***********************************************************************/
+bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
+ {
+ PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
+
+ switch (type) {
+// case TYPE_INT:
+// pp->Value = PlugSubAlloc(g, NULL, sizeof(int));
+// *((int*)pp->Value) = *((int*)val);
+// break;
+ case TYPE_VOID:
+ pp->Value = (void*)(intptr)*(int*)val;
+ break;
+// case TYPE_STRING:
+// pp->Value = PlugSubAlloc(g, NULL, strlen((char*)val) + 1);
+// strcpy((char*)pp->Value, (char*)val);
+// break;
+ case TYPE_PCHAR:
+ pp->Value = val;
+ break;
+ default:
+ return true;
+ } // endswitch type
+
+ pp->Type = type;
+ pp->Domain = 0;
+ pp->Next = *top;
+ *top = pp;
+ return false;
+ } // end of AddListValue
+
+/***********************************************************************/
+/* Store needed values for indexed UPDATE or DELETE. */
+/***********************************************************************/
+int TXTFAM::StoreValues(PGLOBAL g, bool upd)
+{
+ int pos = GetPos();
+ bool rc = AddListValue(g, TYPE_VOID, &pos, &To_Pos);
+
+ if (!rc) {
+ pos = GetNextPos();
+ rc = AddListValue(g, TYPE_VOID, &pos, &To_Sos);
+ } // endif rc
+
+ if (upd && !rc) {
+ char *buf;
+
+ if (Tdbp->PrepareWriting(g))
+ return RC_FX;
+
+ buf = (char*)PlugSubAlloc(g, NULL, strlen(Tdbp->GetLine()) + 1);
+ strcpy(buf, Tdbp->GetLine());
+ rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
+ } // endif upd
+
+ return rc ? RC_FX : RC_OK;
+} // end of StoreValues
+
+/***********************************************************************/
+/* UpdateSortedRows. When updating using indexing, the issue is that */
+/* record are not necessarily updated in sequential order. */
+/* Moving intermediate lines cannot be done while making them because */
+/* this can cause extra wrong records to be included in the new file. */
+/* What we do here is to reorder the updated records and do all the */
+/* updates ordered by record position. */
+/***********************************************************************/
+int TXTFAM::UpdateSortedRows(PGLOBAL g)
+ {
+ int *ix, i;
+
+ /*********************************************************************/
+ /* Get the stored update values and sort them. */
+ /*********************************************************************/
+ if (!(Posar = MakeValueArray(g, To_Pos))) {
+// strcpy(g->Message, "Position array is null");
+// return RC_INFO;
+ return RC_OK; // Nothing to do
+ } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
+ strcpy(g->Message, "Start position array is null");
+ goto err;
+ } else if (!(Updar = MakeValueArray(g, To_Upd))) {
+ strcpy(g->Message, "Updated line array is null");
+ goto err;
+ } else if (!(ix = (int*)Posar->GetSortIndex(g))) {
+ strcpy(g->Message, "Error getting array sort index");
+ goto err;
+ } // endif's
+
+ Rewind();
+
+ for (i = 0; i < Posar->GetNval(); i++) {
+ SetPos(g, Sosar->GetIntValue(ix[i]));
+ Fpos = Posar->GetIntValue(ix[i]);
+ strcpy(Tdbp->To_Line, Updar->GetStringValue(ix[i]));
+
+ // Now write the updated line.
+ if (WriteBuffer(g))
+ goto err;
+
+ } // endfor i
+
+ return RC_OK;
+
+err:
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return RC_FX;
+ } // end of UpdateSortedRows
+
+/***********************************************************************/
+/* DeleteSortedRows. When deleting using indexing, the issue is that */
+/* record are not necessarily deleted in sequential order. Moving */
+/* intermediate lines cannot be done while deleing them because */
+/* this can cause extra wrong records to be included in the new file. */
+/* What we do here is to reorder the deleted record and delete from */
+/* the file from the ordered deleted records. */
+/***********************************************************************/
+int TXTFAM::DeleteSortedRows(PGLOBAL g)
+ {
+ int *ix, i, irc;
+
+ /*********************************************************************/
+ /* Get the stored delete values and sort them. */
+ /*********************************************************************/
+ if (!(Posar = MakeValueArray(g, To_Pos))) {
+// strcpy(g->Message, "Position array is null");
+// return RC_INFO;
+ return RC_OK; // Nothing to do
+ } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
+ strcpy(g->Message, "Start position array is null");
+ goto err;
+ } else if (!(ix = (int*)Posar->GetSortIndex(g))) {
+ strcpy(g->Message, "Error getting array sort index");
+ goto err;
+ } // endif's
+
+ Tpos = Spos = 0;
+
+ for (i = 0; i < Posar->GetNval(); i++) {
+ if ((irc = InitDelete(g, Posar->GetIntValue(ix[i]),
+ Sosar->GetIntValue(ix[i])) == RC_FX))
+ goto err;
+
+ // Now delete the sorted rows
+ if (DeleteRecords(g, irc))
+ goto err;
+
+ } // endfor i
+
+ return RC_OK;
+
+err:
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return RC_FX;
+ } // end of DeleteSortedRows
+
+/***********************************************************************/
+/* The purpose of this function is to deal with access methods that */
+/* are not coherent regarding the use of SetPos and GetPos. */
+/***********************************************************************/
+int TXTFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ strcpy(g->Message, "InitDelete should not be used by this table type");
+ return RC_FX;
+ } // end of InitDelete
+
/* --------------------------- Class DOSFAM -------------------------- */
/***********************************************************************/
@@ -238,7 +455,6 @@ 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
@@ -248,13 +464,21 @@ 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
+DOSFAM::DOSFAM(PBLKFAM tdfp, PDOSDEF tdp) : TXTFAM(tdp)
+ {
+ Tdbp = tdfp->Tdbp;
+ To_Fb = tdfp->To_Fb;
+ To_Fbt = tdfp->To_Fbt;
+ Stream = tdfp->Stream;
+ T_Stream = tdfp->T_Stream;
+ UseTemp = tdfp->UseTemp;
+ Bin = tdfp->Bin;
+ } // end of DOSFAM constructor from BLKFAM
+
/***********************************************************************/
/* Reset: reset position values at the beginning of file. */
/***********************************************************************/
@@ -295,6 +519,15 @@ int DOSFAM::Cardinality(PGLOBAL g)
} // 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)
@@ -304,8 +537,8 @@ bool DOSFAM::OpenTableFile(PGLOBAL g)
MODE mode = Tdbp->Mode;
PDBUSER dbuserp = PlgGetUser(g);
- // This is required when using Unix files under Windows
- Bin = (Ending == 1);
+ // This is required when using Unix files under Windows and vice versa
+ Bin = (Blocked || Ending != CRLF);
switch (mode) {
case MODE_READ:
@@ -347,7 +580,7 @@ bool DOSFAM::OpenTableFile(PGLOBAL g)
} // endswitch Mode
// For blocked I/O or for moving lines, open the table in binary
- strcat(opmode, (Blocked || Bin) ? "b" : "t");
+ strcat(opmode, (Bin) ? "b" : "t");
// Now open the file stream
PlugSetPath(filename, To_File, Tdbp->GetPath());
@@ -381,7 +614,7 @@ bool DOSFAM::AllocateBuffer(PGLOBAL g)
MODE mode = Tdbp->Mode;
// Lrecl does not include line ending
- Buflen = Lrecl + Ending + ((Bin) ? 1 : 0);
+ Buflen = Lrecl + Ending + ((Bin) ? 1 : 0) + 1; // Sergei
if (trace)
htrc("SubAllocating a buffer of %d bytes\n", Buflen);
@@ -458,6 +691,21 @@ bool DOSFAM::RecordPos(PGLOBAL g)
} // end of RecordPos
/***********************************************************************/
+/* Initialize Fpos and the current position for indexed DELETE. */
+/***********************************************************************/
+int DOSFAM::InitDelete(PGLOBAL g, int fpos, int spos)
+ {
+ Fpos = fpos;
+
+ if (fseek(Stream, spos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
+ return RC_FX;
+ } // endif
+
+ return RC_OK;
+ } // end of InitDelete
+
+/***********************************************************************/
/* Skip one record in file. */
/***********************************************************************/
int DOSFAM::SkipRecord(PGLOBAL g, bool header)
@@ -509,19 +757,35 @@ int DOSFAM::ReadBuffer(PGLOBAL g)
if (trace > 1)
htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
- Tdbp, Tdbp->To_Line, Placed);
+ 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);
+ 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;
@@ -594,12 +858,11 @@ int DOSFAM::ReadBuffer(PGLOBAL g)
/***********************************************************************/
int DOSFAM::WriteBuffer(PGLOBAL g)
{
- char *crlf = "\n";
- int curpos = 0;
+ int curpos = 0;
bool moved = true;
// T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
+ if (!T_Stream) {
if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
if (OpenTempFile(g))
return RC_FX;
@@ -607,6 +870,8 @@ int DOSFAM::WriteBuffer(PGLOBAL g)
} else
T_Stream = Stream;
+ } // endif T_Stream
+
if (Tdbp->Mode == MODE_UPDATE) {
/*******************************************************************/
/* Here we simply rewrite a record on itself. There are two cases */
@@ -622,14 +887,14 @@ int DOSFAM::WriteBuffer(PGLOBAL g)
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. */
+ /* 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
+ Spos = curpos; // New start position
} else
// Update is directly written back into the file,
// with this (fast) method, record size cannot change.
@@ -641,13 +906,9 @@ int DOSFAM::WriteBuffer(PGLOBAL g)
} // endif mode
/*********************************************************************/
- /* Prepare the write buffer. */
+ /* Prepare the write the updated line. */
/*********************************************************************/
-#if defined(WIN32)
- if (Bin)
- crlf = "\r\n";
-#endif // WIN32
- strcat(strcpy(To_Buf, Tdbp->To_Line), crlf);
+ strcat(strcpy(To_Buf, Tdbp->To_Line), (Bin) ? CrLf : "\n");
/*********************************************************************/
/* Now start the writing process. */
@@ -675,7 +936,7 @@ int DOSFAM::WriteBuffer(PGLOBAL g)
int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
{
bool moved;
- int curpos = ftell(Stream);
+ int curpos = ftell(Stream);
/*********************************************************************/
/* There is an alternative here: */
@@ -684,8 +945,7 @@ int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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. */
+ /* This will be experimented. */
/*********************************************************************/
if (trace)
htrc(
@@ -750,7 +1010,7 @@ int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
/* Last call after EOF has been reached. */
/* The UseTemp case is treated in CloseTableFile. */
/*******************************************************************/
- if (!UseTemp) {
+ if (!UseTemp & !Abort) {
/*****************************************************************/
/* Because the chsize functionality is only accessible with a */
/* system call we must close the file and reopen it with the */
@@ -875,41 +1135,47 @@ bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
/***********************************************************************/
/* Delete the old file and rename the new temp file. */
+/* If aborting just delete the new temp file. */
+/* If indexed, make the temp file from the arrays. */
/***********************************************************************/
int DOSFAM::RenameTempFile(PGLOBAL g)
{
char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
- int rc;
+ int rc = RC_OK;
- if (!To_Fbt)
+ if (To_Fbt)
+ tempname = (char*)To_Fbt->Fname;
+ else
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)
- if (fb == To_Fb || fb == To_Fbt)
+ if (fb == To_Fb || (fb == To_Fbt))
rc = PlugCloseFile(g, fb);
+
+ if (!Abort) {
+ 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));
+ longjmp(g->jumper[g->jump_level], 51);
+ } else if (rename(tempname, filename)) {
+ sprintf(g->Message, MSG(RENAME_ERROR),
+ tempname, filename, strerror(errno));
+ rc = rename(filetemp, filename); // Restore saved file
+ longjmp(g->jumper[g->jump_level], 52);
+ } else if (remove(filetemp)) {
+ sprintf(g->Message, MSG(REMOVE_ERROR),
+ filetemp, strerror(errno));
+ rc = RC_INFO; // Acceptable
+ } // endif's
- 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;
+ remove(tempname);
return rc;
} // end of RenameTempFile
@@ -917,22 +1183,24 @@ int DOSFAM::RenameTempFile(PGLOBAL g)
/***********************************************************************/
/* Table file close routine for DOS access method. */
/***********************************************************************/
-void DOSFAM::CloseTableFile(PGLOBAL g)
+void DOSFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc;
+ Abort = abort;
+
if (UseTemp && T_Stream) {
- if (Tdbp->Mode == MODE_UPDATE) {
+ if (Tdbp->Mode == MODE_UPDATE && !Abort) {
// Copy eventually remaining lines
bool b;
fseek(Stream, 0, SEEK_END);
Fpos = ftell(Stream);
- rc = MoveIntermediateLines(g, &b);
- } // endif Mode
+ Abort = MoveIntermediateLines(g, &b) != RC_OK;
+ } // endif Abort
// Delete the old file and rename the new temp file.
- RenameTempFile(g); // Also close all files
+ rc = RenameTempFile(g); // Also close all files
} else {
rc = PlugCloseFile(g, To_Fb);
@@ -942,6 +1210,7 @@ void DOSFAM::CloseTableFile(PGLOBAL g)
} // endif UseTemp
Stream = NULL; // So we can know whether table is open
+ T_Stream = NULL;
} // end of CloseTableFile
/***********************************************************************/
@@ -968,7 +1237,7 @@ BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
Last = tdp->GetLast();
Nrec = tdp->GetElemt();
Closing = false;
- BlkPos = NULL;
+ BlkPos = tdp->GetTo_Pos();
CurLine = NULL;
NxtLine = NULL;
OutBuf = NULL;
@@ -998,12 +1267,30 @@ void BLKFAM::Reset(void)
/***********************************************************************/
int BLKFAM::Cardinality(PGLOBAL g)
{
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ return (g) ? ((Block > 0) ? (int)((Block - 1) * Nrec + Last) : 0) : 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. */
@@ -1059,20 +1346,8 @@ int BLKFAM::GetNextPos(void)
/***********************************************************************/
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;
+ strcpy(g->Message, "Blocked variable tables cannot be used indexed");
+ return true;
} // end of SetPos
/***********************************************************************/
@@ -1108,8 +1383,109 @@ int BLKFAM::SkipRecord(PGLOBAL g, bool header)
/***********************************************************************/
int BLKFAM::ReadBuffer(PGLOBAL g)
{
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
+ 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
/***********************************************************************/
@@ -1150,7 +1526,7 @@ int BLKFAM::WriteBuffer(PGLOBAL g)
/*******************************************************************/
/* Mode == MODE_UPDATE. */
/*******************************************************************/
- char *crlf;
+ const char *crlf;
size_t len;
int curpos = ftell(Stream);
bool moved = true;
@@ -1214,27 +1590,24 @@ int BLKFAM::WriteBuffer(PGLOBAL g)
/***********************************************************************/
/* Table file close routine for DOS access method. */
/***********************************************************************/
-void BLKFAM::CloseTableFile(PGLOBAL g)
+void BLKFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc, wrc = RC_OK;
+ Abort = abort;
+
if (UseTemp && T_Stream) {
- if (Tdbp->GetMode() == MODE_UPDATE) {
+ if (Tdbp->GetMode() == MODE_UPDATE && !Abort) {
// 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);
+ Abort = MoveIntermediateLines(g, &b) != RC_OK;
+ } // endif Abort
+ // Delete the old file and rename the new temp file.
+ rc = RenameTempFile(g); // Also close all files
} else {
// Closing is True if last Write was in error
if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
diff --git a/storage/connect/filamtxt.h b/storage/connect/filamtxt.h
index c3ee96ada1a..b89d58965f9 100644
--- a/storage/connect/filamtxt.h
+++ b/storage/connect/filamtxt.h
@@ -1,7 +1,7 @@
/************** FilAMTxt H Declares Source Code File (.H) **************/
-/* Name: FILAMTXT.H Version 1.2 */
+/* Name: FILAMTXT.H Version 1.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* This file contains the file access method classes declares. */
/***********************************************************************/
@@ -10,6 +10,7 @@
#define __FILAMTXT_H
#include "block.h"
+#include "array.h"
typedef class TXTFAM *PTXF;
typedef class DOSFAM *PDOSFAM;
@@ -53,26 +54,38 @@ class DllExport TXTFAM : public BLOCK {
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 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 int DeleteRecords(PGLOBAL g, int irc) = 0;
+ virtual void CloseTableFile(PGLOBAL g, bool abort) = 0;
virtual void Rewind(void) = 0;
+ virtual int InitDelete(PGLOBAL g, int fpos, int spos);
+ bool AddListValue(PGLOBAL g, int type, void *val, PPARM *top);
+ int StoreValues(PGLOBAL g, bool upd);
+ int UpdateSortedRows(PGLOBAL g);
+ int DeleteSortedRows(PGLOBAL g);
protected:
// Members
PTDBDOS Tdbp; // To table class
PSZ To_File; // Points to table file name
PFBLOCK To_Fb; // Pointer to file block
+ PPARM To_Pos; // Pointer to position list
+ PPARM To_Sos; // Pointer to start position list
+ PPARM To_Upd; // Pointer to udated line list
+ PARRAY Posar; // Pointer to position array
+ PARRAY Sosar; // Pointer to start position array
+ PARRAY Updar; // Pointer to udated lines array
bool Placed; // true if Recpos was externally set
bool IsRead; // false for deferred reading
bool Blocked; // true if using blocked I/O
@@ -97,8 +110,12 @@ class DllExport TXTFAM : public BLOCK {
int Modif; // Number of modified lines in block
int Blksize; // Size of padded blocks
int Ending; // Length of line end
+ int Fpos; // Position of last read record
+ int Spos; // Start position for update/delete move
+ int Tpos; // Target Position for delete move
bool Padded; // true if fixed size blocks are padded
bool Eof; // true if an EOF (0xA) character exists
+ bool Abort; // To abort on error
char *CrLf; // End of line character(s)
}; // end of class TXTFAM
@@ -111,6 +128,7 @@ class DllExport DOSFAM : public TXTFAM {
// Constructor
DOSFAM(PDOSDEF tdp);
DOSFAM(PDOSFAM txfp);
+ DOSFAM(PBLKFAM tdfp, PDOSDEF tdp);
// Implementation
virtual AMT GetAmType(void) {return TYPE_AM_DOS;}
@@ -124,31 +142,30 @@ class DllExport DOSFAM : public TXTFAM {
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 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 CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
protected:
virtual bool OpenTempFile(PGLOBAL g);
virtual bool MoveIntermediateLines(PGLOBAL g, bool *b);
virtual int RenameTempFile(PGLOBAL g);
+ virtual int InitDelete(PGLOBAL g, int fpos, int spos);
// 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 UseTemp; // True to use a temporary file in Upd/Del
bool Bin; // True to force binary mode
}; // end of class DOSFAM
@@ -172,14 +189,15 @@ class DllExport BLKFAM : public DOSFAM {
// 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 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 CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
protected:
diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp
index edadc25b50b..b93adbd13dd 100755
--- a/storage/connect/filamvct.cpp
+++ b/storage/connect/filamvct.cpp
@@ -1,16 +1,16 @@
/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMVCT */
/* ------------- */
-/* Version 2.4 */
+/* Version 2.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (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 */
+/* Added in version 2: */
/* - Split Vec format. */
/* - Partial delete. */
/* - Use of tempfile for update. */
@@ -29,7 +29,7 @@
#endif // __BORLAND__
//#include <windows.h>
#include <sys/stat.h>
-#else // !WIN32 F
+#else // !WIN32
#if defined(UNIX)
#include <sys/types.h>
#include <sys/stat.h>
@@ -137,6 +137,39 @@ VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp)
} // end of VCTFAM copy constructor
/***********************************************************************/
+/* VCT GetFileLength: returns file size in number of bytes. */
+/* This function is here to be accessible by VECFAM and VMPFAM. */
+/***********************************************************************/
+int VCTFAM::GetFileLength(PGLOBAL g)
+ {
+ if (Split) {
+ // Get the total file length
+ char filename[_MAX_PATH];
+ char *savfile = To_File;
+ int i, len = 0;
+
+ // 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 = ((PVCTDEF)Tdbp->GetDef())->MakeFnPattern(Colfn);
+ } // endif Colfn
+
+ To_File = filename;
+
+ for (i = 0; i < Ncol; i++) {
+ sprintf(filename, Colfn, i+1);
+ len += TXTFAM::GetFileLength(g);
+ } // endfor i
+
+ To_File = savfile;
+ return len;
+ } else
+ return TXTFAM::GetFileLength(g);
+
+ } // end of GetFileLength
+
+/***********************************************************************/
/* Reset read/write position values. */
/***********************************************************************/
void VCTFAM::Reset(void)
@@ -170,7 +203,7 @@ int VCTFAM::GetBlockInfo(PGLOBAL g)
if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
|| !_filelength(h)) {
// Consider this is a void table
- Last = Nrec;
+ Last = Nrec;
Block = 0;
if (h != -1)
@@ -179,7 +212,7 @@ int VCTFAM::GetBlockInfo(PGLOBAL g)
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;
@@ -187,7 +220,7 @@ int VCTFAM::GetBlockInfo(PGLOBAL g)
sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
vh.MaxRec, MaxBlk, Nrec);
n = -1;
- } else {
+ } else {
Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
Last = (vh.NumRec + Nrec - 1) % Nrec + 1;
} // endif s
@@ -245,6 +278,26 @@ bool VCTFAM::SetBlockInfo(PGLOBAL g)
} // 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). */
@@ -264,20 +317,20 @@ int VCTFAM::Cardinality(PGLOBAL g)
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);
+ len = TXTFAM::GetFileLength(g);
To_File = savfn;
-
+
if (len >= 0) {
if (!(len % clen))
card = len / clen; // Fixed length file
@@ -289,7 +342,7 @@ int VCTFAM::Cardinality(PGLOBAL g)
} else
card = 0;
-
+
// Set number of blocks for later use
Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
Last = (card + Nrec - 1) % Nrec + 1;
@@ -301,7 +354,7 @@ int VCTFAM::Cardinality(PGLOBAL g)
} // endif split
- return (int)((Block - 1) * Nrec + Last);
+ return (Block) ? ((Block - 1) * Nrec + Last) : 0;
} // end of Cardinality
/***********************************************************************/
@@ -310,7 +363,7 @@ int VCTFAM::Cardinality(PGLOBAL g)
int VCTFAM::GetRowID(void)
{
return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk
- : (Block - 1) * Nrec + Last);
+ : (Block - 1) * Nrec + Last);
} // end of GetRowID
/***********************************************************************/
@@ -394,7 +447,7 @@ bool VCTFAM::OpenTableFile(PGLOBAL g)
return true;
strcpy(opmode, "r+b"); // Required to update empty blocks
- } else if (Last == Nrec)
+ } else if (!Block || Last == Nrec)
strcpy(opmode, "ab");
else
strcpy(opmode, "r+b"); // Required to update the last block
@@ -429,7 +482,7 @@ bool VCTFAM::OpenTableFile(PGLOBAL g)
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
@@ -555,9 +608,21 @@ int VCTFAM::ReadBuffer(PGLOBAL g)
/*******************************************************************/
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
@@ -684,7 +749,7 @@ int VCTFAM::WriteBuffer(PGLOBAL g)
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);
@@ -694,7 +759,7 @@ int VCTFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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);
@@ -843,7 +908,7 @@ bool VCTFAM::OpenTempFile(PGLOBAL g)
bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
{
int i, dep, off;
- int n;
+ int n;
bool eof = (b) ? *b : false;
size_t req, len;
@@ -1010,11 +1075,13 @@ bool VCTFAM::CleanUnusedSpace(PGLOBAL g)
/***********************************************************************/
/* Data Base close routine for VCT access method. */
/***********************************************************************/
-void VCTFAM::CloseTableFile(PGLOBAL g)
+void VCTFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = 0, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
if (mode == MODE_INSERT) {
if (Closing)
wrc = RC_FX; // Last write was in error
@@ -1093,10 +1160,10 @@ bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last)
// Update catalog values for Block and Last
PVCTDEF defp = (PVCTDEF)Tdbp->GetDef();
LPCSTR name = Tdbp->GetName();
-
+
defp->SetBlock(Block);
defp->SetLast(Last);
-
+
if (!defp->SetIntCatInfo("Blocks", Block) ||
!defp->SetIntCatInfo("Last", Last)) {
sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
@@ -1189,7 +1256,7 @@ bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
/* Calculate the offset and size of the block to write. */
/*********************************************************************/
if (MaxBlk) // File has Vector format
- len = Headlen
+ len = Headlen
+ Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
else // Old VCT format
len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
@@ -1212,7 +1279,7 @@ bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
(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));
@@ -1537,9 +1604,6 @@ int VCMFAM::WriteBuffer(PGLOBAL g)
/***********************************************************************/
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);
@@ -1549,59 +1613,21 @@ int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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)
+ if (Tpos == Spos) {
/*******************************************************************/
/* First line to delete. Move of eventual preceding 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)MY_MIN(n, Nrec - MY_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
+ Tpos = Spos = Fpos;
+ } else
+ (void)MoveIntermediateLines(g);
if (irc == RC_OK) {
Spos = Fpos + 1; // New start position
@@ -1611,8 +1637,12 @@ int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
} else {
/*******************************************************************/
- /* Last call after EOF has been reached. Reset the Block and */
- /* Last values for TDBVCT::MakeBlockValues. */
+ /* Last call after EOF has been reached. */
+ /*******************************************************************/
+ int i, m, n;
+
+ /*******************************************************************/
+ /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
/*******************************************************************/
Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
Last = (Tpos + Nrec - 1) % Nrec + 1;
@@ -1681,9 +1711,58 @@ int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
} // end of DeleteRecords
/***********************************************************************/
+/* Move intermediate deleted or updated lines. */
+/***********************************************************************/
+bool VCMFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
+ {
+ int i, m, n;
+
+ if ((n = Fpos - Spos) > 0) {
+ /*******************************************************************/
+ /* 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 > 0; n -= req) {
+ soff = Spos % Nrec;
+ toff = Tpos % Nrec;
+ req = (size_t)MY_MIN(n, Nrec - MY_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...
+ 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
+
+ return false;
+ } // end of MoveIntermediate Lines
+
+/***********************************************************************/
/* Data Base close routine for VMP access method. */
/***********************************************************************/
-void VCMFAM::CloseTableFile(PGLOBAL g)
+void VCMFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int wrc = RC_OK;
MODE mode = Tdbp->GetMode();
@@ -1710,7 +1789,7 @@ void VCMFAM::CloseTableFile(PGLOBAL g)
if (wrc != RC_FX)
/*rc =*/ ResetTableSize(g, Block, Last);
- } else if (mode != MODE_DELETE)
+ } else if (mode != MODE_DELETE || Abort)
PlugCloseFile(g, To_Fb);
} // end of CloseTableFile
@@ -1838,7 +1917,7 @@ bool VECFAM::OpenTableFile(PGLOBAL g)
// Selective delete, pass thru
case MODE_UPDATE:
UseTemp = Tdbp->IsUsingTemp(g);
- strcpy(opmode, (UseTemp) ? "r": "r+");
+ strcpy(opmode, (UseTemp) ? "rb": "r+b");
break;
case MODE_INSERT:
strcpy(opmode, "ab");
@@ -1897,10 +1976,13 @@ bool VECFAM::OpenTableFile(PGLOBAL g)
// 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;
+ if (!cp->IsSpecial()) {
+ if (!i++)
+ b = !Streams[cp->Index - 1];
+ else if (b != !Streams[cp->Index - 1])
+ return true;
+
+ } // endif Special
} // endif mode
@@ -2114,18 +2196,10 @@ int VECFAM::WriteBuffer(PGLOBAL g)
/***********************************************************************/
/* Data Base delete line routine for split vertical access methods. */
/* Note: lines are moved directly in the files (ooops...) */
+/* Using temp file depends on the Check setting, false by default. */
/***********************************************************************/
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);
@@ -2135,14 +2209,14 @@ int VECFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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)
+ if (Tpos == Spos) {
// First line to delete
if (UseTemp) {
/*****************************************************************/
@@ -2158,6 +2232,8 @@ int VECFAM::DeleteRecords(PGLOBAL g, int irc)
/*****************************************************************/
Spos = Tpos = Fpos;
+ } // endif Tpos == Spos
+
/*********************************************************************/
/* Move any intermediate lines. */
/*********************************************************************/
@@ -2180,7 +2256,7 @@ int VECFAM::DeleteRecords(PGLOBAL g, int irc)
if (!UseTemp) {
/*****************************************************************/
/* Because the chsize functionality is only accessible with a */
- /* system call we must close the file and reopen it with the */
+ /* system call we must close the files and reopen them with the */
/* open function (_fopen for MS??) this is still to be checked */
/* for compatibility with other OS's. */
/*****************************************************************/
@@ -2288,8 +2364,7 @@ bool VECFAM::MoveLines(PGLOBAL g)
/***********************************************************************/
bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *bn)
{
- int i;
- int n;
+ int i, n;
bool b = false;
size_t req, len;
@@ -2366,25 +2441,30 @@ int VECFAM::RenameTempFile(PGLOBAL g)
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
+
+ if (!Abort) {
+ 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
+
+ } else
+ remove(tempname);
} // endfor i
@@ -2394,11 +2474,13 @@ int VECFAM::RenameTempFile(PGLOBAL g)
/***********************************************************************/
/* Data Base close routine for VEC access method. */
/***********************************************************************/
-void VECFAM::CloseTableFile(PGLOBAL g)
+void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = 0, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
if (mode == MODE_INSERT) {
if (Closing)
wrc = RC_FX; // Last write was in error
@@ -2421,10 +2503,10 @@ void VECFAM::CloseTableFile(PGLOBAL g)
longjmp(g->jumper[g->jump_level], 44);
} else if (mode == MODE_UPDATE) {
- if (UseTemp && !InitUpdate) {
+ if (UseTemp && !InitUpdate && !Abort) {
// Write any intermediate lines to temp file
Fpos = OldBlk * Nrec;
- wrc = MoveIntermediateLines(g);
+ Abort = MoveIntermediateLines(g) != RC_OK;
// Spos = Fpos + Nrec;
} // endif UseTemp
@@ -2434,20 +2516,17 @@ void VECFAM::CloseTableFile(PGLOBAL g)
colp; colp = (PVCTCOL)colp->Next)
colp->WriteBlock(g);
- if (wrc == RC_OK && UseTemp && !InitUpdate) {
+ if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
// Write any intermediate lines to temp file
Fpos = (Block - 1) * Nrec + Last;
- wrc = MoveIntermediateLines(g);
+ Abort = MoveIntermediateLines(g) != RC_OK;
} // 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);
+ rc = RenameTempFile(g);
} else if (Streams)
for (int i = 0; i < Ncol; i++)
@@ -2553,7 +2632,7 @@ bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
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));
@@ -2593,7 +2672,7 @@ VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
bool VMPFAM::OpenTableFile(PGLOBAL g)
{
int i;
- bool b;
+ bool b = false;
MODE mode = Tdbp->GetMode();
PCOLDEF cdp;
PVCTCOL cp;
@@ -2639,7 +2718,7 @@ bool VMPFAM::OpenTableFile(PGLOBAL g)
} else {
/*******************************************************************/
- /* Open the files corresponding updated columns of the query. */
+ /* Open the files corresponding to updated columns of the query. */
/*******************************************************************/
for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
cp = (PVCTCOL)cp->Next)
@@ -2654,14 +2733,18 @@ bool VMPFAM::OpenTableFile(PGLOBAL g)
if (MapColumnFile(g, MODE_READ, cp->Index - 1))
return true;
- } // endif mode
+ // Check for void table or missing columns
+ for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
+ cp = (PVCTCOL)cp->Next)
+ if (!cp->IsSpecial()) {
+ if (!i++)
+ b = !Memcol[cp->Index - 1];
+ else if (b != !Memcol[cp->Index - 1])
+ return true;
- /*********************************************************************/
- /* Check for void table or missing columns */
- /*********************************************************************/
- for (b = !Memcol[0], i = 1; i < Ncol; i++)
- if (b != !Memcol[i])
- return true;
+ } // endif Special
+
+ } // endif mode
/*********************************************************************/
/* Allocate the table and column block buffer. */
@@ -2825,20 +2908,20 @@ int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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)
+ if (Tpos == Spos) {
/*******************************************************************/
/* First line to delete. Move of eventual preceding lines is */
/* not required here, just setting of future Spos and Tpos. */
/*******************************************************************/
Tpos = Fpos; // Spos is set below
- else if ((n = Fpos - Spos) > 0) {
+ } else if ((n = Fpos - Spos) > 0) {
/*******************************************************************/
/* Non consecutive line to delete. Move intermediate lines. */
/*******************************************************************/
@@ -2868,6 +2951,12 @@ int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
/*******************************************************************/
PFBLOCK fp;
+ /*******************************************************************/
+ /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
+ /*******************************************************************/
+// Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
+// Last = (Tpos + Nrec - 1) % Nrec + 1;
+
for (i = 0; i < Ncol; i++) {
fp = To_Fbs[i];
CloseMemMap(fp->Memory, (size_t)fp->Length);
@@ -2918,7 +3007,7 @@ int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
/***********************************************************************/
/* Data Base close routine for VMP access method. */
/***********************************************************************/
-void VMPFAM::CloseTableFile(PGLOBAL g)
+void VMPFAM::CloseTableFile(PGLOBAL g, bool abort)
{
if (Tdbp->GetMode() == MODE_DELETE) {
// Set Block and Nrec values for TDBVCT::MakeBlockValues
@@ -3011,7 +3100,7 @@ bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
} // endelse brc
sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
-
+
if (trace)
htrc("BIGREAD: %s\n", g->Message);
@@ -3025,7 +3114,7 @@ bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
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);
@@ -3079,7 +3168,7 @@ bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
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);
@@ -3132,17 +3221,17 @@ int BGVFAM::GetBlockInfo(PGLOBAL g)
// Consider this is a void table
if (trace)
htrc("Void table h=%d\n", h);
-
- Last = Nrec;
+
+ Last = Nrec;
Block = 0;
if (h != INVALID_HANDLE_VALUE)
CloseFileHandle(h);
return n;
- } else if (Header == 3)
+ } 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;
@@ -3153,10 +3242,10 @@ int BGVFAM::GetBlockInfo(PGLOBAL g)
} 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);
@@ -3297,7 +3386,7 @@ bool BGVFAM::MakeEmptyFile(PGLOBAL g, char *fn)
if (h == -1)
return true;
-
+
pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
if (trace)
@@ -3738,7 +3827,7 @@ int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
/* 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);
@@ -4040,11 +4129,13 @@ bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
/***********************************************************************/
/* Data Base close routine for huge VEC access method. */
/***********************************************************************/
-void BGVFAM::CloseTableFile(PGLOBAL g)
+void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = 0, wrc = RC_OK;
MODE mode = Tdbp->GetMode();
+ Abort = abort;
+
if (mode == MODE_INSERT) {
if (Closing)
wrc = RC_FX; // Last write was in error
diff --git a/storage/connect/filamvct.h b/storage/connect/filamvct.h
index 0dd1c06ad8b..be66232acfb 100644
--- a/storage/connect/filamvct.h
+++ b/storage/connect/filamvct.h
@@ -37,9 +37,11 @@ class DllExport VCTFAM : public FIXFAM {
virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
virtual PTXF Duplicate(PGLOBAL g)
{return (PTXF)new(g) VCTFAM(this);}
+ virtual int GetFileLength(PGLOBAL g);
// 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) {}
@@ -50,8 +52,8 @@ class DllExport VCTFAM : public FIXFAM {
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 int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
// Specific functions
@@ -59,19 +61,19 @@ class DllExport VCTFAM : public FIXFAM {
virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
protected:
- virtual bool MakeEmptyFile(PGLOBAL g, char *fn);
+ 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);
+ 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)
+ char *Colfn; // Pattern for column file names (VEC)
+ char *Tempat; // Pattern for temp file names (VEC)
int *Clens; // Pointer to col size array
int *Deplac; // Pointer to col start position array
bool *Isnum; // Pointer to buffer type isnum result
@@ -107,10 +109,12 @@ class DllExport VCMFAM : public VCTFAM {
// 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 int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
+ protected:
// Specific functions
+ virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
@@ -144,15 +148,15 @@ class DllExport VECFAM : public VCTFAM {
// 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 int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
// Specific functions
virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp);
virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp);
protected:
- virtual bool OpenTempFile(PGLOBAL g);
+ virtual bool OpenTempFile(PGLOBAL g);
virtual bool MoveLines(PGLOBAL g);
virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL);
virtual int RenameTempFile(PGLOBAL g);
@@ -189,7 +193,7 @@ class DllExport VMPFAM : public VCMFAM {
// Database routines
virtual bool OpenTableFile(PGLOBAL g);
virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
protected:
bool MapColumnFile(PGLOBAL g, MODE mode, int i);
@@ -220,7 +224,7 @@ class BGVFAM : public VCTFAM {
virtual bool OpenTableFile(PGLOBAL g);
virtual int WriteBuffer(PGLOBAL g);
virtual int DeleteRecords(PGLOBAL g, int irc);
- virtual void CloseTableFile(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
virtual void Rewind(void);
// Specific functions
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index 0ec9e65c17e..8473011ab8b 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -1,11 +1,11 @@
/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMZIP */
/* ------------- */
-/* Version 1.4 */
+/* Version 1.5 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -306,10 +306,27 @@ int ZIPFAM::ReadBuffer(PGLOBAL g)
/*******************************************************************/
/* 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;
@@ -369,7 +386,7 @@ int ZIPFAM::DeleteRecords(PGLOBAL g, int irc)
/***********************************************************************/
/* Data Base close routine for DOS access method. */
/***********************************************************************/
-void ZIPFAM::CloseTableFile(PGLOBAL g)
+void ZIPFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = gzclose(Zfile);
@@ -402,7 +419,7 @@ ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp)
CurLine = NULL;
NxtLine = NULL;
Closing = false;
- BlkPos = NULL;
+ BlkPos = tdp->GetTo_Pos();
} // end of ZBKFAM standard constructor
ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp)
@@ -413,15 +430,33 @@ ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp)
} // 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;
+ return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
} // end of Cardinality
/***********************************************************************/
@@ -491,8 +526,80 @@ int ZBKFAM::SkipRecord(PGLOBAL g, bool header)
/***********************************************************************/
int ZBKFAM::ReadBuffer(PGLOBAL g)
{
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
+ 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
/***********************************************************************/
@@ -562,7 +669,7 @@ int ZBKFAM::DeleteRecords(PGLOBAL g, int irc)
/***********************************************************************/
/* Data Base close routine for ZBK access method. */
/***********************************************************************/
-void ZBKFAM::CloseTableFile(PGLOBAL g)
+void ZBKFAM::CloseTableFile(PGLOBAL g, bool abort)
{
int rc = RC_OK;
@@ -701,6 +808,32 @@ int ZIXFAM::ReadBuffer(PGLOBAL g)
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) {
@@ -746,4 +879,544 @@ int ZIXFAM::WriteBuffer(PGLOBAL g)
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
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool ZLBFAM::SetPos(PGLOBAL g, int pos)
+ {
+ sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP");
+ return true;
+#if 0 // All this must be checked
+ 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;
+#endif // 0
+ } // end of SetPos
+
+/***********************************************************************/
+/* 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, bool abort)
+ {
+ int rc = RC_OK;
+
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ 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);
+ defp->SetIntCatInfo("Blocks", Block);
+ defp->SetIntCatInfo("Last", Last);
+ } // endif
+
+ fclose(Stream);
+ } else
+ rc = fclose(Stream);
+
+ if (trace)
+ htrc("ZLB CloseTableFile: closing %s mode=%d rc=%d\n",
+ To_File, Tdbp->GetMode(), rc);
+
+ 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 37cc130311c..6d27cb67e81 100644
--- a/storage/connect/filamzip.h
+++ b/storage/connect/filamzip.h
@@ -1,169 +1,170 @@
-/************** 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 bool AllocateBuffer(PGLOBAL g);
- virtual int GetRowID(void);
- virtual bool RecordPos(PGLOBAL g);
+/************** 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, bool abort);
+ 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, bool abort);
+ 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 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 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 0
-/***********************************************************************/
-/* 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 // 0
-
-#endif // __FILAMZIP_H
+ virtual bool AllocateBuffer(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
+ 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
new file mode 100644
index 00000000000..9212432cdde
--- /dev/null
+++ b/storage/connect/filter.cpp
@@ -0,0 +1,1733 @@
+/***************** Filter C++ Class Filter Code (.CPP) *****************/
+/* Name: FILTER.CPP Version 3.9 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* */
+/* This file contains the class FILTER function code. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#include "sql_class.h"
+//#include "sql_time.h"
+
+#if defined(WIN32)
+//#include <windows.h>
+#else // !WIN32
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif // !WIN32
+
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* xobject.h is header containing the XOBJECT derived classes dcls. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "tabcol.h"
+#include "xtable.h"
+#include "array.h"
+//#include "subquery.h"
+#include "filter.h"
+//#include "token.h"
+//#include "select.h"
+#include "xindex.h"
+
+/***********************************************************************/
+/* Static variables. */
+/***********************************************************************/
+extern "C" int trace;
+
+/***********************************************************************/
+/* Utility routines. */
+/***********************************************************************/
+void PlugConvertConstant(PGLOBAL, void* &, short&);
+//void *PlugCopyDB(PTABS, void*, INT);
+void NewPointer(PTABS, void*, void*);
+void AddPointer(PTABS, void*);
+
+static PPARM MakeParm(PGLOBAL g, PXOB xp)
+ {
+ PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
+ pp->Type = TYPE_XOBJECT;
+ pp->Value = xp;
+ pp->Domain = 0;
+ pp->Next = NULL;
+ return pp;
+ } // end of MakeParm
+
+/***********************************************************************/
+/* Routines called externally by FILTER function. */
+/***********************************************************************/
+bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
+//bool ReadSubQuery(PGLOBAL, PSUBQ);
+//PSUBQ OpenSubQuery(PGLOBAL, PSQL);
+//void PlugCloseDB(PGLOBAL, PSQL);
+BYTE OpBmp(PGLOBAL g, OPVAL opc);
+PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
+
+/***********************************************************************/
+/* Routines called externally by CondFilter. */
+/***********************************************************************/
+PFIL MakeFilter(PGLOBAL g, PFIL fp1, OPVAL vop, PFIL fp2)
+ {
+ PFIL filp = new(g) FILTER(g, vop);
+
+ filp->Arg(0) = fp1;
+ filp->Arg(1) = fp2;
+
+ if (filp->Convert(g, false))
+ return NULL;
+
+ return filp;
+ } // end of MakeFilter
+
+PFIL MakeFilter(PGLOBAL g, PCOL *colp, POPER pop, PPARM pfirst, bool neg)
+{
+ PPARM parmp, pp[2];
+ PFIL fp1, fp2, filp = NULL;
+
+ if (pop->Val == OP_IN) {
+ PARRAY par = MakeValueArray(g, pfirst);
+
+ if (par) {
+ pp[0] = MakeParm(g, colp[0]);
+ pp[1] = MakeParm(g, par);
+ fp1 = new(g) FILTER(g, pop, pp);
+
+ if (fp1->Convert(g, false))
+ return NULL;
+
+ filp = (neg) ? MakeFilter(g, fp1, OP_NOT, NULL) : fp1;
+ } // endif par
+
+ } else if (pop->Val == OP_XX) { // BETWEEN
+ if (pfirst && pfirst->Next) {
+ pp[0] = MakeParm(g, colp[0]);
+ pp[1] = pfirst;
+ fp1 = new(g) FILTER(g, neg ? OP_LT : OP_GE, pp);
+
+ if (fp1->Convert(g, false))
+ return NULL;
+
+ pp[1] = pfirst->Next;
+ fp2 = new(g) FILTER(g, neg ? OP_GT : OP_LE, pp);
+
+ if (fp2->Convert(g, false))
+ return NULL;
+
+ filp = MakeFilter(g, fp1, neg ? OP_OR : OP_AND, fp2);
+ } // endif parmp
+
+ } else {
+ parmp = pfirst;
+
+ for (int i = 0; i < 2; i++)
+ if (colp[i]) {
+ pp[i] = MakeParm(g, colp[i]);
+ } else {
+ if (!parmp || parmp->Domain != i)
+ return NULL; // Logical error, should never happen
+
+ pp[i] = parmp;
+ parmp = parmp->Next;
+ } // endif colp
+
+ filp = new(g) FILTER(g, pop, pp);
+
+ if (filp->Convert(g, false))
+ return NULL;
+
+ } // endif's Val
+
+ return filp;
+} // end of MakeFilter
+
+/* --------------------------- Class FILTER -------------------------- */
+
+/***********************************************************************/
+/* FILTER public constructors. */
+/***********************************************************************/
+FILTER::FILTER(PGLOBAL g, POPER pop, PPARM *tp)
+ {
+ Constr(g, pop->Val, pop->Mod, tp);
+ } // end of FILTER constructor
+
+FILTER::FILTER(PGLOBAL g, OPVAL opc, PPARM *tp)
+ {
+ Constr(g, opc, 0, tp);
+ } // end of FILTER constructor
+
+void FILTER::Constr(PGLOBAL g, OPVAL opc, int opm, PPARM *tp)
+ {
+ Next = NULL;
+ Opc = opc;
+ Opm = opm;
+ Bt = 0x00;
+
+ for (int i = 0; i < 2; i++) {
+ Test[i].B_T = TYPE_VOID;
+
+ if (tp && tp[i]) {
+ PlugConvertConstant(g, tp[i]->Value, tp[i]->Type);
+#if defined(_DEBUG)
+ assert(tp[i]->Type == TYPE_XOBJECT);
+#endif
+ Arg(i) = (PXOB)tp[i]->Value;
+ } else
+ Arg(i) = pXVOID;
+
+ Val(i) = NULL;
+ Test[i].Conv = FALSE;
+ } // endfor i
+
+ } // end of Constr
+
+/***********************************************************************/
+/* FILTER copy constructor. */
+/***********************************************************************/
+FILTER::FILTER(PFIL fil1)
+ {
+ Next = NULL;
+ Opc = fil1->Opc;
+ Opm = fil1->Opm;
+ Test[0] = fil1->Test[0];
+ 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 */
+/* from others and filtering operations will be automated by */
+/* making a list of filter operations in polish operation style. */
+/* Returned value points to the first filter of the list, which ends */
+/* with the filter that was pointed by the first call argument, */
+/* except for separators, in which case a loop is needed to find it. */
+/* Note: a loop is used now in all cases (was not for OP_NOT) to be */
+/* able to handle the case of filters whose arguments are already */
+/* linearized, as it is done in LNA semantic routines. Indeed for */
+/* already linearized chains, the first filter is never an OP_AND, */
+/* OP_OR or OP_NOT filter, so this function just returns 'this'. */
+/***********************************************************************/
+PFIL FILTER::Linearize(bool nosep)
+ {
+ int i;
+ PFIL lfp[2], ffp[2] = {NULL,NULL};
+
+ switch (Opc) {
+ case OP_NOT:
+ if (GetArgType(0) == TYPE_FILTER) {
+ lfp[0] = (PFIL)Arg(0);
+ ffp[0] = lfp[0]->Linearize(TRUE);
+ } /* endif */
+
+ if (!ffp[0])
+ return NULL;
+
+ while (lfp[0]->Next) // See Note above
+ lfp[0] = lfp[0]->Next;
+
+ Arg(0) = lfp[0];
+ lfp[0]->Next = this;
+ break;
+ case OP_OR:
+ nosep = TRUE;
+ case OP_AND:
+ for (i = 0; i < 2; i++) {
+ if (GetArgType(i) == TYPE_FILTER) {
+ lfp[i] = (PFIL)Arg(i);
+ ffp[i] = lfp[i]->Linearize(nosep);
+ } /* endif */
+
+ if (!ffp[i])
+ return NULL;
+
+ while (lfp[i]->Next)
+ lfp[i] = lfp[i]->Next;
+
+ Arg(i) = lfp[i];
+ } /* endfor i */
+
+ if (nosep) {
+ lfp[0]->Next = ffp[1];
+ lfp[1]->Next = this;
+ } else {
+ lfp[0]->Next = this;
+ Opc = OP_SEP;
+ Arg(1) = pXVOID;
+ Next = ffp[1];
+ } /* endif */
+
+ break;
+ default:
+ ffp[0] = this;
+ } /* endswitch */
+
+ return (ffp[0]);
+ } // end of Linearize
+
+/***********************************************************************/
+/* Link the fil2 filter chain to the fil1(this) filter chain. */
+/***********************************************************************/
+PFIL FILTER::Link(PGLOBAL g, PFIL fil2)
+ {
+ PFIL fil1;
+
+ if (trace)
+ htrc("Linking filter %p with op=%d... to filter %p with op=%d\n",
+ this, Opc, fil2, (fil2) ? fil2->Opc : 0);
+
+ for (fil1 = this; fil1->Next; fil1 = fil1->Next) ;
+
+ if (fil1->Opc == OP_SEP)
+ fil1->Next = fil2; // Separator already exists
+ else {
+ // Create a filter separator and insert it between the chains
+ PFIL filp = new(g) FILTER(g, OP_SEP);
+
+ filp->Arg(0) = fil1;
+ filp->Next = fil2;
+ fil1->Next = filp;
+ } // endelse
+
+ return (this);
+ } // end of Link
+
+/***********************************************************************/
+/* Remove eventual last separator from a filter chain. */
+/***********************************************************************/
+PFIL FILTER::RemoveLastSep(void)
+ {
+ PFIL filp, gfp = NULL;
+
+ // Find last filter block (filp) and previous one (gfp).
+ for (filp = this; filp->Next; filp = filp->Next)
+ gfp = filp;
+
+ // If last filter is a separator, remove it
+ if (filp->Opc == OP_SEP)
+ if (gfp)
+ gfp->Next = NULL;
+ else
+ return NULL; // chain is now empty
+
+ return this;
+ } // end of RemoveLastSep
+
+/***********************************************************************/
+/* CheckColumn: Checks references to Columns in the filter and change */
+/* them into references to Col Blocks. */
+/* Returns the number of column references or -1 in case of column */
+/* not found and -2 in case of unrecoverable error. */
+/* WHERE filters are called with *aggreg == AGG_NO. */
+/* HAVING filters are called with *aggreg == AGG_ANY. */
+/***********************************************************************/
+int FILTER::CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &p, int &ag)
+ {
+ char errmsg[MAX_STR] = "";
+ int agg, k, n = 0;
+
+ if (trace)
+ htrc("FILTER CheckColumn: sqlp=%p ag=%d\n", sqlp, ag);
+
+ switch (Opc) {
+ case OP_SEP:
+ case OP_AND:
+ case OP_OR:
+ case OP_NOT:
+ return 0; // This because we are called for a linearized filter
+ default:
+ break;
+ } // endswitch Opc
+
+ // Check all arguments even in case of error for when we are called
+ // from CheckHaving, where references to an alias raise an error but
+ // we must have all other arguments to be set.
+ for (int i = 0; i < 2; i++) {
+ if (GetArgType(i) == TYPE_FILTER) // Should never happen in
+ return 0; // current implementation
+
+ agg = ag;
+
+ if ((k = Arg(i)->CheckColumn(g, sqlp, Arg(i), agg)) < -1) {
+ return k;
+ } else if (k < 0) {
+ if (!*errmsg) // Keep first error message
+ strcpy(errmsg, g->Message);
+
+ } else
+ n += k;
+
+ } // endfor i
+
+ if (*errmsg) {
+ strcpy(g->Message, errmsg);
+ return -1;
+ } else
+ return n;
+
+ } // end of CheckColumn
+
+/***********************************************************************/
+/* RefNum: Find the number of references correlated sub-queries make */
+/* to the columns of the outer query (pointed by sqlp). */
+/***********************************************************************/
+int FILTER::RefNum(PSQL sqlp)
+ {
+ int n = 0;
+
+ for (int i = 0; i < 2; i++)
+ n += Arg(i)->RefNum(sqlp);
+
+ return n;
+ } // end of RefNum
+
+/***********************************************************************/
+/* CheckSubQuery: see SUBQUERY::CheckSubQuery for comment. */
+/***********************************************************************/
+PXOB FILTER::CheckSubQuery(PGLOBAL g, PSQL sqlp)
+ {
+ switch (Opc) {
+ case OP_SEP:
+ case OP_AND:
+ case OP_OR:
+ case OP_NOT:
+ break;
+ default:
+ for (int i = 0; i < 2; i++)
+ if (!(Arg(i) = (PXOB)Arg(i)->CheckSubQuery(g, sqlp)))
+ return NULL;
+
+ break;
+ } // endswitch Opc
+
+ return this;
+ } // end of CheckSubQuery
+
+/***********************************************************************/
+/* SortJoin: function that places ahead of the list the 'good' groups */
+/* for join filtering. These are groups with only one filter that */
+/* specify equality between two different table columns, at least */
+/* one is a table key column. Doing so the join filter will be in */
+/* general compatible with linearization of the joined table tree. */
+/* This function has been added a further sorting on column indexing. */
+/***********************************************************************/
+PFIL FILTER::SortJoin(PGLOBAL g)
+ {
+ int k;
+ PCOL cp1, cp2;
+ PTDBASE tp1, tp2;
+ PFIL fp, filp, gfp, filstart = this, filjoin = NULL, lfp = NULL;
+ bool join = TRUE, key = TRUE;
+
+ // This routine requires that the chain ends with a separator
+ // So check for it and eventually add one if necessary
+ for (filp = this; filp->Next; filp = filp->Next) ;
+
+ if (filp->Opc != OP_SEP)
+ filp->Next = new(g) FILTER(g, OP_SEP);
+
+ again:
+ for (k = (key) ? 0 : MAX_MULT_KEY; k <= MAX_MULT_KEY; k++)
+ for (gfp = NULL, fp = filp = filstart; filp; filp = filp->Next)
+ switch (filp->Opc) {
+ case OP_SEP:
+ if (join) {
+ // Put this filter group into the join filter group list.
+ if (!lfp)
+ filjoin = fp;
+ else
+ lfp->Next = fp;
+
+ if (!gfp)
+ filstart = filp->Next;
+ else
+ gfp->Next = filp->Next;
+
+ lfp = filp; // last block of join filter list
+ } else
+ gfp = filp; // last block of bad filter list
+
+ join = TRUE;
+ fp = filp->Next;
+ break;
+ case OP_LOJ:
+ case OP_ROJ:
+ case OP_DTJ:
+ join &= TRUE;
+ break;
+ case OP_EQ:
+ if (join && k > 0 // So specific join operators come first
+ && filp->GetArgType(0) == TYPE_COLBLK
+ && filp->GetArgType(1) == TYPE_COLBLK) {
+ cp1 = (PCOL)filp->Arg(0);
+ cp2 = (PCOL)filp->Arg(1);
+ tp1 = (PTDBASE)cp1->GetTo_Tdb();
+ tp2 = (PTDBASE)cp2->GetTo_Tdb();
+
+ if (tp1->GetTdb_No() != tp2->GetTdb_No()) {
+ if (key)
+ join &= (cp1->GetKey() == k || cp2->GetKey() == k);
+ else
+ join &= (tp1->GetColIndex(cp1) || tp2->GetColIndex(cp2));
+
+ } else
+ join = FALSE;
+
+ } else
+ join = FALSE;
+
+ break;
+ default:
+ join = FALSE;
+ } // endswitch filp->Opc
+
+ if (key) {
+ key = FALSE;
+ goto again;
+ } // endif key
+
+ if (filjoin) {
+ lfp->Next = filstart;
+ filstart = filjoin;
+ } // endif filjoin
+
+ // Removing last separator is perhaps unuseful, but it was so
+ return filstart->RemoveLastSep();
+ } // end of SortJoin
+
+/***********************************************************************/
+/* Check that this filter is a good join filter. */
+/* If so the opj block will be set accordingly. */
+/* opj points to the join block, fprec to the filter block to which */
+/* the rest of the chain must be linked in case of success. */
+/* teq, tek and tk2 indicates the severity of the tests: */
+/* tk2 == TRUE means both columns must be primary keys. */
+/* tc2 == TRUE means both args must be columns (not expression). */
+/* tek == TRUE means at least one column must be a primary key. */
+/* teq == TRUE means the filter operator must be OP_EQ. */
+/* tix == TRUE means at least one column must be a simple index key. */
+/* thx == TRUE means at least one column must be a leading index key. */
+/***********************************************************************/
+bool FILTER::FindJoinFilter(POPJOIN opj, PFIL fprec, bool teq, bool tek,
+ bool tk2, bool tc2, bool tix, bool thx)
+ {
+ if (trace)
+ htrc("FindJoinFilter: opj=%p fprec=%p tests=(%d,%d,%d,%d)\n",
+ opj, fprec, teq, tek, tk2, tc2);
+
+ // Firstly check that this filter is an independent filter
+ // meaning that it is the only one in its own group.
+ if (Next && Next->Opc != OP_SEP)
+ return (Opc < 0);
+
+ // Keep only equi-joins and specific joins (Outer and Distinct)
+ // Normally specific join operators comme first because they have
+ // been placed first by SortJoin.
+ if (teq && Opc > OP_EQ)
+ return FALSE;
+
+ // We have a candidate for join filter, now check that it
+ // fulfil the requirement about its operands, to point to
+ // columns of respectively the two TDB's of that join.
+ int col1 = 0, col2 = 0;
+ bool key = tk2;
+ bool idx = FALSE, ihx = FALSE;
+ PIXDEF pdx;
+
+ for (int i = 0; i < 2; i++)
+ if (GetArgType(i) == TYPE_COLBLK) {
+ PCOL colp = (PCOL)Arg(i);
+
+ if (tk2)
+ key &= (colp->IsKey());
+ else
+ key |= (colp->IsKey());
+
+ pdx = ((PTDBASE)colp->GetTo_Tdb())->GetColIndex(colp);
+ idx |= (pdx && pdx->GetNparts() == 1);
+ ihx |= (pdx != NULL);
+
+ if (colp->VerifyColumn(opj->GetTbx1()))
+ col1 = i + 1;
+ else if (colp->VerifyColumn(opj->GetTbx2()))
+ col2 = i + 1;
+
+ } else if (!tc2 && GetArgType(i) != TYPE_CONST) {
+ PXOB xp = Arg(i);
+
+ if (xp->VerifyColumn(opj->GetTbx1()))
+ col1 = i + 1;
+ else if (xp->VerifyColumn(opj->GetTbx2()))
+ col2 = i + 1;
+
+ } else
+ return (Opc < 0);
+
+ if (col1 == 0 || col2 == 0)
+ return (Opc < 0);
+
+ if (((tek && !key) || (tix && !idx) || (thx && !ihx)) && Opc != OP_DTJ)
+ return FALSE;
+
+ // This is the join filter, set the join block.
+ if (col1 == 1) {
+ opj->SetCol1(Arg(0));
+ opj->SetCol2(Arg(1));
+ } else {
+ opj->SetCol1(Arg(1));
+ opj->SetCol2(Arg(0));
+
+ switch (Opc) {
+// case OP_GT: Opc = OP_LT; break;
+// case OP_LT: Opc = OP_GT; break;
+// case OP_GE: Opc = OP_LE; break;
+// case OP_LE: Opc = OP_GE; break;
+ case OP_LOJ:
+ case OP_ROJ:
+ case OP_DTJ:
+ // For expended join operators, the filter must indicate
+ // the way the join should be done, and not the order of
+ // appearance of tables in the table list (which is kept
+ // because tables are sorted in AddTdb). Therefore the
+ // join is inversed, not the filter.
+ opj->InverseJoin();
+ default: break;
+ } // endswitch Opc
+
+ } // endif col1
+
+ if (Opc < 0) {
+ // For join operators, special processing is needed
+ int knum = 0;
+ PFIL fp;
+
+ switch (Opc) {
+ case OP_LOJ:
+ opj->SetJtype(JT_LEFT);
+ knum = opj->GetCol2()->GetKey();
+ break;
+ case OP_ROJ:
+ opj->SetJtype(JT_RIGHT);
+ knum = opj->GetCol1()->GetKey();
+ break;
+ case OP_DTJ:
+ for (knum = 1, fp = this->Next; fp; fp = fp->Next)
+ if (fp->Opc == OP_DTJ)
+ knum++;
+ else if (fp->Opc != OP_SEP)
+ break;
+
+ opj->SetJtype(JT_DISTINCT);
+ opj->GetCol2()->SetKey(knum);
+ break;
+ default:
+ break;
+ } // endswitch Opc
+
+ if (knum > 1) {
+ // Lets take care of a multiple key join
+ // We do a minimum of checking here as it will done later
+ int k = 1;
+ OPVAL op;
+ BYTE tmp[sizeof(Test[0])];
+
+ for (fp = this->Next; k < knum && fp; fp = fp->Next) {
+ switch (op = fp->Opc) {
+ case OP_SEP:
+ continue;
+ case OP_LOJ:
+ if (Opc == OP_ROJ) {
+ op = Opc;
+ memcpy(tmp, &fp->Test[0], sizeof(Test[0]));
+ fp->Test[0] = fp->Test[1];
+ memcpy(&fp->Test[1], tmp, sizeof(Test[0]));
+ } // endif Opc
+
+ k++;
+ break;
+ case OP_ROJ:
+ if (Opc == OP_LOJ) {
+ op = Opc;
+ memcpy(tmp, &fp->Test[0], sizeof(Test[0]));
+ fp->Test[0] = fp->Test[1];
+ memcpy(&fp->Test[1], tmp, sizeof(Test[0]));
+ } // endif Opc
+
+ k++;
+ break;
+ case OP_DTJ:
+ if (op == Opc && fp->GetArgType(1) == TYPE_COLBLK)
+ ((PCOL)fp->Arg(1))->SetKey(knum);
+
+ k++;
+ break;
+ default:
+ break;
+ } // endswitch op
+
+ if (op != Opc)
+ return TRUE;
+
+ fp->Opc = OP_EQ;
+ } // endfor fp
+
+ } // endif k
+
+ Opc = OP_EQ;
+ } // endif Opc
+
+ // Set the join filter operator
+ opj->SetOpc(Opc);
+
+ // Now mark the columns involved in the join filter because
+ // this information will be used by the linearize program.
+ // Note: this should be replaced in the future by something
+ // enabling to mark tables as Parent or Child.
+ opj->GetCol1()->MarkCol(U_J_EXT);
+ opj->GetCol2()->MarkCol(U_J_EXT);
+
+ // Remove the filter from the filter chain. If the filter is
+ // not last in the chain, also remove the SEP filter after it.
+ if (Next) // Next->Opc == OP_SEP
+ Next = Next->Next;
+
+ if (!fprec)
+ opj->SetFilter(Next);
+ else
+ fprec->Next = Next;
+
+ return FALSE;
+ } // end of FindJoinFilter
+
+/***********************************************************************/
+/* CheckHaving: check and process a filter of an HAVING clause. */
+/* Check references to Columns and Functions in the filter. */
+/* All these references can correspond to items existing in the */
+/* SELECT list, else if it is a function, allocate a SELECT block */
+/* to be added to the To_Sel list (non projected blocks). */
+/***********************************************************************/
+bool FILTER::CheckHaving(PGLOBAL g, PSQL sqlp)
+ {
+ int agg = AGG_ANY;
+ PXOB xp;
+
+//sqlp->SetOk(TRUE); // Ok to look into outer queries for filters
+
+ switch (Opc) {
+ case OP_SEP:
+ case OP_AND:
+ case OP_OR:
+ case OP_NOT:
+ return FALSE;
+ default:
+ if (CheckColumn(g, sqlp, xp, agg) < -1)
+ return TRUE; // Unrecovable error
+
+ break;
+ } // endswitch Opc
+
+ sqlp->SetOk(TRUE); // Ok to look into outer queries for filters
+
+ for (int i = 0; i < 2; i++)
+ if (!(xp = Arg(i)->SetSelect(g, sqlp, TRUE)))
+ return TRUE;
+ else if (xp != Arg(i)) {
+ Arg(i) = xp;
+ Val(i) = Arg(i)->GetValue();
+ } // endif
+
+ sqlp->SetOk(FALSE);
+ return FALSE;
+ } // end of CheckHaving
+
+/***********************************************************************/
+/* Used while building a table index. This function split the filter */
+/* attached to the tdbp table into the local and not local part. */
+/* The local filter is used to restrict the size of the index and the */
+/* not local part remains to be executed later. This has been added */
+/* recently and not only to improve the performance but chiefly to */
+/* avoid loosing rows when processing distinct joins. */
+/* Returns: */
+/* 0: the whole filter is local (both arguments are) */
+/* 1: the whole filter is not local */
+/* 2: the filter was split in local (attached to fp[0]) and */
+/* not local (attached to fp[1]). */
+/***********************************************************************/
+int FILTER::SplitFilter(PFIL *fp)
+ {
+ int i, rc[2];
+
+ if (Opc == OP_AND) {
+ for (i = 0; i < 2; i++)
+ rc[i] = ((PFIL)Arg(i))->SplitFilter(fp);
+
+ // Filter first argument should never be split because of the
+ // algorithm used to de-linearize the filter.
+ assert(rc[0] != 2);
+
+ if (rc[0] != rc[1]) {
+ // Splitting to be done
+ if (rc[1] == 2) {
+ // 2nd argument already split, add 1st to the proper filter
+ assert(fp[*rc]);
+ Arg(1) = fp[*rc];
+ Val(1) = fp[*rc]->GetValue();
+ fp[*rc] = this;
+ } else for (i = 0; i < 2; i++) {
+ // Split the filter arguments
+ assert(!fp[rc[i]]);
+ fp[rc[i]] = (PFIL)Arg(i);
+ } // endfor i
+
+ *rc = 2;
+ } // endif rc
+
+ } else
+ *rc = (CheckLocal(NULL)) ? 0 : 1;
+
+ return *rc;
+ } // end of SplitFilter
+
+/***********************************************************************/
+/* This function is called when making a Kindex after the filter was */
+/* split in local and nolocal part in the case of many to many joins. */
+/* Indeed the whole filter must be reconstructed to take care of next */
+/* same values when doing the explosive join. In addition, the link */
+/* must be done respecting the way filters are de-linearized, no AND */
+/* filter in the first argument of an AND filter, because this is */
+/* expected to be true if SplitFilter is used again on this filter. */
+/***********************************************************************/
+PFIL FILTER::LinkFilter(PGLOBAL g, PFIL fp2)
+ {
+ PFIL fp1, filp, filand = NULL;
+
+ assert(fp2); // Test must be made by caller
+
+ // Find where the new AND filter must be attached
+ for (fp1 = this; fp1->Opc == OP_AND; fp1 = (PFIL)fp1->Arg(1))
+ filand = fp1;
+
+ filp = new(g) FILTER(g, OP_AND);
+ filp->Arg(0) = fp1;
+ filp->Val(0) = fp1->GetValue();
+ filp->Test[0].B_T = TYPE_INT;
+ filp->Test[0].Conv = FALSE;
+ filp->Arg(1) = fp2;
+ filp->Val(1) = fp2->GetValue();
+ filp->Test[1].B_T = TYPE_INT;
+ filp->Test[1].Conv = FALSE;
+ filp->Value = AllocateValue(g, TYPE_INT);
+
+ if (filand) {
+ // filp must be inserted here
+ filand->Arg(1) = filp;
+ filand->Val(1) = filp->GetValue();
+ filp = this;
+ } // endif filand
+
+ return filp;
+ } // end of LinkFilter
+
+/***********************************************************************/
+/* Checks whether filter contains reference to a previous table that */
+/* is not logically joined to the currently openned table, or whether */
+/* it is a Sub-Select filter. In any case, local is set to FALSE. */
+/* Note: This function is now applied to de-linearized filters. */
+/***********************************************************************/
+bool FILTER::CheckLocal(PTDB tdbp)
+ {
+ bool local = TRUE;
+
+ if (trace) {
+ if (tdbp)
+ htrc("CheckLocal: filp=%p R%d\n", this, tdbp->GetTdb_No());
+ else
+ htrc("CheckLocal: filp=%p\n", this);
+ } // endif trace
+
+ for (int i = 0; local && i < 2; i++)
+ local = Arg(i)->CheckLocal(tdbp);
+
+ if (trace)
+ htrc("FCL: returning %d\n", local);
+
+ return (local);
+ } // end of CheckLocal
+
+/***********************************************************************/
+/* This routine is used to split the filter attached to the tdbp */
+/* table into the local and not local part where "local" means that */
+/* it applies "locally" to the FILEID special column with crit = 2 */
+/* and to the SERVID and/or TABID special columns with crit = 3. */
+/* Returns: */
+/* 0: the whole filter is local (both arguments are) */
+/* 1: the whole filter is not local */
+/* 2: the filter was split in local (attached to fp[0]) and */
+/* not local (attached to fp[1]). */
+/* Note: "Locally" means that the "local" filter can be evaluated */
+/* before opening the table. This implies that the special column be */
+/* compared only with constants and that this filter not to be or'ed */
+/* with a non "local" filter. */
+/***********************************************************************/
+int FILTER::SplitFilter(PFIL *fp, PTDB tp, int crit)
+ {
+ int i, rc[2];
+
+ if (Opc == OP_AND) {
+ for (i = 0; i < 2; i++)
+ rc[i] = ((PFIL)Arg(i))->SplitFilter(fp, tp, crit);
+
+ // Filter first argument should never be split because of the
+ // algorithm used to de-linearize the filter.
+ assert(rc[0] != 2);
+
+ if (rc[0] != rc[1]) {
+ // Splitting to be done
+ if (rc[1] == 2) {
+ // 2nd argument already split, add 1st to the proper filter
+ assert(fp[*rc]);
+ Arg(1) = fp[*rc];
+ Val(1) = fp[*rc]->GetValue();
+ fp[*rc] = this;
+ } else for (i = 0; i < 2; i++) {
+ // Split the filter arguments
+ assert(!fp[rc[i]]);
+ fp[rc[i]] = (PFIL)Arg(i);
+ } // endfor i
+
+ *rc = 2;
+ } // endif rc
+
+ } else
+ *rc = (CheckSpcCol(tp, crit) == 1) ? 0 : 1;
+
+ return *rc;
+ } // end of SplitFilter
+
+/***********************************************************************/
+/* Checks whether filter contains only references to FILEID, SERVID, */
+/* or TABID with constants or pseudo constants. */
+/***********************************************************************/
+int FILTER::CheckSpcCol(PTDB tdbp, int n)
+ {
+ int n1 = Arg(0)->CheckSpcCol(tdbp, n);
+ int n2 = Arg(1)->CheckSpcCol(tdbp, n);
+
+ return max(n1, n2);
+ } // end of CheckSpcCol
+#endif // 0
+
+/***********************************************************************/
+/* Reset the filter arguments to non evaluated yet. */
+/***********************************************************************/
+void FILTER::Reset(void)
+ {
+ for (int i = 0; i < 2; i++)
+ Arg(i)->Reset();
+
+ } // end of Reset
+
+/***********************************************************************/
+/* Init: called when reinitializing a query (Correlated subqueries) */
+/***********************************************************************/
+bool FILTER::Init(PGLOBAL g)
+ {
+ for (int i = 0; i < 2; i++)
+ Arg(i)->Init(g);
+
+ return FALSE;
+ } // end of Init
+
+/***********************************************************************/
+/* Convert: does all filter setting and conversions. */
+/* (having = TRUE for Having Clauses, FALSE for Where Clauses) */
+/* Note: hierarchy of types is implied by the ConvertType */
+/* function, currently FLOAT, int, STRING and TOKEN. */
+/* Returns FALSE if successful or TRUE in case of error. */
+/* Note on result type for filters: */
+/* Currently the result type is of TYPE_INT (should be TYPE_BOOL). */
+/* This avoids to introduce a new type and perhaps will permit */
+/* conversions. However the boolean operators will result in a */
+/* boolean int result, meaning that result shall be only 0 or 1 . */
+/***********************************************************************/
+bool FILTER::Convert(PGLOBAL g, bool having)
+ {
+ int i, comtype = TYPE_ERROR;
+
+ if (trace)
+ htrc("converting(?) %s %p opc=%d\n",
+ (having) ? "having" : "filter", this, Opc);
+
+ for (i = 0; i < 2; i++) {
+ switch (GetArgType(i)) {
+ case TYPE_COLBLK:
+ if (((PCOL)Arg(i))->InitValue(g))
+ return TRUE;
+
+ break;
+ case TYPE_ARRAY:
+ if ((Opc != OP_IN && !Opm) || i == 0) {
+ strcpy(g->Message, MSG(BAD_ARRAY_OPER));
+ return TRUE;
+ } // endif
+
+ if (((PARRAY)Arg(i))->Sort(g)) // Sort the array
+ return TRUE; // Error
+
+ break;
+ case TYPE_VOID:
+ if (i == 1) {
+ Val(0) = Arg(0)->GetValue();
+ goto TEST; // Filter has only one argument
+ } // endif i
+
+ strcpy(g->Message, MSG(VOID_FIRST_ARG));
+ return TRUE;
+ } // endswitch
+
+ if (trace)
+ htrc("Filter(%d): Arg type=%d\n", i, GetArgType(i));
+
+ // Set default values
+ Test[i].B_T = Arg(i)->GetResultType();
+ Test[i].Conv = FALSE;
+
+ // Special case of the LIKE operator.
+ if (Opc == OP_LIKE) {
+ if (!IsTypeChar((int)Test[i].B_T)) {
+ sprintf(g->Message, MSG(BAD_TYPE_LIKE), i, Test[i].B_T);
+ return TRUE;
+ } // endif
+
+ comtype = TYPE_STRING;
+ } else {
+ // Set the common type for both (eventually converted) arguments
+ int argtyp = Test[i].B_T;
+
+ if (GetArgType(i) == TYPE_CONST && argtyp == TYPE_INT) {
+ // If possible, downcast the type to smaller types to avoid
+ // convertion as much as possible.
+ int n = Arg(i)->GetValue()->GetIntValue();
+
+ if (n >= INT_MIN8 && n <= INT_MAX8)
+ argtyp = TYPE_TINY;
+ else if (n >= INT_MIN16 && n <= INT_MAX16)
+ argtyp = TYPE_SHORT;
+
+ } else if (GetArgType(i) == TYPE_ARRAY) {
+ // If possible, downcast int arrays target type to TYPE_SHORT
+ // to take care of filters written like shortcol in (34,35,36).
+ if (((PARRAY)Arg(i))->CanBeShort())
+ argtyp = TYPE_SHORT;
+
+ } // endif TYPE_CONST
+
+ comtype = ConvertType(comtype, argtyp, CNV_ANY);
+ } // endif Opc
+
+ if (comtype == TYPE_ERROR) {
+ strcpy(g->Message, MSG(ILL_FILTER_CONV));
+ return TRUE;
+ } // endif
+
+ if (trace)
+ htrc(" comtype=%d, B_T(%d)=%d Val(%d)=%p\n",
+ comtype, i, Test[i].B_T, i, Val(i));
+
+ } // endfor i
+
+ // Set or allocate the filter argument values and buffers
+ for (i = 0; i < 2; i++) {
+ if (trace)
+ htrc(" conv type %d ? i=%d B_T=%d comtype=%d\n",
+ GetArgType(i), i, Test[i].B_T, comtype);
+
+ if (Test[i].B_T == comtype) {
+ // No conversion, set Value to argument Value
+ Val(i) = Arg(i)->GetValue();
+#if defined(_DEBUG)
+ assert (Val(i) && Val(i)->GetType() == Test[i].B_T);
+#endif
+ } else {
+ // Conversion between filter arguments to be done.
+ // Note that the argument must be converted, not only the
+ // buffer and buffer type, so GetArgType() returns the new type.
+ switch (GetArgType(i)) {
+ case TYPE_CONST:
+ if (comtype == TYPE_DATE && Test[i].B_T == TYPE_STRING) {
+ // Convert according to the format of the other argument
+ Val(i) = AllocateValue(g, comtype, Arg(i)->GetLength());
+
+ if (((DTVAL*)Val(i))->SetFormat(g, Val(1-i)))
+ return TRUE;
+
+ Val(i)->SetValue_psz(Arg(i)->GetValue()->GetCharValue());
+ } else {
+ ((PCONST)Arg(i))->Convert(g, comtype);
+ Val(i) = Arg(i)->GetValue();
+ } // endif comtype
+
+ break;
+ case TYPE_ARRAY:
+ // Conversion PSZ or int array to int or double FLOAT.
+ if (((PARRAY)Arg(i))->Convert(g, comtype, Val(i-1)) == TYPE_ERROR)
+ return TRUE;
+
+ break;
+ case TYPE_FILTER:
+ strcpy(g->Message, MSG(UNMATCH_FIL_ARG));
+ return TRUE;
+ default:
+ // Conversion from Column, Select/Func, Expr, Scalfnc...
+ // The argument requires conversion during Eval
+ // A separate Value block must be allocated.
+ // Note: the test on comtype is to prevent unnecessary
+ // domain initialization and get the correct length in
+ // case of Token -> numeric conversion.
+ Val(i) = AllocateValue(g, comtype, (comtype == TYPE_STRING)
+ ? Arg(i)->GetLengthEx() : Arg(i)->GetLength());
+
+ if (comtype == TYPE_DATE && Test[i].B_T == TYPE_STRING)
+ // Convert according to the format of the other argument
+ if (((DTVAL*)Val(i))->SetFormat(g, Val(1 - i)))
+ return TRUE;
+
+ Test[i].Conv = TRUE;
+ break;
+ } // endswitch GetType
+
+ Test[i].B_T = comtype;
+ } // endif comtype
+
+ } // endfor i
+
+ // Last check to be sure all is correct.
+ if (Test[0].B_T != Test[1].B_T) {
+ sprintf(g->Message, MSG(BAD_FILTER_CONV), Test[0].B_T, Test[1].B_T);
+ return TRUE;
+//} else if (Test[0].B_T == TYPE_LIST &&
+// ((LSTVAL*)Val(0))->GetN() != ((LSTVAL*)Val(1))->GetN()) {
+// sprintf(g->Message, MSG(ROW_ARGNB_ERR),
+// ((LSTVAL*)Val(0))->GetN(), ((LSTVAL*)Val(1))->GetN());
+// return TRUE;
+ } // endif's B_T
+
+
+ TEST: // Test for possible Eval optimization
+
+ if (trace)
+ htrc("Filp %p op=%d argtypes=(%d,%d)\n",
+ this, Opc, GetArgType(0), GetArgType(1));
+
+ // Check whether we have a "simple" filter and in that case
+ // change its class so an optimized Eval function will be used
+ if (!Test[0].Conv && !Test[1].Conv) {
+ if (Opm) switch (Opc) {
+ case OP_EQ:
+ case OP_NE:
+ case OP_GT:
+ case OP_GE:
+ case OP_LT:
+ case OP_LE:
+ if (GetArgType(1) != TYPE_ARRAY)
+ break; // On subquery, do standard processing
+
+ // Change the FILTER class to FILTERIN
+ new(this) FILTERIN;
+ break;
+ default:
+ break;
+ } // endswitch Opc
+
+ else switch (Opc) {
+#if 0
+ case OP_EQ: new(this) FILTEREQ; break;
+ case OP_NE: new(this) FILTERNE; break;
+ case OP_GT: new(this) FILTERGT; break;
+ case OP_GE: new(this) FILTERGE; break;
+ case OP_LT: new(this) FILTERLT; break;
+ case OP_LE: new(this) FILTERLE; break;
+#endif // 0
+ case OP_EQ:
+ case OP_NE:
+ case OP_GT:
+ case OP_GE:
+ case OP_LT:
+ case OP_LE: new(this) FILTERCMP(g); break;
+ case OP_AND: new(this) FILTERAND; break;
+ case OP_OR: new(this) FILTEROR; break;
+ case OP_NOT: new(this) FILTERNOT; break;
+ case OP_EXIST:
+ if (GetArgType(1) == TYPE_VOID) {
+ // For EXISTS it is the first argument that should be null
+ Arg(1) = Arg(0);
+ Arg(0) = pXVOID;
+ } // endif void
+
+ // pass thru
+ case OP_IN:
+ // For IN operator do optimize if operand is an array
+ if (GetArgType(1) != TYPE_ARRAY)
+ break; // IN on subquery, do standard processing
+
+ // Change the FILTER class to FILTERIN
+ new(this) FILTERIN;
+ break;
+ default:
+ break;
+ } // endswitch Opc
+
+ } // endif Conv
+
+ // The result value (should be TYPE_BOOL ???)
+ Value = AllocateValue(g, TYPE_INT);
+ return FALSE;
+ } // end of Convert
+
+/***********************************************************************/
+/* Eval: Compute filter result value. */
+/* New algorithm: evaluation is now done from the root for each group */
+/* so Eval is now a recursive process for FILTER operands. */
+/***********************************************************************/
+bool FILTER::Eval(PGLOBAL g)
+ {
+ int i; // n = 0;
+//PSUBQ subp = NULL;
+ PARRAY ap = NULL;
+ PDBUSER dup = PlgGetUser(g);
+
+ if (Opc <= OP_XX)
+ for (i = 0; i < 2; i++)
+ // Evaluate the object and eventually convert it.
+ if (Arg(i)->Eval(g))
+ return TRUE;
+ else if (Test[i].Conv)
+ Val(i)->SetValue_pval(Arg(i)->GetValue());
+
+ if (trace)
+ htrc(" Filter: op=%d type=%d %d B_T=%d %d val=%p %p\n",
+ Opc, GetArgType(0), GetArgType(1), Test[0].B_T, Test[1].B_T,
+ Val(0), Val(1));
+
+ // Main switch on filtering according to operator type.
+ switch (Opc) {
+ case OP_EQ:
+ case OP_NE:
+ case OP_GT:
+ case OP_GE:
+ case OP_LT:
+ case OP_LE:
+ if (!Opm) {
+ // Comparison boolean operators.
+#if defined(_DEBUG)
+ if (Val(0)->GetType() != Val(1)->GetType())
+ goto FilterError;
+#endif
+ // Compare the two arguments
+ // New algorithm to take care of TYPE_LIST
+ Bt = OpBmp(g, Opc);
+ Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
+ break;
+ } // endif Opm
+
+ // For modified operators, pass thru
+ case OP_IN:
+ case OP_EXIST:
+ // For IN operations, special processing is done here
+ switch (GetArgType(1)) {
+ case TYPE_ARRAY:
+ ap = (PARRAY)Arg(1);
+ break;
+ default:
+ strcpy(g->Message, MSG(IN_WITHOUT_SUB));
+ goto FilterError;
+ } // endswitch Type
+
+ if (trace) {
+ htrc(" IN filtering: ap=%p\n", ap);
+
+ if (ap)
+ htrc(" Array: type=%d size=%d other_type=%d\n",
+ ap->GetType(), ap->GetSize(), Test[0].B_T);
+
+ } // endif trace
+
+ /*****************************************************************/
+ /* Implementation note: The Find function is now able to do a */
+ /* conversion but limited to SHORT, int, and FLOAT arrays. */
+ /*****************************************************************/
+// Value->SetValue_bool(ap->Find(g, Val(0)));
+
+ if (ap)
+ Value->SetValue_bool(ap->FilTest(g, Val(0), Opc, Opm));
+
+ break;
+
+ case OP_LIKE:
+#if defined(_DEBUG)
+ if (!IsTypeChar((int)Test[0].B_T) || !IsTypeChar((int)Test[1].B_T))
+ goto FilterError;
+#endif
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue_bool(PlugEvalLike(g, Val(0)->GetCharValue(),
+ Val(1)->GetCharValue(),
+ Val(0)->IsCi()));
+ break;
+
+ case OP_AND:
+#if defined(_DEBUG)
+ if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
+ goto FilterError;
+#endif
+
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(0)->GetIntValue());
+
+ if (!Value->GetIntValue())
+ return FALSE; // No need to evaluate 2nd argument
+
+ if (Arg(1)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(1)->GetIntValue());
+ break;
+
+ case OP_OR:
+#if defined(_DEBUG)
+ if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
+ goto FilterError;
+#endif
+
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(0)->GetIntValue());
+
+ if (Value->GetIntValue())
+ return FALSE; // No need to evaluate 2nd argument
+
+ if (Arg(1)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(1)->GetIntValue());
+ break;
+
+ case OP_NOT:
+#if defined(_DEBUG)
+ if (Test[0].B_T != TYPE_INT) // Should be type bool ???
+ goto FilterError;
+#endif
+
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue_bool(!Val(0)->GetIntValue());
+ break;
+
+ case OP_SEP: // No more used while evaluating
+ default:
+ goto FilterError;
+ } // endswitch Opc
+
+ if (trace)
+ htrc("Eval: filter %p Opc=%d result=%d\n",
+ this, Opc, Value->GetIntValue());
+
+ return FALSE;
+
+ FilterError:
+ sprintf(g->Message, MSG(BAD_FILTER),
+ Opc, Test[0].B_T, Test[1].B_T, GetArgType(0), GetArgType(1));
+ return TRUE;
+ } // end of Eval
+
+#if 0
+/***********************************************************************/
+/* Called by PlugCopyDB to make a copy of a (linearized) filter chain.*/
+/***********************************************************************/
+PFIL FILTER::Copy(PTABS t)
+ {
+ int i;
+ PFIL fil1, fil2, newfilchain = NULL, fprec = NULL;
+
+ for (fil1 = this; fil1; fil1 = fil1->Next) {
+ fil2 = new(t->G) FILTER(fil1);
+
+ if (!fprec)
+ newfilchain = fil2;
+ else
+ fprec->Next = fil2;
+
+ NewPointer(t, fil1, fil2);
+
+ for (i = 0; i < 2; i++)
+ if (fil1->GetArgType(i) == TYPE_COLBLK ||
+ fil1->GetArgType(i) == TYPE_FILTER)
+ AddPointer(t, &fil2->Arg(i));
+
+ fprec = fil2;
+ } /* endfor fil1 */
+
+ return newfilchain;
+ } // end of Copy
+#endif // 0
+
+/*********************************************************************/
+/* Make file output of FILTER contents. */
+/*********************************************************************/
+void FILTER::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ char m[64];
+
+ memset(m, ' ', n); // Make margin string
+ m[n] = '\0';
+
+ bool lin = (Next != NULL); // lin == TRUE if linearized
+
+ for (PFIL fp = this; fp; fp = fp->Next) {
+ fprintf(f, "%sFILTER: at %p opc=%d lin=%d result=%d\n",
+ m, fp, fp->Opc, lin,
+ (Value) ? Value->GetIntValue() : 0);
+
+ for (int i = 0; i < 2; i++) {
+ fprintf(f, "%s Arg(%d) type=%d value=%p B_T=%d val=%p\n",
+ m, i, fp->GetArgType(i), fp->Arg(i),
+ fp->Test[i].B_T, fp->Val(i));
+
+ if (lin && fp->GetArgType(i) == TYPE_FILTER)
+ fprintf(f, "%s Filter at %p\n", m, fp->Arg(i));
+ else
+ fp->Arg(i)->Print(g, f, n + 2);
+
+ } // endfor i
+
+ } // endfor fp
+
+ } // end of Print
+
+/***********************************************************************/
+/* Make string output of TABLE contents (z should be checked). */
+/***********************************************************************/
+void FILTER::Print(PGLOBAL g, char *ps, uint z)
+ {
+ #define FLEN 100
+
+ typedef struct _bc {
+ struct _bc *Next;
+ char Cold[FLEN+1];
+ } BC, *PBC;
+
+ char *p;
+ int n;
+ PFIL fp;
+ PBC bxp, bcp = NULL;
+
+ *ps = '\0';
+
+ for (fp = this; fp && z > 0; fp = fp->Next) {
+ if (fp->Opc < OP_CNC || fp->Opc == OP_IN || fp->Opc == OP_NULL
+ || fp->Opc == OP_LIKE || fp->Opc == OP_EXIST) {
+ if (!(bxp = new BC)) {
+ strncat(ps, "Filter(s)", z);
+ return;
+ } /* endif */
+
+ bxp->Next = bcp;
+ bcp = bxp;
+ p = bcp->Cold;
+ n = FLEN;
+ fp->Arg(0)->Print(g, p, n);
+ n = FLEN - strlen(p);
+
+ switch (fp->Opc) {
+ case OP_EQ:
+ strncat(bcp->Cold, "=", n);
+ break;
+ case OP_NE:
+ strncat(bcp->Cold, "!=", n);
+ break;
+ case OP_GT:
+ strncat(bcp->Cold, ">", n);
+ break;
+ case OP_GE:
+ strncat(bcp->Cold, ">=", n);
+ break;
+ case OP_LT:
+ strncat(bcp->Cold, "<", n);
+ break;
+ case OP_LE:
+ strncat(bcp->Cold, "<=", n);
+ break;
+ case OP_IN:
+ strncat(bcp->Cold, " in ", n);
+ break;
+ case OP_NULL:
+ strncat(bcp->Cold, " is null", n);
+ break;
+ case OP_LIKE:
+ strncat(bcp->Cold, " like ", n);
+ break;
+ case OP_EXIST:
+ strncat(bcp->Cold, " exists ", n);
+ break;
+ case OP_AND:
+ strncat(bcp->Cold, " and ", n);
+ break;
+ case OP_OR:
+ strncat(bcp->Cold, " or ", n);
+ break;
+ default:
+ strncat(bcp->Cold, "?", n);
+ } // endswitch Opc
+
+ n = FLEN - strlen(p);
+ p += strlen(p);
+ fp->Arg(1)->Print(g, p, n);
+ } else
+ if (!bcp) {
+ strncat(ps, "???", z);
+ z -= 3;
+ } else
+ switch (fp->Opc) {
+ case OP_SEP: // Filter list separator
+ strncat(ps, bcp->Cold, z);
+ z -= strlen(bcp->Cold);
+ strncat(ps, ";", z--);
+ bxp = bcp->Next;
+ delete bcp;
+ bcp = bxp;
+ break;
+ case OP_NOT: // Filter NOT operator
+ for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-3); n >= 0; n--)
+ bcp->Cold[n+2] = bcp->Cold[n];
+ bcp->Cold[0] = '^';
+ bcp->Cold[1] = '(';
+ strcat(bcp->Cold, ")");
+ break;
+ default:
+ for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-4); n >= 0; n--)
+ bcp->Cold[n+3] = bcp->Cold[n];
+ bcp->Cold[0] = ')';
+ switch (fp->Opc) {
+ case OP_AND: bcp->Cold[1] = '&'; break;
+ case OP_OR: bcp->Cold[1] = '|'; break;
+ default: bcp->Cold[1] = '?';
+ } // endswitch
+ bcp->Cold[2] = '(';
+ strcat(bcp->Cold, ")");
+ bxp = bcp->Next;
+ for (n = MY_MIN((int)strlen(bxp->Cold), FLEN-1); n >= 0; n--)
+ bxp->Cold[n+1] = bxp->Cold[n];
+ bxp->Cold[0] = '(';
+ strncat(bxp->Cold, bcp->Cold, FLEN-strlen(bxp->Cold));
+ delete bcp;
+ bcp = bxp;
+ } // endswitch
+
+ } // endfor fp
+
+ n = 0;
+
+ if (!bcp)
+ strncat(ps, "Null-Filter", z);
+ else do {
+ if (z > 0) {
+ if (n++ > 0) {
+ strncat(ps, "*?*", z);
+ z = MY_MAX(0, (int)z-3);
+ } // endif
+ strncat(ps, bcp->Cold, z);
+ z -= strlen(bcp->Cold);
+ } // endif
+
+ bxp = bcp->Next;
+ delete bcp;
+ bcp = bxp;
+ } while (bcp); // enddo
+
+ } // end of Print
+
+
+/* -------------------- Derived Classes Functions -------------------- */
+
+/***********************************************************************/
+/* FILTERCMP constructor. */
+/***********************************************************************/
+FILTERCMP::FILTERCMP(PGLOBAL g)
+ {
+ Bt = OpBmp(g, Opc);
+ } // end of FILTERCMP constructor
+
+/***********************************************************************/
+/* Eval: Compute result value for comparison operators. */
+/***********************************************************************/
+bool FILTERCMP::Eval(PGLOBAL g)
+ {
+ if (Arg(0)->Eval(g) || Arg(1)->Eval(g))
+ return TRUE;
+
+ Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
+ return FALSE;
+ } // end of Eval
+
+/***********************************************************************/
+/* Eval: Compute result value for AND filters. */
+/***********************************************************************/
+bool FILTERAND::Eval(PGLOBAL g)
+ {
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(0)->GetIntValue());
+
+ if (!Value->GetIntValue())
+ return FALSE; // No need to evaluate 2nd argument
+
+ if (Arg(1)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(1)->GetIntValue());
+ return FALSE;
+ } // end of Eval
+
+/***********************************************************************/
+/* Eval: Compute result value for OR filters. */
+/***********************************************************************/
+bool FILTEROR::Eval(PGLOBAL g)
+ {
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(0)->GetIntValue());
+
+ if (Value->GetIntValue())
+ return FALSE; // No need to evaluate 2nd argument
+
+ if (Arg(1)->Eval(g))
+ return TRUE;
+
+ Value->SetValue(Val(1)->GetIntValue());
+ return FALSE;
+ } // end of Eval
+
+/***********************************************************************/
+/* Eval: Compute result value for NOT filters. */
+/***********************************************************************/
+bool FILTERNOT::Eval(PGLOBAL g)
+ {
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue_bool(!Val(0)->GetIntValue());
+ return FALSE;
+ } // end of Eval
+
+/***********************************************************************/
+/* Eval: Compute result value for IN filters. */
+/***********************************************************************/
+bool FILTERIN::Eval(PGLOBAL g)
+ {
+ if (Arg(0)->Eval(g))
+ return TRUE;
+
+ Value->SetValue_bool(((PARRAY)Arg(1))->FilTest(g, Val(0), Opc, Opm));
+ return FALSE;
+ } // end of Eval
+
+/***********************************************************************/
+/* FILTERTRUE does nothing and returns TRUE. */
+/***********************************************************************/
+void FILTERTRUE::Reset(void)
+ {
+ } // end of Reset
+
+bool FILTERTRUE::Eval(PGLOBAL)
+ {
+ return FALSE;
+ } // end of Eval
+
+/* ------------------------- Friend Functions ------------------------ */
+
+#if 0
+/***********************************************************************/
+/* Prepare: prepare a filter for execution. This implies two things: */
+/* 1) de-linearize the filter to be able to evaluate it recursively. */
+/* This permit to conditionally evaluate only the first argument */
+/* of OP_OR and OP_AND filters without having to pass by an */
+/* intermediate Apply function (as this has a performance cost). */
+/* 2) do all the necessary conversion for all filter block arguments. */
+/***********************************************************************/
+PFIL PrepareFilter(PGLOBAL g, PFIL fp, bool having)
+ {
+ PFIL filp = NULL;
+
+ if (trace)
+ htrc("PrepareFilter: fp=%p having=%d\n", fp, having);
+//if (fp)
+// fp->Print(g, debug, 0);
+
+ while (fp) {
+ if (fp->Opc == OP_SEP)
+ // If separator is not last transform it into an AND filter
+ if (fp->Next) {
+ filp = PrepareFilter(g, fp->Next, having);
+ fp->Arg(1) = filp;
+ fp->Opc = OP_AND;
+ fp->Next = NULL; // This will end the loop
+ } else
+ break; // Remove eventual ending separator(s)
+
+// if (fp->Convert(g, having))
+// longjmp(g->jumper[g->jump_level], TYPE_FILTER);
+
+ filp = fp;
+ fp = fp->Next;
+ filp->Next = NULL;
+ } // endwhile
+
+ if (trace)
+ htrc(" returning filp=%p\n", filp);
+//if (filp)
+// filp->Print(g, debug, 0);
+
+ return filp;
+ } // end of PrepareFilter
+#endif // 0
+
+/***********************************************************************/
+/* ApplyFilter: Apply filtering for a table (where or having clause). */
+/* New algorithm: evaluate from the root a de-linearized filter so */
+/* AND/OR clauses can be optimized throughout the whole tree. */
+/***********************************************************************/
+DllExport bool ApplyFilter(PGLOBAL g, PFIL filp)
+ {
+ if (!filp)
+ return TRUE;
+
+ // Must be done for null tables
+ filp->Reset();
+
+//if (tdbp && tdbp->IsNull())
+// return TRUE;
+
+ if (filp->Eval(g))
+ longjmp(g->jumper[g->jump_level], TYPE_FILTER);
+
+ if (trace > 1)
+ htrc("PlugFilter filp=%p result=%d\n",
+ filp, filp->GetResult());
+
+ return filp->GetResult();
+ } // end of ApplyFilter
diff --git a/storage/connect/filter.h b/storage/connect/filter.h
new file mode 100644
index 00000000000..78e066d9ab7
--- /dev/null
+++ b/storage/connect/filter.h
@@ -0,0 +1,178 @@
+/*************** Filter H Declares Source Code File (.H) ***************/
+/* Name: FILTER.H Version 1.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2010-2012 */
+/* */
+/* This file contains the FILTER and derived classes declares. */
+/***********************************************************************/
+#ifndef __FILTER__
+#define __FILTER__
+
+/***********************************************************************/
+/* Include required application header files */
+/***********************************************************************/
+#include "xobject.h"
+
+/***********************************************************************/
+/* Utilities for WHERE condition building. */
+/***********************************************************************/
+PFIL MakeFilter(PGLOBAL g, PFIL filp, OPVAL vop, PFIL fp);
+PFIL MakeFilter(PGLOBAL g, PCOL *colp, POPER pop, PPARM pfirst, bool neg);
+
+/***********************************************************************/
+/* Definition of class FILTER with all its method functions. */
+/* Note: Most virtual implementation functions are not in use yet */
+/* but could be in future system evolution. */
+/***********************************************************************/
+class DllExport FILTER : public XOBJECT { /* Filter description block */
+//friend PFIL PrepareFilter(PGLOBAL, PFIL, bool);
+ friend DllExport bool ApplyFilter(PGLOBAL, PFIL);
+ public:
+ // Constructors
+ FILTER(PGLOBAL g, POPER pop, PPARM *tp = NULL);
+ FILTER(PGLOBAL g, OPVAL opc, PPARM *tp = NULL);
+ FILTER(PFIL fil1);
+
+ // Implementation
+ virtual int GetType(void) {return TYPE_FILTER;}
+ virtual int GetResultType(void) {return TYPE_INT;}
+ virtual int GetLength(void) {return 1;}
+ virtual int GetLengthEx(void) {assert(FALSE); return 0;}
+ virtual int GetScale() {return 0;};
+ PFIL GetNext(void) {return Next;}
+ OPVAL GetOpc(void) {return Opc;}
+ int GetOpm(void) {return Opm;}
+ int GetArgType(int i) {return Arg(i)->GetType();}
+ bool GetResult(void) {return Value->GetIntValue() != 0;}
+ PXOB &Arg(int i) {return Test[i].Arg;}
+ PVAL &Val(int i) {return Test[i].Value;}
+ bool &Conv(int i) {return Test[i].Conv;}
+ void SetNext(PFIL filp) {Next = filp;}
+
+ // Methods
+ virtual void Reset(void);
+ virtual bool Compare(PXOB) {return FALSE;} // Not used yet
+ 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 PXOB CheckSubQuery(PGLOBAL, PSQL);
+//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 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);
+// PFIL Copy(PTABS t);
+
+ protected:
+ FILTER(void) {} // Standard constructor not to be used
+ void Constr(PGLOBAL g, OPVAL opc, int opm, PPARM *tp);
+
+ // Members
+ PFIL Next; // Used for linearization
+ OPVAL Opc; // Comparison operator
+ int Opm; // Modificator
+ BYTE Bt; // Operator bitmap
+ struct {
+ int B_T; // Buffer type
+ PXOB Arg; // Points to argument
+ PVAL Value; // Points to argument value
+ bool Conv; // TRUE if argument must be converted
+ } Test[2];
+ }; // end of class FILTER
+
+/***********************************************************************/
+/* Derived class FILTERX: used to replace a filter by a derived class */
+/* using an Eval method optimizing the filtering evaluation. */
+/* 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 FILTERX : public FILTER {
+ public:
+ // Methods
+ virtual bool Eval(PGLOBAL) = 0; // just to prevent direct FILTERX use
+
+ // Fake operator new used to change a filter into a derived filter
+ void * operator new(size_t size, PFIL filp) {return filp;}
+#if defined(WIN32)
+ // Avoid warning C4291 by defining a matching dummy delete operator
+ void operator delete(void *, PFIL) {}
+#else
+ void operator delete(void *) {}
+#endif
+ }; // end of class FILTERX
+
+/***********************************************************************/
+/* Derived class FILTEREQ: OP_EQ, no conversion and Xobject args. */
+/***********************************************************************/
+class FILTERCMP : public FILTERX {
+ public:
+ // Constructor
+ FILTERCMP(PGLOBAL g);
+
+ // Methods
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTEREQ
+
+/***********************************************************************/
+/* Derived class FILTERAND: OP_AND, no conversion and Xobject args. */
+/***********************************************************************/
+class FILTERAND : public FILTERX {
+ public:
+ // Methods
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTERAND
+
+/***********************************************************************/
+/* Derived class FILTEROR: OP_OR, no conversion and Xobject args. */
+/***********************************************************************/
+class FILTEROR : public FILTERX {
+ public:
+ // Methods
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTEROR
+
+/***********************************************************************/
+/* Derived class FILTERNOT: OP_NOT, no conversion and Xobject args. */
+/***********************************************************************/
+class FILTERNOT : public FILTERX {
+ public:
+ // Methods
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTERNOT
+
+/***********************************************************************/
+/* Derived class FILTERIN: OP_IN, no conversion and Array 2nd arg. */
+/***********************************************************************/
+class FILTERIN : public FILTERX {
+ public:
+ // Methods
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTERIN
+
+/***********************************************************************/
+/* Derived class FILTERTRUE: Always returns TRUE. */
+/***********************************************************************/
+class FILTERTRUE : public FILTERX {
+ public:
+ // Constructor
+ FILTERTRUE(PVAL valp) {Value = valp; Value->SetValue_bool(TRUE);}
+
+ // Methods
+ virtual void Reset(void);
+ virtual bool Eval(PGLOBAL);
+ }; // end of class FILTERTRUE
+
+#endif // __FILTER__
diff --git a/storage/connect/global.h b/storage/connect/global.h
index c0746e37db7..d35cef2de6f 100644
--- a/storage/connect/global.h
+++ b/storage/connect/global.h
@@ -1,6 +1,6 @@
/***********************************************************************/
/* GLOBAL.H: Declaration file used by all CONNECT implementations. */
-/* (C) Copyright Olivier Bertrand 1993-2012 */
+/* (C) Copyright Olivier Bertrand 1993-2014 */
/***********************************************************************/
/***********************************************************************/
@@ -23,12 +23,12 @@
#define XML_SUPPORT 1
#endif
-#if defined(XMSG)
+#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)
+#elif defined(NEWMSG)
// Definition used to get messages from resource.
#include "msgid.h"
#define MSG(I) PlugGetMessage(NULL, MSG_##I)
@@ -85,6 +85,7 @@
#define TYPE_INT 7
#define TYPE_DECIM 9
#define TYPE_BIN 10
+#define TYPE_PCHAR 11
#if defined(OS32)
#define SYS_STAMP "OS32"
@@ -223,6 +224,7 @@ typedef struct _global { /* Global structure */
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];
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index d29e9cd4422..93327fa4530 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -28,7 +28,7 @@
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,
+ 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
@@ -108,7 +108,6 @@
#define MYSQL_SERVER 1
#define DONT_DEFINE_VOID
-//#include "sql_partition.h"
#include "sql_class.h"
#include "create_options.h"
#include "mysql_com.h"
@@ -116,6 +115,10 @@
#include "sql_parse.h"
#include "sql_base.h"
#include <sys/stat.h>
+#if defined(NEW_WAY)
+#include "sql_table.h"
+#endif // NEW_WAY
+#include "sql_partition.h"
#undef OFFSET
#define NOPARSE
@@ -167,55 +170,72 @@
#define SZWMIN 4194304 // Minimum work area size 4M
extern "C" {
- char version[]= "Version 1.02.0002 March 16, 2014";
+ char version[]= "Version 1.03.0003 August 22, 2014";
+ char compver[]= "Version 1.03.0003 " __DATE__ " " __TIME__;
+
+#if defined(WIN32)
+ char slash= '\\';
+#else // !WIN32
+ char slash= '/';
+#endif // !WIN32
#if defined(XMSG)
- char msglang[]; // Default message language
+ char msglang[]; // Default message language
#endif
int trace= 0; // The general trace value
int xconv= 0; // The type conversion option
int zconv= SZCONV; // The text conversion size
+ USETEMP Use_Temp= TMP_AUTO; // The temporary file use
} // extern "C"
#if defined(XMAP)
bool xmap= false;
#endif // XMAP
+ bool xinfo= false;
uint worksize= SZWORK;
ulong ha_connect::num= 0;
//int DTVAL::Shift= 0;
-/* CONNECT system variables */
+/* CONNECT system variables */
static int xtrace= 0;
static int conv_size= SZCONV;
static uint work_size= SZWORK;
static ulong type_conv= 0;
+static ulong use_tempfile= 1;
#if defined(XMAP)
static my_bool indx_map= 0;
#endif // XMAP
+static my_bool exact_info= 0;
/***********************************************************************/
/* Utility functions. */
/***********************************************************************/
PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
void PushWarning(PGLOBAL g, THD *thd, int level);
+bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
+ const char *db, char *tab, const char *src, int port);
+
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);
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root);
static int connect_assisted_discovery(handlerton *hton, THD* thd,
TABLE_SHARE *table_s,
HA_CREATE_INFO *info);
+/***********************************************************************/
+/* Global variables update functions. */
+/***********************************************************************/
static void update_connect_xtrace(MYSQL_THD thd,
struct st_mysql_sys_var *var,
void *var_ptr, const void *save)
{
- xtrace= *(int *)var_ptr= *(int *)save;
+ trace= *(int *)var_ptr= *(int *)save;
} // end of update_connect_xtrace
static void update_connect_zconv(MYSQL_THD thd,
@@ -239,6 +259,13 @@ static void update_connect_worksize(MYSQL_THD thd,
worksize= (uint)(*(ulong *)var_ptr= *(ulong *)save);
} // end of update_connect_worksize
+static void update_connect_usetemp(MYSQL_THD thd,
+ struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ Use_Temp= (USETEMP)(*(ulong *)var_ptr= *(ulong *)save);
+} // end of update_connect_usetemp
+
#if defined(XMAP)
static void update_connect_xmap(MYSQL_THD thd,
struct st_mysql_sys_var *var,
@@ -248,6 +275,13 @@ static void update_connect_xmap(MYSQL_THD thd,
} // end of update_connect_xmap
#endif // XMAP
+static void update_connect_xinfo(MYSQL_THD thd,
+ struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ xinfo= (bool)(*(my_bool *)var_ptr= *(my_bool *)save);
+} // end of update_connect_xinfo
+
/***********************************************************************/
/* The CONNECT handlerton object. */
/***********************************************************************/
@@ -304,13 +338,29 @@ ha_create_table_option connect_table_option_list[]=
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_ENUM("DISTRIB", opt, "scattered,clustered,sorted", 0),
HA_FOPTION_END
};
+/*
+ CREATE TABLE option list (index options)
+
+ These can be specified in the CREATE TABLE per index:
+ CREATE TABLE ( field ..., .., INDEX .... *here*, ... )
+*/
+ha_create_table_option connect_index_option_list[]=
+{
+ HA_IOPTION_BOOL("DYNAM", dynamic, 0),
+ HA_IOPTION_BOOL("MAPPED", mapped, 0),
+ HA_IOPTION_END
+};
+
/***********************************************************************/
/* Push G->Message as a MySQL warning. */
/***********************************************************************/
@@ -390,7 +440,7 @@ DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir)
*/
static const char *ha_connect_exts[]= {
".dos", ".fix", ".csv", ".bin", ".fmt", ".dbf", ".xml", ".ini", ".vec",
- ".dnx", ".fnx", ".bnx", ".vnx", ".dbx",
+ ".dnx", ".fnx", ".bnx", ".vnx", ".dbx", ".dop", ".fop", ".bop", ".vop",
NULL};
/**
@@ -401,7 +451,7 @@ static int connect_init_func(void *p)
{
DBUG_ENTER("connect_init_func");
- sql_print_information("CONNECT: %s", version);
+ sql_print_information("CONNECT: %s", compver);
// xtrace is now a system variable
trace= xtrace;
@@ -415,9 +465,11 @@ static int connect_init_func(void *p)
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->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_NO_PARTITION;
+ connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED;
connect_hton->table_options= connect_table_option_list;
connect_hton->field_options= connect_field_option_list;
+ connect_hton->index_options= connect_index_option_list;
connect_hton->tablefile_extensions= ha_connect_exts;
connect_hton->discover_table_structure= connect_assisted_discovery;
@@ -426,7 +478,7 @@ static int connect_init_func(void *p)
DTVAL::SetTimeShift(); // Initialize time zone shift once for all
DBUG_RETURN(0);
-}
+} // end of connect_init_func
/**
@@ -456,13 +508,13 @@ static int connect_done_func(void *p)
} // endfor pc
DBUG_RETURN(error);
-}
+} // end of connect_done_func
/**
@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
+ structure we will pass to each CONNECT handler. Do you have to have
one of these? Well, you have pieces that are used for locking, and
they are needed to function.
*/
@@ -470,20 +522,22 @@ static int connect_done_func(void *p)
CONNECT_SHARE *ha_connect::get_share()
{
CONNECT_SHARE *tmp_share;
+
lock_shared_ha_data();
- if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr())))
- {
+
+ if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr()))) {
tmp_share= new CONNECT_SHARE;
if (!tmp_share)
goto err;
mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
&tmp_share->mutex, MY_MUTEX_INIT_FAST);
set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
- }
-err:
+ } // endif tmp_share
+
+ err:
unlock_shared_ha_data();
return tmp_share;
-}
+} // end of get_share
static handler* connect_create_handler(handlerton *hton,
@@ -509,20 +563,29 @@ ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
if (xp)
xp->SetHandler(this);
+#if defined(WIN32)
+ datapath= ".\\";
+#else // !WIN32
+ datapath= "./";
+#endif // !WIN32
tdbp= NULL;
sdvalin= NULL;
sdvalout= NULL;
xmod= MODE_ANY;
istable= false;
-//*tname= '\0';
+ *partname= 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;
+ nox= true;
+ abort= false;
indexing= -1;
locked= 0;
+ part_id= NULL;
data_file_name= NULL;
index_file_name= NULL;
enable_activate_all_index= 0;
@@ -640,7 +703,13 @@ TABTYPE ha_connect::GetRealType(PTOS pos)
const char *ha_connect::index_type(uint inx)
{
switch (GetIndexType(GetRealType())) {
- case 1: return "XPLUG";
+ case 1:
+ if (table_share)
+ return (GetIndexOption(&table_share->key_info[inx], "Dynamic"))
+ ? "KINDEX" : "XINDEX";
+ else
+ return "XINDEX";
+
case 2: return "REMOTE";
} // endswitch
@@ -696,12 +765,15 @@ ulonglong ha_connect::table_flags() const
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))
+ if (!IsFileType(type) || hp->FileExists(pos->filename, true))
flags|= HA_NO_COPY_ON_ALTER;
} // endif pos
@@ -710,7 +782,7 @@ ulonglong ha_connect::table_flags() const
} // end of table_flags
/****************************************************************************/
-/* Return the value of an option specified in the option list. */
+/* Return the value of an option specified in an option list. */
/****************************************************************************/
char *GetListOption(PGLOBAL g, const char *opname,
const char *oplist, const char *def)
@@ -771,6 +843,22 @@ PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s)
} // end of GetTableOptionStruct
/****************************************************************************/
+/* Return the string eventually formatted with partition name. */
+/****************************************************************************/
+char *ha_connect::GetRealString(const char *s)
+{
+ char *sv;
+
+ if (IsPartitioned() && s) {
+ sv= (char*)PlugSubAlloc(xp->g, NULL, strlen(s) + strlen(partname));
+ sprintf(sv, s, partname);
+ } else
+ sv= (char*)s;
+
+ return sv;
+} // end of GetRealString
+
+/****************************************************************************/
/* Return the value of a string option or NULL if not specified. */
/****************************************************************************/
char *ha_connect::GetStringOption(char *opname, char *sdef)
@@ -778,16 +866,26 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
char *opval= NULL;
PTOS options= GetTableOptionStruct();
- if (!options)
+ if (!stricmp(opname, "Connect")) {
+ LEX_STRING cnc= (tshp) ? tshp->connect_string : table->s->connect_string;
+
+ if (cnc.length)
+ opval= GetRealString(cnc.str);
+
+ } else if (!stricmp(opname, "Query_String"))
+ opval= thd_query_string(table->in_use)->str;
+ else if (!stricmp(opname, "Partname"))
+ opval= partname;
+ else if (!options)
;
else if (!stricmp(opname, "Type"))
opval= (char*)options->type;
else if (!stricmp(opname, "Filename"))
- opval= (char*)options->filename;
+ opval= GetRealString(options->filename);
else if (!stricmp(opname, "Optname"))
opval= (char*)options->optname;
else if (!stricmp(opname, "Tabname"))
- opval= (char*)options->tabname;
+ opval= GetRealString(options->tabname);
else if (!stricmp(opname, "Tablist"))
opval= (char*)options->tablist;
else if (!stricmp(opname, "Database") ||
@@ -795,8 +893,6 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
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"))
@@ -811,8 +907,6 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
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);
@@ -823,7 +917,7 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database"))
opval= (char*)GetDBName(NULL); // Current database
else if (!stricmp(opname, "Type")) // Default type
- opval= (!options) ? NULL :
+ opval= (!options) ? NULL :
(options->srcdef) ? (char*)"MYSQL" :
(options->tabname) ? (char*)"PROXY" : (char*)"DOS";
else if (!stricmp(opname, "User")) // Connected user
@@ -897,21 +991,21 @@ bool ha_connect::SetBooleanOption(char *opname, bool b)
/****************************************************************************/
int ha_connect::GetIntegerOption(char *opname)
{
- ulonglong opval= NO_IVAL;
- char *pv;
- PTOS options= GetTableOptionStruct();
+ ulonglong opval= NO_IVAL;
+ char *pv;
+ PTOS options= GetTableOptionStruct();
+ TABLE_SHARE *tsp= (tshp) ? tshp : table_share;
- if (!options)
+ if (!stricmp(opname, "Avglen"))
+ opval= (ulonglong)tsp->avg_row_length;
+ else if (!stricmp(opname, "Estimate"))
+ opval= (ulonglong)tsp->max_rows;
+ else 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"))
@@ -925,7 +1019,7 @@ int ha_connect::GetIntegerOption(char *opname)
if (opval == (ulonglong)NO_IVAL && options && options->oplist)
if ((pv= GetListOption(xp->g, opname, options->oplist)))
- opval= CharToNumber(pv, strlen(pv), ULONGLONG_MAX, true);
+ opval= CharToNumber(pv, strlen(pv), ULONGLONG_MAX, true);
return (int)opval;
} // end of GetIntegerOption
@@ -1012,6 +1106,7 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
} // endif special
pcf->Scale= 0;
+ pcf->Opt= (fop) ? (int)fop->opt : 0;
if ((pcf->Length= fp->field_length) < 0)
pcf->Length= 256; // BLOB?
@@ -1020,10 +1115,12 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
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
@@ -1050,6 +1147,7 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
// 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;
@@ -1065,7 +1163,7 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
// Field_length is only used for DATE columns
if (fop && fop->fldlen)
pcf->Length= (int)fop->fldlen;
- else {
+ else {
int len;
if (pcf->Datefmt) {
@@ -1119,6 +1217,50 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
} // end of GetColumnOption
/****************************************************************************/
+/* Return an index option structure. */
+/****************************************************************************/
+PXOS ha_connect::GetIndexOptionStruct(KEY *kp)
+{
+ return kp->option_struct;
+} // end of GetIndexOptionStruct
+
+/****************************************************************************/
+/* Return a Boolean index option or false if not specified. */
+/****************************************************************************/
+bool ha_connect::GetIndexOption(KEY *kp, char *opname)
+{
+ bool opval= false;
+ PXOS options= GetIndexOptionStruct(kp);
+
+ if (options) {
+ if (!stricmp(opname, "Dynamic"))
+ opval= options->dynamic;
+ else if (!stricmp(opname, "Mapped"))
+ opval= options->mapped;
+
+ } else if (kp->comment.str != NULL) {
+ char *pv, *oplist= kp->comment.str;
+
+ if ((pv= GetListOption(xp->g, opname, oplist)))
+ opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
+
+ } // endif comment
+
+ return opval;
+} // end of GetIndexOption
+
+/****************************************************************************/
+/* Returns the index description structure used to make the index. */
+/****************************************************************************/
+bool ha_connect::IsUnique(uint n)
+{
+ TABLE_SHARE *s= (table) ? table->s : NULL;
+ KEY kp= s->key_info[n];
+
+ return (kp.flags & 1) != 0;
+} // end of IsUnique
+
+/****************************************************************************/
/* Returns the index description structure used to make the index. */
/****************************************************************************/
PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
@@ -1162,7 +1304,7 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
#if 0 // NIY
// Index on auto increment column can be an XXROW index
- if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
+ 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);
@@ -1180,6 +1322,8 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
} // endfor k
xdp->SetNParts(kp.user_defined_key_parts);
+ xdp->Dynamic= GetIndexOption(&kp, "Dynamic");
+ xdp->Mapped= GetIndexOption(&kp, "Mapped");
if (pxd)
pxd->SetNext(xdp);
@@ -1192,6 +1336,17 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
return toidx;
} // end of GetIndexInfo
+bool ha_connect::IsPartitioned(void)
+{
+ if (tshp)
+ return tshp->partition_info_str_len > 0;
+ else if (table && table->part_info)
+ return true;
+ else
+ return false;
+
+} // end of IsPartitioned
+
const char *ha_connect::GetDBName(const char* name)
{
return (name) ? name : table->s->db.str;
@@ -1202,6 +1357,11 @@ const char *ha_connect::GetTableName(void)
return (tshp) ? tshp->table_name.str : table_share->table_name.str;
} // end of GetTableName
+char *ha_connect::GetPartName(void)
+{
+ return (IsPartitioned()) ? partname : (char*)GetTableName();
+} // end of GetTableName
+
#if 0
/****************************************************************************/
/* Returns the column real or special name length of a field. */
@@ -1247,6 +1407,14 @@ void ha_connect::AddColName(char *cp, Field *fp)
} // end of AddColName
#endif // 0
+/***********************************************************************/
+/* This function sets the current database path. */
+/***********************************************************************/
+void ha_connect::SetDataPath(PGLOBAL g, const char *path)
+{
+ datapath= SetPath(g, path);
+} // end of SetDataPath
+
/****************************************************************************/
/* Get the table description block of a CONNECT table. */
/****************************************************************************/
@@ -1270,7 +1438,7 @@ PTDB ha_connect::GetTDB(PGLOBAL g)
tp->SetMode(xmod);
} else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
valid_query_id= xp->last_query_id;
- tp->SetMode(xmod);
+// tp->SetMode(xmod);
} else
htrc("GetTDB: %s\n", g->Message);
@@ -1349,6 +1517,17 @@ int ha_connect::OpenTable(PGLOBAL g, bool del)
for (field= table->field; fp= *field; field++)
if (bitmap_is_set(ump, fp->field_index)) {
strcpy(p, (char*)fp->field_name);
+
+ if (part_id && bitmap_is_set(part_id, fp->field_index)) {
+ // Trying to update a column used for partitioning
+ // This cannot be currently done because it may require
+ // a row to be moved in another partition.
+ sprintf(g->Message,
+ "Cannot update column %s because it is used for partitioning",
+ p);
+ return HA_ERR_INTERNAL_ERROR;
+ } // endif part_id
+
p+= (strlen(p) + 1);
} // endif used field
@@ -1381,6 +1560,50 @@ int ha_connect::OpenTable(PGLOBAL g, bool del)
/****************************************************************************/
+/* CheckColumnList: check that all bitmap columns do exist. */
+/****************************************************************************/
+bool ha_connect::CheckColumnList(PGLOBAL g)
+{
+ // Check the list of used fields (columns)
+ int rc;
+ bool brc= false;
+ PCOL colp;
+ Field* *field;
+ Field* fp;
+ MY_BITMAP *map= table->read_set;
+
+ // 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) {
+ for (field= table->field; fp= *field; field++)
+ if (bitmap_is_set(map, fp->field_index)) {
+ if (!(colp= tdbp->ColDB(g, (PSZ)fp->field_name, 0))) {
+ sprintf(g->Message, "Column %s not found in %s",
+ fp->field_name, tdbp->GetName());
+ brc= true;
+ goto fin;
+ } // endif colp
+
+ if ((brc= colp->InitValue(g)))
+ goto fin;
+
+ colp->AddColUse(U_P); // For PLG tables
+ } // endif
+
+ } else
+ brc= true;
+
+ fin:
+ g->jump_level--;
+ return brc;
+} // end of CheckColumnList
+
+
+/****************************************************************************/
/* IsOpened: returns true if the table is already opened. */
/****************************************************************************/
bool ha_connect::IsOpened(void)
@@ -1395,12 +1618,14 @@ bool ha_connect::IsOpened(void)
/****************************************************************************/
int ha_connect::CloseTable(PGLOBAL g)
{
- int rc= CntCloseTable(g, tdbp);
+ int rc= CntCloseTable(g, tdbp, nox, abort);
tdbp= NULL;
sdvalin=NULL;
sdvalout=NULL;
valid_info= false;
indexing= -1;
+ nox= true;
+ abort= false;
return rc;
} // end of CloseTable
@@ -1447,10 +1672,14 @@ int ha_connect::MakeRecord(char *buf)
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 (!stricmp(colp->GetName(), (char*)fp->field_name))
+ 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);
@@ -1465,7 +1694,7 @@ int ha_connect::MakeRecord(char *buf)
case TYPE_DATE:
if (!sdvalout)
sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
-
+
switch (fp->type()) {
case MYSQL_TYPE_DATE:
fmt= "%Y-%m-%d";
@@ -1480,7 +1709,7 @@ int ha_connect::MakeRecord(char *buf)
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();
@@ -1502,10 +1731,10 @@ int ha_connect::MakeRecord(char *buf)
// Store functions returns 1 on overflow and -1 on fatal error
if (rc > 0) {
- char buf[128];
+ char buf[256];
THD *thd= ha_thd();
- sprintf(buf, "Out of range value %s for column '%s' at row %ld",
+ sprintf(buf, "Out of range value %.140s for column '%s' at row %ld",
value->GetCharString(val),
fp->field_name,
thd->get_stmt_da()->current_row_for_warning());
@@ -1524,6 +1753,11 @@ int ha_connect::MakeRecord(char *buf)
} // endfor field
+ // This is sometimes required for partition tables because the buf
+ // can be different from the table->record[0] buffer
+ if (buf != (char*)table->record[0])
+ memcpy(buf, table->record[0], table->s->stored_rec_length);
+
// 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);
@@ -1674,7 +1908,11 @@ bool ha_connect::MakeKeyWhere(PGLOBAL g, char *qry, OPVAL op, char *q,
KEY_PART_INFO *kpart;
if (active_index == MAX_KEY)
- return 0;
+ return false;
+ else if (!key) {
+ strcpy(g->Message, "MakeKeyWhere: No key");
+ return true;
+ } // endif key
strcat(qry, " WHERE (");
kfp= &table->key_info[active_index];
@@ -1815,6 +2053,196 @@ const char *ha_connect::GetValStr(OPVAL vop, bool neg)
return val;
} // end of GetValStr
+#if 0
+/***********************************************************************/
+/* Check the WHERE condition and return a CONNECT filter. */
+/***********************************************************************/
+PFIL ha_connect::CheckFilter(PGLOBAL g)
+{
+ return CondFilter(g, (Item *)pushed_cond);
+} // end of CheckFilter
+#endif // 0
+
+/***********************************************************************/
+/* Check the WHERE condition and return a CONNECT filter. */
+/***********************************************************************/
+PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
+{
+ unsigned int i;
+ bool ismul= false;
+ OPVAL vop= OP_XX;
+ PFIL filp= NULL;
+
+ if (!cond)
+ return NULL;
+
+ if (xtrace)
+ htrc("Cond type=%d\n", cond->type());
+
+ if (cond->type() == COND::COND_ITEM) {
+ PFIL fp;
+ Item_cond *cond_item= (Item_cond *)cond;
+
+ if (xtrace)
+ htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
+ cond_item->func_name());
+
+ switch (cond_item->functype()) {
+ case Item_func::COND_AND_FUNC: vop= OP_AND; break;
+ case Item_func::COND_OR_FUNC: vop= OP_OR; break;
+ default: return NULL;
+ } // endswitch functype
+
+ List<Item>* arglist= cond_item->argument_list();
+ List_iterator<Item> li(*arglist);
+ Item *subitem;
+
+ for (i= 0; i < arglist->elements; i++)
+ if ((subitem= li++)) {
+ if (!(fp= CondFilter(g, subitem))) {
+ if (vop == OP_OR)
+ return NULL;
+ } else
+ filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
+
+ } else
+ return NULL;
+
+ } else if (cond->type() == COND::FUNC_ITEM) {
+ unsigned int i;
+ bool iscol, neg= FALSE;
+ PCOL colp[2]= {NULL,NULL};
+ PPARM pfirst= NULL, pprec= NULL;
+ POPER pop;
+ Item_func *condf= (Item_func *)cond;
+ Item* *args= condf->arguments();
+
+ if (xtrace)
+ htrc("Func type=%d argnum=%d\n", condf->functype(),
+ condf->argument_count());
+
+ switch (condf->functype()) {
+ case Item_func::EQUAL_FUNC:
+ case Item_func::EQ_FUNC: vop= OP_EQ; break;
+ case Item_func::NE_FUNC: vop= OP_NE; break;
+ case Item_func::LT_FUNC: vop= OP_LT; break;
+ case Item_func::LE_FUNC: vop= OP_LE; break;
+ case Item_func::GE_FUNC: vop= OP_GE; break;
+ case Item_func::GT_FUNC: vop= OP_GT; break;
+ case Item_func::IN_FUNC: vop= OP_IN;
+ case Item_func::BETWEEN:
+ ismul= true;
+ neg= ((Item_func_opt_neg *)condf)->negated;
+ break;
+ default: return NULL;
+ } // endswitch functype
+
+ pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
+ pop->Name= NULL;
+ pop->Val=vop;
+ pop->Mod= 0;
+
+ if (condf->argument_count() < 2)
+ return NULL;
+
+ for (i= 0; i < condf->argument_count(); i++) {
+ if (xtrace)
+ htrc("Argtype(%d)=%d\n", i, args[i]->type());
+
+ if (i >= 2 && !ismul) {
+ if (xtrace)
+ htrc("Unexpected arg for vop=%d\n", vop);
+
+ continue;
+ } // endif i
+
+ if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
+ Item_field *pField= (Item_field *)args[i];
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (i && ismul)
+ return NULL;
+
+ if (pField->field->table != table ||
+ !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name, 0)))
+ return NULL; // Column does not belong to this table
+
+ if (xtrace) {
+ htrc("Field index=%d\n", pField->field->field_index);
+ htrc("Field name=%s\n", pField->field->field_name);
+ } // endif xtrace
+
+ } else {
+ char buff[256];
+ String *res, tmp(buff, sizeof(buff), &my_charset_bin);
+ Item_basic_constant *pval= (Item_basic_constant *)args[i];
+ PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
+
+ // IN and BETWEEN clauses should be col VOP list
+ if (!i && (ismul))
+ return NULL;
+
+ if ((res= pval->val_str(&tmp)) == NULL)
+ return NULL; // To be clarified
+
+ switch (args[i]->real_type()) {
+ case COND::STRING_ITEM:
+ pp->Type= TYPE_STRING;
+ pp->Value= PlugSubAlloc(g, NULL, res->length() + 1);
+ strncpy((char*)pp->Value, res->ptr(), res->length() + 1);
+ break;
+ case COND::INT_ITEM:
+ pp->Type= TYPE_INT;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
+ *((int*)pp->Value)= (int)pval->val_int();
+ break;
+ case COND::DATE_ITEM:
+ pp->Type= TYPE_DATE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
+ *((int*)pp->Value)= (int)pval->val_int_from_date();
+ break;
+ case COND::REAL_ITEM:
+ pp->Type= TYPE_DOUBLE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
+ *((double*)pp->Value)= pval->val_real();
+ break;
+ case COND::DECIMAL_ITEM:
+ pp->Type= TYPE_DOUBLE;
+ pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
+ *((double*)pp->Value)= pval->val_real_from_decimal();
+ break;
+ case COND::CACHE_ITEM: // Possible ???
+ case COND::NULL_ITEM: // TODO: handle this
+ default:
+ return NULL;
+ } // endswitch type
+
+ if (xtrace)
+ htrc("Value=%.*s\n", res->length(), res->ptr());
+
+ // Append the value to the argument list
+ if (pprec)
+ pprec->Next= pp;
+ else
+ pfirst= pp;
+
+ pp->Domain= i;
+ pp->Next= NULL;
+ pprec= pp;
+ } // endif type
+
+ } // endfor i
+
+ filp= MakeFilter(g, colp, pop, pfirst, neg);
+ } else {
+ if (xtrace)
+ htrc("Unsupported condition\n");
+
+ return NULL;
+ } // endif's type
+
+ return filp;
+} // end of CondFilter
/***********************************************************************/
/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
@@ -1901,7 +2329,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
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:
+ case Item_func::BETWEEN:
ismul= true;
neg= ((Item_func_opt_neg *)condf)->negated;
break;
@@ -2071,35 +2499,36 @@ 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);
+ 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) {
- PGLOBAL& g= xp->g;
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
+ } else
+ tdbp->SetFilter(CondFilter(g, (Item *)cond));
} // endif tdbp
@@ -2116,7 +2545,7 @@ ha_rows ha_connect::records()
if (!valid_info)
info(HA_STATUS_VARIABLE);
- if (tdbp && tdbp->Cardinality(NULL))
+ if (tdbp)
return stats.records;
else
return HA_POS_ERROR;
@@ -2158,6 +2587,21 @@ bool ha_connect::get_error_message(int error, String* buf)
DBUG_RETURN(false);
} // end of get_error_message
+/**
+ Convert a filename partition name to system
+*/
+static char *decode(PGLOBAL g, const char *pn)
+ {
+ char *buf= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
+ uint dummy_errors;
+ uint32 len= copy_and_convert(buf, strlen(pn) + 1,
+ system_charset_info,
+ pn, strlen(pn),
+ &my_charset_filename,
+ &dummy_errors);
+ buf[len]= '\0';
+ return buf;
+ } // end of decode
/**
@brief
@@ -2197,9 +2641,29 @@ int ha_connect::open(const char *name, int mode, uint test_if_locked)
PGLOBAL g= (xp) ? xp->g : NULL;
// Try to set the database environment
- if (g)
+ if (g) {
rc= (CntCheckDB(g, this, name)) ? (-2) : 0;
- else
+
+ if (g->Mrr) {
+ // This should only happen for the mrr secondary handler
+ mrr= true;
+ g->Mrr= false;
+ } else
+ mrr= false;
+
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ if (table->part_info) {
+ if (GetStringOption("Filename") || GetStringOption("Tabname")
+ || GetStringOption("Connect")) {
+ strcpy(partname, decode(g, strrchr(name, '#') + 1));
+// strcpy(partname, table->part_info->curr_part_elem->partition_name);
+ part_id= &table->part_info->full_part_field_set;
+ } else // Inward table
+ strcpy(partname, strrchr(name, slash) + 1);
+ part_id= &table->part_info->full_part_field_set; // Temporary
+ } // endif part_info
+#endif // WITH_PARTITION_STORAGE_ENGINE
+ } else
rc= HA_ERR_INTERNAL_ERROR;
DBUG_RETURN(rc);
@@ -2221,20 +2685,17 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT* check_opt)
dup->Check |= CHK_OPT;
if (tdbp) {
- if (((PTDBASE)tdbp)->GetDef()->Indexable() == 2) {
- // Nothing to do for remote index
- } else 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, true))) {
+ bool dop= IsTypeIndexable(GetRealType(NULL));
+ bool dox= (((PTDBASE)tdbp)->GetDef()->Indexable() == 1);
+
+ if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, dop, dox))) {
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 rc
} else
rc= HA_ERR_INTERNAL_ERROR;
@@ -2306,8 +2767,14 @@ int ha_connect::write_row(uchar *buf)
DBUG_ENTER("ha_connect::write_row");
// This is not tested yet
- if (xmod == MODE_ALTER)
+ if (xmod == MODE_ALTER) {
+ if (IsPartitioned() && GetStringOption("Filename", NULL))
+ // Why does this happen now that check_if_supported_inplace_alter is called?
+ DBUG_RETURN(0); // Alter table on an outward partition table
+
xmod= MODE_INSERT;
+ } else if (xmod == MODE_ANY)
+ DBUG_RETURN(0); // Probably never met
// Open the table if it was not opened yet (locked)
if (!IsOpened() || xmod != tdbp->GetMode()) {
@@ -2319,9 +2786,6 @@ int ha_connect::write_row(uchar *buf)
} // 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;
@@ -2341,7 +2805,8 @@ int ha_connect::write_row(uchar *buf)
DBUG_PRINT("write_row", ("%s", g->Message));
htrc("write_row: %s\n", g->Message);
rc= HA_ERR_INTERNAL_ERROR;
- } // endif RC
+ } else // Table is modified
+ nox= false; // Indexes to be remade
DBUG_RETURN(rc);
} // end of write_row
@@ -2380,13 +2845,14 @@ int ha_connect::update_row(const uchar *old_data, uchar *new_data)
// Check values for possible change in indexed column
if ((rc= CheckRecord(g, old_data, new_data)))
- return rc;
+ DBUG_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
+ } else
+ nox= false; // Table is modified
DBUG_RETURN(rc);
} // end of update_row
@@ -2419,7 +2885,8 @@ int ha_connect::delete_row(const uchar *buf)
if (CntDeleteRow(xp->g, tdbp, false)) {
rc= HA_ERR_INTERNAL_ERROR;
htrc("delete_row CONNECT: %s\n", xp->g->Message);
- } // endif DeleteRow
+ } else
+ nox= false; // To remake indexes
DBUG_RETURN(rc);
} // end of delete_row
@@ -2438,12 +2905,19 @@ int ha_connect::index_init(uint idx, bool sorted)
htrc("index_init: this=%p idx=%u sorted=%d\n", this, idx, sorted);
if (GetIndexType(GetRealType()) == 2) {
- // This is a remote index
- xmod= MODE_READX;
+ if (xmod == MODE_READ)
+ // This is a remote index
+ xmod= MODE_READX;
if (!(rc= rnd_init(0))) {
- active_index= idx;
- indexing= 2; // TO DO: mul?
+// if (xmod == MODE_READX) {
+ active_index= idx;
+ indexing= IsUnique(idx) ? 1 : 2;
+// } else {
+// active_index= MAX_KEY;
+// indexing= 0;
+// } // endif xmod
+
} //endif rc
DBUG_RETURN(rc);
@@ -2459,27 +2933,29 @@ int ha_connect::index_init(uint idx, bool sorted)
DBUG_RETURN(0);
} // endif locked
- indexing= CntIndexInit(g, tdbp, (signed)idx);
+ indexing= CntIndexInit(g, tdbp, (signed)idx, sorted);
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 {
+ } else if (((PTDBDOX)tdbp)->To_Kindex) {
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;
+// } else { // Void table
+// active_index= MAX_KEY;
+// indexing= 0;
+ } // endif Num
rc= 0;
} // endif indexing
if (xtrace)
- htrc("index_init: rc=%d indexing=%d active_index=%d\n",
+ htrc("index_init: rc=%d indexing=%d active_index=%d\n",
rc, indexing, active_index);
DBUG_RETURN(rc);
@@ -2492,6 +2968,7 @@ 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
@@ -2505,7 +2982,7 @@ int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len
//statistic_increment(ha_read_key_count, &LOCK_status);
- switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len)) {
+ switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) {
case RC_OK:
xp->fnd++;
rc= MakeRecord((char*)buf);
@@ -2563,16 +3040,22 @@ int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
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;
+ default: DBUG_RETURN(-1); break;
} // endswitch find_flag
if (xtrace > 1)
htrc("%p index_read: op=%d\n", this, op);
- if (indexing > 0)
+ if (indexing > 0) {
rc= ReadIndexed(buf, op, key, key_len);
- else
- rc= HA_ERR_INTERNAL_ERROR;
+
+ if (rc == HA_ERR_INTERNAL_ERROR) {
+ nox= true; // To block making indexes
+ abort= true; // Don't rename temp file
+ } // endif rc
+
+ } else
+ rc= HA_ERR_INTERNAL_ERROR; // HA_ERR_KEY_NOT_FOUND ?
DBUG_RETURN(rc);
} // end of index_read
@@ -2666,7 +3149,7 @@ int ha_connect::index_last(uchar *buf)
rc= ReadIndexed(buf, OP_LAST);
DBUG_RETURN(rc);
-} // end of index_last
+}
/****************************************************************************/
@@ -2718,7 +3201,7 @@ int ha_connect::rnd_init(bool scan)
} // endif xmod
if (xtrace)
- htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
+ htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
this, scan, xmod, alter);
if (!g || !table || xmod == MODE_INSERT)
@@ -2726,7 +3209,11 @@ int ha_connect::rnd_init(bool scan)
// Do not close the table if it was opened yet (locked?)
if (IsOpened()) {
- if (tdbp->OpenDB(g)) // Rewind table
+ if (IsPartitioned() && xmod != MODE_INSERT)
+ if (CheckColumnList(g)) // map can have been changed
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+
+ if (tdbp->OpenDB(g)) // Rewind table
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
else
DBUG_RETURN(0);
@@ -2735,7 +3222,7 @@ int ha_connect::rnd_init(bool scan)
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
+ // 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);
@@ -2768,6 +3255,7 @@ int ha_connect::rnd_end()
// if (tdbp && xp->last_query_id == valid_query_id)
// rc= CloseTable(xp->g);
+ ds_mrr.dsmrr_close();
DBUG_RETURN(rc);
} // end of rnd_end
@@ -2861,6 +3349,10 @@ 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());
+
+ if (trace)
+ htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos());
+
DBUG_VOID_RETURN;
} // end of position
@@ -2887,9 +3379,13 @@ int ha_connect::rnd_pos(uchar *buf, uchar *pos)
PTDBASE tp= (PTDBASE)tdbp;
DBUG_ENTER("ha_connect::rnd_pos");
- if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length)))
+ if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) {
+ if (trace)
+ htrc("rnd_pos: %d\n", tp->GetRecpos());
+
+ tp->SetFilter(NULL);
rc= rnd_next(buf);
- else
+ } else
rc= HA_ERR_KEY_NOT_FOUND;
DBUG_RETURN(rc);
@@ -2944,27 +3440,32 @@ int ha_connect::info(uint flag)
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) {
- PDBUSER dup= PlgGetUser(g);
- PCATLG cat= (dup) ? dup->Catalog : NULL;
-
- if (xmod == MODE_ANY || xmod == MODE_ALTER) {
- // Pure info, not a query
- pure= true;
- xp->CheckCleanup();
- } // endif xmod
-
- // This is necessary for getting file length
- if (cat && table)
- cat->SetDataPath(g, table->s->db.str);
- else
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR); // Should never happen
+ // tdbp must be available to get updated info
+ if (xp->CheckQuery(valid_query_id) || !tdbp) {
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
- tdbp= GetTDB(g);
- } // endif tdbp
+ if (xmod == MODE_ANY || xmod == MODE_ALTER) {
+ // Pure info, not a query
+ pure= true;
+ xp->CheckCleanup();
+ } // endif xmod
+ // This is necessary for getting file length
+// if (cat && table)
+// cat->SetDataPath(g, table->s->db.str);
+ if (table)
+ SetDataPath(g, table->s->db.str);
+ else
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR); // Should never happen
+
+ if (!(tdbp= GetTDB(g)))
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR); // Should never happen
+
+ valid_info = false;
+ } // endif tdbp
+
+ if (!valid_info) {
valid_info= CntInfo(g, tdbp, &xinfo);
if (((signed)xinfo.records) < 0)
@@ -3070,7 +3571,8 @@ int ha_connect::delete_all_rows()
if (CntDeleteRow(g, tdbp, true)) {
htrc("%s\n", g->Message);
rc= HA_ERR_INTERNAL_ERROR;
- } // endif
+ } else
+ nox= false;
} // endif rc
@@ -3106,15 +3608,15 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
if (options->filename && *options->filename) {
char *s, path[FN_REFLEN], dbpath[FN_REFLEN];
#if defined(WIN32)
- s= "\\";
+ s= "\\";
#else // !WIN32
- s= "/";
+ 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);
@@ -3148,7 +3650,7 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
return true;
} // end of check_privileges
-// Check that two indexes are equivalent
+// Check that two indexes are equivalent
bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
{
bool b= true;
@@ -3173,7 +3675,7 @@ bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
return b;
} // end of IsSameIndex
-MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
+MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
MODE newmode, bool *chk, bool *cras)
{
if ((trace= xtrace)) {
@@ -3216,11 +3718,6 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
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;
@@ -3228,6 +3725,13 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
case SQLCOM_ALTER_TABLE:
newmode= MODE_ALTER;
break;
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_CREATE_INDEX:
+// if (!IsPartitioned()) {
+ newmode= MODE_ANY;
+ break;
+// } // endif partitioned
+
default:
htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
strcpy(g->Message, "CONNECT Unsupported command");
@@ -3257,10 +3761,6 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
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;
@@ -3273,6 +3773,14 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
*chk= true;
newmode= MODE_ALTER;
break;
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_CREATE_INDEX:
+// if (!IsPartitioned()) {
+ *chk= true;
+ newmode= MODE_ANY;
+ break;
+// } // endif partitioned
+
default:
htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
strcpy(g->Message, "CONNECT Unsupported command");
@@ -3361,7 +3869,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
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);
@@ -3389,7 +3897,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
sprintf(g->Message, "external_lock: unexpected command %d", sqlcom);
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
DBUG_RETURN(0);
- } else if (g->Xchk) {
+ } else if (g->Xchk) {
if (!tdbp) {
if (!(tdbp= GetTDB(g)))
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
@@ -3493,6 +4001,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
} // endif Close
locked= 0;
+ xmod= MODE_ANY; // For info commands
DBUG_RETURN(rc);
} // endif MODE_ANY
@@ -3526,7 +4035,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
} // endif Xchk
} else
- g->Xchk= NULL;
+ g->Xchk= NULL;
#endif // 0
if (cras)
@@ -3650,11 +4159,6 @@ 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);
@@ -3679,10 +4183,10 @@ filename_to_dbname_and_tablename(const char *filename,
/**
@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
+ 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
+ You will need to remove or rename any files you have created at
this point.
@details
@@ -3708,7 +4212,7 @@ int ha_connect::delete_or_rename_table(const char *name, const char *to)
if (xtrace) {
if (to)
- htrc("rename_table: this=%p thd=%p sqlcom=%d from=%s to=%s\n",
+ 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",
@@ -3729,29 +4233,27 @@ int ha_connect::delete_or_rename_table(const char *name, const char *to)
// 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 != '#') {
+ // Also, for partitioned tables, no test can be done because when
+ // this function is called, the .par file is already deleted and
+ // this causes the open_table_def function to fail.
+ // Not having any other clues (table and table_share are NULL)
+ // the only mean we have to test for partitioning is this:
+ if (*tabname != '#' && !strstr(tabname, "#P#")) {
// 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;
+// if ((p= strstr(tabname, "#P#"))) won't work, see above
+// *p= 0; // Get the main the table name
+
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
@@ -3761,7 +4263,7 @@ int ha_connect::delete_or_rename_table(const char *name, const char *to)
else
if (IsFileType(GetRealType(pos)) && !pos->filename)
ok= true;
-
+
} // endif pos
} else // Avoid infamous DBUG_ASSERT
@@ -3840,10 +4342,10 @@ ha_rows ha_connect::records_in_range(uint inx, key_range *min_key,
else
rows= (ha_rows)nval;
- } else if (indexing < 0)
- rows= HA_POS_ERROR;
- else
+ } else if (indexing == 0)
rows= 100000000; // Don't use missing index
+ else
+ rows= HA_POS_ERROR;
DBUG_RETURN(rows);
} // end of records_in_range
@@ -3851,7 +4353,7 @@ ha_rows ha_connect::records_in_range(uint inx, key_range *min_key,
/**
Convert an ISO-8859-1 column name to UTF-8
*/
-static char *encode(PGLOBAL g, char *cnm)
+static char *encode(PGLOBAL g, const char *cnm)
{
char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3);
uint dummy_errors;
@@ -3862,7 +4364,7 @@ static char *encode(PGLOBAL g, char *cnm)
&dummy_errors);
buf[len]= '\0';
return buf;
- } // end of Encode
+ } // end of encode
/**
Store field definition for create.
@@ -3870,6 +4372,83 @@ static char *encode(PGLOBAL g, char *cnm)
@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)
@@ -3899,7 +4478,7 @@ static bool add_field(String *sql, const char *field_name, int typ,
error|= sql->append(')');
} // endif len
-
+
if (v == 'U')
error|= sql->append(" UNSIGNED");
else if (v == 'Z')
@@ -3939,6 +4518,7 @@ static bool add_field(String *sql, const char *field_name, int typ,
error|= sql->append(',');
return error;
} // end of add_field
+#endif // !NEW_WAY
/**
Initialise the table share with the new columns.
@@ -3946,8 +4526,112 @@ static bool add_field(String *sql, const char *field_name, int typ,
@return
Return 0 if ok
*/
-static int init_table_share(THD* thd,
- TABLE_SHARE *table_s,
+#if defined(NEW_WAY)
+//static bool sql_unusable_for_discovery(THD *thd, const char *sql);
+
+static int init_table_share(THD *thd,
+ TABLE_SHARE *table_s,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
+{
+ KEY *not_used_1;
+ uint not_used_2;
+ int rc= 0;
+ handler *file;
+ LEX_CUSTRING frm= {0,0};
+
+ DBUG_ENTER("init_table_share");
+
+#if 0
+ ulonglong saved_mode= thd->variables.sql_mode;
+ CHARSET_INFO *old_cs= thd->variables.character_set_client;
+ Parser_state parser_state;
+ char *sql_copy;
+ LEX *old_lex;
+ Query_arena *arena, backup;
+ LEX tmp_lex;
+
+ /*
+ Ouch. Parser may *change* the string it's working on.
+ Currently (2013-02-26) it is used to permanently disable
+ conditional comments.
+ Anyway, let's copy the caller's string...
+ */
+ if (!(sql_copy= thd->strmake(sql, sql_length)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ if (parser_state.init(thd, sql_copy, sql_length))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE;
+ thd->variables.character_set_client= system_charset_info;
+ old_lex= thd->lex;
+ thd->lex= &tmp_lex;
+
+ arena= thd->stmt_arena;
+
+ if (arena->is_conventional())
+ arena= 0;
+ else
+ thd->set_n_backup_active_arena(arena, &backup);
+
+ lex_start(thd);
+
+ if ((error= parse_sql(thd, & parser_state, NULL)))
+ goto ret;
+
+ if (table_s->sql_unusable_for_discovery(thd, NULL)) {
+ my_error(ER_SQL_DISCOVER_ERROR, MYF(0), plugin_name(db_plugin)->str,
+ db.str, table_name.str, sql_copy);
+ goto ret;
+ } // endif unusable
+
+ thd->lex->create_info.db_type= plugin_data(db_plugin, handlerton *);
+
+ if (tabledef_version.str)
+ thd->lex->create_info.tabledef_version= tabledef_version;
+#endif // 0
+
+ tmp_disable_binlog(thd);
+
+ file= mysql_create_frm_image(thd, table_s->db.str, table_s->table_name.str,
+ create_info, alter_info, C_ORDINARY_CREATE,
+ &not_used_1, &not_used_2, &frm);
+ if (file)
+ delete file;
+ else
+ rc= OPEN_FRM_CORRUPTED;
+
+ if (!rc && frm.str) {
+ table_s->option_list= 0; // cleanup existing options ...
+ table_s->option_struct= 0; // ... if it's an assisted discovery
+ rc= table_s->init_from_binary_frm_image(thd, true, frm.str, frm.length);
+ } // endif frm
+
+//ret:
+ my_free(const_cast<uchar*>(frm.str));
+ reenable_binlog(thd);
+#if 0
+ lex_end(thd->lex);
+ thd->lex= old_lex;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->variables.sql_mode= saved_mode;
+ thd->variables.character_set_client= old_cs;
+#endif // 0
+
+ if (thd->is_error() || rc) {
+ thd->clear_error();
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_s->db.str,
+ table_s->table_name.str);
+ DBUG_RETURN(HA_ERR_NOT_A_TABLE);
+ } else
+ DBUG_RETURN(0);
+
+} // end of init_table_share
+#else // !NEW_WAY
+static int init_table_share(THD* thd,
+ TABLE_SHARE *table_s,
HA_CREATE_INFO *create_info,
// char *dsn,
String *sql)
@@ -4037,9 +4721,26 @@ static int init_table_share(THD* thd,
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,
+bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
const char *db, char *tab, const char *src, int port)
{
if (src)
@@ -4077,7 +4778,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
const char *fncn= "?";
const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src;
const char *col, *ocl, *rnk, *pic, *fcl, *skc;
- char *tab, *dsn, *shm;
+ char *tab, *dsn, *shm, *dpath;
#if defined(WIN32)
char *nsp= NULL, *cls= NULL;
#endif // WIN32
@@ -4093,10 +4794,15 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
PDBUSER dup= PlgGetUser(g);
PCATLG cat= (dup) ? dup->Catalog : NULL;
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;
@@ -4156,6 +4862,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
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));
@@ -4188,6 +4895,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} 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) {
@@ -4262,7 +4972,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} else if (!user)
user= "root";
- if (CheckSelf(g, table_s, host, db, tab, src, port))
+ if (ok && CheckSelf(g, table_s, host, db, tab, src, port))
ok= false;
break;
@@ -4313,10 +5023,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
char *cnm, *rem, *dft, *xtra;
int i, len, prec, dec, typ, flg;
- if (cat)
- cat->SetDataPath(g, table_s->db.str);
- else
- return HA_ERR_INTERNAL_ERROR; // Should never happen
+// if (cat)
+// cat->SetDataPath(g, table_s->db.str);
+// else
+// return HA_ERR_INTERNAL_ERROR; // Should never happen
+
+ dpath= SetPath(g, table_s->db.str);
if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
qrp= SrcColumns(g, host, db, user, pwd, src, port);
@@ -4329,7 +5041,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} else switch (ttp) {
case TAB_DBF:
- qrp= DBFColumns(g, fn, fnc == FNC_COL);
+ qrp= DBFColumns(g, dpath, fn, fnc == FNC_COL);
break;
#if defined(ODBC_SUPPORT)
case TAB_ODBC:
@@ -4339,7 +5051,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (src) {
qrp= ODBCSrcCols(g, dsn, (char*)src);
src= NULL; // for next tests
- } else
+ } else
qrp= ODBCColumns(g, dsn, shm, tab, NULL, mxr, fnc == FNC_COL);
break;
@@ -4361,12 +5073,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
#endif // ODBC_SUPPORT
#if defined(MYSQL_SUPPORT)
case TAB_MYSQL:
- qrp= MyColumns(g, thd, host, db, user, pwd, tab,
+ qrp= MyColumns(g, thd, 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);
+ qrp= CSVColumns(g, dpath, fn, spc, qch, hdr, mxe, fnc == FNC_COL);
break;
#if defined(WIN32)
case TAB_WMI:
@@ -4415,15 +5127,19 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
dec= crp->Prec;
flg= crp->Flag;
v= crp->Var;
-
+
if (!len && typ == TYPE_STRING)
len= 256; // STRBLK's have 0 length
// Now add the field
+#if defined(NEW_WAY)
+ rc= add_fields(g, thd, &alter_info, cnm, typ, len, dec,
+ NOT_NULL_FLAG, "", flg, dbf, v);
+#else // !NEW_WAY
if (add_field(&sql, cnm, typ, len, dec, NOT_NULL_FLAG,
NULL, NULL, NULL, flg, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
-
+#endif // !NEW_WAY
} // endfor crp
} else {
@@ -4443,7 +5159,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
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) {
@@ -4472,7 +5193,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case FLD_REM:
rem= crp->Kdata->GetCharValue(i);
break;
-// case FLD_CHARSET:
+// case FLD_CHARSET:
// No good because remote table is already translated
// if (*(csn= crp->Kdata->GetCharValue(i)))
// cs= get_charset_by_name(csn, 0);
@@ -4525,16 +5246,25 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
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
} // endif fnc
+#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
@@ -4594,12 +5324,15 @@ int ha_connect::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
int rc= RC_OK;
- bool dbf;
+ bool dbf, inward;
Field* *field;
Field *fp;
TABTYPE type;
TABLE *st= table; // Probably unuseful
- THD *thd= ha_thd();
+ THD *thd= ha_thd();
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ partition_info *part_info= table_arg->part_info;
+#endif // WITH_PARTITION_STORAGE_ENGINE
xp= GetUser(thd, xp);
PGLOBAL g= xp->g;
@@ -4619,7 +5352,7 @@ int ha_connect::create(const char *name, TABLE *table_arg,
// Check table type
if (type == TAB_UNDEF) {
- options->type= (options->srcdef) ? "MYSQL" :
+ 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);
@@ -4636,6 +5369,8 @@ int ha_connect::create(const char *name, TABLE *table_arg,
if (check_privileges(thd, options, GetDBfromName(name)))
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ inward= IsFileType(type) && !options->filename;
+
if (options->data_charset) {
const CHARSET_INFO *data_charset;
@@ -4687,6 +5422,9 @@ int ha_connect::create(const char *name, TABLE *table_arg,
} // endif tabname
case TAB_MYSQL:
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ if (!part_info)
+#endif // WITH_PARTITION_STORAGE_ENGINE
{const char *src= options->srcdef;
char *host, *db, *tab= (char*)options->tabname;
int port;
@@ -4730,7 +5468,7 @@ int ha_connect::create(const char *name, TABLE *table_arg,
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
} // endif CheckSelf
- }break;
+ }break;
default: /* do nothing */;
break;
} // endswitch ttp
@@ -4826,8 +5564,8 @@ int ha_connect::create(const char *name, TABLE *table_arg,
sprintf(g->Message, "Unsupported 0 length for column %s",
fp->field_name);
rc= HA_ERR_INTERNAL_ERROR;
- my_printf_error(ER_UNKNOWN_ERROR,
- "Unsupported 0 length for column %s",
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Unsupported 0 length for column %s",
MYF(0), fp->field_name);
DBUG_RETURN(rc);
} // endif fp
@@ -4854,7 +5592,7 @@ int ha_connect::create(const char *name, TABLE *table_arg,
} // endswitch type
if ((fp)->real_maybe_null() && !IsTypeNullable(type)) {
- my_printf_error(ER_UNKNOWN_ERROR,
+ my_printf_error(ER_UNKNOWN_ERROR,
"Table type %s does not support nullable columns",
MYF(0), options->type);
DBUG_RETURN(HA_ERR_UNSUPPORTED);
@@ -4880,8 +5618,7 @@ int ha_connect::create(const char *name, TABLE *table_arg,
} // endfor field
- if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#')
- && IsFileType(type) && !options->filename) {
+ if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#') && inward) {
// 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
@@ -4889,8 +5626,6 @@ int ha_connect::create(const char *name, TABLE *table_arg,
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,
@@ -4899,12 +5634,12 @@ int ha_connect::create(const char *name, TABLE *table_arg,
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,
+ 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,
+ 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);
@@ -4917,16 +5652,31 @@ int ha_connect::create(const char *name, TABLE *table_arg,
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);
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ if (part_info) {
+ char *p;
+
+ strcpy(dbpath, name);
+ p= strrchr(dbpath, slash);
+ strcpy(partname, ++p);
+ strcat(strcat(strcpy(buf, p), "."), lwt);
+ *p= 0;
+ } else {
+#endif // WITH_PARTITION_STORAGE_ENGINE
+ strcat(strcat(strcpy(buf, GetTableName()), "."), 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), "/");
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ } // endif part_info
+#endif // WITH_PARTITION_STORAGE_ENGINE
- 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);
@@ -4941,32 +5691,47 @@ int ha_connect::create(const char *name, TABLE *table_arg,
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
"Congratulation, you just created a read-only void table!");
- } // endif
+ } // endif sqlcom
if (xtrace)
htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
- // To check whether indices have to be made or remade
+ // To check whether indexes 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)
+ // We should be in CREATE TABLE, ALTER_TABLE or CREATE INDEX
+ if (!(sqlcom == SQLCOM_CREATE_TABLE || sqlcom == SQLCOM_ALTER_TABLE ||
+ sqlcom == SQLCOM_CREATE_INDEX || sqlcom == SQLCOM_DROP_INDEX))
+// (sqlcom == SQLCOM_CREATE_INDEX && part_info) ||
+// (sqlcom == SQLCOM_DROP_INDEX && part_info)))
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);
+ "Unexpected command in create, please contact CONNECT team");
+
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ if (part_info && !inward)
+ strcpy(partname, decode(g, strrchr(name, '#') + 1));
+// strcpy(partname, part_info->curr_part_elem->partition_name);
+#endif // WITH_PARTITION_STORAGE_ENGINE
+
+ if (g->Alchecked == 0 &&
+ (!IsFileType(type) || FileExists(options->filename, false))) {
+ if (part_info) {
+ sprintf(g->Message, "Data repartition in %s is unchecked", partname);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ } else if (sqlcom == SQLCOM_ALTER_TABLE) {
+ // This is an ALTER to CONNECT from another engine.
+ // It cannot be accepted because the table data would be modified
+ // except when the target file does not exist.
+ strcpy(g->Message, "Operation denied. Table data would be modified.");
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif part_info
+
} // endif outward
// Get the index definitions
- if (xdp= GetIndexInfo()) {
+ if ((xdp= GetIndexInfo()) || sqlcom == SQLCOM_DROP_INDEX) {
if (options->multiple) {
strcpy(g->Message, "Multiple tables are not indexable");
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
@@ -4978,17 +5743,25 @@ int ha_connect::create(const char *name, TABLE *table_arg,
} else if (GetIndexType(type) == 1) {
PDBUSER dup= PlgGetUser(g);
PCATLG cat= (dup) ? dup->Catalog : NULL;
-
+
+ SetDataPath(g, table_arg->s->db.str);
+
if (cat) {
- cat->SetDataPath(g, table_arg->s->db.str);
-
+// cat->SetDataPath(g, table_arg->s->db.str);
+
+#if defined(WITH_PARTITION_STORAGE_ENGINE)
+ if (part_info)
+ strcpy(partname,
+ decode(g, strrchr(name, (inward ? slash : '#')) + 1));
+#endif // WITH_PARTITION_STORAGE_ENGINE
+
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 if (!GetIndexType(type)) {
@@ -5016,13 +5789,16 @@ int ha_connect::create(const char *name, TABLE *table_arg,
- file does not exist or is void
- user has file privilege
*/
-bool ha_connect::FileExists(const char *fn)
+bool ha_connect::FileExists(const char *fn, bool bf)
{
if (!fn || !*fn)
return false;
+ else if (IsPartitioned() && bf)
+ return true;
if (table) {
- char *s, filename[_MAX_PATH], path[128];
+ char *s, tfn[_MAX_PATH], filename[_MAX_PATH], path[128];
+ bool b= false;
int n;
struct stat info;
@@ -5031,13 +5807,22 @@ bool ha_connect::FileExists(const char *fn)
return true;
#if defined(WIN32)
- s= "\\";
+ s= "\\";
#else // !WIN32
- s= "/";
+ s= "/";
#endif // !WIN32
+ if (IsPartitioned()) {
+ sprintf(tfn, fn, GetPartName());
+
+ // This is to avoid an initialization error raised by the
+ // test on check_table_flags made in ha_partition::open
+ // that can fail if some partition files are empty.
+ b= true;
+ } else
+ strcpy(tfn, fn);
strcat(strcat(strcat(strcpy(path, "."), s), table->s->db.str), s);
- PlugSetPath(filename, fn, path);
+ PlugSetPath(filename, tfn, path);
n= stat(filename, &info);
if (n < 0) {
@@ -5051,7 +5836,7 @@ bool ha_connect::FileExists(const char *fn)
return false;
} else
- return (info.st_size) ? true : false;
+ return (info.st_size || b) ? true : false;
} // endif table
@@ -5174,7 +5959,7 @@ bool ha_connect::NoFieldOptionChange(TABLE *tab)
*/
enum_alter_inplace_result
ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info)
+ Alter_inplace_info *ha_alter_info)
{
DBUG_ENTER("check_if_supported_alter");
@@ -5211,11 +5996,11 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
// Index operations
Alter_inplace_info::HA_ALTER_FLAGS index_operations=
- Alter_inplace_info::ADD_INDEX |
+ Alter_inplace_info::ADD_INDEX |
Alter_inplace_info::DROP_INDEX |
- Alter_inplace_info::ADD_UNIQUE_INDEX |
+ Alter_inplace_info::ADD_UNIQUE_INDEX |
Alter_inplace_info::DROP_UNIQUE_INDEX |
- Alter_inplace_info::ADD_PK_INDEX |
+ Alter_inplace_info::ADD_PK_INDEX |
Alter_inplace_info::DROP_PK_INDEX;
Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
@@ -5223,7 +6008,8 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
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;
+ Alter_inplace_info::ALTER_RENAME |
+ Alter_inplace_info::ALTER_PARTITIONED | index_operations;
if (ha_alter_info->handler_flags & index_operations ||
!SameString(altered_table, "optname") ||
@@ -5277,7 +6063,7 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
char *fn= GetStringOption("filename");
tshp= NULL;
- if (FileExists(fn)) {
+ if (FileExists(fn, false)) {
strcpy(g->Message, "Operation denied. Table data would be lost.");
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
DBUG_RETURN(HA_ALTER_ERROR);
@@ -5321,7 +6107,7 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
// This was in check_if_incompatible_data
if (NoFieldOptionChange(altered_table) &&
- type == newtyp &&
+ type == newtyp &&
SameInt(altered_table, "lrecl") &&
SameInt(altered_table, "elements") &&
SameInt(altered_table, "header") &&
@@ -5333,12 +6119,14 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
fin:
if (idx) {
// Indexing is only supported inplace
- my_message(ER_ALTER_OPERATION_NOT_SUPPORTED,
+ 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.");
+ if (IsFileType(type))
+ 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);
@@ -5363,15 +6151,95 @@ bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *info,
{
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,
+ 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())))
+ *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())))
+ *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 };
+/***********************************************************************/
+/* CONNECT global variables definitions. */
+/***********************************************************************/
// Tracing: 0 no, 1 yes, >1 more tracing
static MYSQL_SYSVAR_INT(xtrace, xtrace,
PLUGIN_VAR_RQCMDARG, "Console trace value.",
@@ -5409,6 +6277,35 @@ static MYSQL_SYSVAR_ENUM(
0, // def (no)
&xconv_typelib); // typelib
+/**
+ Temporary file usage:
+ no: Not using temporary file
+ auto: Using temporary file when needed
+ yes: Allways using temporary file
+ force: Force using temporary file (no MAP)
+ test: Reserved
+*/
+const char *usetemp_names[]=
+{
+ "NO", "AUTO", "YES", "FORCE", "TEST", NullS
+};
+
+TYPELIB usetemp_typelib=
+{
+ array_elements(usetemp_names) - 1, "usetemp_typelib",
+ usetemp_names, NULL
+};
+
+static MYSQL_SYSVAR_ENUM(
+ use_tempfile, // name
+ use_tempfile, // varname
+ PLUGIN_VAR_RQCMDARG, // opt
+ "Temporary file use.", // comment
+ NULL, // check
+ update_connect_usetemp, // update function
+ 1, // def (AUTO)
+ &usetemp_typelib); // typelib
+
#if defined(XMAP)
// Using file mapping for indexes if true
static MYSQL_SYSVAR_BOOL(indx_map, indx_map, PLUGIN_VAR_RQCMDARG,
@@ -5421,6 +6318,11 @@ static MYSQL_SYSVAR_UINT(work_size, work_size,
PLUGIN_VAR_RQCMDARG, "Size of the CONNECT work area.",
NULL, update_connect_worksize, SZWORK, SZWMIN, UINT_MAX, 1);
+// Getting exact info values
+static MYSQL_SYSVAR_BOOL(exact_info, exact_info, PLUGIN_VAR_RQCMDARG,
+ "Getting exact info values",
+ NULL, update_connect_xinfo, 0);
+
static struct st_mysql_sys_var* connect_system_variables[]= {
MYSQL_SYSVAR(xtrace),
MYSQL_SYSVAR(conv_size),
@@ -5429,6 +6331,8 @@ static struct st_mysql_sys_var* connect_system_variables[]= {
MYSQL_SYSVAR(indx_map),
#endif // XMAP
MYSQL_SYSVAR(work_size),
+ MYSQL_SYSVAR(use_tempfile),
+ MYSQL_SYSVAR(exact_info),
NULL
};
@@ -5442,10 +6346,10 @@ maria_declare_plugin(connect)
PLUGIN_LICENSE_GPL,
connect_init_func, /* Plugin Init */
connect_done_func, /* Plugin Deinit */
- 0x0102, /* version number (1.02) */
+ 0x0103, /* version number (1.03) */
NULL, /* status variables */
connect_system_variables, /* system variables */
- "1.02", /* string version */
+ "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 a8d0be4c03e..9a73c85cdc7 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2013
+/* 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
@@ -50,7 +50,7 @@ typedef struct _xinfo {
class XCHK : public BLOCK {
public:
- XCHK(void) {oldsep= newsep= false;
+ XCHK(void) {oldsep= newsep= false;
oldopn= newopn= NULL;
oldpix= newpix= NULL;}
@@ -71,7 +71,8 @@ public:
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;
+typedef struct ha_field_option_struct FOS, *PFOS;
+typedef struct ha_index_option_struct XOS, *PXOS;
extern handlerton *connect_hton;
@@ -122,12 +123,27 @@ struct ha_table_option_struct {
struct ha_field_option_struct
{
ulonglong offset;
+ ulonglong freq;
ulonglong fldlen;
+ uint opt;
const char *dateformat;
const char *fieldformat;
char *special;
};
+/*
+ index options can be declared similarly
+ using the ha_index_option_struct structure.
+
+ Their values can be specified in the CREATE TABLE per index:
+ CREATE TABLE ( field ..., .., INDEX .... *here*, ... )
+*/
+struct ha_index_option_struct
+{
+ bool dynamic;
+ bool mapped;
+};
+
/** @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.
@@ -166,32 +182,41 @@ public:
static bool connect_init(void);
static bool connect_end(void);
TABTYPE GetRealType(PTOS pos= NULL);
+ char *GetRealString(const char *s);
char *GetStringOption(char *opname, char *sdef= NULL);
PTOS GetTableOptionStruct(TABLE_SHARE *s= NULL);
bool GetBooleanOption(char *opname, bool bdef);
bool SetBooleanOption(char *opname, bool b);
int GetIntegerOption(char *opname);
+ bool GetIndexOption(KEY *kp, 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 FileExists(const char *fn, bool bf);
bool NoFieldOptionChange(TABLE *tab);
PFOS GetFieldOptionStruct(Field *fp);
void *GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf);
+ PXOS GetIndexOptionStruct(KEY *kp);
PIXDEF GetIndexInfo(TABLE_SHARE *s= NULL);
const char *GetDBName(const char *name);
const char *GetTableName(void);
+ char *GetPartName(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);
+ bool IsPartitioned(void);
+ bool IsUnique(uint n);
+ char *GetDataPath(void) {return (char*)datapath;}
+ void SetDataPath(PGLOBAL g, const char *path);
PTDB GetTDB(PGLOBAL g);
int OpenTable(PGLOBAL g, bool del= false);
- bool IsOpened(void);
+ bool CheckColumnList(PGLOBAL g);
+ bool IsOpened(void);
int CloseTable(PGLOBAL g);
int MakeRecord(char *buf);
int ScanRecord(PGLOBAL g, uchar *buf);
@@ -318,17 +343,19 @@ public:
@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
+ 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);
+//PFIL CheckFilter(PGLOBAL g);
/**
Number of rows in table. It will only be called if
@@ -336,7 +363,7 @@ const char *GetValStr(OPVAL vop, bool neg);
*/
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
@@ -463,6 +490,28 @@ int index_prev(uchar *buf);
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);
@@ -474,22 +523,27 @@ protected:
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
+ char *datapath; // Is the Path of DB data directory
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
+ char partname[64]; // The partition 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
+ bool nox; // True when index should not be made
+ bool abort; // True after error in UPDATE/DELETE
int indexing; // Type of indexing for CONNECT
int locked; // Table lock
+ MY_BITMAP *part_id; // Columns used for partition func
THR_LOCK_DATA lock_data;
public:
- TABLE_SHARE *tshp; // Used by called tables
+ TABLE_SHARE *tshp; // Used by called tables
char *data_file_name;
char *index_file_name;
uint int_table_flags; // Inherited from MyISAM
diff --git a/storage/connect/macutil.cpp b/storage/connect/macutil.cpp
index 3069aa71cd6..4d3022b91b6 100644
--- a/storage/connect/macutil.cpp
+++ b/storage/connect/macutil.cpp
@@ -103,7 +103,7 @@ int MACINFO::GetNadap(PGLOBAL g)
} // endif MaxSize
return N;
- } // end of GetMaxSize
+ } // end of GetNadap
/***********************************************************************/
/* GetMacInfo: Get info for all found adapters. */
diff --git a/storage/connect/maputil.cpp b/storage/connect/maputil.cpp
index 7104259ebad..97c638b4254 100644
--- a/storage/connect/maputil.cpp
+++ b/storage/connect/maputil.cpp
@@ -154,7 +154,7 @@ HANDLE CreateFileMap(PGLOBAL g, LPCSTR fileName,
} // endswitch
// Try to open the addressed file.
- fd= global_open(g, MSGID_NONE, fileName, openMode);
+ fd= global_open(g, MSGID_NONE, fileName, openMode);
if (fd != INVALID_HANDLE_VALUE && mode != MODE_INSERT) {
/* We must know about the size of the file. */
@@ -164,17 +164,19 @@ HANDLE CreateFileMap(PGLOBAL g, LPCSTR fileName,
return INVALID_HANDLE_VALUE;
} // endif fstat
- filesize = st.st_size;
-
- // Now we are ready to load the file. If mmap() is available we try
- // this first. If not available or it failed we try to load it.
- mm->memory = mmap(NULL, filesize, protmode, MAP_SHARED, fd, 0);
+ if ((filesize = st.st_size))
+ // Now we are ready to load the file. If mmap() is available we try
+ // this first. If not available or it failed we try to load it.
+ mm->memory = mmap(NULL, filesize, protmode, MAP_SHARED, fd, 0);
+ else
+ mm->memory = 0;
if (mm->memory != MAP_FAILED) {
mm->lenL = (mm->memory != 0) ? filesize : 0;
mm->lenH = 0;
} else {
strcpy(g->Message, "Memory mapping failed");
+ close(fd);
return INVALID_HANDLE_VALUE;
} // endif memory
diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc
index 10b12c0809b..660e2adec2f 100644
--- a/storage/connect/mycat.cc
+++ b/storage/connect/mycat.cc
@@ -405,9 +405,9 @@ PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info)
CATALOG::CATALOG(void)
{
#if defined(WIN32)
- DataPath= ".\\";
+//DataPath= ".\\";
#else // !WIN32
- DataPath= "./";
+//DataPath= "./";
#endif // !WIN32
memset(&Ctb, 0, sizeof(CURTAB));
Cbuf= NULL;
@@ -433,6 +433,7 @@ void MYCAT::Reset(void)
{
} // end of Reset
+#if 0
/***********************************************************************/
/* This function sets the current database path. */
/***********************************************************************/
@@ -463,6 +464,7 @@ void MYCAT::SetPath(PGLOBAL g, LPCSTR *datapath, const char *path)
} // endif path
} // end of SetDataPath
+#endif // 0
/***********************************************************************/
/* GetTableDesc: retrieve a table descriptor. */
@@ -560,7 +562,7 @@ PTDB MYCAT::GetTable(PGLOBAL g, PTABLE tablep, MODE mode, LPCSTR type)
printf("tdb=%p type=%s\n", tdp, tdp->GetType());
if (tablep->GetQualifier())
- SetPath(g, &tdp->Database, tablep->GetQualifier());
+ tdp->Database = SetPath(g, tablep->GetQualifier());
tdbp= tdp->GetTable(g, mode);
} // endif tdp
@@ -571,6 +573,7 @@ PTDB MYCAT::GetTable(PGLOBAL g, PTABLE tablep, MODE mode, LPCSTR type)
tdbp->GetAmType());
tablep->SetTo_Tdb(tdbp);
tdbp->SetTable(tablep);
+ tdbp->SetMode(mode);
} // endif tdbp
return (tdbp);
diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h
index b45d3a08725..1aaee4ed1e8 100644
--- a/storage/connect/mycat.h
+++ b/storage/connect/mycat.h
@@ -56,8 +56,8 @@ class MYCAT : public CATALOG {
// Methods
void Reset(void);
- void SetDataPath(PGLOBAL g, const char *path)
- {SetPath(g, &DataPath, path);}
+//void SetDataPath(PGLOBAL g, const char *path)
+// {SetPath(g, &DataPath, path);}
bool StoreIndex(PGLOBAL g, PTABDEF defp) {return false;} // Temporary
PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name,
LPCSTR type, PRELDEF *prp = NULL);
@@ -67,7 +67,7 @@ class MYCAT : public CATALOG {
protected:
PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am);
- void SetPath(PGLOBAL g, LPCSTR *datapath, const char *path);
+//void SetPath(PGLOBAL g, LPCSTR *datapath, const char *path);
// Members
ha_connect *Hc; // The Connect handler
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 8700a24ac96..0c4b50f1d0b 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -364,7 +364,7 @@ PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
if (!port)
port = mysqld_port;
- if (!strnicmp(srcdef, "select ", 7)) {
+ if (!strnicmp(srcdef, "select ", 7)) {
query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 9);
strcat(strcpy(query, srcdef), " LIMIT 0");
} else
@@ -686,19 +686,48 @@ int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w)
rc = RC_NF;
} // endif field count
-if (w)
-//*w = mysql_warning_count(m_DB);
- *w = m_DB->warning_count;
+ if (w)
+// *w = mysql_warning_count(m_DB);
+ *w = m_DB->warning_count;
return rc;
} // end of ExecSQL
/***********************************************************************/
+/* Get table size by executing "select count(*) from table_name". */
+/***********************************************************************/
+int MYSQLC::GetTableSize(PGLOBAL g, PSZ query)
+ {
+ if (mysql_real_query(m_DB, query, strlen(query))) {
+#if defined(_DEBUG)
+ char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
+
+ sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
+ mysql_error(m_DB), query);
+ strncpy(g->Message, msg, sizeof(g->Message) - 1);
+ g->Message[sizeof(g->Message) - 1] = 0;
+#endif // _DEBUG
+ return -2;
+ } // endif mysql_real_query
+
+ if (!(m_Res = mysql_store_result(m_DB)))
+ return -3;
+
+ // Get the resulting count value
+ m_Rows = (int)mysql_num_rows(m_Res); // Should be 1
+
+ if (m_Rows && (m_Row = mysql_fetch_row(m_Res)))
+ return atoi(*m_Row);
+
+ return -4;
+ } // end of GetTableSize
+
+/***********************************************************************/
/* Move to a specific row and column */
/***********************************************************************/
void MYSQLC::DataSeek(my_ulonglong row)
{
- MYSQL_ROWS *tmp=0;
+ MYSQL_ROWS *tmp = 0;
//DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row));
if (m_Res->data)
@@ -873,7 +902,7 @@ PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb)
else {
if (!*row && crp->Nulls)
crp->Nulls[n] = '*'; // Null value
-
+
crp->Kdata->Reset(n);
} // endelse *row
}
@@ -970,7 +999,7 @@ void MYSQLC::DiscardResults(void)
while (!mysql_next_result(m_DB)) {
res = mysql_store_result(m_DB);
mysql_free_result(res);
- } // endwhile next result
+ } // endwhile next result
} // end of DiscardResults
#endif // 0
diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h
index 7e892eece34..65e6531aee4 100644
--- a/storage/connect/myconn.h
+++ b/storage/connect/myconn.h
@@ -64,6 +64,7 @@ class DllItem MYSQLC {
// Methods
int GetResultSize(PGLOBAL g, PSZ sql);
+ int GetTableSize(PGLOBAL g, PSZ query);
int Open(PGLOBAL g, const char *host, const char *db,
const char *user= "root", const char *pwd= "*",
int pt= 0);
diff --git a/storage/connect/mysql-test/connect/r/alter.result b/storage/connect/mysql-test/connect/r/alter.result
index ccfae3f4ddb..77d775220ec 100644
--- a/storage/connect/mysql-test/connect/r/alter.result
+++ b/storage/connect/mysql-test/connect/r/alter.result
@@ -21,8 +21,8 @@ DROP INDEX xd ON t1;
ALTER TABLE t1 ADD INDEX xc (c), ADD INDEX xd (d);
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
-t1 1 xc 1 c A NULL NULL NULL XPLUG
-t1 1 xd 1 d A NULL NULL NULL XPLUG
+t1 1 xc 1 c A NULL NULL NULL XINDEX
+t1 1 xd 1 d A NULL NULL NULL XINDEX
ALTER TABLE t1 DROP INDEX xc, DROP INDEX xd;
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
@@ -111,8 +111,8 @@ t1 CREATE TABLE `t1` (
) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
-t1 1 xc 1 c A NULL NULL NULL XPLUG
-t1 1 xd 1 d A NULL NULL NULL XPLUG
+t1 1 xc 1 c A NULL NULL NULL XINDEX
+t1 1 xd 1 d A NULL NULL NULL XINDEX
SELECT * FROM t1;
c d
1 One
@@ -143,8 +143,8 @@ line
ALTER TABLE t1 ADD INDEX xc (c), ADD INDEX xd (d);
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
-t1 1 xc 1 c A NULL NULL NULL XPLUG
-t1 1 xd 1 d A NULL NULL NULL XPLUG
+t1 1 xc 1 c A NULL NULL NULL XINDEX
+t1 1 xd 1 d A NULL NULL NULL XINDEX
SELECT d FROM t1 WHERE c = 2;
d
Two
@@ -218,13 +218,22 @@ Three 3
# Changing to another engine is Ok
# However, the data file is not deleted.
#
-ALTER TABLE t1 ENGINE=MARIA;
+ALTER TABLE t1 ENGINE=ARIA;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `d` char(10) NOT NULL /* `FLAG`=11 */,
+ `c` int(11) NOT NULL /* `FLAG`=0 */
+) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 /* `TABLE_TYPE`=fix `FILE_NAME`='tf1.txt' `ENDING`=1 */
+set @old_sql_mode=@@sql_mode;
+set sql_mode=ignore_bad_table_options;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`d` char(10) NOT NULL `FLAG`=11,
`c` int(11) NOT NULL `FLAG`=0
) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 `TABLE_TYPE`=fix `FILE_NAME`='tf1.txt' `ENDING`=1
+set sql_mode=@old_sql_mode;
SELECT * from t1;
d c
One 1
@@ -240,7 +249,7 @@ line
# Sure enough, the data file was not deleted.
#
ALTER TABLE t1 ENGINE=CONNECT;
-ERROR HY000: Operation denied. Table data would be lost.
+ERROR HY000: Operation denied. Table data would be modified.
#
# But changing back to CONNECT succeed
# if the data file does not exist.
diff --git a/storage/connect/mysql-test/connect/r/alter_xml.result b/storage/connect/mysql-test/connect/r/alter_xml.result
index bd3b281b05b..f2250b78d2d 100644
--- a/storage/connect/mysql-test/connect/r/alter_xml.result
+++ b/storage/connect/mysql-test/connect/r/alter_xml.result
@@ -35,7 +35,7 @@ Warning 1105 No table_type. Will be set to DOS
SELECT * FROM t2;
line
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Created by CONNECT Version 1.02.0002 March 16, 2014 -->
+<!-- Created by the MariaDB CONNECT Storage Engine-->
<t1>
<row>
<TH>c</TH>
@@ -71,7 +71,7 @@ t1 CREATE TABLE `t1` (
SELECT * FROM t2;
line
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Created by CONNECT Version 1.02.0002 March 16, 2014 -->
+<!-- Created by the MariaDB CONNECT Storage Engine-->
<t1>
<row d="One">
<c>1</c>
diff --git a/storage/connect/mysql-test/connect/r/mysql.result b/storage/connect/mysql-test/connect/r/mysql.result
index fc2fe2418cf..29f077c3d9f 100644
--- a/storage/connect/mysql-test/connect/r/mysql.result
+++ b/storage/connect/mysql-test/connect/r/mysql.result
@@ -282,8 +282,6 @@ a
20
30
ALTER TABLE t2 MODIFY a TINYINT;
-Warnings:
-Warning 1105 This is an outward table, table data were not modified.
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
diff --git a/storage/connect/mysql-test/connect/r/occur.result b/storage/connect/mysql-test/connect/r/occur.result
index fbcda4660be..a497dfc9942 100644
--- a/storage/connect/mysql-test/connect/r/occur.result
+++ b/storage/connect/mysql-test/connect/r/occur.result
@@ -193,8 +193,6 @@ Kevin 8
Lisbeth 2
Mary 2
ALTER TABLE xpet MODIFY number INT NOT NULL;
-Warnings:
-Warning 1105 This is an outward table, table data were not modified.
SELECT * FROM xpet;
name race number
John dog 2
diff --git a/storage/connect/mysql-test/connect/r/part_file.result b/storage/connect/mysql-test/connect/r/part_file.result
new file mode 100644
index 00000000000..c679ed95062
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/part_file.result
@@ -0,0 +1,346 @@
+set @@global.connect_exact_info=ON;
+# This will be used to see what data files are created
+CREATE TABLE dr1 (
+fname VARCHAR(256) NOT NULL FLAG=2,
+ftype CHAR(8) NOT NULL FLAG=3
+# ,FSIZE INT(6) NOT NULL FLAG=5 removed because Unix size != Windows size
+) engine=CONNECT table_type=DIR file_name='t1#P#*.*';
+#
+# Testing partitioning on inward table
+#
+CREATE TABLE t1 (
+id INT NOT NULL,
+msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=10
+PARTITION BY RANGE(id) (
+PARTITION first VALUES LESS THAN(10),
+PARTITION middle VALUES LESS THAN(50),
+PARTITION last VALUES LESS THAN(MAXVALUE));
+INSERT INTO t1 VALUES(4, 'four'),(24, 'twenty four');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+first 2
+middle 3
+last 2
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+24 twenty four
+10 ten
+40 forty
+60 sixty
+81 eighty one
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id > 50;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 last ALL NULL NULL NULL NULL 3 Using where
+SELECT * FROM t1 WHERE id > 50;
+id msg
+60 sixty
+81 eighty one
+UPDATE t1 set id = 41 WHERE msg = 'four';
+ERROR HY000: Got error 174 'Cannot update column id because it is used for partitioning' from CONNECT
+UPDATE t1 set msg = 'quatre' WHERE id = 4;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+t1#P#first .csv
+t1#P#last .csv
+t1#P#middle .csv
+#
+# Altering partitioning on inward table
+#
+ALTER TABLE t1
+PARTITION by range(id) (
+PARTITION first VALUES LESS THAN(11),
+PARTITION middle VALUES LESS THAN(50),
+PARTITION last VALUES LESS THAN(MAXVALUE));
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+first 3
+middle 2
+last 2
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+t1#P#first .csv
+t1#P#last .csv
+t1#P#middle .csv
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id=10;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 first ALL NULL NULL NULL NULL 3 Using where
+SELECT * FROM t1 WHERE id=10;
+id msg
+10 ten
+DELETE FROM t1 WHERE id in (4,60);
+SELECT * FROM t1;
+id msg
+7 seven
+10 ten
+24 twenty four
+40 forty
+81 eighty one
+DROP TABLE t1;
+#
+# Testing partitioning on a void outward table
+#
+ALTER TABLE dr1 FILE_NAME='part*.*';
+CREATE TABLE t1 (
+rwid INT(6) DEFAULT 0 SPECIAL=ROWID,
+rnum INT(6) DEFAULT 0 SPECIAL=ROWNUM,
+prtn VARCHAR(64) DEFAULT '' SPECIAL=PARTID,
+tbn VARCHAR(64) DEFAULT '' SPECIAL=TABID,
+fid VARCHAR(256) DEFAULT '' SPECIAL=FNAME,
+id INT KEY NOT NULL,
+msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='part%s.txt';
+ALTER TABLE t1
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t1 0 PRIMARY 1 id A NULL NULL NULL XINDEX
+INSERT INTO t1(id,msg) VALUES(4, 'four');
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+INSERT INTO t1(id,msg) VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+INSERT INTO t1(id,msg) VALUES(72,'seventy two'),(20,'twenty'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 4
+2 4
+3 3
+SELECT * FROM t1;
+rwid rnum prtn tbn fid id msg
+1 1 1 t1 part1 4 four
+2 2 1 t1 part1 7 seven
+3 3 1 t1 part1 1 one
+4 4 1 t1 part1 8 eight
+1 1 2 t1 part2 10 ten
+2 2 2 t1 part2 40 forty
+3 3 2 t1 part2 20 twenty
+4 4 2 t1 part2 35 thirty five
+1 1 3 t1 part3 60 sixty
+2 2 3 t1 part3 81 eighty one
+3 3 3 t1 part3 72 seventy two
+SELECT * FROM t1 order by id;
+rwid rnum prtn tbn fid id msg
+3 3 1 t1 part1 1 one
+1 1 1 t1 part1 4 four
+2 2 1 t1 part1 7 seven
+4 4 1 t1 part1 8 eight
+1 1 2 t1 part2 10 ten
+3 3 2 t1 part2 20 twenty
+4 4 2 t1 part2 35 thirty five
+2 2 2 t1 part2 40 forty
+1 1 3 t1 part3 60 sixty
+3 3 3 t1 part3 72 seventy two
+2 2 3 t1 part3 81 eighty one
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 2 const PRIMARY PRIMARY 4 const 1
+SELECT * FROM t1 WHERE id = 10;
+rwid rnum prtn tbn fid id msg
+1 1 2 t1 part2 10 ten
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id >= 10;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 2,3 range PRIMARY PRIMARY 4 NULL 7 Using where
+SELECT * FROM t1 WHERE id >= 10;
+rwid rnum prtn tbn fid id msg
+1 1 2 t1 part2 10 ten
+3 3 2 t1 part2 20 twenty
+4 4 2 t1 part2 35 thirty five
+2 2 2 t1 part2 40 forty
+1 1 3 t1 part3 60 sixty
+3 3 3 t1 part3 72 seventy two
+2 2 3 t1 part3 81 eighty one
+SELECT count(*) FROM t1 WHERE id < 10;
+count(*)
+4
+SELECT case when id < 10 then 1 when id < 50 then 2 else 3 end as pn, count(*) FROM t1 group by pn;
+pn count(*)
+1 4
+2 4
+3 3
+SELECT prtn, count(*) FROM t1 group by prtn;
+prtn count(*)
+1 4
+2 4
+3 3
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id > 50;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 3 range PRIMARY PRIMARY 4 NULL 3 Using where
+SELECT * FROM t1 WHERE id = 35;
+rwid rnum prtn tbn fid id msg
+4 4 2 t1 part2 35 thirty five
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+# This does not change the partition file data and is WRONG
+ALTER TABLE t1
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(11),
+PARTITION `2` VALUES LESS THAN(70),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+Warnings:
+Warning 1105 Data repartition in 1 is unchecked
+Warning 1105 Data repartition in 2 is unchecked
+Warning 1105 Data repartition in 3 is unchecked
+SELECT CASE WHEN id < 11 THEN 1 WHEN id < 70 THEN 2 ELSE 3 END AS pn, COUNT(*) FROM t1 GROUP BY pn;
+pn COUNT(*)
+1 5
+2 4
+3 2
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 4
+2 4
+3 3
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+#
+# This is the correct way to change partitioning:
+# Save table values, erase the table, then re-insert saved values in modified table
+#
+CREATE TABLE t2 (
+id INT NOT NULL,
+msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX;
+Warnings:
+Warning 1105 No file name. Table will use t2.fix
+INSERT INTO t2 SELECT id, msg FROM t1;
+DELETE FROM t1;
+INSERT INTO t1(id,msg) SELECT * FROM t2;
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 5
+2 4
+3 2
+SELECT * FROM t1;
+rwid rnum prtn tbn fid id msg
+1 1 1 t1 part1 4 four
+2 2 1 t1 part1 7 seven
+3 3 1 t1 part1 1 one
+4 4 1 t1 part1 8 eight
+5 5 1 t1 part1 10 ten
+1 1 2 t1 part2 40 forty
+2 2 2 t1 part2 20 twenty
+3 3 2 t1 part2 35 thirty five
+4 4 2 t1 part2 60 sixty
+1 1 3 t1 part3 81 eighty one
+2 2 3 t1 part3 72 seventy two
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+DROP TABLE t2;
+DROP TABLE t1;
+#
+# Testing partitioning on a populated outward table
+#
+CREATE TABLE t1 (
+id INT NOT NULL,
+msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='part%s.txt'
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(11),
+PARTITION `2` VALUES LESS THAN(70),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+Warnings:
+Warning 1105 Data repartition in 1 is unchecked
+Warning 1105 Data repartition in 2 is unchecked
+Warning 1105 Data repartition in 3 is unchecked
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 5
+2 4
+3 2
+SELECT * FROM t1 WHERE id < 11;
+id msg
+4 four
+7 seven
+1 one
+8 eight
+10 ten
+SELECT * FROM t1 WHERE id >= 70;
+id msg
+81 eighty one
+72 seventy two
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+#
+# Testing indexing on a partitioned table
+#
+CREATE INDEX XID ON t1(id);
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t1 1 XID 1 id A NULL NULL NULL XINDEX
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 1 ref XID XID 4 const 1
+DROP INDEX XID ON t1;
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .txt
+part2 .txt
+part3 .txt
+ALTER TABLE t1 ADD PRIMARY KEY (id);
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t1 0 PRIMARY 1 id A NULL NULL NULL XINDEX
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .fnx
+part1 .txt
+part2 .fnx
+part2 .txt
+part3 .fnx
+part3 .txt
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 1 const PRIMARY PRIMARY 4 const 1
+ALTER TABLE t1 DROP PRIMARY KEY;
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+SELECT * FROM dr1 ORDER BY fname, ftype;
+fname ftype
+part1 .txt
+part2 .txt
+part3 .txt
+DROP TABLE t1;
+DROP TABLE dr1;
+set @@global.connect_exact_info=OFF;
diff --git a/storage/connect/mysql-test/connect/r/part_table.result b/storage/connect/mysql-test/connect/r/part_table.result
new file mode 100644
index 00000000000..122c328fa59
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/part_table.result
@@ -0,0 +1,197 @@
+set @@global.connect_exact_info=ON;
+CREATE TABLE xt1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=MyISAM;
+INSERT INTO xt1 VALUES(4, 'four'),(7,'seven'),(1,'one'),(8,'eight');
+SELECT * FROM xt1;
+id msg
+4 four
+7 seven
+1 one
+8 eight
+CREATE TABLE xt2 (
+id INT KEY NOT NULL,
+msg VARCHAR(32));
+INSERT INTO xt2 VALUES(10,'ten'),(40,'forty'),(11,'eleven'),(35,'thirty five');
+SELECT * FROM xt2;
+id msg
+10 ten
+40 forty
+11 eleven
+35 thirty five
+CREATE TABLE xt3 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=10;
+Warnings:
+Warning 1105 No file name. Table will use xt3.csv
+INSERT INTO xt3 VALUES(60,'sixty'),(81,'eighty one'),(72,'seventy two');
+SELECT * FROM xt3;
+id msg
+60 sixty
+81 eighty one
+72 seventy two
+CREATE TABLE t1 (
+id INT NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=PROXY TABNAME='xt%s'
+PARTITION BY RANGE COLUMNS(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+Warnings:
+Warning 1105 Data repartition in 1 is unchecked
+Warning 1105 Data repartition in 2 is unchecked
+Warning 1105 Data repartition in 3 is unchecked
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 4
+2 4
+3 3
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+1 one
+8 eight
+10 ten
+40 forty
+11 eleven
+35 thirty five
+60 sixty
+81 eighty one
+72 seventy two
+DELETE FROM t1;
+Warnings:
+Note 1105 xt1: 4 affected rows
+Note 1105 xt2: 4 affected rows
+ALTER TABLE t1 ADD INDEX XID(id);
+ERROR HY000: Table type PROXY is not indexable
+INSERT INTO t1 VALUES(4, 'four');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+INSERT INTO t1 VALUES(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 4
+2 4
+3 3
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+1 one
+8 eight
+10 ten
+40 forty
+11 eleven
+35 thirty five
+60 sixty
+81 eighty one
+72 seventy two
+EXPLAIN PARTITIONS
+SELECT * FROM t1 WHERE id = 81;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 3 ALL NULL NULL NULL NULL 4 Using where
+DELETE FROM t1;
+Warnings:
+Note 1105 xt1: 4 affected rows
+Note 1105 xt2: 4 affected rows
+DROP TABLE t1;
+CREATE TABLE t1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='xt%s'
+PARTITION BY RANGE COLUMNS(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+Warnings:
+Warning 1105 Data repartition in 1 is unchecked
+Warning 1105 Data repartition in 2 is unchecked
+Warning 1105 Data repartition in 3 is unchecked
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t1 0 PRIMARY 1 id NULL NULL NULL NULL REMOTE
+INSERT INTO t1 VALUES(4, 'four');
+INSERT INTO t1 VALUES(40, 'forty');
+INSERT INTO t1 VALUES(72,'seventy two');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(60,'sixty'),(81,'eighty one'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+partition_name table_rows
+1 4
+2 4
+3 3
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+1 one
+8 eight
+40 forty
+10 ten
+11 eleven
+35 thirty five
+72 seventy two
+60 sixty
+81 eighty one
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 81;
+id select_type table partitions type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 3 const PRIMARY PRIMARY 4 const 1
+SELECT * FROM t1 WHERE id = 7;
+id msg
+7 seven
+SELECT * FROM t1 WHERE id = 35;
+id msg
+35 thirty five
+UPDATE t1 SET msg = 'number' WHERE id in (60,72);
+Warnings:
+Note 1105 xt3: 2 affected rows
+Note 1105 xt3: 0 affected rows
+UPDATE t1 SET msg = 'soixante' WHERE id = 60;
+Warnings:
+Note 1105 xt3: 1 affected rows
+SELECT * FROM t1 WHERE id > 50;
+id msg
+60 soixante
+72 number
+81 eighty one
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+Warnings:
+Note 1105 xt3: 3 affected rows
+UPDATE t1 SET msg = 'sept' WHERE id = 7;
+Warnings:
+Note 1105 xt1: 1 affected rows
+SELECT * FROM t1;
+id msg
+4 four
+7 sept
+1 one
+8 eight
+40 forty
+10 ten
+11 eleven
+35 thirty five
+72 big
+60 big
+81 big
+DELETE FROM t1 WHERE id in (60,72);
+Warnings:
+Note 1105 xt3: 2 affected rows
+Note 1105 xt3: 0 affected rows
+SELECT * FROM t1;
+id msg
+4 four
+7 sept
+1 one
+8 eight
+40 forty
+10 ten
+11 eleven
+35 thirty five
+81 big
+DROP TABLE t1;
+DROP TABLE xt1;
+DROP TABLE xt2;
+DROP TABLE xt3;
+set @@global.connect_exact_info=OFF;
diff --git a/storage/connect/mysql-test/connect/r/pivot.result b/storage/connect/mysql-test/connect/r/pivot.result
index 4b39a21d3d9..349db89fa35 100644
--- a/storage/connect/mysql-test/connect/r/pivot.result
+++ b/storage/connect/mysql-test/connect/r/pivot.result
@@ -59,8 +59,6 @@ Joe 5 14.00 0.00 12.00
# Restricting the columns in a Pivot Table
#
ALTER TABLE pivex DROP COLUMN week;
-Warnings:
-Warning 1105 This is an outward table, table data were not modified.
SELECT * FROM pivex;
Who Beer Car Food
Beth 51.00 0.00 29.00
diff --git a/storage/connect/mysql-test/connect/r/temporary.result b/storage/connect/mysql-test/connect/r/temporary.result
new file mode 100644
index 00000000000..c4e992f2a64
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/temporary.result
@@ -0,0 +1,6 @@
+CREATE TEMPORARY TABLE t1 (a int not null)
+ENGINE=CONNECT table_type=MYSQL CONNECTION='mysql://root@127.0.0.1/test/t2';
+ERROR HY000: Table storage engine 'CONNECT' does not support the create option 'TEMPORARY'
+CREATE TEMPORARY TABLE t1
+ENGINE=CONNECT table_type=MYSQL CONNECTION='mysql://root@127.0.0.1/test/t2';
+ERROR HY000: Table storage engine 'CONNECT' does not support the create option 'TEMPORARY'
diff --git a/storage/connect/mysql-test/connect/r/updelx.result b/storage/connect/mysql-test/connect/r/updelx.result
new file mode 100644
index 00000000000..2aed1e06928
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/updelx.result
@@ -0,0 +1,2570 @@
+#
+# Testing indexed UPDATE and DELETE for all table types
+#
+# CSV table
+CREATE TABLE t1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=6;
+Warnings:
+Warning 1105 No file name. Table will use t1.csv
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=NO BLOCK_SIZE=6;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# DOS table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=DOS;
+Warnings:
+Warning 1105 No file name. Table will use t1.dos
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=NO BLOCK_SIZE=4;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# FIX table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16) CHARSET BINARY DISTRIB=CLUSTERED)
+ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=4;
+Warnings:
+Warning 1105 No file name. Table will use t1.fix
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# BIN table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16) CHARSET BINARY DISTRIB=CLUSTERED)
+ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=8;
+Warnings:
+Warning 1105 No file name. Table will use t1.bin
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# DBF table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=DBF BLOCK_SIZE=12;
+Warnings:
+Warning 1105 No file name. Table will use t1.dbf
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# VEC table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=VEC BLOCK_SIZE=6 MAX_ROWS=16;
+Warnings:
+Warning 1105 No file name. Table will use t1.vec
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
+# Split VEC table (outward)
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=VEC BLOCK_SIZE=6 FILE_NAME='tx.vec';
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+ALTER TABLE t1 MAPPED=YES;
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 thirty five
+8 eight
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 sixty
+81 eighty one
+72 seventy two
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 big
+81 big
+72 big
+11 eleven
+1 one
+35 bof
+8 eight
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 ten
+40 forty
+60 updated
+81 big
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+id msg
+4 four
+7 seven
+10 twin
+40 forty
+60 updated
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+id msg
+60 sixty
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+40 forty
+60 sixty
+81 twin
+72 updated
+11 eleven
+1 one
+35 updated
+8 updated
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+id msg
+7 seven
+10 twin
+60 sixty
+81 twin
+72 updated
+1 one
+8 updated
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+msg
+seven
+twin
+twin
+updated
+updated
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+id
+7
+8
+10
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+id msg
+8 updated
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
+id msg
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result
index 5018eec47fc..eea53bf55c7 100644
--- a/storage/connect/mysql-test/connect/r/xml.result
+++ b/storage/connect/mysql-test/connect/r/xml.result
@@ -416,7 +416,7 @@ DROP TABLE t1;
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/t1.xml');
SELECT CAST(@a AS CHAR CHARACTER SET latin1);
CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Created by CONNECT Version 1.02.0002 March 16, 2014 -->
+<!-- Created by the MariaDB CONNECT Storage Engine-->
<t1>
<line>
<node>ÀÁÂÃ</node>
diff --git a/storage/connect/mysql-test/connect/t/alter.test b/storage/connect/mysql-test/connect/t/alter.test
index 299381b925a..49f34996bbd 100644
--- a/storage/connect/mysql-test/connect/t/alter.test
+++ b/storage/connect/mysql-test/connect/t/alter.test
@@ -105,8 +105,12 @@ SELECT * FROM t1;
--echo # Changing to another engine is Ok
--echo # However, the data file is not deleted.
--echo #
-ALTER TABLE t1 ENGINE=MARIA;
+ALTER TABLE t1 ENGINE=ARIA;
SHOW CREATE TABLE t1;
+set @old_sql_mode=@@sql_mode;
+set sql_mode=ignore_bad_table_options;
+SHOW CREATE TABLE t1;
+set sql_mode=@old_sql_mode;
SELECT * from t1;
SELECT * from t2;
diff --git a/storage/connect/mysql-test/connect/t/part_file.test b/storage/connect/mysql-test/connect/t/part_file.test
new file mode 100644
index 00000000000..6efd2b9b580
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/part_file.test
@@ -0,0 +1,166 @@
+--source include/have_partition.inc
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+set @@global.connect_exact_info=ON;
+
+--echo # This will be used to see what data files are created
+CREATE TABLE dr1 (
+ fname VARCHAR(256) NOT NULL FLAG=2,
+ ftype CHAR(8) NOT NULL FLAG=3
+# ,FSIZE INT(6) NOT NULL FLAG=5 removed because Unix size != Windows size
+) engine=CONNECT table_type=DIR file_name='t1#P#*.*';
+
+--echo #
+--echo # Testing partitioning on inward table
+--echo #
+CREATE TABLE t1 (
+ id INT NOT NULL,
+ msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=10
+PARTITION BY RANGE(id) (
+PARTITION first VALUES LESS THAN(10),
+PARTITION middle VALUES LESS THAN(50),
+PARTITION last VALUES LESS THAN(MAXVALUE));
+INSERT INTO t1 VALUES(4, 'four'),(24, 'twenty four');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id > 50;
+SELECT * FROM t1 WHERE id > 50;
+#TODO: Differences between Linux and Windows
+#SHOW TABLE STATUS LIKE 't1';
+--error ER_GET_ERRMSG
+UPDATE t1 set id = 41 WHERE msg = 'four';
+UPDATE t1 set msg = 'quatre' WHERE id = 4;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+--echo #
+--echo # Altering partitioning on inward table
+--echo #
+ALTER TABLE t1
+PARTITION by range(id) (
+PARTITION first VALUES LESS THAN(11),
+PARTITION middle VALUES LESS THAN(50),
+PARTITION last VALUES LESS THAN(MAXVALUE));
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM dr1 ORDER BY fname, ftype;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id=10;
+SELECT * FROM t1 WHERE id=10;
+DELETE FROM t1 WHERE id in (4,60);
+SELECT * FROM t1;
+DROP TABLE t1;
+# TODO: this fails on Linux
+#SELECT * FROM dr1;
+
+--echo #
+--echo # Testing partitioning on a void outward table
+--echo #
+ALTER TABLE dr1 FILE_NAME='part*.*';
+CREATE TABLE t1 (
+ rwid INT(6) DEFAULT 0 SPECIAL=ROWID,
+ rnum INT(6) DEFAULT 0 SPECIAL=ROWNUM,
+ prtn VARCHAR(64) DEFAULT '' SPECIAL=PARTID,
+ tbn VARCHAR(64) DEFAULT '' SPECIAL=TABID,
+ fid VARCHAR(256) DEFAULT '' SPECIAL=FNAME,
+ id INT KEY NOT NULL,
+ msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='part%s.txt';
+--replace_result $MYSQLD_DATADIR "DATADIR/"
+ALTER TABLE t1
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SHOW INDEX FROM t1;
+# TODO: this fails on Linux
+#SELECT * FROM dr1 ORDER BY fname, ftype;
+INSERT INTO t1(id,msg) VALUES(4, 'four');
+SELECT * FROM dr1 ORDER BY fname, ftype;
+INSERT INTO t1(id,msg) VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+INSERT INTO t1(id,msg) VALUES(72,'seventy two'),(20,'twenty'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+SELECT * FROM t1 order by id;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+SELECT * FROM t1 WHERE id = 10;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id >= 10;
+SELECT * FROM t1 WHERE id >= 10;
+SELECT count(*) FROM t1 WHERE id < 10;
+SELECT case when id < 10 then 1 when id < 50 then 2 else 3 end as pn, count(*) FROM t1 group by pn;
+SELECT prtn, count(*) FROM t1 group by prtn;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id > 50;
+SELECT * FROM t1 WHERE id = 35;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+--echo # This does not change the partition file data and is WRONG
+ALTER TABLE t1
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(11),
+PARTITION `2` VALUES LESS THAN(70),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SELECT CASE WHEN id < 11 THEN 1 WHEN id < 70 THEN 2 ELSE 3 END AS pn, COUNT(*) FROM t1 GROUP BY pn;
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM dr1 ORDER BY fname, ftype;
+--echo #
+--echo # This is the correct way to change partitioning:
+--echo # Save table values, erase the table, then re-insert saved values in modified table
+--echo #
+CREATE TABLE t2 (
+ id INT NOT NULL,
+ msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX;
+INSERT INTO t2 SELECT id, msg FROM t1;
+DELETE FROM t1;
+INSERT INTO t1(id,msg) SELECT * FROM t2;
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+DROP TABLE t2;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing partitioning on a populated outward table
+--echo #
+CREATE TABLE t1 (
+ id INT NOT NULL,
+ msg VARCHAR(32)
+) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='part%s.txt'
+PARTITION by range columns(id) (
+PARTITION `1` VALUES LESS THAN(11),
+PARTITION `2` VALUES LESS THAN(70),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1 WHERE id < 11;
+SELECT * FROM t1 WHERE id >= 70;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+
+--echo #
+--echo # Testing indexing on a partitioned table
+--echo #
+CREATE INDEX XID ON t1(id);
+SHOW INDEX FROM t1;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+DROP INDEX XID ON t1;
+SHOW INDEX FROM t1;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+ALTER TABLE t1 ADD PRIMARY KEY (id);
+SHOW INDEX FROM t1;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 10;
+ALTER TABLE t1 DROP PRIMARY KEY;
+SHOW INDEX FROM t1;
+SELECT * FROM dr1 ORDER BY fname, ftype;
+DROP TABLE t1;
+DROP TABLE dr1;
+
+#
+# Clean up
+#
+set @@global.connect_exact_info=OFF;
+
+--remove_file $MYSQLD_DATADIR/test/part1.txt
+--remove_file $MYSQLD_DATADIR/test/part2.txt
+--remove_file $MYSQLD_DATADIR/test/part3.txt
+#--remove_file $MYSQLD_DATADIR/test/part%s.fnx
+#--remove_file $MYSQLD_DATADIR/test/part1.fnx
+#--remove_file $MYSQLD_DATADIR/test/part2.fnx
+#--remove_file $MYSQLD_DATADIR/test/part3.fnx
diff --git a/storage/connect/mysql-test/connect/t/part_table.test b/storage/connect/mysql-test/connect/t/part_table.test
new file mode 100644
index 00000000000..c5358d63c8e
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/part_table.test
@@ -0,0 +1,92 @@
+--source include/not_embedded.inc
+--source include/have_partition.inc
+
+set @@global.connect_exact_info=ON;
+
+#
+# These will be used by the t1 table partition table
+#
+CREATE TABLE xt1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=MyISAM;
+INSERT INTO xt1 VALUES(4, 'four'),(7,'seven'),(1,'one'),(8,'eight');
+SELECT * FROM xt1;
+
+CREATE TABLE xt2 (
+id INT KEY NOT NULL,
+msg VARCHAR(32));
+INSERT INTO xt2 VALUES(10,'ten'),(40,'forty'),(11,'eleven'),(35,'thirty five');
+SELECT * FROM xt2;
+
+CREATE TABLE xt3 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=10;
+INSERT INTO xt3 VALUES(60,'sixty'),(81,'eighty one'),(72,'seventy two');
+SELECT * FROM xt3;
+
+#
+# Based on PROXY the table is not indexable
+#
+CREATE TABLE t1 (
+id INT NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=PROXY TABNAME='xt%s'
+PARTITION BY RANGE COLUMNS(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+DELETE FROM t1;
+--error ER_UNKNOWN_ERROR
+ALTER TABLE t1 ADD INDEX XID(id);
+INSERT INTO t1 VALUES(4, 'four');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one');
+INSERT INTO t1 VALUES(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+EXPLAIN PARTITIONS
+SELECT * FROM t1 WHERE id = 81;
+DELETE FROM t1;
+DROP TABLE t1;
+
+#
+# Based on MYSQL the table is indexable
+#
+CREATE TABLE t1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='xt%s'
+PARTITION BY RANGE COLUMNS(id) (
+PARTITION `1` VALUES LESS THAN(10),
+PARTITION `2` VALUES LESS THAN(50),
+PARTITION `3` VALUES LESS THAN(MAXVALUE));
+SHOW INDEX FROM t1;
+INSERT INTO t1 VALUES(4, 'four');
+INSERT INTO t1 VALUES(40, 'forty');
+INSERT INTO t1 VALUES(72,'seventy two');
+INSERT INTO t1 VALUES(7,'seven'),(10,'ten'),(60,'sixty'),(81,'eighty one'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT partition_name, table_rows FROM information_schema.partitions WHERE table_name = 't1';
+SELECT * FROM t1;
+EXPLAIN PARTITIONS SELECT * FROM t1 WHERE id = 81;
+SELECT * FROM t1 WHERE id = 7;
+SELECT * FROM t1 WHERE id = 35;
+UPDATE t1 SET msg = 'number' WHERE id in (60,72);
+UPDATE t1 SET msg = 'soixante' WHERE id = 60;
+SELECT * FROM t1 WHERE id > 50;
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+UPDATE t1 SET msg = 'sept' WHERE id = 7;
+SELECT * FROM t1;
+DELETE FROM t1 WHERE id in (60,72);
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP TABLE xt1;
+DROP TABLE xt2;
+DROP TABLE xt3;
+
+#
+# Clean up
+#
+set @@global.connect_exact_info=OFF;
diff --git a/storage/connect/mysql-test/connect/t/temporary.test b/storage/connect/mysql-test/connect/t/temporary.test
new file mode 100644
index 00000000000..dda066c874b
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/temporary.test
@@ -0,0 +1,13 @@
+#
+# CONNECT tables cannot be TEMPORARY
+#
+
+--error ER_ILLEGAL_HA_CREATE_OPTION
+CREATE TEMPORARY TABLE t1 (a int not null)
+ ENGINE=CONNECT table_type=MYSQL CONNECTION='mysql://root@127.0.0.1/test/t2';
+
+# also with assisted discovery
+--error ER_ILLEGAL_HA_CREATE_OPTION
+CREATE TEMPORARY TABLE t1
+ ENGINE=CONNECT table_type=MYSQL CONNECTION='mysql://root@127.0.0.1/test/t2';
+
diff --git a/storage/connect/mysql-test/connect/t/updelx.inc b/storage/connect/mysql-test/connect/t/updelx.inc
new file mode 100644
index 00000000000..f38a59b9983
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/updelx.inc
@@ -0,0 +1,25 @@
+DELETE FROM t1;
+INSERT INTO t1 VALUES(4, 'four'),(7,'seven'),(10,'ten'),(40,'forty'),(60,'sixty'),(81,'eighty one'),(72,'seventy two'),(11,'eleven'),(1,'one'),(35,'thirty five'),(8,'eight');
+SELECT * FROM t1;
+UPDATE t1 SET msg = 'bof' WHERE id = 35;
+SELECT * FROM t1;
+UPDATE t1 SET msg = 'big' WHERE id > 50;
+SELECT * FROM t1;
+UPDATE t1 SET msg = 'updated' WHERE id IN (8,35,60,72);
+SELECT * FROM t1;
+UPDATE t1 SET msg = 'twin' WHERE id IN (81,10);
+SELECT * FROM t1;
+UPDATE t1 SET msg = 'sixty' WHERE id = 60;
+SELECT * FROM t1 WHERE id = 60;
+DELETE FROM t1 WHERE id = 4;
+SELECT * FROM t1;
+DELETE FROM t1 WHERE id IN (40,11,35);
+SELECT * FROM t1;
+DELETE FROM t1 WHERE id IN (4,60,1);
+SELECT msg FROM t1;
+DELETE FROM t1 WHERE id IN (81,72);
+SELECT id FROM t1;
+DELETE FROM t1 WHERE id IN (7,10);
+SELECT * FROM t1;
+DELETE FROM t1 WHERE id = 8;
+SELECT * FROM t1;
diff --git a/storage/connect/mysql-test/connect/t/updelx.test b/storage/connect/mysql-test/connect/t/updelx.test
new file mode 100644
index 00000000000..ab336d4b168
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/updelx.test
@@ -0,0 +1,96 @@
+-- source include/not_embedded.inc
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--echo #
+--echo # Testing indexed UPDATE and DELETE for all table types
+--echo #
+
+--echo # CSV table
+CREATE TABLE t1 (
+id INT KEY NOT NULL,
+msg VARCHAR(32))
+ENGINE=CONNECT TABLE_TYPE=CSV AVG_ROW_LENGTH=6;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=NO BLOCK_SIZE=6;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+--echo # DOS table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=DOS;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=NO BLOCK_SIZE=4;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+--echo # FIX table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16) CHARSET BINARY DISTRIB=CLUSTERED)
+ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=4;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+--echo # BIN table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16) CHARSET BINARY DISTRIB=CLUSTERED)
+ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=8;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+--echo # DBF table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=DBF BLOCK_SIZE=12;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+#ALTER TABLE t1 MAPPED=NO HUGE=YES;
+#-- source updelx.inc
+DROP TABLE t1;
+
+--echo # VEC table
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=VEC BLOCK_SIZE=6 MAX_ROWS=16;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=NO HUGE=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+--echo # Split VEC table (outward)
+CREATE TABLE t1 (
+id INT(4) KEY NOT NULL,
+msg VARCHAR(16))
+ENGINE=CONNECT TABLE_TYPE=VEC BLOCK_SIZE=6 FILE_NAME='tx.vec';
+-- source updelx.inc
+ALTER TABLE t1 MAPPED=YES;
+-- source updelx.inc
+DROP TABLE t1;
+
+# Cleanup
+--remove_file $MYSQLD_DATADIR/test/tx1.vec
+--remove_file $MYSQLD_DATADIR/test/tx2.vec
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index 9466bd83bea..7d5931285ce 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -28,6 +28,8 @@ 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 */
@@ -83,6 +85,7 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
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_PRTID = 8, /* PARTID 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 */
@@ -371,6 +374,7 @@ 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;
@@ -416,9 +420,10 @@ typedef struct { /* User application block */
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 */
+//USETEMP UseTemp; /* Use temporary file */
int Vtdbno; /* Used for TDB number setting */
bool Remote; /* true: if remotely called */
bool Proginfo; /* true: return progress info */
@@ -461,6 +466,21 @@ typedef struct _tabs {
} 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 */
@@ -529,7 +549,9 @@ typedef struct _colres {
PPARM Vcolist(PGLOBAL, PTDB, PSZ, bool);
void PlugPutOut(PGLOBAL, FILE *, short, void *, uint);
void PlugLineDB(PGLOBAL, PSZ, short, void *, uint);
-char *PlgGetDataPath(PGLOBAL g);
+//ar *PlgGetDataPath(PGLOBAL g);
+char *SetPath(PGLOBAL g, const char *path);
+char *ExtractFromPath(PGLOBAL, char *, char *, OPVAL);
void AddPointer(PTABS, void *);
PDTP MakeDateFormat(PGLOBAL, PSZ, bool, bool, int);
int ExtractDate(char *, PDTP, int, int val[6]);
@@ -538,14 +560,15 @@ 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,
+ int *buftyp, XFLD *fldtyp,
+ unsigned int *length,
bool blank, bool nonull);
/***********************************************************************/
/* Exported utility routines. */
/***********************************************************************/
DllExport FILE *PlugOpenFile(PGLOBAL, LPCSTR, LPCSTR);
+DllExport FILE *PlugReopenFile(PGLOBAL, PFBLOCK, LPCSTR);
DllExport int PlugCloseFile(PGLOBAL, PFBLOCK, bool all = false);
DllExport void PlugCleanup(PGLOBAL, bool);
DllExport bool GetPromptAnswer(PGLOBAL, char *);
@@ -561,7 +584,7 @@ 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 *,
+DllExport char *GetListOption(PGLOBAL, const char *, const char *,
const char *def=NULL);
#define MSGID_NONE 0
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index c0f7fc1c253..c5b66e8f5e6 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -336,17 +336,8 @@ 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;
-//dbuserp->Maxbmp = MAXBMP;
-//dbuserp->AlgChoice = AMOD_AUTO;
- dbuserp->UseTemp = TMP_AUTO;
+ dbuserp->Maxbmp = MAXBMP;
+//dbuserp->UseTemp = TMP_AUTO;
dbuserp->Check = CHK_ALL;
strcpy(dbuserp->Server, "CONNECT");
return dbuserp;
@@ -382,6 +373,7 @@ PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
return cat;
} // end of PlgGetCatalog
+#if 0
/***********************************************************************/
/* PlgGetDataPath: returns the default data path. */
/***********************************************************************/
@@ -391,6 +383,64 @@ char *PlgGetDataPath(PGLOBAL g)
return (cat) ? cat->GetDataPath() : NULL;
} // end of PlgGetDataPath
+#endif // 0
+
+/***********************************************************************/
+/* This function returns a database path. */
+/***********************************************************************/
+char *SetPath(PGLOBAL g, const char *path)
+{
+ char *buf= NULL;
+
+ if (path) {
+ size_t len= strlen(path) + (*path != '.' ? 4 : 1);
+
+ buf= (char*)PlugSubAlloc(g, NULL, len);
+
+ if (PlugIsAbsolutePath(path)) {
+ strcpy(buf, path);
+ return buf;
+ } // endif path
+
+ if (*path != '.') {
+#if defined(WIN32)
+ char *s= "\\";
+#else // !WIN32
+ char *s= "/";
+#endif // !WIN32
+ strcat(strcat(strcat(strcpy(buf, "."), s), path), s);
+ } else
+ strcpy(buf, path);
+
+ } // endif path
+
+ return buf;
+} // end of SetPath
+
+/***********************************************************************/
+/* Extract from a path name the required component. */
+/* This function assumes there is enough space in the buffer. */
+/***********************************************************************/
+char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
+ {
+ char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
+
+ switch (op) { // Determine which part to extract
+#if !defined(UNIX)
+ case OP_FDISK: drive = pBuff; break;
+#endif // !UNIX
+ case OP_FPATH: direc = pBuff; break;
+ case OP_FNAME: fname = pBuff; break;
+ case OP_FTYPE: ftype = pBuff; break;
+ default:
+ sprintf(g->Message, MSG(INVALID_OPER), op, "ExtractFromPath");
+ return NULL;
+ } // endswitch op
+
+ // Now do the extraction
+ _splitpath(FileName, drive, direc, fname, ftype);
+ return pBuff;
+ } // end of PlgExtractFromPath
/***********************************************************************/
/* Check the occurence and matching of a pattern against a string. */
@@ -820,6 +870,23 @@ FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
/* Close file routine: the purpose of this routine is to avoid */
/* double closing that freeze the system on some Unix platforms. */
/***********************************************************************/
+FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
+ {
+ FILE *fop;
+
+ if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
+ fp->Count = 1;
+ fp->Type = TYPE_FB_FILE;
+ fp->File = fop;
+ } /* endif fop */
+
+ return (fop);
+ } // end of PlugOpenFile
+
+/***********************************************************************/
+/* Close file routine: the purpose of this routine is to avoid */
+/* double closing that freeze the system on some Unix platforms. */
+/***********************************************************************/
int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
{
int rc = 0;
diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c
index 201aa5a4371..c3b77544983 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -2,11 +2,11 @@
/* */
/* PROGRAM NAME: PLUGUTIL */
/* ------------- */
-/* Version 2.7 */
+/* Version 2.8 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1993-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 1993-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -134,7 +134,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
PGLOBAL g;
if (trace > 1)
- htrc("PlugInit: Language='%s'\n",
+ htrc("PlugInit: Language='%s'\n",
((!Language) ? "Null" : (char*)Language));
if (!(g = malloc(sizeof(GLOBAL)))) {
@@ -145,6 +145,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
g->Trace = 0;
g->Createas = 0;
g->Alchecked = 0;
+ g->Mrr = 0;
g->Activityp = g->ActivityStart = NULL;
g->Xchk = NULL;
strcpy(g->Message, "");
@@ -298,7 +299,7 @@ LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
case '/':
break;
default:
- // This supposes that defdir ends with a SLASH
+ // This supposes that defdir ends with a SLASH
strcpy(direc, strcat(defdir, direc));
} // endswitch
@@ -321,13 +322,13 @@ LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
/***********************************************************************/
/* PlugGetMessage: get a message from the message file. */
/***********************************************************************/
-char *PlugReadMessage(PGLOBAL g, int mid, char *m)
+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",
+ GetPrivateProfileString("Message", msglang, "Message\\english.msg",
msgfile, _MAX_PATH, plgini);
if (!(mfile = fopen(msgfile, "rt"))) {
@@ -377,7 +378,7 @@ char *PlugReadMessage(PGLOBAL g, int mid, char *m)
/***********************************************************************/
/* PlugGetMessage: get a message from the resource string table. */
/***********************************************************************/
-char *PlugGetMessage(PGLOBAL g, int mid)
+char *PlugGetMessage(PGLOBAL g, int mid)
{
char *msg;
@@ -442,7 +443,7 @@ void *PlugAllocMem(PGLOBAL g, uint size)
htrc("Memory of %u allocated at %p\n", size, areap);
else
htrc("PlugAllocMem: %s\n", g->Message);
-
+
} // endif trace
return (areap);
@@ -521,7 +522,7 @@ void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
/***********************************************************************/
char *PlugDup(PGLOBAL g, const char *str)
{
- char *buf;
+ char *buf;
size_t len;
if (str && (len = strlen(str))) {
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index e7a96a12908..22076b78086 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -1,11 +1,11 @@
/************* RelDef CPP Program Source Code File (.CPP) **************/
/* PROGRAM NAME: REFDEF */
/* ------------- */
-/* Version 1.3 */
+/* Version 1.4 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -49,6 +49,8 @@
#include "tabmul.h"
#include "ha_connect.h"
+extern "C" int trace;
+extern "C" USETEMP Use_Temp;
/* --------------------------- Class RELDEF -------------------------- */
@@ -127,24 +129,39 @@ int RELDEF::GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size)
} // end of GetCharCatInfo
/***********************************************************************/
+/* To be used by any TDB's. */
+/***********************************************************************/
+bool RELDEF::Partitioned(void)
+ {
+ return Hc->IsPartitioned();
+ } // end of Partitioned
+
+/***********************************************************************/
/* This function returns string table information. */
/* Default parameter is "*" to get the handler default. */
/***********************************************************************/
char *RELDEF::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef)
{
- char *sval= NULL, *s= Hc->GetStringOption(what, sdef);
+ char *name, *sval= NULL, *s= Hc->GetStringOption(what, sdef);
if (s) {
- sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1);
- strcpy(sval, s);
+ if (!Hc->IsPartitioned() ||
+ (stricmp(what, "filename") && stricmp(what, "tabname")
+ && stricmp(what, "connect"))) {
+ sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1);
+ strcpy(sval, s);
+ } else
+ sval= s;
+
} else if (!stricmp(what, "filename")) {
// Return default file name
char *ftype= Hc->GetStringOption("Type", "*");
int i, n;
if (IsFileType(GetTypeID(ftype))) {
- sval= (char*)PlugSubAlloc(g, NULL, strlen(Hc->GetTableName()) + 12);
- strcat(strcpy(sval, Hc->GetTableName()), ".");
+ name= Hc->GetPartName();
+ sval= (char*)PlugSubAlloc(g, NULL, strlen(name) + 12);
+ strcat(strcpy(sval, name), ".");
n= strlen(sval);
// Fold ftype to lower case
@@ -211,6 +228,14 @@ bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
} // end of Define
/***********************************************************************/
+/* This function returns the database data path. */
+/***********************************************************************/
+PSZ TABDEF::GetPath(void)
+ {
+ return (Database) ? (PSZ)Database : Hc->GetDataPath();
+ } // end of GetPath
+
+/***********************************************************************/
/* This function returns column table information. */
/***********************************************************************/
int TABDEF::GetColCatInfo(PGLOBAL g)
@@ -546,7 +571,7 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
PTXF txfp = NULL;
PDOSDEF defp = (PDOSDEF)Pxdef;
bool map = defp->Mapped && mode != MODE_INSERT &&
- !(PlgGetUser(g)->UseTemp == TMP_FORCE &&
+ !(Use_Temp == TMP_FORCE &&
(mode == MODE_UPDATE || mode == MODE_DELETE));
int cmpr = defp->Compressed;
@@ -559,10 +584,8 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
#if defined(ZIP_SUPPORT)
if (cmpr == 1)
txfp = new(g) ZIPFAM(defp);
- else {
- strcpy(g->Message, "Compress 2 not supported yet");
- return NULL;
- } // endelse
+ else
+ txfp = new(g) ZLBFAM(defp);
#else // !ZIP_SUPPORT
strcpy(g->Message, "Compress not supported");
return NULL;
@@ -613,8 +636,10 @@ COLCRT::COLCRT(PSZ name)
Offset = -1;
Long = -1;
Precision = -1;
+ Freq = -1;
Key = -1;
Scale = -1;
+ Opt = -1;
DataType = '*';
} // end of COLCRT constructor for table creation
@@ -628,8 +653,10 @@ COLCRT::COLCRT(void)
Offset = 0;
Long = 0;
Precision = 0;
+ Freq = 0;
Key = 0;
Scale = 0;
+ Opt = 0;
DataType = '*';
} // end of COLCRT constructor for table & view definition
@@ -640,6 +667,14 @@ COLCRT::COLCRT(void)
/***********************************************************************/
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;
@@ -671,7 +706,9 @@ int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
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);
diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h
index 29e4bf77f44..a1dfe87dca8 100644
--- a/storage/connect/reldef.h
+++ b/storage/connect/reldef.h
@@ -1,7 +1,7 @@
/*************** RelDef H Declares Source Code File (.H) ***************/
-/* Name: RELDEF.H Version 1.3 */
+/* Name: RELDEF.H Version 1.5 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
/* */
/* This file contains the DEF classes definitions. */
/***********************************************************************/
@@ -13,7 +13,7 @@
#include "catalog.h"
#include "my_sys.h"
-typedef class INDEXDEF *PIXDEF;
+typedef class INDEXDEF *PIXDEF;
typedef class ha_connect *PHC;
/***********************************************************************/
@@ -42,6 +42,7 @@ class DllExport RELDEF : public BLOCK { // Relation definition block
// Methods
bool GetBoolCatInfo(PSZ what, bool bdef);
bool SetIntCatInfo(PSZ what, int ival);
+ bool Partitioned(void);
int GetIntCatInfo(PSZ what, int idef);
int GetSizeCatInfo(PSZ what, PSZ sdef);
int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size);
@@ -78,8 +79,9 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
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();}
+ PSZ GetPath(void);
+//PSZ GetPath(void)
+// {return (Database) ? (PSZ)Database : Cat->GetDataPath();}
bool SepIndex(void) {return GetBoolCatInfo("SepIndex", false);}
bool IsReadOnly(void) {return Read_Only;}
virtual AMT GetDefType(void) {return TYPE_AM_TAB;}
@@ -89,7 +91,7 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
const CHARSET_INFO *data_charset() {return m_data_charset;}
// Methods
- int GetColCatInfo(PGLOBAL g);
+ int GetColCatInfo(PGLOBAL g);
void SetIndexInfo(void);
bool DropTable(PGLOBAL g, PSZ name);
virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am);
@@ -156,6 +158,8 @@ class DllExport COLCRT : public BLOCK { /* Column description block
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;}
@@ -172,6 +176,8 @@ class DllExport COLCRT : public BLOCK { /* Column description block
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
@@ -193,12 +199,34 @@ class DllExport COLDEF : public COLCRT { /* Column description block
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);
bool IsSpecial(void) {return (Flags & U_SPECIAL) ? true : false;}
bool IsVirtual(void) {return (Flags & U_VIRTUAL) ? true : false;}
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 */
diff --git a/storage/connect/tabcol.h b/storage/connect/tabcol.h
index fdee653207e..3bfc37e69c1 100644
--- a/storage/connect/tabcol.h
+++ b/storage/connect/tabcol.h
@@ -97,7 +97,7 @@ class DllExport COLUMN: public XOBJECT { // Column Name/Qualifier block.
/***********************************************************************/
/* Definition of class SPCCOL with all its method functions. */
/* Note: Currently the special columns are ROWID, ROWNUM, FILEID, */
-/* SERVID, TABID, and CONID. */
+/* SERVID, TABID, PARTID, and CONID. */
/***********************************************************************/
class SPCCOL: public COLUMN { // Special Column Name/Qualifier block.
public:
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index e66a84f2fa4..c60c9b034f9 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -57,12 +57,29 @@
#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;
+
+extern "C" int trace;
+extern "C" USETEMP Use_Temp;
+extern bool xinfo;
+
+/***********************************************************************/
+/* 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 -------------------------- */
@@ -81,6 +98,9 @@ DOSDEF::DOSDEF(void)
Huge = false;
Accept = false;
Eof = false;
+ To_Pos = NULL;
+ Optimized = 0;
+ AllocBlks = 0;
Compressed = 0;
Lrecl = 0;
AvgLen = 0;
@@ -90,7 +110,6 @@ DOSDEF::DOSDEF(void)
Maxerr = 0;
ReadMode = 0;
Ending = 0;
-//Mtime = 0;
} // end of DOSDEF constructor
/***********************************************************************/
@@ -116,8 +135,8 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Compressed = GetIntCatInfo("Compressed", 0);
Mapped = GetBoolCatInfo("Mapped", map);
- Block = GetIntCatInfo("Blocks", 0);
- Last = GetIntCatInfo("Last", 0);
+//Block = GetIntCatInfo("Blocks", 0);
+//Last = GetIntCatInfo("Last", 0);
Ending = GetIntCatInfo("Ending", CRLF);
if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) {
@@ -137,46 +156,62 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
return false;
} // end of DefineAM
-#if 0
/***********************************************************************/
-/* 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. */
+/* Get the full path/name of the optization file. */
/***********************************************************************/
-bool DOSDEF::DeleteTableFile(PGLOBAL g)
+bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename)
{
- char filename[_MAX_PATH];
- bool rc = false;
+ char *ftype;
- // Now delete the table file itself if not protected
- if (!IsReadOnly()) {
- rc = Erase(filename);
- } else
- rc =true;
+ 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
- return rc; // Return true if error
- } // end of DeleteTableFile
+ PlugSetPath(filename, Ofn, GetPath());
+ strcat(PlugRemoveType(filename, filename), ftype);
+ return false;
+ } // end of GetOptFileName
/***********************************************************************/
-/* 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. */
+/* After an optimize error occured, remove all set optimize values. */
/***********************************************************************/
-bool DOSDEF::Erase(char *filename)
+void DOSDEF::RemoveOptValues(PGLOBAL g)
{
- bool rc;
-
- PlugSetPath(filename, Fn, GetPath());
+ 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)
- rc = !DeleteFile(filename);
+ DeleteFile(filename);
#else // UNIX
- rc = remove(filename);
-#endif // UNIX
+ remove(filename);
+#endif // WIN32
- return rc; // Return true if error
- } // end of Erase
-#endif // 0
+ Optimized = 0;
+ } // end of RemoveOptValues
/***********************************************************************/
/* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */
@@ -191,7 +226,7 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
return false; // No index
// If true indexes are in separate files
- sep = GetBoolCatInfo("SepIndex", false);
+ sep = GetBoolCatInfo("SepIndex", false);
if (!sep && pxdf) {
strcpy(g->Message, MSG(NO_RECOV_SPACE));
@@ -221,6 +256,10 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
#endif
char direc[_MAX_DIR];
char fname[_MAX_FNAME];
+ bool all = !pxdf;
+
+ if (all)
+ pxdf = To_Indx;
for (; pxdf; pxdf = pxdf->GetNext()) {
_splitpath(Ofn, drive, direc, fname, NULL);
@@ -228,10 +267,16 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
_makepath(filename, drive, direc, fname, ftype);
PlugSetPath(filename, filename, GetPath());
#if defined(WIN32)
- rc |= !DeleteFile(filename);
+ if (!DeleteFile(filename))
+ rc |= (GetLastError() != ERROR_FILE_NOT_FOUND);
#else // UNIX
- rc |= remove(filename);
+ if (remove(filename))
+ rc |= (errno != ENOENT);
#endif // UNIX
+
+ if (!all)
+ break;
+
} // endfor pxdf
} else { // !sep
@@ -239,9 +284,11 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
PlugSetPath(filename, Ofn, GetPath());
strcat(PlugRemoveType(filename, filename), ftype);
#if defined(WIN32)
- rc = !DeleteFile(filename);
+ if (!DeleteFile(filename))
+ rc = (GetLastError() != ERROR_FILE_NOT_FOUND);
#else // UNIX
- rc = remove(filename);
+ if (remove(filename))
+ rc = (errno != ENOENT);
#endif // UNIX
} // endif sep
@@ -269,7 +316,7 @@ bool DOSDEF::InvalidateIndex(PGLOBAL g)
PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
{
// Mapping not used for insert
- USETEMP tmp = PlgGetUser(g)->UseTemp;
+ USETEMP tmp = Use_Temp;
bool map = Mapped && mode != MODE_INSERT &&
!(tmp != TMP_NO && Recfm == RECFM_VAR
&& mode == MODE_UPDATE) &&
@@ -314,10 +361,9 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
#if defined(ZIP_SUPPORT)
if (Compressed == 1)
txfp = new(g) ZIPFAM(this);
- else {
- strcpy(g->Message, "Compress 2 not supported yet");
- return NULL;
- } // endelse
+ else
+ txfp = new(g) ZLBFAM(this);
+
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL;
@@ -334,6 +380,36 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
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
@@ -353,7 +429,13 @@ TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
AvgLen = tdp->AvgLen;
Ftype = tdp->Recfm;
To_Line = NULL;
- Cardinal = -1;
+//To_BlkIdx = NULL;
+ To_BlkFil = NULL;
+ SavFil = NULL;
+//Xeval = 0;
+ Beval = 0;
+ Abort = false;
+ Indxd = false;
} // end of TDBDOS standard constructor
TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
@@ -363,7 +445,13 @@ TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
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;
+ Abort = tdbp->Abort;
+ Indxd = tdbp->Indxd;
} // end of TDBDOS copy constructor
// Method
@@ -406,37 +494,1098 @@ void TDBDOS::PrintAM(FILE *f, char *m)
/***********************************************************************/
/* Remake the indexes after the table was modified. */
/***********************************************************************/
-int TDBDOS::ResetTableOpt(PGLOBAL g, bool dox)
+int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{
- int rc = RC_OK;
+ int prc = RC_OK, rc = RC_OK;
+
+ if (!GetFileLength(g)) {
+ // Void table, delete all opt and index files
+ PDOSDEF defp = (PDOSDEF)To_Def;
+
+ defp->RemoveOptValues(g);
+ return (defp->DeleteIndexFile(g, NULL)) ? RC_INFO : RC_OK;
+ } // endif GetFileLength
MaxSize = -1; // Size must be recalculated
Cardinal = -1; // as well as Cardinality
- if (dox) {
+ PTXF xp = Txfp;
+
+ To_Filter = NULL; // Disable filtering
+//To_BlkIdx = NULL; // and index filtering
+ To_BlkFil = NULL; // and block filtering
+
+ // After the table was modified the indexes
+ // are invalid and we should mark them as such...
+ (void)((PDOSDEF)To_Def)->InvalidateIndex(g);
+
+ 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 if (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
+// if (Mode != MODE_UPDATE)
+ To_SetCols = NULL; // Positions are changed
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.
+ if (PlgGetUser(g)->Check & CHK_OPT)
+ // We must remake all indexes.
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, n = 0;
+ int curnum, curblk, block, savndv, savnbm;
+ int last __attribute__((unused));
+ 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) {
+ if (!To_Def->Partitioned()) {
+ // This may be wrong to do in some cases
+ strcpy(g->Message, MSG(TABLE_NOT_OPT));
+ return RC_INFO; // Not to be optimized
+ } else
+ return RC_OK;
+
+ } 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));
+
+ // Valgrind complains if there are uninitialised bytes
+ // after the null character ending
+ if (IsTypeChar(cdp->GetType())) {
+ memset(cdp->GetMin(), 0, block * lg);
+ memset(cdp->GetMax(), 0, block * lg);
+ } // endif Type
+
+ 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
+
+ // No optimised columns. Still useful for blocked variable tables.
+ if (!colp && defp->Recfm != RECFM_VAR) {
+ strcpy(g->Message, "No optimised columns");
+ return RC_INFO;
+ } // endif colp
+
+ 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(PROG_INFO)
+ if (!dup->Step) {
+ strcpy(g->Message, MSG(OPT_CANCELLED));
+ goto err;
+ } else
+ dup->ProgCur = GetProgCur();
+#endif // PROG_INFO
+
+ n++; // Used to calculate block and last
+ } // endwhile
+
+ if (rc == RC_EF) {
+ Txfp->Nrec = nrec;
+
+#if 0 // No good because Curblk and CurNum after EOF are different
+ // depending on whether the file is mapped or not mapped.
+ if (blocked) {
+// Txfp->Block = Txfp->CurBlk + 1;
+ Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum : nrec;
+// Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec;
+ Txfp->Block = Txfp->CurBlk + (Txfp->Last == nrec ? 0 : 1);
+ } else {
+ Txfp->Block = curblk + 1;
+ Txfp->Last = last;
+ } // endif blocked
+#endif // 0
+
+ // New values of Block and Last
+ Txfp->Block = (n + nrec - 1) / nrec;
+ Txfp->Last = (n % nrec) ? (n % nrec) : nrec;
+
+ // This is needed to be able to calculate the last block size
+ Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos();
+ } else
+ goto err;
+
+ /*********************************************************************/
+ /* Save the optimization values for this table. */
+ /*********************************************************************/
+ if (!SaveBlockValues(g)) {
+ defp->Block = Txfp->Block;
+ defp->Last = Txfp->Last;
+ CloseDB(g);
+ defp->SetIntCatInfo("Blocks", Txfp->Block);
+ defp->SetIntCatInfo("Last", Txfp->Last);
+ return RC_OK;
+ } // endif SaveBlockValues
+
+ err:
+ // Restore Desc memory suballocation
+//if (memp)
+// *(PPOOLHEADER)memp = save;
+
+ defp->RemoveOptValues(g);
+ CloseDB(g);
+ return RC_FX;
+ } // end of MakeBlockValues
+
+/***********************************************************************/
+/* Save the block and Min/Max values for this table. */
+/* The problem here is to avoid name duplication, because more than */
+/* one data file can have the same name (but different types) and/or */
+/* the same data file can be used with different block sizes. This is */
+/* why we use Ofn that defaults to the file name but can be set to a */
+/* different name if necessary. */
+/***********************************************************************/
+bool TDBDOS::SaveBlockValues(PGLOBAL g)
+ {
+ char filename[_MAX_PATH];
+ int lg, n[NZ + 2];
+ size_t nbk, ndv, nbm, block = Txfp->Block;
+ bool rc = false;
+ FILE *opfile;
+ PDOSCOL colp;
+ PDOSDEF defp = (PDOSDEF)To_Def;
+
+ if (defp->GetOptFileName(g, filename))
+ return true;
+
+ if (!(opfile = fopen(filename, "wb"))) {
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "wb", (int)errno, filename);
+ strcat(strcat(g->Message, ": "), strerror(errno));
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return true;
+ } // endif opfile
+
+ memset(n, 0, sizeof(n)); // To avoid valgrind warning
+
+ 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 = 0, 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 0
+ if (Mode == MODE_INSERT && Txfp->GetAmType() == TYPE_AM_DOS)
+ return false;
+#endif // WIN32
+
+ 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. */
+ /*******************************************************************/
+ nrec = (defp->GetElemt()) ? defp->GetElemt() : 1;
+
+ 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);
+
+ if (h != -1)
+ close(h);
+
+ if (!flen) {
+ defp->SetOptimized(1);
+ return false;
+ } // endif flen
+
+ } else
+ return false; // Not optimisable
+
+ 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 (Ftype == RECFM_VAR || defp->Compressed == 2) {
+ /*******************************************************************/
+ /* Read block starting positions from the opt file. */
+ /*******************************************************************/
+ 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[1] != lg || n[2] != nrec) {
+ sprintf(g->Message, MSG(OPT_NOT_MATCH), filename);
+ goto err;
+ } // endif
+
+ last = n[0];
+ block = n[3];
+ blk = block + 1;
+
+ 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);
+ defp->Last = last; // For Cardinality
+ defp->SetAllocBlks(block);
+ defp->SetOptimized(1);
+ fclose(opfile);
+ MaxSize = -1; // Can be refined later
+ return false;
+
+ err:
+ defp->RemoveOptValues(g);
+ fclose(opfile);
+
+ // 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, n = 0, type[2] = {0,0};
+ int ctype __attribute__((unused));
+ 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;
+
+ ctype= TYPE_ERROR;
+ 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. Warn and abort.
+ sprintf(g->Message, "Block opt: %s", MSG(VALTYPE_NOMATCH));
+ PushWarning(g, this);
+ return NULL;
+ } // 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, or make */
+/* the block filter if To_Filter was not set when opening the table. */
+/***********************************************************************/
+void TDBDOS::ResetBlockFilter(PGLOBAL g)
+ {
+ if (!To_BlkFil) {
+ if (To_Filter)
+ if ((To_BlkFil = InitBlockFilter(g, To_Filter))) {
+ htrc("BlkFil=%p\n", To_BlkFil);
+ MaxSize = -1; // To be recalculated
+ } // endif To_BlkFil
+
+ return;
+ } // endif To_BlkFil
+
+ 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)
@@ -454,7 +1603,13 @@ int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
Mode = MODE_READ;
Use = USE_READY;
dfp = (PDOSDEF)To_Def;
- fixed = Cardinality(g) >= 0;
+
+ if (!Cardinality(g)) {
+ // Void table erase eventual index file(s)
+ (void)dfp->DeleteIndexFile(g, NULL);
+ return RC_OK;
+ } else
+ fixed = Ftype != RECFM_VAR;
// Are we are called from CreateTable or CreateIndex?
if (pxdf) {
@@ -482,7 +1637,7 @@ int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
// 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)
+ // 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))) {
@@ -580,6 +1735,113 @@ err:
} // end of MakeIndex
/***********************************************************************/
+/* Make a dynamic index. */
+/***********************************************************************/
+bool TDBDOS::InitialyzeIndex(PGLOBAL g, PIXDEF xdp, bool sorted)
+ {
+ int k, rc;
+ bool brc, dynamic;
+ PCOL colp;
+ PCOLDEF cdp;
+ PVAL valp;
+ PXLOAD pxp;
+ PKXBASE kxp;
+ PKPDEF kdp;
+
+ if (!xdp && !(xdp = To_Xdp)) {
+ strcpy(g->Message, "NULL dynamic index");
+ return true;
+ } else
+ dynamic = To_Filter && xdp->IsUnique() && xdp->IsDynamic();
+// dynamic = To_Filter && xdp->IsDynamic(); NIY
+
+ // Allocate the key columns definition block
+ Knum = xdp->GetNparts();
+ To_Key_Col = (PCOL*)PlugSubAlloc(g, NULL, Knum * sizeof(PCOL));
+
+ // Get the key column description list
+ for (k = 0, kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext())
+ if (!(colp = ColDB(g, kdp->GetName(), 0)) || colp->InitValue(g)) {
+ sprintf(g->Message, "Wrong column %s", kdp->GetName());
+ return true;
+ } else
+ To_Key_Col[k++] = colp;
+
+#if defined(_DEBUG)
+ if (k != 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
+ To_Link = (PXOB*)PlugSubAlloc(g, NULL, Knum * sizeof(PXOB));
+
+ for (k = 0, kdp = xdp->GetToKeyParts(); kdp; k++, kdp = kdp->GetNext()) {
+ cdp = Key(k)->GetCdp();
+ valp = AllocateValue(g, cdp->GetType(), cdp->GetLength());
+ To_Link[k]= new(g) CONSTANT(valp);
+ } // endfor k
+
+ // Make the index on xdp
+ if (!xdp->IsAuto()) {
+ if (!dynamic) {
+ if (((PDOSDEF)To_Def)->Huge)
+ pxp = new(g) XHUGE;
+ else
+ pxp = new(g) XFILE;
+
+ } else
+ pxp = NULL;
+
+ if (Knum == 1) // Single index
+ kxp = new(g) XINDXS(this, xdp, pxp, To_Key_Col, To_Link);
+ else // Multi-Column index
+ kxp = new(g) XINDEX(this, xdp, pxp, To_Key_Col, To_Link);
+
+ } else // Column contains same values as ROWID
+ kxp = new(g) XXROW(this);
+
+ // 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) {
+ if (dynamic) {
+ ResetBlockFilter(g);
+ kxp->SetDynamic(dynamic);
+ brc = kxp->Make(g, xdp);
+ } else
+ brc = kxp->Init(g);
+
+ if (!brc) {
+ if (Txfp->GetAmType() == TYPE_AM_BLK) {
+ // Cannot use indexing in DOS block mode
+ Txfp = new(g) DOSFAM((PBLKFAM)Txfp, (PDOSDEF)To_Def);
+ Txfp->AllocateBuffer(g);
+ To_BlkFil = NULL;
+ } // endif AmType
+
+ To_Kindex= kxp;
+
+ if (!(sorted && To_Kindex->IsSorted()) &&
+ ((Mode == MODE_UPDATE && IsUsingTemp(g)) ||
+ (Mode == MODE_DELETE && Txfp->GetAmType() != TYPE_AM_DBF)))
+ Indxd = true;
+
+ } // endif brc
+
+ } else
+ brc = true;
+
+ g->jump_level--;
+ return brc;
+ } // end of InitialyzeIndex
+
+/***********************************************************************/
/* DOS GetProgMax: get the max value for progress information. */
/***********************************************************************/
int TDBDOS::GetProgMax(PGLOBAL g)
@@ -619,11 +1881,82 @@ int TDBDOS::RowNumber(PGLOBAL g, bool b)
/***********************************************************************/
int TDBDOS::Cardinality(PGLOBAL g)
{
+ int n = Txfp->Cardinality(NULL);
+
if (!g)
- return Txfp->Cardinality(g);
+ return (Mode == MODE_ANY) ? 1 : n;
+
+ if (Cardinal < 0) {
+ if (!Txfp->Blocked && n == 0) {
+ // Info command, we try to return exact row number
+ PDOSDEF dfp = (PDOSDEF)To_Def;
+ PIXDEF xdp = dfp->To_Indx;
+
+ if (xdp && xdp->IsValid()) {
+ // Cardinality can be retreived from one index
+ PXLOAD pxp;
+
+ if (dfp->Huge)
+ pxp = new(g) XHUGE;
+ else
+ pxp = new(g) XFILE;
+
+ PXINDEX kxp = new(g) XINDEX(this, xdp, pxp, NULL, NULL);
+
+ if (!(kxp->GetAllSizes(g, Cardinal)))
+ return Cardinal;
+
+ } // endif Mode
+
+ if (Mode == MODE_ANY && xinfo) {
+ // Using index impossible or failed, do it the hard way
+ Mode = MODE_READ;
+ To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1);
+
+ if (Txfp->OpenTableFile(g))
+ return (Cardinal = Txfp->Cardinality(g));
+
+ for (Cardinal = 0; n != RC_EF;)
+ if (!(n = Txfp->ReadBuffer(g)))
+ Cardinal++;
+
+ Txfp->CloseTableFile(g, false);
+ Mode = MODE_ANY;
+ } else {
+ // Return the best estimate
+ int len = GetFileLength(g);
+
+ if (len >= 0) {
+ int rec;
+
+ 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 average record length. */
+ /*************************************************************/
+ rec = ((PDOSDEF)To_Def)->Ending;
+
+ if (AvgLen <= 0) // No given average estimate
+ rec += EstimatedLength(g);
+ else // An estimate was given for the average record length
+ rec += AvgLen;
+
+ Cardinal = (len + rec - 1) / rec;
- if (Cardinal < 0)
- Cardinal = Txfp->Cardinality(g);
+ if (trace)
+ htrc("avglen=%d MaxSize%d\n", rec, Cardinal);
+
+ } // endif len
+
+ } // endif Mode
+
+ } else
+ Cardinal = Txfp->Cardinality(g);
+
+ } // endif Cardinal
return Cardinal;
} // end of Cardinality
@@ -641,30 +1974,21 @@ int TDBDOS::GetMaxSize(PGLOBAL g)
int len = GetFileLength(g);
if (len >= 0) {
+ int rec;
+
if (trace)
- htrc("Estimating lines len=%d ending=%d\n",
+ 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). */
+ /* dividing the file length by minimum record length. */
/*****************************************************************/
- 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);
-
+ rec = EstimatedLength(g) + ((PDOSDEF)To_Def)->Ending;
MaxSize = (len + rec - 1) / rec;
if (trace)
- htrc(" Estimated max_K=%d\n", MaxSize);
+ htrc("avglen=%d MaxSize%d\n", rec, MaxSize);
} // endif len
@@ -697,10 +2021,8 @@ int TDBDOS::EstimatedLength(PGLOBAL g)
/***********************************************************************/
bool TDBDOS::IsUsingTemp(PGLOBAL g)
{
- USETEMP usetemp = PlgGetUser(g)->UseTemp;
-
- return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
- (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
+ return (Use_Temp == TMP_YES || Use_Temp == TMP_FORCE ||
+ (Use_Temp == TMP_AUTO && Mode == MODE_UPDATE));
} // end of IsUsingTemp
/***********************************************************************/
@@ -723,7 +2045,7 @@ bool TDBDOS::OpenDB(PGLOBAL g)
Txfp->Rewind(); // see comment in Work.log
if (SkipHeader(g))
- return TRUE;
+ return true;
} else
/*****************************************************************/
@@ -731,6 +2053,7 @@ bool TDBDOS::OpenDB(PGLOBAL g)
/*****************************************************************/
To_Kindex->Reset();
+ ResetBlockFilter(g);
return false;
} // endif use
@@ -739,7 +2062,7 @@ bool TDBDOS::OpenDB(PGLOBAL g)
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))) {
+ (Mode == MODE_UPDATE && Use_Temp != TMP_NO))) {
/*******************************************************************/
/* Delete is not currently handled in block mode neither Update */
/* when using a temporary file. */
@@ -750,7 +2073,7 @@ bool TDBDOS::OpenDB(PGLOBAL g)
else if (Txfp->GetAmType() == TYPE_AM_ZIP)
Txfp = new(g) ZIPFAM((PDOSDEF)To_Def);
#endif // ZIP_SUPPORT
- else if (Txfp->GetAmType() != TYPE_AM_DOS)
+ else // if (Txfp->GetAmType() != TYPE_AM_DOS) ???
Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
@@ -767,6 +2090,11 @@ bool TDBDOS::OpenDB(PGLOBAL g)
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);
@@ -847,13 +2175,10 @@ int TDBDOS::ReadDB(PGLOBAL g)
} // end of ReadDB
/***********************************************************************/
-/* WriteDB: Data Base write routine for DOS access method. */
+/* PrepareWriting: Prepare the line to write. */
/***********************************************************************/
-int TDBDOS::WriteDB(PGLOBAL g)
+bool TDBDOS::PrepareWriting(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;
@@ -868,6 +2193,20 @@ int TDBDOS::WriteDB(PGLOBAL g)
*(++p) = '\0';
} // endif Mode
+ return false;
+ } // end of WriteDB
+
+/***********************************************************************/
+/* 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);
+
+ // Make the line to write
+ (void)PrepareWriting(g);
+
if (trace > 1)
htrc("Write: line is='%s'\n", To_Line);
@@ -894,7 +2233,8 @@ void TDBDOS::CloseDB(PGLOBAL g)
To_Kindex = NULL;
} // endif
- Txfp->CloseTableFile(g);
+ Txfp->CloseTableFile(g, Abort);
+ RestoreNrec();
} // end of CloseDB
// ------------------------ DOSCOL functions ----------------------------
@@ -923,6 +2263,40 @@ DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am)
Deplac = cdp->GetOffset();
Long = cdp->GetLong();
To_Val = NULL;
+ Clustered = cdp->GetOpt();
+ Sorted = (cdp->GetOpt() == 2) ? 1 : 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
Dsp = 0;
Ldz = false;
@@ -970,9 +2344,39 @@ DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
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)
@@ -1260,6 +2664,144 @@ void DOSCOL::WriteColumn(PGLOBAL g)
} // 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;
+ uint *bmp;
+ PTDBDOS tp = (PTDBDOS)To_Tdb;
+ PDBUSER dup = PlgGetUser(g);
+
+ n = tp->Txfp->CurNum;
+ bmp = (uint*)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)
diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h
index 79a2659fb70..1c772e8bf23 100644
--- a/storage/connect/tabdos.h
+++ b/storage/connect/tabdos.h
@@ -12,9 +12,12 @@
#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. */
@@ -47,6 +50,11 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
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 int Indexable(void)
@@ -55,6 +63,8 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
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);
@@ -69,6 +79,9 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
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 */
@@ -112,6 +125,7 @@ class DllExport TDBDOS : public TDBASE {
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 void SetAbort(bool b) {Abort = b;}
virtual RECFM GetFtype(void) {return Ftype;}
virtual bool SkipHeader(PGLOBAL g) {return false;}
virtual void RestoreNrec(void) {Txfp->SetNrec(1);}
@@ -122,8 +136,15 @@ class DllExport TDBDOS : public TDBASE {
virtual PTDB CopyOne(PTABS t);
virtual void ResetDB(void) {Txfp->Reset();}
virtual bool IsUsingTemp(PGLOBAL g);
+ virtual bool IsIndexed(void) {return Indxd;}
virtual void ResetSize(void) {MaxSize = Cardinal = -1;}
- virtual int ResetTableOpt(PGLOBAL g, bool dox);
+ 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
@@ -132,7 +153,7 @@ class DllExport TDBDOS : public TDBASE {
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 GetAffectedRows(void) {return Txfp->GetDelRows();}
virtual int GetRecpos(void) {return Txfp->GetPos();}
virtual bool SetRecpos(PGLOBAL g, int recpos)
{return Txfp->SetPos(g, recpos);}
@@ -151,15 +172,26 @@ class DllExport TDBDOS : public TDBASE {
// Optimization routines
virtual int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
+ bool InitialyzeIndex(PGLOBAL g, PIXDEF xdp, bool sorted);
+ void ResetBlockFilter(PGLOBAL g);
+ bool GetDistinctColumnValues(PGLOBAL g, int nrec);
protected:
+ virtual bool PrepareWriting(PGLOBAL g);
+ 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)
+ bool Abort; // TRUE when aborting UPDATE/DELETE
+ bool Indxd; // TRUE for indexed UPDATE/DELETE
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
/***********************************************************************/
@@ -178,19 +210,38 @@ class DllExport DOSCOL : public COLBLK {
// 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 read/write operations
@@ -199,6 +250,10 @@ class DllExport DOSCOL : public COLBLK {
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 cda08b7e3a6..91f06536272 100644
--- a/storage/connect/tabfix.cpp
+++ b/storage/connect/tabfix.cpp
@@ -45,11 +45,15 @@
#include "filamfix.h"
#include "filamdbf.h"
#include "tabfix.h" // TDBFIX, FIXCOL classes declares
+#include "array.h"
+#include "blkfil.h"
/***********************************************************************/
/* DB static variables. */
/***********************************************************************/
-extern "C" int trace;
+extern "C" int trace;
+extern "C" USETEMP Use_Temp;
+
extern int num_read, num_there, num_eq[2]; // Statistics
static const longlong M2G = 0x80000000;
static const longlong M4G = (longlong)2 * M2G;
@@ -61,12 +65,10 @@ static const longlong M4G = (longlong)2 * M2G;
/***********************************************************************/
TDBFIX::TDBFIX(PDOSDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
{
-//Cardinal = -1;
} // end of TDBFIX standard constructor
TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp)
{
-//Cardinal = tdbp->Cardinal;
} // end of TDBFIX copy constructor
// Method
@@ -123,10 +125,48 @@ PCOL TDBFIX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
/***********************************************************************/
/* Remake the indexes after the table was modified. */
/***********************************************************************/
-int TDBFIX::ResetTableOpt(PGLOBAL g, bool dox)
+int TDBFIX::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{
+ int prc, rc = RC_OK;
+
+ To_Filter = NULL; // Disable filtering
+//To_BlkIdx = NULL; // and block filtering
+ To_BlkFil = NULL; // and index filtering
+ Cardinality(g); // If called by create
RestoreNrec(); // May have been modified
- return TDBDOS::ResetTableOpt(g, dox);
+ MaxSize = -1; // Size must be recalculated
+ Cardinal = -1; // as well as Cardinality
+
+ // After the table was modified the indexes
+ // are invalid and we should mark them as such...
+ rc = ((PDOSDEF)To_Def)->InvalidateIndex(g);
+
+ if (dop) {
+ Columns = NULL; // Not used anymore
+ Txfp->Reset();
+// OldBlk = CurBlk = -1;
+// ReadBlks = CurNum = Rbuf = Modif = 0;
+ 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
+ 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)
+ // We must remake indexes.
+ rc = MakeIndex(g, NULL, FALSE);
+
+ rc = (rc == RC_INFO) ? prc : rc;
+ } // endif dox
+
+ return rc;
} // end of ResetTableOpt
/***********************************************************************/
@@ -138,6 +178,11 @@ void TDBFIX::RestoreNrec(void)
Txfp->Nrec = (To_Def && To_Def->GetElemt()) ? To_Def->GetElemt()
: DOS_BUFF_LEN;
Txfp->Blksize = Txfp->Nrec * Txfp->Lrecl;
+
+ if (Cardinal >= 0)
+ Txfp->Block = (Cardinal > 0)
+ ? (Cardinal + Txfp->Nrec - 1) / Txfp->Nrec : 0;
+
} // endif Padded
} // end of RestoreNrec
@@ -163,9 +208,18 @@ int TDBFIX::Cardinality(PGLOBAL g)
/***********************************************************************/
int TDBFIX::GetMaxSize(PGLOBAL g)
{
- if (MaxSize < 0)
+ if (MaxSize < 0) {
MaxSize = Cardinality(g);
+ 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 MaxSize
+
return MaxSize;
} // end of GetMaxSize
@@ -217,9 +271,11 @@ int TDBFIX::RowNumber(PGLOBAL g, bool b)
/***********************************************************************/
bool TDBFIX::IsUsingTemp(PGLOBAL g)
{
- USETEMP usetemp = PlgGetUser(g)->UseTemp;
-
- return (usetemp == TMP_YES || usetemp == TMP_FORCE);
+ // Not ready yet to handle using a temporary file with mapping
+ // or while deleting from DBF files.
+ return ((Use_Temp == TMP_YES && Txfp->GetAmType() != TYPE_AM_MAP &&
+ !(Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_DBF)) ||
+ Use_Temp == TMP_FORCE || Use_Temp == TMP_TEST);
} // end of IsUsingTemp
/***********************************************************************/
@@ -246,11 +302,13 @@ bool TDBFIX::OpenDB(PGLOBAL g)
else
Txfp->Rewind(); // see comment in Work.log
+ ResetBlockFilter(g);
return false;
} // endif use
- if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() == TYPE_AM_MAP) {
- // Delete all lines. Not handled in MAP mode
+ if (Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_MAP &&
+ (!Next || Use_Temp == TMP_FORCE)) {
+ // Delete all lines or using temp. Not handled in MAP mode
Txfp = new(g) FIXFAM((PDOSDEF)To_Def);
Txfp->SetTdbp(this);
} // endif Mode
@@ -277,8 +335,13 @@ bool TDBFIX::OpenDB(PGLOBAL g)
/*********************************************************************/
To_Line = Txfp->GetBuf(); // For WriteDB
+ /*********************************************************************/
+ /* Allocate the block filter tree if evaluation is possible. */
+ /*********************************************************************/
+ To_BlkFil = InitBlockFilter(g, To_Filter);
+
if (trace)
- htrc("OpenDos: R%hd mode=%d\n", Tdb_No, Mode);
+ htrc("OpenFix: R%hd mode=%d BlkFil=%p\n", Tdb_No, Mode, To_BlkFil);
/*********************************************************************/
/* Reset buffer access according to indexing and to mode. */
diff --git a/storage/connect/tabfix.h b/storage/connect/tabfix.h
index bcd171b37bb..7d5b964da2a 100644
--- a/storage/connect/tabfix.h
+++ b/storage/connect/tabfix.h
@@ -38,7 +38,7 @@ class DllExport TDBFIX : public TDBDOS {
virtual void ResetDB(void);
virtual bool IsUsingTemp(PGLOBAL g);
virtual int RowNumber(PGLOBAL g, bool b = false);
- virtual int ResetTableOpt(PGLOBAL g, bool dox);
+ virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
virtual void ResetSize(void);
virtual int GetBadLines(void) {return Txfp->GetNerr();}
@@ -51,6 +51,8 @@ class DllExport TDBFIX : public TDBDOS {
virtual int WriteDB(PGLOBAL g);
protected:
+ virtual bool PrepareWriting(PGLOBAL g) {return false;}
+
// Members are inherited from TDBDOS
}; // end of class TDBFIX
@@ -89,7 +91,8 @@ class TDBDCL : public TDBCAT {
protected:
// Specific routines
- virtual PQRYRES GetResult(PGLOBAL g) {return DBFColumns(g, Fn, false);}
+ virtual PQRYRES GetResult(PGLOBAL g)
+ {return DBFColumns(g, ((PTABDEF)To_Def)->GetPath(), Fn, false);}
// Members
char *Fn; // The DBF file (path) name
diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp
index 7665395167d..c1119c57065 100644
--- a/storage/connect/tabfmt.cpp
+++ b/storage/connect/tabfmt.cpp
@@ -1,11 +1,11 @@
/************* TabFmt C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABFMT */
/* ------------- */
-/* Version 3.8 */
+/* Version 3.9 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2001 - 2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2001 - 2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -16,7 +16,7 @@
/***********************************************************************/
/***********************************************************************/
-/* Include relevant MariaDB header file. */
+/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
@@ -66,7 +66,8 @@
#define MAXCOL 200 /* Default max column nb in result */
#define TYPE_UNKNOWN 10 /* Must be greater than other types */
-extern "C" int trace;
+extern "C" int trace;
+extern "C" USETEMP Use_Temp;
/***********************************************************************/
/* CSVColumns: constructs the result blocks containing the description */
@@ -75,8 +76,8 @@ extern "C" int trace;
/* 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)
+PQRYRES CSVColumns(PGLOBAL g, char *dp, 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};
@@ -130,7 +131,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
/*********************************************************************/
/* Open the input file. */
/*********************************************************************/
- PlugSetPath(filename, fn, PlgGetDataPath(g));
+ PlugSetPath(filename, fn, dp);
if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
return NULL;
@@ -145,7 +146,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
n = strlen(buf) + 1;
buf[n - 2] = '\0';
#if defined(UNIX)
- // The file can be imported from Windows
+ // The file can be imported from Windows
if (buf[n - 3] == '\r')
buf[n - 3] = 0;
#endif // UNIX
@@ -202,7 +203,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
n = strlen(buf);
buf[n - 1] = '\0';
#if defined(UNIX)
- // The file can be imported from Windows
+ // The file can be imported from Windows
if (buf[n - 2] == '\r')
buf[n - 2] = 0;
#endif // UNIX
@@ -392,7 +393,7 @@ CSVDEF::CSVDEF(void)
Fmtd = Accept = Header = false;
Maxerr = 0;
Quoted = -1;
- Sep = ',';
+ Sep = ',';
Qot = '\0';
} // end of CSVDEF constructor
@@ -441,7 +442,7 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
PTDBASE tdbp;
if (Catfunc != FNC_COL) {
- USETEMP tmp = PlgGetUser(g)->UseTemp;
+ USETEMP tmp = Use_Temp;
bool map = Mapped && mode != MODE_INSERT &&
!(tmp != TMP_NO && mode == MODE_UPDATE) &&
!(tmp == TMP_FORCE &&
@@ -458,10 +459,9 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
#if defined(ZIP_SUPPORT)
if (Compressed == 1)
txfp = new(g) ZIPFAM(this);
- else {
- strcpy(g->Message, "Compress 2 not supported yet");
- return NULL;
- } // endelse
+ else
+ txfp = new(g) ZLBFAM(this);
+
#else // !ZIP_SUPPORT
strcpy(g->Message, "Compress not supported");
return NULL;
@@ -480,6 +480,36 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
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 (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
+
+ } // endelse
} else
tdbp = new(g)TDBCCL(this);
@@ -591,34 +621,27 @@ bool TDBCSV::CheckErr(void)
/***********************************************************************/
int TDBCSV::EstimatedLength(PGLOBAL g)
{
+ int n = 0;
+ PCOLDEF cdp;
+
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() && !colp->IsVirtual()) // A true column
- Fields = MY_MAX(Fields, (int)colp->Fldnum);
-
- if (Columns)
- Fields++; // Fldnum was 0 based
- } // endif Fields
+ for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
+ if (!cdp->IsSpecial() && !cdp->IsVirtual()) // A true column
+ n++;
- return (int)Fields; // Number of separators if all fields are null
+ return --n; // Number of separators if all fields are null
} // end of Estimated Length
#if 0
/***********************************************************************/
-/* CSV tables favor the use temporary files for Update. */
+/* CSV tables needs 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));
+ return (Use_Temp == TMP_YES || Use_Temp == TMP_FORCE ||
+ (Use_Temp == TMP_AUTO && Mode == MODE_UPDATE));
} // end of IsUsingTemp
#endif // 0 (Same as TDBDOS one)
@@ -649,7 +672,7 @@ bool TDBCSV::OpenDB(PGLOBAL g)
} else
for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- if (!cdp->IsVirtual())
+ if (!cdp->IsSpecial() && !cdp->IsVirtual())
Fields++;
Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
@@ -686,7 +709,7 @@ bool TDBCSV::OpenDB(PGLOBAL g)
} else // MODE_UPDATE
for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- if (!cdp->IsVirtual()) {
+ if (!cdp->IsSpecial() && !cdp->IsVirtual()) {
i = cdp->GetOffset() - 1;
len = cdp->GetLength();
Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
@@ -905,9 +928,9 @@ int TDBCSV::ReadBuffer(PGLOBAL g)
} // end of ReadBuffer
/***********************************************************************/
-/* Data Base write routine CSV file access method. */
+/* Prepare the line to write. */
/***********************************************************************/
-int TDBCSV::WriteDB(PGLOBAL g)
+bool TDBCSV::PrepareWriting(PGLOBAL g)
{
char sep[2], qot[2];
int i, nlen, oldlen = strlen(To_Line);
@@ -918,7 +941,7 @@ int TDBCSV::WriteDB(PGLOBAL g)
// Before writing the line we must check its length
if ((nlen = CheckWrite(g)) < 0)
- return RC_FX;
+ return true;
// Before writing the line we must make it
sep[0] = Sep;
@@ -981,6 +1004,18 @@ int TDBCSV::WriteDB(PGLOBAL g)
if (trace > 1)
htrc("Write: line is=%s", To_Line);
+ return false;
+ } // end of PrepareWriting
+
+/***********************************************************************/
+/* Data Base write routine CSV file access method. */
+/***********************************************************************/
+int TDBCSV::WriteDB(PGLOBAL g)
+ {
+ // Before writing the line we must check and prepare it
+ if (PrepareWriting(g))
+ return RC_FX;
+
/*********************************************************************/
/* Now start the writing process. */
/*********************************************************************/
@@ -1080,7 +1115,7 @@ PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
int TDBFMT::EstimatedLength(PGLOBAL g)
{
// This is rather stupid !!!
- return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
+ return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
} // end of EstimatedLength
/***********************************************************************/
@@ -1118,7 +1153,8 @@ bool TDBFMT::OpenDB(PGLOBAL g)
// Get the column formats
for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
- if (!cdp->IsVirtual() && (i = cdp->GetOffset() - 1) < Fields) {
+ if (!cdp->IsSpecial() && !cdp->IsVirtual()
+ && (i = cdp->GetOffset() - 1) < Fields) {
if (!(pfm = cdp->GetFmt())) {
sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
return true;
@@ -1275,6 +1311,25 @@ CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
} // 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. */
/***********************************************************************/
@@ -1408,7 +1463,7 @@ TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
Hdr = tdp->Header;
Mxr = tdp->Maxerr;
Qtd = tdp->Quoted;
- Sep = tdp->Sep;
+ Sep = tdp->Sep;
} // end of TDBCCL constructor
/***********************************************************************/
@@ -1416,7 +1471,8 @@ TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
/***********************************************************************/
PQRYRES TDBCCL::GetResult(PGLOBAL g)
{
- return CSVColumns(g, Fn, Sep, Qtd, Hdr, Mxr, false);
- } // end of GetResult
+ return CSVColumns(g, ((PTABDEF)To_Def)->GetPath(),
+ 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 5efa824c2e2..1b39a47e7d9 100644
--- a/storage/connect/tabfmt.h
+++ b/storage/connect/tabfmt.h
@@ -1,7 +1,7 @@
/*************** TabFmt H Declares Source Code File (.H) ***************/
-/* Name: TABFMT.H Version 2.3 */
+/* Name: TABFMT.H Version 2.4 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */
/* */
/* This file contains the CSV and FMT classes declares. */
/***********************************************************************/
@@ -13,8 +13,8 @@ typedef class TDBFMT *PTDBFMT;
/***********************************************************************/
/* Functions used externally. */
/***********************************************************************/
-PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
- int hdr, int mxr, bool info);
+PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep,
+ char q, int hdr, int mxr, bool info);
/***********************************************************************/
/* CSV table. */
@@ -80,6 +80,8 @@ class TDBCSV : public TDBDOS {
virtual bool CheckErr(void);
protected:
+ virtual bool PrepareWriting(PGLOBAL g);
+
// Members
PSZ *Field; // Field to write to current line
int *Offset; // Column offsets for current record
@@ -111,6 +113,7 @@ class CSVCOL : public DOSCOL {
virtual int GetAmType() {return TYPE_AM_CSV;}
// Methods
+ virtual bool VarSize(void);
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
@@ -157,6 +160,9 @@ class TDBFMT : public TDBCSV {
virtual int EstimatedLength(PGLOBAL g);
protected:
+ virtual bool PrepareWriting(PGLOBAL g)
+ {strcpy(g->Message, "FMT is read only"); return true;}
+
// Members
PSZ *FldFormat; // Field read format
void *To_Fld; // To field test buffer
@@ -173,15 +179,15 @@ class TDBCCL : public TDBCAT {
TDBCCL(PCSVDEF tdp);
protected:
- // Specific routines
- virtual PQRYRES GetResult(PGLOBAL g);
+ // 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
+ 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 325e80945f7..5db50d44787 100644
--- a/storage/connect/table.cpp
+++ b/storage/connect/table.cpp
@@ -45,19 +45,22 @@ 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;
+ Mode = MODE_ANY;
+ Cardinal = -1;
} // 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;
@@ -65,6 +68,7 @@ TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum)
Columns = NULL;
Degree = tdbp->Degree;
Mode = tdbp->Mode;
+ Cardinal = tdbp->Cardinal;
} // end of TDB copy constructor
// Methods
@@ -137,7 +141,9 @@ TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
To_Link = NULL;
To_Key_Col = NULL;
To_Kindex = NULL;
+ To_Xdp = NULL;
To_SetCols = NULL;
+ Ftype = RECFM_NAF;
MaxSize = -1;
Knum = 0;
Read_Only = (tdp) ? tdp->IsReadOnly() : false;
@@ -147,8 +153,14 @@ TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
{
To_Def = tdbp->To_Def;
+ To_Link = tdbp->To_Link;
+ To_Key_Col = tdbp->To_Key_Col;
+ To_Kindex = tdbp->To_Kindex;
+ To_Xdp = tdbp->To_Xdp;
To_SetCols = tdbp->To_SetCols; // ???
+ Ftype = tdbp->Ftype;
MaxSize = tdbp->MaxSize;
+ Knum = tdbp->Knum;
Read_Only = tdbp->Read_Only;
m_data_charset= tdbp->m_data_charset;
} // end of TDBASE copy constructor
@@ -167,7 +179,7 @@ PCATLG TDBASE::GetCat(void)
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
+ // 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
@@ -204,10 +216,11 @@ PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num)
/* Also find where to insert the new block. */
/*****************************************************************/
for (cp = Columns; cp; cp = cp->GetNext())
- if (cp->GetIndex() < i)
+ if ((num && cp->GetIndex() == i) ||
+ (name && !stricmp(cp->GetName(), name)))
+ break; // Found
+ else 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);
@@ -219,12 +232,12 @@ PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num)
colp = cp;
else if (!(cdp->Flags & U_SPECIAL))
colp = MakeCol(g, cdp, cprec, i);
- else if (Mode == MODE_READ)
+ else if (Mode != MODE_INSERT)
colp = InsertSpcBlk(g, cdp);
if (trace)
htrc("colp=%p\n", colp);
-
+
if (name || num)
break;
else if (colp && !colp->IsSpecial())
@@ -259,22 +272,38 @@ PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
PCOL colp;
cp= new(g) COLUMN(cdp->GetName());
- cp->SetTo_Table(To_Table);
- if (!stricmp(name, "FILEID") ||
- !stricmp(name, "SERVID")) {
+ if (! To_Table) {
+ strcpy(g->Message, "Cannot make special column: To_Table is NULL");
+ return NULL;
+ } else
+ cp->SetTo_Table(To_Table);
+
+ if (!stricmp(name, "FILEID") || !stricmp(name, "FDISK") ||
+ !stricmp(name, "FPATH") || !stricmp(name, "FNAME") ||
+ !stricmp(name, "FTYPE") || !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);
+ colp = new(g) FIDBLK(cp, OP_XX);
+ else if (!stricmp(name, "FDISK"))
+ colp = new(g) FIDBLK(cp, OP_FDISK);
+ else if (!stricmp(name, "FPATH"))
+ colp = new(g) FIDBLK(cp, OP_FPATH);
+ else if (!stricmp(name, "FNAME"))
+ colp = new(g) FIDBLK(cp, OP_FNAME);
+ else if (!stricmp(name, "FTYPE"))
+ colp = new(g) FIDBLK(cp, OP_FTYPE);
else
colp = new(g) SIDBLK(cp);
} else if (!stricmp(name, "TABID")) {
colp = new(g) TIDBLK(cp);
+ } else if (!stricmp(name, "PARTID")) {
+ colp = new(g) PRTBLK(cp);
//} else if (!stricmp(name, "CONID")) {
// colp = new(g) CIDBLK(cp);
} else if (!stricmp(name, "ROWID")) {
@@ -297,7 +326,7 @@ PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
/***********************************************************************/
/* ResetTableOpt: Wrong for this table type. */
/***********************************************************************/
-int TDBASE::ResetTableOpt(PGLOBAL g, bool dox)
+int TDBASE::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
{
strcpy(g->Message, "This table is not indexable");
return RC_INFO;
@@ -324,7 +353,7 @@ void TDBASE::ResetKindex(PGLOBAL g, PKXBASE kxp)
/***********************************************************************/
/* SetRecpos: Replace the table at the specified position. */
/***********************************************************************/
-bool TDBASE::SetRecpos(PGLOBAL g, int recpos)
+bool TDBASE::SetRecpos(PGLOBAL g, int recpos)
{
strcpy(g->Message, MSG(SETRECPOS_NIY));
return true;
@@ -389,8 +418,8 @@ PCOL TDBCAT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
/***********************************************************************/
bool TDBCAT::Initialize(PGLOBAL g)
{
- if (Init)
- return false;
+ if (Init)
+ return false;
if (!(Qrp = GetResult(g)))
return true;
@@ -405,9 +434,9 @@ bool TDBCAT::Initialize(PGLOBAL g)
PushWarning(g, this);
} // endif Badlines
- Init = true;
- return false;
- } // end of Initialize
+ Init = true;
+ return false;
+ } // end of Initialize
/***********************************************************************/
/* CAT: Get the number of properties. */
@@ -487,7 +516,7 @@ bool TDBCAT::InitCol(PGLOBAL g)
/***********************************************************************/
/* SetRecpos: Replace the table at the specified position. */
/***********************************************************************/
-bool TDBCAT::SetRecpos(PGLOBAL g, int recpos)
+bool TDBCAT::SetRecpos(PGLOBAL g, int recpos)
{
N = recpos - 1;
return false;
diff --git a/storage/connect/tabmac.h b/storage/connect/tabmac.h
index eb115b18049..5e6c98d68fb 100644
--- a/storage/connect/tabmac.h
+++ b/storage/connect/tabmac.h
@@ -58,6 +58,7 @@ class TDBMAC : public TDBASE {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g) {return GetMaxSize(g);}
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp
index 6e4a63d0f6d..4b40e6c5509 100755
--- a/storage/connect/tabmul.cpp
+++ b/storage/connect/tabmul.cpp
@@ -680,7 +680,7 @@ char* TDBDIR::Path(PGLOBAL g)
#if defined(WIN32)
if (!*Drive) {
- PlugSetPath(Fpath, To_File, cat->GetDataPath());
+ PlugSetPath(Fpath, To_File, ((PTABDEF)To_Def)->GetPath());
_splitpath(Fpath, Drive, Direc, Fname, Ftype);
} else
_makepath(Fpath, Drive, Direc, Fname, Ftype); // Usefull ???
@@ -688,7 +688,7 @@ char* TDBDIR::Path(PGLOBAL g)
return Fpath;
#else // !WIN32
if (!Done) {
- PlugSetPath(Fpath, To_File, cat->GetDataPath());
+ PlugSetPath(Fpath, To_File, ((PTABDEF)To_Def)->GetPath());
_splitpath(Fpath, NULL, Direc, Fname, Ftype);
strcat(strcpy(Pattern, Fname), Ftype);
Done = true;
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index 37c72501840..6acdcbb3a8e 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1,11 +1,11 @@
/************* TabMySQL C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABMYSQL */
/* ------------- */
-/* Version 1.7 */
+/* Version 1.9 */
/* */
/* AUTHOR: */
/* ------- */
-/* Olivier BERTRAND 2007-2013 */
+/* Olivier BERTRAND 2007-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -68,6 +68,11 @@ void PrintResult(PGLOBAL, PSEM, PQRYRES);
#endif // _CONSOLE
extern "C" int trace;
+extern bool xinfo;
+
+// Used to check whether a MYSQL table is created on itself
+bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
+ const char *db, char *tab, const char *src, int port);
/* -------------- Implementation of the MYSQLDEF class --------------- */
@@ -109,7 +114,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
} // endif server_name
// get_server_by_name() clones the server if exists and allocates
- // copies of strings in the supplied mem_root
+ // 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 */
@@ -181,7 +186,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
// 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,
+ // If there is a single '/' in the connection string,
// this means the user is specifying a table name
*Tabname++= '\0';
@@ -260,7 +265,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
} // endif /
} // endif Tabname
-
+
} // endif database
if ((sport = strchr(Hostname, ':')))
@@ -311,7 +316,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
// Normal case of specific MYSQL table
url = GetStringCatInfo(g, "Connect", NULL);
- if (!url || !*url) {
+ if (!url || !*url) {
// Not using the connection URL
Hostname = GetStringCatInfo(g, "Host", "localhost");
Database = GetStringCatInfo(g, "Database", "*");
@@ -353,8 +358,12 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Tabname = Name;
} // endif am
- if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
+ Read_Only = true;
Isview = true;
+ } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Database,
+ Tabname, Srcdef, Portnumber))
+ return true;
// Used for Update and Delete
Qrystr = GetStringCatInfo(g, "Query_String", "?");
@@ -436,7 +445,7 @@ TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp)
Tabname = tdbp->Tabname;
Srcdef = tdbp->Srcdef;
User = tdbp->User;
- Pwd = tdbp->Pwd;
+ Pwd = tdbp->Pwd;
Qrystr = tdbp->Qrystr;
Quoted = tdbp->Quoted;
Port = tdbp->Port;
@@ -603,9 +612,7 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g)
else
qlen += colp->GetLength();
- } // endif Prep
-
- if (Prep)
+ } else // Prep
strcat(valist, "?");
} // endfor colp
@@ -647,7 +654,7 @@ int TDBMYSQL::MakeCommand(PGLOBAL g)
// Make a lower case copy of the originale query
- qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
+ qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 5);
strlwr(strcpy(qrystr, Qrystr));
// Check whether the table name is equal to a keyword
@@ -667,6 +674,7 @@ int TDBMYSQL::MakeCommand(PGLOBAL g)
strcat(Query, Tabname);
strcat(Query, Qrystr + (p - qrystr) + strlen(name));
+ strlwr(strcpy(qrystr, Query));
} else {
sprintf(g->Message, "Cannot use this %s command",
(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
@@ -740,33 +748,50 @@ int TDBMYSQL::MakeDelete(PGLOBAL g)
#endif // 0
/***********************************************************************/
-/* XCV GetMaxSize: returns the maximum number of rows in the table. */
+/* MYSQL Cardinality: returns the number of rows in the table. */
/***********************************************************************/
-int TDBMYSQL::GetMaxSize(PGLOBAL g)
- {
- if (MaxSize < 0) {
-#if 0
- if (MakeSelect(g))
- return -2;
+int TDBMYSQL::Cardinality(PGLOBAL g)
+{
+ if (!g)
+ return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
- if (!Myc.Connected()) {
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
- return -1;
+ if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && xinfo) {
+ // Info command, we must return the exact table row number
+ char query[96];
+ MYSQLC myc;
- } // endif connected
+ if (myc.Open(g, Host, Database, User, Pwd, Port))
+ return -1;
- if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) {
- Myc.Close();
- return -3;
- } // endif MaxSize
+ strcpy(query, "SELECT COUNT(*) FROM ");
- // FIXME: Columns should be known when Info calls GetMaxSize
- if (!Columns)
- Query = NULL; // Must be remade when columns are known
-#endif // 0
+ if (Quoted > 0)
+ strcat(strcat(strcat(query, "`"), Tabname), "`");
+ else
+ strcat(query, Tabname);
+
+ Cardinal = myc.GetTableSize(g, query);
+ myc.Close();
+ } else
+ Cardinal = 10; // To make MySQL happy
+
+ return Cardinal;
+} // end of Cardinality
+
+/***********************************************************************/
+/* MYSQL GetMaxSize: returns the maximum number of rows in the table. */
+/***********************************************************************/
+int TDBMYSQL::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0) {
+ if (Mode == MODE_DELETE)
+ // Return 0 in mode DELETE in case of delete all.
+ MaxSize = 0;
+ else if (!Cardinality(NULL))
+ MaxSize = 10; // To make MySQL happy
+ else if ((MaxSize = Cardinality(g)) < 0)
+ MaxSize = 12; // So we can see an error occured
- // Return 0 in mode DELETE in case of delete all.
- MaxSize = (Mode == MODE_DELETE) ? 0 : 10; // To make MySQL happy
} // endif MaxSize
return MaxSize;
@@ -881,11 +906,12 @@ bool TDBMYSQL::OpenDB(PGLOBAL g)
} // endif MakeInsert
if (m_Rc != RC_FX) {
+ int rc __attribute__((unused));
char cmd[64];
int w;
sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
- m_Rc = Myc.ExecSQL(g, cmd, &w);
+ rc = Myc.ExecSQL(g, cmd, &w); // may fail for some engines
} // endif m_Rc
} else
@@ -1012,7 +1038,8 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
{
int oldlen = strlen(Query);
- if (op == OP_NEXT)
+ if (!key || op == OP_NEXT ||
+ Mode == MODE_UPDATE || Mode == MODE_DELETE)
return false;
else if (op == OP_FIRST) {
if (To_CondFil)
@@ -1031,7 +1058,7 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
m_Rc = Myc.ExecSQL(g, Query);
Query[oldlen] = 0;
- return false;
+ return (m_Rc == RC_FX) ? true : false;
} // end of ReadKey
/***********************************************************************/
@@ -1081,13 +1108,13 @@ int TDBMYSQL::WriteDB(PGLOBAL g)
// Make the Insert command value list
for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
if (!colp->GetValue()->IsNull()) {
- if (colp->GetResultType() == TYPE_STRING ||
+ if (colp->GetResultType() == TYPE_STRING ||
colp->GetResultType() == TYPE_DATE)
strcat(Qbuf, "'");
strcat(Qbuf, colp->GetValue()->GetCharString(buf));
- if (colp->GetResultType() == TYPE_STRING ||
+ if (colp->GetResultType() == TYPE_STRING ||
colp->GetResultType() == TYPE_DATE)
strcat(Qbuf, "'");
@@ -1109,7 +1136,7 @@ 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;
+ return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
else
return RC_OK; // Ignore
@@ -1129,7 +1156,7 @@ void TDBMYSQL::CloseDB(PGLOBAL 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);
+ m_Rc = Myc.ExecSQL(g, cmd, &w); // May fail for some engines
} // endif m_Rc
Myc.Close();
@@ -1178,6 +1205,7 @@ MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
char v = (!strcmp(chset, "binary")) ? 'B' : 0;
Name = fld->name;
+ Opt = 0;
Precision = Long = fld->length;
Buf_Type = MYSQLtoPLG(fld->type, &v);
strcpy(Format.Type, GetFormatType(Buf_Type));
@@ -1376,7 +1404,7 @@ void MYSQLCOL::WriteColumn(PGLOBAL g)
/***********************************************************************/
/* Implementation of the TDBMYEXC class. */
/***********************************************************************/
-TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
+TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
{
Cmdlist = NULL;
Cmdcol = NULL;
@@ -1528,7 +1556,7 @@ int TDBMYEXC::ReadDB(PGLOBAL g)
if (Cmdlist) {
// Process query to send
int rc;
-
+
do {
Query = Cmdlist->Cmd;
@@ -1548,7 +1576,7 @@ int TDBMYEXC::ReadDB(PGLOBAL g)
case RC_INFO:
Shw = true;
} // endswitch rc
-
+
Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
} while (rc == RC_INFO);
@@ -1645,11 +1673,11 @@ void MYXCOL::WriteColumn(PGLOBAL g)
/***********************************************************************/
TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
{
- Host = tdp->Hostname;
- Db = tdp->Database;
- Tab = tdp->Tabname;
- User = tdp->Username;
- Pwd = tdp->Password;
+ Host = tdp->Hostname;
+ Db = tdp->Database;
+ Tab = tdp->Tabname;
+ User = tdp->Username;
+ Pwd = tdp->Password;
Port = tdp->Portnumber;
} // end of TDBMCL constructor
@@ -1659,4 +1687,4 @@ TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
PQRYRES TDBMCL::GetResult(PGLOBAL g)
{
return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false);
- } // end of GetResult
+ } // end of GetResult
diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h
index 96991fb14c1..68cf453a9e6 100644
--- a/storage/connect/tabmysql.h
+++ b/storage/connect/tabmysql.h
@@ -1,4 +1,4 @@
-// TDBMYSQL.H Olivier Bertrand 2007-2013
+// TDBMYSQL.H Olivier Bertrand 2007-2014
#include "myconn.h" // MySQL connection declares
typedef class MYSQLDEF *PMYDEF;
@@ -81,7 +81,7 @@ class TDBMYSQL : public TDBASE {
// Methods
virtual PTDB CopyOne(PTABS t);
- virtual int GetAffectedRows(void) {return AftRows;}
+//virtual int GetAffectedRows(void) {return AftRows;}
virtual int GetRecpos(void) {return N;}
virtual int GetProgMax(PGLOBAL g);
virtual void ResetDB(void) {N = 0;}
@@ -92,6 +92,7 @@ class TDBMYSQL : public TDBASE {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index 65226c9e36f..023d7efa708 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -1,11 +1,11 @@
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
-/* Version 2.7 */
+/* Version 2.8 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2000-2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -76,6 +76,7 @@
#include "sql_string.h"
extern "C" int trace;
+extern bool xinfo;
/***********************************************************************/
/* DB static variables. */
@@ -100,7 +101,13 @@ ODBCDEF::ODBCDEF(void)
/***********************************************************************/
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
- Desc = Connect = GetStringCatInfo(g, "Connect", "");
+ Desc = Connect = GetStringCatInfo(g, "Connect", NULL);
+
+ if (!Connect && !Catfunc) {
+ sprintf(g->Message, "Missing connection for ODBC table %s", Name);
+ return true;
+ } // endif Connect
+
Tabname = GetStringCatInfo(g, "Name",
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
Tabname = GetStringCatInfo(g, "Tabname", Tabname);
@@ -108,7 +115,10 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
- Srcdef = GetStringCatInfo(g, "Srcdef", NULL);
+
+ if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ Read_Only = true;
+
Qrystr = GetStringCatInfo(g, "Query_String", "?");
Sep = GetStringCatInfo(g, "Separator", NULL);
Catver = GetIntCatInfo("Catver", 2);
@@ -655,40 +665,58 @@ void TDBODBC::ResetSize(void)
} // end of ResetSize
/***********************************************************************/
+/* ODBC Cardinality: returns table size in number of rows. */
+/***********************************************************************/
+int TDBODBC::Cardinality(PGLOBAL g)
+ {
+ if (!g)
+ return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
+
+ if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && xinfo) {
+ // Info command, we must return the exact table row number
+ char qry[96], tbn[64];
+ ODBConn *ocp = new(g) ODBConn(g, this);
+
+ if (ocp->Open(Connect, Options) < 1)
+ return -1;
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, tbn, sizeof(tbn));
+ strcpy(qry, "SELECT COUNT(*) FROM ");
+
+ if (Quote)
+ strcat(strcat(strcat(qry, Quote), tbn), Quote);
+ else
+ strcat(qry, tbn);
+
+ // Allocate a Count(*) column (must not use the default constructor)
+ Cnp = new(g) ODBCCOL;
+ Cnp->InitValue(g);
+
+ if ((Cardinal = ocp->GetResultSize(qry, Cnp)) < 0)
+ return -3;
+
+ ocp->Close();
+ } else
+ Cardinal = 10; // To make MySQL happy
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
/* ODBC GetMaxSize: returns table size estimate in number of lines. */
/***********************************************************************/
int TDBODBC::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
- // Make MariaDB happy
- MaxSize = (Mode == MODE_DELETE) ? 0 : 10;
-#if 0
- // This is unuseful and takes time
- if (Srcdef) {
- // Return a reasonable guess
- MaxSize = 100;
- return MaxSize;
- } // endif Srcdef
-
- if (!Ocp)
- Ocp = new(g) ODBConn(g, this);
-
- if (!Ocp->IsOpen())
- if (Ocp->Open(Connect, Options) < 1)
- return -1;
-
- if (!Count && !(Count = MakeSQL(g, true)))
- return -2;
-
- if (!Cnp) {
- // Allocate a Count(*) column (must not use the default constructor)
- Cnp = new(g) ODBCCOL;
- Cnp->InitValue(g);
- } // endif Cnp
-
- if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0)
- return -3;
-#endif // 0
+ if (Mode == MODE_DELETE)
+ // Return 0 in mode DELETE in case of delete all.
+ MaxSize = 0;
+ else if (!Cardinality(NULL))
+ MaxSize = 10; // To make MySQL happy
+ else if ((MaxSize = Cardinality(g)) < 0)
+ MaxSize = 12; // So we can see an error occured
+
} // endif MaxSize
return MaxSize;
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index 5db8cbb8cff..360f52c9d21 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -88,14 +88,15 @@ class TDBODBC : public TDBASE {
virtual PSZ GetFile(PGLOBAL g);
virtual void SetFile(PGLOBAL g, PSZ fn);
virtual void ResetSize(void);
- virtual int GetAffectedRows(void) {return AftRows;}
+//virtual int GetAffectedRows(void) {return AftRows;}
virtual PSZ GetServer(void) {return "ODBC";}
virtual int Indexable(void) {return 2;}
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetProgMax(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
+ virtual int GetProgMax(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h
index c7248ee2e1d..25d139e895f 100644
--- a/storage/connect/tabpivot.h
+++ b/storage/connect/tabpivot.h
@@ -105,6 +105,7 @@ class TDBPIVOT : public TDBPRX {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g) {return (g) ? 10 : 0;}
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
diff --git a/storage/connect/tabsys.cpp b/storage/connect/tabsys.cpp
index e8ea7f4e43a..ae92c0771b6 100644
--- a/storage/connect/tabsys.cpp
+++ b/storage/connect/tabsys.cpp
@@ -1,9 +1,9 @@
/************* TabSys C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABSYS */
/* ------------- */
-/* Version 2.2 */
+/* Version 2.3 */
/* */
-/* Author Olivier BERTRAND 2004-2013 */
+/* Author Olivier BERTRAND 2004-2014 */
/* */
/* This program are the INI/CFG tables classes. */
/***********************************************************************/
@@ -203,18 +203,35 @@ PCOL TDBINI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
} // end of MakeCol
/***********************************************************************/
-/* INI GetMaxSize: returns the number of sections in the INI file. */
+/* INI Cardinality: returns the number of sections in the INI file. */
/***********************************************************************/
-int TDBINI::GetMaxSize(PGLOBAL g)
+int TDBINI::Cardinality(PGLOBAL g)
{
- if (MaxSize < 0 && GetSeclist(g)) {
+ if (!g)
+ return 1;
+
+ if (Cardinal < 0) {
// Count the number of sections from the section list
- char *p;
+ char *p = GetSeclist(g);
+
+ Cardinal = 0;
+
+ if (p)
+ for (; *p; p += (strlen(p) + 1))
+ Cardinal++;
- for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1))
- MaxSize++;
+ } // endif Cardinal
- } // endif MaxSize
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* INI GetMaxSize: returns the table cardinality. */
+/***********************************************************************/
+int TDBINI::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0)
+ MaxSize = Cardinality(g);
return MaxSize;
} // end of GetMaxSize
@@ -609,22 +626,28 @@ PCOL TDBXIN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
} // end of MakeCol
/***********************************************************************/
-/* XIN GetMaxSize: returns the number of sections in the XIN file. */
+/* XIN Cardinality: returns the number of keys in the XIN file. */
/***********************************************************************/
-int TDBXIN::GetMaxSize(PGLOBAL g)
+int TDBXIN::Cardinality(PGLOBAL g)
{
- if (MaxSize < 0 && GetSeclist(g)) {
+ if (!g)
+ return 1;
+
+ if (Cardinal < 0) {
// Count the number of keys from the section list
- char *p, *k;
+ char *k, *p = GetSeclist(g);
- for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1))
- for (k = GetKeylist(g, p); *k; k += (strlen(k) + 1))
- MaxSize++;
+ Cardinal = 0;
- } // endif MaxSize
+ if (p)
+ for (; *p; p += (strlen(p) + 1))
+ for (k = GetKeylist(g, p); *k; k += (strlen(k) + 1))
+ Cardinal++;
- return MaxSize;
- } // end of GetMaxSize
+ } // endif Cardinal
+
+ return Cardinal;
+ } // end of Cardinality
/***********************************************************************/
/* Record position is Section+Key. */
@@ -633,7 +656,7 @@ int TDBXIN::GetRecpos(void)
{
union {
short X[2]; // Section and Key offsets
- int Xpos; // File position
+ int Xpos; // File position
}; // end of union
X[0] = (short)(Section - Seclist);
@@ -648,7 +671,7 @@ bool TDBXIN::SetRecpos(PGLOBAL g, int recpos)
{
union {
short X[2]; // Section and Key offsets
- int Xpos; // File position
+ int Xpos; // File position
}; // end of union
Xpos = recpos;
diff --git a/storage/connect/tabsys.h b/storage/connect/tabsys.h
index 38b71d62ac4..aa45c260bc2 100644
--- a/storage/connect/tabsys.h
+++ b/storage/connect/tabsys.h
@@ -1,181 +1,182 @@
-/*************** 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
+/*************** TabSys H Declares Source Code File (.H) ***************/
+/* Name: TABSYS.H Version 2.3 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
+/* */
+/* 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 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);
+
+ 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 Cardinality(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.cpp b/storage/connect/tabtbl.cpp
index 0aeeb0b9d8d..f5a516ad1d0 100644
--- a/storage/connect/tabtbl.cpp
+++ b/storage/connect/tabtbl.cpp
@@ -350,7 +350,34 @@ bool TDBTBL::TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp)
} // end of TestFil
/***********************************************************************/
-/* Sum up the sizes of all sub-tables. */
+/* Sum up the cardinality of all sub-tables. */
+/***********************************************************************/
+int TDBTBL::Cardinality(PGLOBAL g)
+ {
+ if (Cardinal < 0) {
+ int tsz;
+
+ if (!Tablist && InitTableList(g))
+ return 0; // Cannot be calculated at this stage
+
+ Cardinal = 0;
+
+ for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext()) {
+ if ((tsz = tabp->GetTo_Tdb()->Cardinality(g)) < 0) {
+ Cardinal = -1;
+ return tsz;
+ } // endif mxsz
+
+ Cardinal += tsz;
+ } // endfor i
+
+ } // endif Cardinal
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Sum up the maximum sizes of all sub-tables. */
/***********************************************************************/
int TDBTBL::GetMaxSize(PGLOBAL g)
{
@@ -435,7 +462,7 @@ bool TDBTBL::OpenDB(PGLOBAL g)
if ((CurTable = Tablist)) {
Tdbp = (PTDBASE)CurTable->GetTo_Tdb();
- Tdbp->SetMode(Mode);
+// Tdbp->SetMode(Mode);
// Tdbp->ResetDB();
// Tdbp->ResetSize();
@@ -685,7 +712,7 @@ bool TDBTBM::OpenDB(PGLOBAL g)
/*********************************************************************/
if ((CurTable = Tablist)) {
Tdbp = (PTDBASE)CurTable->GetTo_Tdb();
- Tdbp->SetMode(Mode);
+// Tdbp->SetMode(Mode);
// Check and initialize the subtable columns
for (PCOL cp = Columns; cp; cp = cp->GetNext())
diff --git a/storage/connect/tabtbl.h b/storage/connect/tabtbl.h
index fc35179f2ea..8bf440985ea 100644
--- a/storage/connect/tabtbl.h
+++ b/storage/connect/tabtbl.h
@@ -78,6 +78,7 @@ class DllExport TDBTBL : public TDBPRX {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL scp);
diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp
index 4b9046e08d1..f4a8f2ee470 100644
--- a/storage/connect/tabutil.cpp
+++ b/storage/connect/tabutil.cpp
@@ -313,7 +313,7 @@ bool PRXDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
if (!(tab = GetStringCatInfo(g, "Tabname", NULL))) {
if (!def) {
strcpy(g->Message, "Missing object table definition");
- return TRUE;
+ return true;
} else
tab = "Noname";
@@ -327,7 +327,7 @@ bool PRXDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Tablep = new(g) XTAB(tab, def);
Tablep->SetQualifier(db);
- return FALSE;
+ return false;
} // end of DefineAM
/***********************************************************************/
@@ -352,6 +352,28 @@ TDBPRX::TDBPRX(PPRXDEF tdp) : TDBASE(tdp)
Tdbp = NULL; // The object table
} // end of TDBPRX constructor
+TDBPRX::TDBPRX(PGLOBAL g, PTDBPRX tdbp) : TDBASE(tdbp)
+ {
+ Tdbp = tdbp->Tdbp;
+ } // end of TDBPRX copy constructor
+
+// Method
+PTDB TDBPRX::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PPRXCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBPRX(g, this);
+
+ for (cp1 = (PPRXCOL)Columns; cp1; cp1 = (PPRXCOL)cp1->GetNext()) {
+ cp2 = new(g) PRXCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
/***********************************************************************/
/* Get the PTDB of the sub-table. */
/***********************************************************************/
@@ -403,7 +425,7 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
if (mysql) {
#if defined(MYSQL_SUPPORT)
// Access sub-table via MySQL API
- if (!(tdbp= cat->GetTable(g, tabp, MODE_READ, "MYPRX"))) {
+ if (!(tdbp= cat->GetTable(g, tabp, Mode, "MYPRX"))) {
char buf[MAX_STR];
strcpy(buf, g->Message);
@@ -415,6 +437,9 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
if (db)
((PTDBMY)tdbp)->SetDatabase(tabp->GetQualifier());
+ if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
+ tdbp->SetName(Name); // For Make_Command
+
#else // !MYSQL_SUPPORT
sprintf(g->Message, "%s.%s is not a CONNECT table",
db, tblp->Name);
@@ -423,7 +448,7 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
} else {
// Sub-table is a CONNECT table
tabp->Next = To_Table; // For loop checking
- tdbp = cat->GetTable(g, tabp);
+ tdbp = cat->GetTable(g, tabp, Mode);
} // endif mysql
if (s) {
@@ -456,11 +481,12 @@ bool TDBPRX::InitTable(PGLOBAL g)
if (!Tdbp) {
// Get the table description block of this table
if (!(Tdbp = GetSubTable(g, ((PPRXDEF)To_Def)->Tablep)))
- return TRUE;
+ return true;
+// Tdbp->SetMode(Mode);
} // endif Tdbp
- return FALSE;
+ return false;
} // end of InitTable
/***********************************************************************/
@@ -472,6 +498,21 @@ PCOL TDBPRX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
} // end of MakeCol
/***********************************************************************/
+/* PRX Cardinality: returns the number of rows in the table. */
+/***********************************************************************/
+int TDBPRX::Cardinality(PGLOBAL g)
+ {
+ if (Cardinal < 0) {
+ if (InitTable(g))
+ return 0;
+
+ Cardinal = Tdbp->Cardinality(g);
+ } // endif MaxSize
+
+ return Cardinal;
+ } // end of GetMaxSize
+
+/***********************************************************************/
/* PRX GetMaxSize: returns the maximum number of rows in the table. */
/***********************************************************************/
int TDBPRX::GetMaxSize(PGLOBAL g)
@@ -507,32 +548,49 @@ bool TDBPRX::OpenDB(PGLOBAL g)
return Tdbp->OpenDB(g);
} // endif use
- if (Mode != MODE_READ) {
- /*******************************************************************/
- /* Currently XCOL tables cannot be modified. */
- /*******************************************************************/
- strcpy(g->Message, "PROXY tables are read only");
- return TRUE;
- } // endif Mode
-
if (InitTable(g))
- return TRUE;
+ return true;
+ else if (Mode != MODE_READ && (Read_Only || Tdbp->IsReadOnly())) {
+ strcpy(g->Message, "Cannot modify a read only table");
+ return true;
+ } // endif tp
/*********************************************************************/
/* Check and initialize the subtable columns. */
/*********************************************************************/
for (PCOL cp = Columns; cp; cp = cp->GetNext())
- if (((PPRXCOL)cp)->Init(g))
- return TRUE;
+ if (((PPRXCOL)cp)->Init(g, Tdbp))
+ return true;
+
+ /*********************************************************************/
+ /* 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());
+ return true;
+ } // endif tp
+
+ for (PCOL cp = To_SetCols; cp; cp = cp->GetNext())
+ if (((PPRXCOL)cp)->Init(g, utp))
+ return true;
+
+ } else if (Mode == MODE_DELETE)
+ Tdbp->SetNext(Next);
/*********************************************************************/
/* Physically open the object table. */
/*********************************************************************/
if (Tdbp->OpenDB(g))
- return TRUE;
+ return true;
+ Tdbp->SetNext(NULL);
Use = USE_OPEN;
- return FALSE;
+ return false;
} // end of OpenDB
/***********************************************************************/
@@ -551,8 +609,7 @@ int TDBPRX::ReadDB(PGLOBAL g)
/***********************************************************************/
int TDBPRX::WriteDB(PGLOBAL g)
{
- sprintf(g->Message, "%s tables are read only", To_Def->GetType());
- return RC_FX;
+ return Tdbp->WriteDB(g);
} // end of WriteDB
/***********************************************************************/
@@ -560,9 +617,7 @@ int TDBPRX::WriteDB(PGLOBAL g)
/***********************************************************************/
int TDBPRX::DeleteDB(PGLOBAL g, int irc)
{
- sprintf(g->Message, "Delete not enabled for %s tables",
- To_Def->GetType());
- return RC_FX;
+ return Tdbp->DeleteDB(g, irc);
} // end of DeleteDB
/***********************************************************************/
@@ -594,7 +649,7 @@ PRXCOL::PRXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
//strcpy(F_Date, cdp->F_Date);
Colp = NULL;
To_Val = NULL;
- Pseudo = FALSE;
+ Pseudo = false;
Colnum = cdp->GetOffset(); // If columns are retrieved by number
if (trace)
@@ -603,29 +658,48 @@ PRXCOL::PRXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
} // end of PRXCOL constructor
/***********************************************************************/
+/* PRXCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+PRXCOL::PRXCOL(PRXCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+ {
+ Colp = col1->Colp;
+ To_Val = col1->To_Val;
+ Pseudo = col1->Pseudo;
+ Colnum = col1->Colnum;
+ } // end of PRXCOL copy constructor
+
+/***********************************************************************/
/* PRXCOL initialization routine. */
/* Look for the matching column in the object table. */
/***********************************************************************/
-bool PRXCOL::Init(PGLOBAL g)
+bool PRXCOL::Init(PGLOBAL g, PTDBASE tp)
{
- PTDBPRX tdbp = (PTDBPRX)To_Tdb;
+ if (!tp)
+ tp = ((PTDBPRX)To_Tdb)->Tdbp;
- if (!(Colp = tdbp->Tdbp->ColDB(g, Name, 0)) && Colnum)
- Colp = tdbp->Tdbp->ColDB(g, NULL, Colnum);
+ if (!(Colp = tp->ColDB(g, Name, 0)) && Colnum)
+ Colp = tp->ColDB(g, NULL, Colnum);
if (Colp) {
+ MODE mode = To_Tdb->GetMode();
+
// May not have been done elsewhere
Colp->InitValue(g);
To_Val = Colp->GetValue();
+ if (mode == MODE_INSERT || mode == MODE_UPDATE)
+ if (Colp->SetBuffer(g, Colp->GetValue(), true, false))
+ return true;
+
// this may be needed by some tables (which?)
Colp->SetColUse(ColUse);
} else {
- sprintf(g->Message, MSG(NO_MATCHING_COL), Name, tdbp->Tdbp->GetName());
- return TRUE;
+ sprintf(g->Message, MSG(NO_MATCHING_COL), Name, tp->GetName());
+ return true;
} // endif Colp
- return FALSE;
+ return false;
} // end of Init
/***********************************************************************/
@@ -659,6 +733,21 @@ void PRXCOL::ReadColumn(PGLOBAL g)
} // end of ReadColumn
+/***********************************************************************/
+/* WriteColumn: */
+/***********************************************************************/
+void PRXCOL::WriteColumn(PGLOBAL g)
+ {
+ if (trace > 1)
+ htrc("PRX WriteColumn: name=%s\n", Name);
+
+ if (Colp) {
+ To_Val->SetValue_pval(Value);
+ Colp->WriteColumn(g);
+ } // endif Colp
+
+ } // end of WriteColumn
+
/* ---------------------------TDBTBC class --------------------------- */
/***********************************************************************/
diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h
index c87065befba..11f18be074a 100644
--- a/storage/connect/tabutil.h
+++ b/storage/connect/tabutil.h
@@ -57,13 +57,17 @@ class DllExport TDBPRX : public TDBASE {
friend class PRXDEF;
friend class PRXCOL;
public:
- // Constructor
+ // Constructors
TDBPRX(PPRXDEF tdp);
+ TDBPRX(PGLOBAL g, PTDBPRX tdbp);
// Implementation
virtual AMT GetAmType(void) {return TYPE_AM_PRX;}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBPRX(g, this);}
// Methods
+ virtual PTDB CopyOne(PTABS t);
virtual int GetRecpos(void) {return Tdbp->GetRecpos();}
virtual void ResetDB(void) {Tdbp->ResetDB();}
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
@@ -72,6 +76,7 @@ class DllExport TDBPRX : public TDBASE {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual bool InitTable(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
@@ -97,15 +102,19 @@ class DllExport PRXCOL : public COLBLK {
public:
// Constructors
PRXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "PRX");
+ PRXCOL(PRXCOL *colp, PTDB tdbp); // Constructor used in copy process
// Implementation
- virtual int GetAmType(void) {return TYPE_AM_PRX;}
+ virtual int GetAmType(void) {return TYPE_AM_PRX;}
// Methods
- virtual void Reset(void);
- virtual bool IsSpecial(void) {return Pseudo;}
- virtual void ReadColumn(PGLOBAL g);
- bool Init(PGLOBAL g);
+ virtual void Reset(void);
+ virtual bool IsSpecial(void) {return Pseudo;}
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+ {return false;}
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ bool Init(PGLOBAL g, PTDBASE tp = NULL);
protected:
// Default constructor not to be used
diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp
index 73dfef6a4d6..6d7059e2306 100644
--- a/storage/connect/tabvct.cpp
+++ b/storage/connect/tabvct.cpp
@@ -32,7 +32,7 @@
/***********************************************************************/
/***********************************************************************/
-/* Include relevant MariaDB header file. */
+/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
#if defined(WIN32)
@@ -76,7 +76,8 @@
char *strerror(int num);
#endif // UNIX
-extern "C" int trace;
+extern "C" int trace;
+extern "C" USETEMP Use_Temp;
/***********************************************************************/
/* Char VCT column blocks are right filled with blanks (blank = true) */
@@ -95,7 +96,10 @@ bool VCTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
DOSDEF::DefineAM(g, "BIN", poff);
- Estimate = GetIntCatInfo("Estimate", 0);
+ if ((Estimate = GetIntCatInfo("Estimate", 0)))
+ Elemt = MY_MIN(Elemt, Estimate);
+
+ // Split treated as INT to get default value
Split = GetIntCatInfo("Split", (Estimate) ? 0 : 1);
Header = GetIntCatInfo("Header", 0);
@@ -103,7 +107,7 @@ bool VCTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
if (Estimate && !Split && !Header) {
char *fn = GetStringCatInfo(g, "Filename", "?");
- // No separate header file fo urbi tables
+ // No separate header file for urbi tables
Header = (*fn == '?') ? 3 : 2;
} // endif Estimate
@@ -205,7 +209,7 @@ PTDB VCTDEF::GetTable(PGLOBAL g, MODE mode)
// Mapping not used for insert (except for true VEC not split tables)
// or when UseTemp is forced
bool map = Mapped && (Estimate || mode != MODE_INSERT) &&
- !(PlgGetUser(g)->UseTemp == TMP_FORCE &&
+ !(Use_Temp == TMP_FORCE &&
(mode == MODE_UPDATE || mode == MODE_DELETE));
PTXF txfp;
PTDB tdbp;
@@ -282,6 +286,15 @@ PCOL TDBVCT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
} // end of MakeCol
/***********************************************************************/
+/* VEC tables are not ready yet to use temporary files. */
+/***********************************************************************/
+bool TDBVCT::IsUsingTemp(PGLOBAL g)
+ {
+ // For developpers
+ return (Use_Temp == TMP_TEST);
+ } // end of IsUsingTemp
+
+/***********************************************************************/
/* 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 */
@@ -302,14 +315,19 @@ bool TDBVCT::OpenDB(PGLOBAL g)
To_Kindex->Reset();
Txfp->Rewind();
+ ResetBlockFilter(g);
return false;
} // endif Use
/*********************************************************************/
/* Delete all is not handled using file mapping. */
/*********************************************************************/
- if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() == TYPE_AM_MAP) {
- Txfp = new(g) VCTFAM((PVCTDEF)To_Def);
+ if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() == TYPE_AM_VMP) {
+ if (IsSplit())
+ Txfp = new(g) VECFAM((PVCTDEF)To_Def);
+ else
+ Txfp = new(g) VCTFAM((PVCTDEF)To_Def);
+
Txfp->SetTdbp(this);
} // endif Mode
@@ -324,6 +342,11 @@ bool TDBVCT::OpenDB(PGLOBAL g)
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);
+
+ /*********************************************************************/
/* Reset buffer access according to indexing and to mode. */
/*********************************************************************/
Txfp->ResetBuffer(g);
@@ -382,7 +405,7 @@ void TDBVCT::CloseDB(PGLOBAL g)
To_Kindex = NULL;
} // endif
- Txfp->CloseTableFile(g);
+ Txfp->CloseTableFile(g, false);
} // end of CloseDB
// ------------------------ VCTCOL functions ----------------------------
diff --git a/storage/connect/tabvct.h b/storage/connect/tabvct.h
index 7dc416a5779..8ad3c8e21be 100644
--- a/storage/connect/tabvct.h
+++ b/storage/connect/tabvct.h
@@ -20,12 +20,13 @@ typedef class VCTCOL *PVCTCOL;
/* VCT table. */
/***********************************************************************/
class DllExport VCTDEF : public DOSDEF { /* Logical table description */
+ friend class TDBVCT;
friend class VCTFAM;
friend class VECFAM;
friend class VMPFAM;
public:
// Constructor
- VCTDEF(void) {Split = Estimate = Header = 0;}
+ VCTDEF(void) {Split = false; Estimate = Header = 0;}
// Implementation
virtual const char *GetType(void) {return "VCT";}
@@ -39,9 +40,9 @@ class DllExport VCTDEF : public DOSDEF { /* Logical table description */
int MakeFnPattern(char *fpat);
// Members
- int Split; /* Columns in separate files */
+ bool Split; /* Columns in separate files */
int Estimate; /* Estimated maximum size of table */
- int Header; /* 0: no, 1: separate, 2: in data file */
+ int Header; /* 0: no, 1: separate, 2: in data file */
}; // end of VCTDEF
/***********************************************************************/
@@ -64,9 +65,11 @@ class DllExport TDBVCT : public TDBFIX {
virtual AMT GetAmType(void) {return TYPE_AM_VCT;}
virtual PTDB Duplicate(PGLOBAL g)
{return (PTDB)new(g) TDBVCT(g, this);}
+ bool IsSplit(void) {return ((VCTDEF*)To_Def)->Split;}
// Methods
virtual PTDB CopyOne(PTABS t);
+ virtual bool IsUsingTemp(PGLOBAL g);
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
@@ -101,7 +104,7 @@ class DllExport VCTCOL : public DOSCOL {
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);
+ virtual void SetOk(void);
protected:
virtual void ReadBlock(PGLOBAL g);
diff --git a/storage/connect/tabwmi.h b/storage/connect/tabwmi.h
index 8ff5262941e..6f25c0de258 100644
--- a/storage/connect/tabwmi.h
+++ b/storage/connect/tabwmi.h
@@ -1,150 +1,151 @@
-// TABWMI.H Olivier Bertrand 2012
-// WMI: Virtual table to Get WMI information
-#define _WIN32_DCOM
-#include <wbemidl.h>
-# pragma comment(lib, "wbemuuid.lib")
-#include <iostream>
-using namespace std;
-#include <comdef.h>
-
-/***********************************************************************/
-/* Definitions. */
-/***********************************************************************/
-typedef class WMIDEF *PWMIDEF;
-typedef class TDBWMI *PTDBWMI;
-typedef class WMICOL *PWMICOL;
-typedef class TDBWCL *PTDBWCL;
-typedef class WCLCOL *PWCLCOL;
-
-/***********************************************************************/
-/* Structure used by WMI column info functions. */
-/***********************************************************************/
-typedef struct _WMIutil {
- IWbemServices *Svc;
- IWbemClassObject *Cobj;
-} WMIUTIL, *PWMIUT;
-
-/***********************************************************************/
-/* Functions used externally. */
-/***********************************************************************/
-PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info);
-
-/* -------------------------- WMI classes ---------------------------- */
-
-/***********************************************************************/
-/* WMI: Virtual table to get the WMI information. */
-/***********************************************************************/
-class WMIDEF : public TABDEF { /* Logical table description */
- friend class TDBWMI;
- friend class TDBWCL;
- friend class TDBWCX;
- public:
- // Constructor
- WMIDEF(void) {Pseudo = 3; Nspace = NULL; Wclass = NULL; Ems = 0;}
-
- // Implementation
- virtual const char *GetType(void) {return "WMI";}
-
- // Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
- virtual PTDB GetTable(PGLOBAL g, MODE m);
-
- protected:
- // Members
- char *Nspace;
- char *Wclass;
- int Ems;
- }; // end of WMIDEF
-
-/***********************************************************************/
-/* This is the class declaration for the WMI table. */
-/***********************************************************************/
-class TDBWMI : public TDBASE {
- friend class WMICOL;
- public:
- // Constructor
- TDBWMI(PWMIDEF tdp);
-
- // Implementation
- virtual AMT GetAmType(void) {return TYPE_AM_WMI;}
-
- // Methods
- virtual int GetRecpos(void);
- virtual int GetProgCur(void) {return N;}
- virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
-
- // Database routines
- virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
- virtual bool OpenDB(PGLOBAL g);
- virtual int ReadDB(PGLOBAL g);
- virtual int WriteDB(PGLOBAL g);
- virtual int DeleteDB(PGLOBAL g, int irc);
- virtual void CloseDB(PGLOBAL g);
-
- protected:
- // Specific routines
- bool Initialize(PGLOBAL g);
- char *MakeWQL(PGLOBAL g);
- void DoubleSlash(PGLOBAL g);
- bool GetWMIInfo(PGLOBAL g);
-
- // Members
- IWbemServices *Svc; // IWbemServices pointer
- IEnumWbemClassObject *Enumerator;
- IWbemClassObject *ClsObj;
- char *Nspace; // Namespace
- char *Wclass; // Class name
- char *ObjPath; // Used for direct access
- char *Kvp; // Itou
- int Ems; // Estimated max size
- PCOL Kcol; // Key column
- HRESULT Res;
- PVBLK Vbp;
- bool Init;
- bool Done;
- ULONG Rc;
- int N; // Row number
- }; // end of class TDBWMI
-
-/***********************************************************************/
-/* Class WMICOL: WMI Address column. */
-/***********************************************************************/
-class WMICOL : public COLBLK {
- friend class TDBWMI;
- public:
- // Constructors
- WMICOL(PCOLDEF cdp, PTDB tdbp, int n);
-
- // Implementation
- virtual int GetAmType(void) {return TYPE_AM_WMI;}
-
- // Methods
- virtual void ReadColumn(PGLOBAL g);
-
- protected:
- WMICOL(void) {} // Default constructor not to be used
-
- // Members
- PTDBWMI Tdbp; // Points to WMI table block
- VARIANT Prop; // Property value
- CIMTYPE Ctype; // CIM Type
- HRESULT Res;
- }; // end of class WMICOL
-
-/***********************************************************************/
-/* This is the class declaration for the WMI catalog table. */
-/***********************************************************************/
-class TDBWCL : public TDBCAT {
- public:
- // Constructor
- TDBWCL(PWMIDEF tdp);
-
- protected:
- // Specific routines
- virtual PQRYRES GetResult(PGLOBAL g);
-
- // Members
- char *Nsp; // Name space
- char *Cls; // Class
- }; // end of class TDBWCL
+// TABWMI.H Olivier Bertrand 2012
+// WMI: Virtual table to Get WMI information
+#define _WIN32_DCOM
+#include <wbemidl.h>
+# pragma comment(lib, "wbemuuid.lib")
+#include <iostream>
+using namespace std;
+#include <comdef.h>
+
+/***********************************************************************/
+/* Definitions. */
+/***********************************************************************/
+typedef class WMIDEF *PWMIDEF;
+typedef class TDBWMI *PTDBWMI;
+typedef class WMICOL *PWMICOL;
+typedef class TDBWCL *PTDBWCL;
+typedef class WCLCOL *PWCLCOL;
+
+/***********************************************************************/
+/* Structure used by WMI column info functions. */
+/***********************************************************************/
+typedef struct _WMIutil {
+ IWbemServices *Svc;
+ IWbemClassObject *Cobj;
+} WMIUTIL, *PWMIUT;
+
+/***********************************************************************/
+/* Functions used externally. */
+/***********************************************************************/
+PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info);
+
+/* -------------------------- WMI classes ---------------------------- */
+
+/***********************************************************************/
+/* WMI: Virtual table to get the WMI information. */
+/***********************************************************************/
+class WMIDEF : public TABDEF { /* Logical table description */
+ friend class TDBWMI;
+ friend class TDBWCL;
+ friend class TDBWCX;
+ public:
+ // Constructor
+ WMIDEF(void) {Pseudo = 3; Nspace = NULL; Wclass = NULL; Ems = 0;}
+
+ // Implementation
+ virtual const char *GetType(void) {return "WMI";}
+
+ // Methods
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+ protected:
+ // Members
+ char *Nspace;
+ char *Wclass;
+ int Ems;
+ }; // end of WMIDEF
+
+/***********************************************************************/
+/* This is the class declaration for the WMI table. */
+/***********************************************************************/
+class TDBWMI : public TDBASE {
+ friend class WMICOL;
+ public:
+ // Constructor
+ TDBWMI(PWMIDEF tdp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_WMI;}
+
+ // Methods
+ virtual int GetRecpos(void);
+ virtual int GetProgCur(void) {return N;}
+ virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g) {return GetMaxSize(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);
+
+ 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.cpp b/storage/connect/tabxml.cpp
index 1e9c172cdb3..88c029aefd2 100644
--- a/storage/connect/tabxml.cpp
+++ b/storage/connect/tabxml.cpp
@@ -366,7 +366,7 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
/*********************************************************************/
/* Firstly we check whether this file have been already loaded. */
/*********************************************************************/
- if (Mode == MODE_READ)
+ if (Mode == MODE_READ || Mode == MODE_ANY)
for (fp = dup->Openlist; fp; fp = fp->Next)
if (fp->Type == type && fp->Length && fp->Count)
if (!stricmp(fp->Fname, filename))
@@ -522,8 +522,8 @@ bool TDBXML::Initialize(PGLOBAL g)
To_Xb = Docp->LinkXblock(g, Mode, rc, filename);
// Add a CONNECT comment node
-// sprintf(buf, MSG(CREATED_PLUGDB), version);
- sprintf(buf, " Created by CONNECT %s ", version);
+// sprintf(buf, " Created by CONNECT %s ", version);
+ strcpy(buf, " Created by the MariaDB CONNECT Storage Engine");
Docp->AddComment(g, buf);
if (XmlDB) {
diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h
index 23c46c03a8b..a3dc0a2b54c 100644
--- a/storage/connect/tabxml.h
+++ b/storage/connect/tabxml.h
@@ -1,4 +1,3 @@
-
/*************** Tabxml H Declares Source Code File (.H) ***************/
/* Name: TABXML.H Version 1.6 */
/* */
diff --git a/storage/connect/user_connect.cc b/storage/connect/user_connect.cc
index de32be8cdb3..b5f835c9cc9 100644
--- a/storage/connect/user_connect.cc
+++ b/storage/connect/user_connect.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2012
+/* 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
@@ -68,7 +68,7 @@ user_connect::user_connect(THD *thd, const char *dbn)
g= NULL;
last_query_id= 0;
count= 0;
-
+
// Statistics
nrd= fnd= nfd= 0;
tb1= 0;
@@ -95,9 +95,9 @@ bool user_connect::user_init()
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
- g= PlugInit(NULL, worksize);
+//g= PlugInit(NULL, 67108864);
+//g= PlugInit(NULL, 134217728); // 128M was because of old embedded tests
+ g= PlugInit(NULL, worksize);
// Check whether the initialization is complete
if (!g || !g->Sarea || PlugSubSet(g, g->Sarea, g->Sarea_Size)
@@ -162,6 +162,7 @@ bool user_connect::CheckCleanup(void)
g->Xchk = NULL;
g->Createas = 0;
g->Alchecked = 0;
+ g->Mrr = 0;
last_query_id= thdp->query_id;
if (trace)
diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp
index ded7240cb2e..3827deec43d 100644
--- a/storage/connect/valblk.cpp
+++ b/storage/connect/valblk.cpp
@@ -101,6 +101,9 @@ PVBLK AllocValBlock(PGLOBAL g, void *mp, int type, int nval, int len,
blkp = new(g) TYPBLK<char>(mp, nval, type);
break;
+ case TYPE_PCHAR:
+ blkp = new(g) PTRBLK(g, mp, nval);
+ break;
default:
sprintf(g->Message, MSG(BAD_VALBLK_TYPE), type);
return NULL;
@@ -468,6 +471,36 @@ template <>
uchar TYPBLK<uchar>::GetTypedValue(PVBLK blk, int n)
{return blk->GetUTinyValue(n);}
+/***********************************************************************/
+/* Set one value in a block if val is less than the current value. */
+/***********************************************************************/
+template <class TYPE>
+void TYPBLK<TYPE>::SetMin(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ TYPE tval = GetTypedValue(valp);
+ TYPE& tmin = Typp[n];
+
+ if (tval < tmin)
+ tmin = tval;
+
+ } // end of SetMin
+
+/***********************************************************************/
+/* Set one value in a block if val is greater than the current value. */
+/***********************************************************************/
+template <class TYPE>
+void TYPBLK<TYPE>::SetMax(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ TYPE tval = GetTypedValue(valp);
+ TYPE& tmin = Typp[n];
+
+ if (tval > tmin)
+ tmin = tval;
+
+ } // end of SetMax
+
#if 0
/***********************************************************************/
/* Set many values in a block from values in another block. */
@@ -794,14 +827,44 @@ void CHRBLK::SetValue(PVBLK pv, int n1, int n2)
longjmp(g->jumper[g->jump_level], Type);
} // endif Type
- if (!(b = pv->IsNull(n2) && Nullable))
+ if (!(b = pv->IsNull(n2)))
memcpy(Chrp + n1 * Long, ((CHRBLK*)pv)->Chrp + n2 * Long, Long);
else
Reset(n1);
- SetNull(n1, b);
+ SetNull(n1, b && Nullable);
} // end of SetValue
+/***********************************************************************/
+/* Set one value in a block if val is less than the current value. */
+/***********************************************************************/
+void CHRBLK::SetMin(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ CheckBlanks
+ char *vp = valp->GetCharValue();
+ char *bp = Chrp + n * Long;
+
+ if (((Ci) ? strnicmp(vp, bp, Long) : strncmp(vp, bp, Long)) < 0)
+ memcpy(bp, vp, Long);
+
+ } // end of SetMin
+
+/***********************************************************************/
+/* Set one value in a block if val is greater than the current value. */
+/***********************************************************************/
+void CHRBLK::SetMax(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ CheckBlanks
+ char *vp = valp->GetCharValue();
+ char *bp = Chrp + n * Long;
+
+ if (((Ci) ? strnicmp(vp, bp, Long) : strncmp(vp, bp, Long)) > 0)
+ memcpy(bp, vp, Long);
+
+ } // end of SetMax
+
#if 0
/***********************************************************************/
/* Set many values in a block from values in another block. */
@@ -1127,6 +1190,34 @@ void STRBLK::SetValue(char *sp, uint len, int n)
} // end of SetValue
/***********************************************************************/
+/* Set one value in a block if val is less than the current value. */
+/***********************************************************************/
+void STRBLK::SetMin(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ char *vp = valp->GetCharValue();
+ char *bp = Strp[n];
+
+ if (strcmp(vp, bp) < 0)
+ SetValue(valp, n);
+
+ } // end of SetMin
+
+/***********************************************************************/
+/* Set one value in a block if val is greater than the current value. */
+/***********************************************************************/
+void STRBLK::SetMax(PVAL valp, int n)
+ {
+ CheckParms(valp, n)
+ char *vp = valp->GetCharValue();
+ char *bp = Strp[n];
+
+ if (strcmp(vp, bp) > 0)
+ SetValue(valp, n);
+
+ } // end of SetMax
+
+/***********************************************************************/
/* Move one value from i to j. */
/***********************************************************************/
void STRBLK::Move(int i, int j)
@@ -1265,5 +1356,61 @@ void DATBLK::SetValue(PSZ p, int n)
} // end of SetValue
+
+/* -------------------------- Class PTRBLK --------------------------- */
+
+/***********************************************************************/
+/* Compare two values of the block. */
+/***********************************************************************/
+int PTRBLK::CompVal(int i1, int i2)
+ {
+ return (Strp[i1] > Strp[i2]) ? 1 : (Strp[i1] < Strp[i2]) ? (-1) : 0;
+ } // end of CompVal
+
+
+/* -------------------------- Class MBVALS --------------------------- */
+
+/***********************************************************************/
+/* Allocate a value block according to type,len, and nb of values. */
+/***********************************************************************/
+PVBLK MBVALS::Allocate(PGLOBAL g, int type, int len, int prec,
+ int n, bool sub)
+ {
+ Mblk.Sub = sub;
+ Mblk.Size = n * GetTypeSize(type, len);
+
+ if (!PlgDBalloc(g, NULL, Mblk)) {
+ sprintf(g->Message, MSG(ALLOC_ERROR), "MBVALS::Allocate");
+ return NULL;
+ } else
+ Vblk = AllocValBlock(g, Mblk.Memp, type, n, len, prec,
+ TRUE, TRUE, FALSE);
+
+ return Vblk;
+ } // end of Allocate
+
+/***********************************************************************/
+/* Reallocate the value block according to the new size. */
+/***********************************************************************/
+bool MBVALS::ReAllocate(PGLOBAL g, int n)
+ {
+ if (!PlgDBrealloc(g, NULL, Mblk, n * Vblk->GetVlen())) {
+ sprintf(g->Message, MSG(ALLOC_ERROR), "MBVALS::ReAllocate");
+ return TRUE;
+ } else
+ Vblk->ReAlloc(Mblk.Memp, n);
+
+ return FALSE;
+ } // end of ReAllocate
+
+/***********************************************************************/
+/* Free the value block. */
+/***********************************************************************/
+void MBVALS::Free(void)
+ {
+ PlgDBfree(Mblk);
+ Vblk = NULL;
+ } // end of Free
+
/* ------------------------- End of Valblk --------------------------- */
diff --git a/storage/connect/valblk.h b/storage/connect/valblk.h
index a9b1debe098..654db0b57b7 100644
--- a/storage/connect/valblk.h
+++ b/storage/connect/valblk.h
@@ -18,11 +18,41 @@
/***********************************************************************/
/* Utility used to allocate value blocks. */
/***********************************************************************/
-DllExport PVBLK AllocValBlock(PGLOBAL, void*, int, int, int, int,
+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 {
@@ -78,6 +108,8 @@ class VALBLK : public BLOCK {
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;
@@ -153,6 +185,8 @@ class TYPBLK : public VALBLK {
{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);
@@ -203,6 +237,8 @@ class CHRBLK : public VALBLK {
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);
@@ -213,11 +249,11 @@ class CHRBLK : public VALBLK {
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
+ 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
/***********************************************************************/
@@ -254,6 +290,8 @@ class STRBLK : public VALBLK {
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);
@@ -291,5 +329,28 @@ class DATBLK : public TYPBLK<int> {
PVAL Dvalp; // Date value used to convert string
}; // end of class DATBLK
+/***********************************************************************/
+/* Class PTRBLK: represent a block of char pointers. */
+/* Currently this class is used only by the ARRAY class to make and */
+/* sort a list of char pointers. */
+/***********************************************************************/
+class PTRBLK : public STRBLK {
+ friend class ARRAY;
+ friend PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
+ bool, bool, bool);
+ protected:
+ // Constructors
+ PTRBLK(PGLOBAL g, void *mp, int size) : STRBLK(g, mp, size) {}
+
+ // Implementation
+
+ // Methods
+ virtual void SetValue(PSZ p, int n) {Strp[n] = p;}
+ virtual int CompVal(int i1, int i2);
+
+ protected:
+ // Members
+ }; // end of class PTRBLK
+
#endif // __VALBLK__H__
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index 12b6aced1cd..4c1c36369ef 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -92,6 +92,32 @@ 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 */
@@ -101,7 +127,7 @@ PSZ strlwr(PSZ s);
/* OUT minus: Set to true if the number is negative */
/* Returned val: The resulting number */
/***********************************************************************/
-ulonglong CharToNumber(char *p, int n, ulonglong maxval,
+ulonglong CharToNumber(char *p, int n, ulonglong maxval,
bool un, bool *minus, bool *rc)
{
char *p2;
@@ -110,7 +136,7 @@ ulonglong CharToNumber(char *p, int n, ulonglong maxval,
if (minus) *minus = false;
if (rc) *rc = false;
-
+
// Eliminate leading blanks or 0
for (p2 = p + n; p < p2 && (*p == ' ' || *p == '0'); p++) ;
@@ -158,6 +184,7 @@ PSZ GetTypeName(int type)
case TYPE_TINY: name = "TINY"; break;
case TYPE_DECIM: name = "DECIMAL"; break;
case TYPE_BIN: name = "BINARY"; break;
+ case TYPE_PCHAR: name = "PCHAR"; break;
default: name = "UNKNOWN"; break;
} // endswitch type
@@ -179,6 +206,7 @@ int GetTypeSize(int type, int len)
case TYPE_DATE: len = sizeof(int); break;
case TYPE_DOUBLE: len = sizeof(double); break;
case TYPE_TINY: len = sizeof(char); break;
+ case TYPE_PCHAR: len = sizeof(char*); break;
default: len = 0;
} // endswitch type
@@ -202,6 +230,7 @@ char *GetFormatType(int type)
case TYPE_TINY: c = "T"; break;
case TYPE_DECIM: c = "M"; break;
case TYPE_BIN: c = "B"; break;
+ case TYPE_PCHAR: c = "P"; break;
} // endswitch type
return c;
@@ -224,6 +253,7 @@ int GetFormatType(char c)
case 'T': type = TYPE_TINY; break;
case 'M': type = TYPE_DECIM; break;
case 'B': type = TYPE_BIN; break;
+ case 'P': type = TYPE_PCHAR; break;
} // endswitch type
return type;
@@ -284,6 +314,53 @@ const char *GetFmt(int type, bool un)
} // 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)
@@ -300,7 +377,7 @@ PVAL AllocateValue(PGLOBAL g, void *value, short type)
case TYPE_SHORT:
valp = new(g) TYPVAL<short>(*(short*)value, TYPE_SHORT);
break;
- case TYPE_INT:
+ case TYPE_INT:
valp = new(g) TYPVAL<int>(*(int*)value, TYPE_INT);
break;
case TYPE_BIGINT:
@@ -333,10 +410,10 @@ PVAL AllocateValue(PGLOBAL g, int type, int len, int prec,
case TYPE_STRING:
valp = new(g) TYPVAL<PSZ>(g, (PSZ)NULL, len, prec);
break;
- case TYPE_DATE:
+ case TYPE_DATE:
valp = new(g) DTVAL(g, len, prec, fmt);
break;
- case TYPE_INT:
+ case TYPE_INT:
if (uns)
valp = new(g) TYPVAL<uint>((uint)0, TYPE_INT, 0, true);
else
@@ -382,6 +459,74 @@ PVAL AllocateValue(PGLOBAL g, int type, int len, int prec,
return valp;
} // end of AllocateValue
+/***********************************************************************/
+/* Allocate a constant Value converted to newtype. */
+/* Can also be used to copy a Value eventually converted. */
+/***********************************************************************/
+PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
+ {
+ PSZ p, sp;
+ bool un = (uns < 0) ? false : (uns > 0) ? true : valp->IsUnsigned();
+
+ if (newtype == TYPE_VOID) // Means allocate a value of the same type
+ newtype = valp->GetType();
+
+ switch (newtype) {
+ case TYPE_STRING:
+ p = (PSZ)PlugSubAlloc(g, NULL, 1 + valp->GetValLen());
+
+ if ((sp = valp->GetCharString(p)) != p)
+ strcpy (p, sp);
+
+ valp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
+ break;
+ case TYPE_SHORT:
+ if (un)
+ valp = new(g) TYPVAL<ushort>(valp->GetUShortValue(),
+ TYPE_SHORT, 0, true);
+ else
+ valp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT);
+
+ break;
+ case TYPE_INT:
+ if (un)
+ valp = new(g) TYPVAL<uint>(valp->GetUIntValue(), TYPE_INT, 0, true);
+ else
+ valp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT);
+
+ break;
+ case TYPE_BIGINT:
+ if (un)
+ valp = new(g) TYPVAL<ulonglong>(valp->GetUBigintValue(),
+ TYPE_BIGINT, 0, true);
+ else
+ valp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT);
+
+ break;
+ case TYPE_DATE:
+ valp = new(g) DTVAL(g, valp->GetIntValue());
+ break;
+ case TYPE_DOUBLE:
+ valp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_DOUBLE,
+ valp->GetValPrec());
+ break;
+ case TYPE_TINY:
+ if (un)
+ valp = new(g) TYPVAL<uchar>(valp->GetUTinyValue(),
+ TYPE_TINY, 0, true);
+ else
+ valp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY);
+
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_VALUE_TYPE), newtype);
+ return NULL;
+ } // endswitch type
+
+ valp->SetGlobal(g);
+ return valp;
+ } // end of AllocateValue
+
/* -------------------------- Class VALUE ---------------------------- */
/***********************************************************************/
@@ -418,6 +563,18 @@ const char *VALUE::GetXfmt(void)
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 ---------------------------- */
/***********************************************************************/
@@ -543,8 +700,8 @@ bool TYPVAL<TYPE>::SetValue_char(char *p, int n)
{
bool rc, minus;
ulonglong maxval = MaxVal();
- ulonglong val = CharToNumber(p, n, maxval, Unsigned, &minus, &rc);
-
+ ulonglong val = CharToNumber(p, n, maxval, Unsigned, &minus, &rc);
+
if (minus && val < maxval)
Tval = (TYPE)(-(signed)val);
else
@@ -566,7 +723,7 @@ bool TYPVAL<double>::SetValue_char(char *p, int n)
if (p) {
char buf[64];
- for (; n > 0 && *p == ' '; p++)
+ for (; n > 0 && *p == ' '; p++)
n--;
memcpy(buf, p, MY_MIN(n, 31));
@@ -789,6 +946,24 @@ bool TYPVAL<TYPE>::IsEqual(PVAL vp, bool chktype)
} // end of IsEqual
/***********************************************************************/
+/* Compare values and returns 1, 0 or -1 according to comparison. */
+/* This function is used for evaluation of numeric filters. */
+/***********************************************************************/
+template <class TYPE>
+int TYPVAL<TYPE>::CompareValue(PVAL vp)
+ {
+//assert(vp->GetType() == Type);
+
+ // Process filtering on numeric values.
+ TYPE n = GetTypedValue(vp);
+
+//if (trace)
+// htrc(" Comparing: val=%d,%d\n", Tval, n);
+
+ return (Tval > n) ? 1 : (Tval < n) ? (-1) : 0;
+ } // end of CompareValue
+
+/***********************************************************************/
/* FormatValue: This function set vp (a STRING value) to the string */
/* constructed from its own value formated using the fmt format. */
/* This function assumes that the format matches the value type. */
@@ -870,11 +1045,11 @@ TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c)
if (!s) {
if (g) {
- Strp = (char *)PlugSubAlloc(g, NULL, Len + 1);
- Strp[Len] = '\0';
- } else
- assert(false);
-
+ Strp = (char *)PlugSubAlloc(g, NULL, Len + 1);
+ Strp[Len] = '\0';
+ } else
+ assert(false);
+
} else
Strp = s;
@@ -888,8 +1063,8 @@ TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c)
char TYPVAL<PSZ>::GetTinyValue(void)
{
bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX8, false, &m);
-
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX8, false, &m);
+
return (m && val < INT_MAX8) ? (char)(-(signed)val) : (char)val;
} // end of GetTinyValue
@@ -898,7 +1073,7 @@ char TYPVAL<PSZ>::GetTinyValue(void)
/***********************************************************************/
uchar TYPVAL<PSZ>::GetUTinyValue(void)
{
- return (uchar)CharToNumber(Strp, strlen(Strp), UINT_MAX8, true);
+ return (uchar)CharToNumber(Strp, strlen(Strp), UINT_MAX8, true);
} // end of GetUTinyValue
/***********************************************************************/
@@ -907,8 +1082,8 @@ uchar TYPVAL<PSZ>::GetUTinyValue(void)
short TYPVAL<PSZ>::GetShortValue(void)
{
bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX16, false, &m);
-
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX16, false, &m);
+
return (m && val < INT_MAX16) ? (short)(-(signed)val) : (short)val;
} // end of GetShortValue
@@ -917,7 +1092,7 @@ short TYPVAL<PSZ>::GetShortValue(void)
/***********************************************************************/
ushort TYPVAL<PSZ>::GetUShortValue(void)
{
- return (ushort)CharToNumber(Strp, strlen(Strp), UINT_MAX16, true);
+ return (ushort)CharToNumber(Strp, strlen(Strp), UINT_MAX16, true);
} // end of GetUshortValue
/***********************************************************************/
@@ -926,8 +1101,8 @@ ushort TYPVAL<PSZ>::GetUShortValue(void)
int TYPVAL<PSZ>::GetIntValue(void)
{
bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX32, false, &m);
-
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX32, false, &m);
+
return (m && val < INT_MAX32) ? (int)(-(signed)val) : (int)val;
} // end of GetIntValue
@@ -936,7 +1111,7 @@ int TYPVAL<PSZ>::GetIntValue(void)
/***********************************************************************/
uint TYPVAL<PSZ>::GetUIntValue(void)
{
- return (uint)CharToNumber(Strp, strlen(Strp), UINT_MAX32, true);
+ return (uint)CharToNumber(Strp, strlen(Strp), UINT_MAX32, true);
} // end of GetUintValue
/***********************************************************************/
@@ -945,8 +1120,8 @@ uint TYPVAL<PSZ>::GetUIntValue(void)
longlong TYPVAL<PSZ>::GetBigintValue(void)
{
bool m;
- ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX64, false, &m);
-
+ ulonglong val = CharToNumber(Strp, strlen(Strp), INT_MAX64, false, &m);
+
return (m && val < INT_MAX64) ? (-(signed)val) : (longlong)val;
} // end of GetBigintValue
@@ -955,7 +1130,7 @@ longlong TYPVAL<PSZ>::GetBigintValue(void)
/***********************************************************************/
ulonglong TYPVAL<PSZ>::GetUBigintValue(void)
{
- return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true);
+ return CharToNumber(Strp, strlen(Strp), ULONGLONG_MAX, true);
} // end of GetUBigintValue
/***********************************************************************/
@@ -989,18 +1164,18 @@ bool TYPVAL<PSZ>::SetValue_char(char *p, int n)
if ((n = MY_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 == ' ' || *p == '\0'); p--) ;
for (p = Strp + n - 1; p >= Strp; p--)
if (*p && *p != ' ')
break;
- *(++p) = '\0';
+ *(++p) = '\0';
- if (trace > 1)
- htrc(" Setting string to: '%s'\n", Strp);
-
- } else
- Reset();
+ if (trace > 1)
+ htrc(" Setting string to: '%s'\n", Strp);
+
+ } else
+ Reset();
Null = false;
} else {
@@ -1239,6 +1414,32 @@ bool TYPVAL<PSZ>::IsEqual(PVAL vp, bool chktype)
} // end of IsEqual
/***********************************************************************/
+/* Compare values and returns 1, 0 or -1 according to comparison. */
+/* This function is used for evaluation of numeric filters. */
+/***********************************************************************/
+int TYPVAL<PSZ>::CompareValue(PVAL vp)
+ {
+ int n;
+//assert(vp->GetType() == Type);
+
+ if (trace)
+ htrc(" Comparing: val='%s','%s'\n", Strp, vp->GetCharValue());
+
+ // Process filtering on character strings.
+ if (Ci || vp->IsCi())
+ n = stricmp(Strp, vp->GetCharValue());
+ else
+ n = strcmp(Strp, vp->GetCharValue());
+
+#if defined(WIN32)
+ if (n == _NLSCMPERROR)
+ return n; // Here we should raise an error
+#endif // WIN32
+
+ return (n > 0) ? 1 : (n < 0) ? -1 : 0;
+ } // end of CompareValue
+
+/***********************************************************************/
/* FormatValue: This function set vp (a STRING value) to the string */
/* constructed from its own value formated using the fmt format. */
/* This function assumes that the format matches the value type. */
@@ -1304,7 +1505,7 @@ bool DECVAL::IsZero(void)
/***********************************************************************/
/* DECIMAL: Reset value to zero. */
/***********************************************************************/
-void DECVAL::Reset(void)
+void DECVAL::Reset(void)
{
int i = 0;
@@ -1383,18 +1584,18 @@ bool DECVAL::SetValue_char(char *p, int n)
if ((n = MY_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 == ' ' || *p == '\0'); p--) ;
for (p = Strp + n - 1; p >= Strp; p--)
if (*p && *p != ' ')
break;
- *(++p) = '\0';
+ *(++p) = '\0';
- if (trace > 1)
- htrc(" Setting string to: '%s'\n", Strp);
-
- } else
- Reset();
+ if (trace > 1)
+ htrc(" Setting string to: '%s'\n", Strp);
+
+ } else
+ Reset();
Null = false;
} else {
@@ -1464,6 +1665,23 @@ bool DECVAL::IsEqual(PVAL vp, bool chktype)
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 */
@@ -2062,7 +2280,7 @@ bool DTVAL::MakeTime(struct tm *ptm)
time_t t = mktime_mysql(ptm);
if (trace > 1)
- htrc("MakeTime from (%d,%d,%d,%d,%d,%d)\n",
+ 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);
@@ -2085,7 +2303,7 @@ bool DTVAL::MakeTime(struct tm *ptm)
Tval= (int) t;
if (trace > 1)
- htrc("MakeTime Ival=%d\n", Tval);
+ htrc("MakeTime Ival=%d\n", Tval);
return false;
} // end of MakeTime
@@ -2169,7 +2387,7 @@ bool DTVAL::MakeDate(PGLOBAL g, int *val, int nval)
} // endfor i
if (trace > 1)
- htrc("MakeDate datm=(%d,%d,%d,%d,%d,%d)\n",
+ 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);
diff --git a/storage/connect/value.h b/storage/connect/value.h
index 56992d5bc26..151ddacf509 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -46,10 +46,13 @@ 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,
+DllExport ulonglong CharToNumber(char *, int, ulonglong, bool,
bool *minus = NULL, bool *rc = NULL);
+DllExport BYTE OpBmp(PGLOBAL g, OPVAL opc);
/***********************************************************************/
/* Class VALUE represents a constant or variable of any valid type. */
@@ -93,6 +96,9 @@ class DllExport VALUE : public BLOCK {
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);}
@@ -161,6 +167,8 @@ class DllExport TYPVAL : public VALUE {
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;}
@@ -199,7 +207,7 @@ class DllExport TYPVAL : public VALUE {
/* Specific STRING class. */
/***********************************************************************/
template <>
-class DllExport TYPVAL<PSZ>: public VALUE {
+class DllExport TYPVAL<PSZ>: public VALUE {
public:
// Constructors
TYPVAL(PSZ s);
@@ -240,6 +248,7 @@ class DllExport TYPVAL<PSZ>: public VALUE {
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);
@@ -256,7 +265,7 @@ class DllExport TYPVAL<PSZ>: public VALUE {
/***********************************************************************/
/* Specific DECIMAL class. */
/***********************************************************************/
-class DllExport DECVAL: public TYPVAL<PSZ> {
+class DllExport DECVAL: public TYPVAL<PSZ> {
public:
// Constructors
DECVAL(PSZ s);
@@ -272,6 +281,7 @@ class DllExport DECVAL: public TYPVAL<PSZ> {
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
@@ -279,7 +289,7 @@ class DllExport DECVAL: public TYPVAL<PSZ> {
/***********************************************************************/
/* Specific BINARY class. */
/***********************************************************************/
-class DllExport BINVAL: public VALUE {
+class DllExport BINVAL: public VALUE {
public:
// Constructors
//BINVAL(void *p);
@@ -320,6 +330,7 @@ class DllExport BINVAL: public VALUE {
virtual void SetValue(double f);
virtual void SetBinValue(void *p);
virtual bool GetBinValue(void *buf, int buflen, bool go);
+ virtual int CompareValue(PVAL vp) {assert(false); return 0;}
virtual char *ShowValue(char *buf, int);
virtual char *GetCharString(char *p);
virtual bool IsEqual(PVAL vp, bool chktype);
diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp
index fad7495fa82..7cc52580760 100755
--- a/storage/connect/xindex.cpp
+++ b/storage/connect/xindex.cpp
@@ -1,7 +1,7 @@
/***************** Xindex C++ Class Xindex Code (.CPP) *****************/
-/* Name: XINDEX.CPP Version 2.8 */
+/* Name: XINDEX.CPP Version 2.9 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
/* */
/* This file contains the class XINDEX implementation code. */
/***********************************************************************/
@@ -45,11 +45,12 @@
//nclude "array.h"
#include "filamtxt.h"
#include "tabdos.h"
+#include "tabvct.h"
/***********************************************************************/
/* Macro or external routine definition */
/***********************************************************************/
-#define NZ 7
+#define NZ 8
#define NW 5
#define MAX_INDX 10
#ifndef INVALID_SET_FILE_POINTER
@@ -112,6 +113,8 @@ INDEXDEF::INDEXDEF(char *name, bool uniq, int n)
Unique = uniq;
Invalid = false;
AutoInc = false;
+ Dynamic = false;
+ Mapped = false;
Nparts = 0;
ID = n;
//Offset = 0;
@@ -165,6 +168,8 @@ XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b),
Op = OP_EQ;
To_KeyCol = NULL;
Mul = false;
+ Srtd = false;
+ Dynamic = false;
Val_K = -1;
Nblk = Sblk = 0;
Thresh = 7;
@@ -237,25 +242,27 @@ void XINDEX::Reset(void)
/***********************************************************************/
/* XINDEX Close: terminate index and free all allocated data. */
-/* Do not reset other values that are used at return to make. */
+/* Do not reset values that are used at return to make. */
/***********************************************************************/
void XINDEX::Close(void)
{
// Close file or view of file
- X->Close();
+ if (X)
+ 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();
+ for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next) {
+ // Column values cannot be retrieved from key anymore
+ if (kcp->Colp)
+ kcp->Colp->SetKcol(NULL);
- // Column values cannot be retrieved from key anymore
- for (int k = 0; k < Nk; k++)
- To_Cols[k]->SetKcol(NULL);
+ // De-allocate Key data
+ kcp->FreeData();
+ } // endfor kcp
} // end of Close
@@ -276,6 +283,25 @@ int XINDEX::Qcompare(int *i1, int *i2)
} // end of Qcompare
/***********************************************************************/
+/* AddColumns: here we try to determine whether it is worthwhile to */
+/* add to the keys the values of the columns selected for this table. */
+/* Sure enough, it is done while records are read and permit to avoid */
+/* reading the table while doing the join (Dynamic index only) */
+/***********************************************************************/
+bool XINDEX::AddColumns(PIXDEF xdp)
+ {
+ if (!Dynamic)
+ return false; // Not applying to static index
+ else if (IsMul())
+ return false; // Not done yet for multiple index
+ else if (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit())
+ return false; // This would require to read additional files
+ else
+ return true;
+
+ } // end of AddColumns
+
+/***********************************************************************/
/* Make: Make and index on key column(s). */
/***********************************************************************/
bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
@@ -283,13 +309,18 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
/*********************************************************************/
/* Table can be accessed through an index. */
/*********************************************************************/
- int k, rc = RC_OK;
+ int k, nk = Nk, rc = RC_OK;
int *bof, i, j, n, ndf, nkey;
PKPDEF kdfp = Xdp->GetToKeyParts();
- bool brc = true;
+ bool brc = false;
PCOL colp;
- PXCOL kp, prev = NULL, kcp = NULL;
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+ PFIL filp = Tdbp->GetFilter();
+ PXCOL kp, addcolp, prev = NULL, kcp = NULL;
+//PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+#if defined(_DEBUG)
+ assert(X || Nk == 1);
+#endif // _DEBUG
/*********************************************************************/
/* Allocate the storage that will contain the keys and the file */
@@ -347,6 +378,51 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
To_LastCol = prev;
+ if (AddColumns(sxp)) {
+ PCOL kolp = To_Cols[0]; // Temporary while imposing Nk = 1
+
+ i = 0;
+
+ // Allocate the accompanying
+ for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) {
+ // Count how many columns to add
+// for (k = 0; k < Nk; k++)
+// if (colp == To_Cols[k])
+// break;
+
+// if (k == nk)
+ if (colp != kolp)
+ i++;
+
+ } // endfor colp
+
+ if (i && i < 10) // Should be a parameter
+ for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) {
+// for (k = 0; k < Nk; k++)
+// if (colp == To_Cols[k])
+// break;
+
+// if (k < nk)
+ if (colp == kolp)
+ continue; // This is a key column
+
+ kcp = new(g) KXYCOL(this);
+
+ if (kcp->Init(g, colp, n, true, 0))
+ return true;
+
+ if (trace)
+ htrc("Adding colp=%p Buf_Type=%d size=%d\n",
+ colp, colp->GetResultType(), n);
+
+ nk++;
+ prev->Next = kcp;
+ prev = kcp;
+ } // endfor colp
+
+ } // endif AddColumns
+
+#if 0
/*********************************************************************/
/* Get the starting information for progress. */
/*********************************************************************/
@@ -354,18 +430,19 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name);
dup->ProgMax = Tdbp->GetProgMax(g);
dup->ProgCur = 0;
+#endif // 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)
+ for (i = nkey = 0; rc != RC_EF; i++) {
+#if 0
if (!dup->Step) {
strcpy(g->Message, MSG(QUERY_CANCELLED));
longjmp(g->jumper[g->jump_level], 99);
} // endif Step
-#endif // THREAD
+#endif // 0
/*******************************************************************/
/* Read a valid record from table file. */
@@ -373,16 +450,19 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
rc = Tdbp->ReadDB(g);
// Update progress information
- dup->ProgCur = Tdbp->GetProgCur();
+// 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;
+ if (ApplyFilter(g, filp))
+ break;
+
+ // passthru
case RC_NF:
continue;
+ case RC_EF:
+ goto end_of_file;
default:
sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name);
goto err;
@@ -392,20 +472,25 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
/* Get and Store the file position of the last read record for */
/* future direct access. */
/*******************************************************************/
- To_Rec[nkey] = Tdbp->GetRecpos();
+ if (nkey == n) {
+ sprintf(g->Message, MSG(TOO_MANY_KEYS), nkey);
+ return true;
+ } else
+ 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 < nk && kcp;
k++, kcp = kcp->Next) {
- colp = To_Cols[k];
- colp->Reset();
+// colp = To_Cols[k];
+ colp = kcp->Colp;
- colp->ReadColumn(g);
-// if (colp->ReadColumn(g))
-// goto err;
+ if (!colp->GetStatus(BUF_READ))
+ colp->ReadColumn(g);
+ else
+ colp->Reset();
kcp->SetValue(colp, nkey);
} // endfor k
@@ -416,7 +501,7 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
end_of_file:
// Update progress information
- dup->ProgCur = Tdbp->GetProgMax(g);
+//dup->ProgCur = Tdbp->GetProgMax(g);
/*********************************************************************/
/* Record the Index size and eventually resize memory allocation. */
@@ -451,18 +536,30 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
goto err; // Error
} // endif alloc
+ // We must separate keys and added columns before sorting
+ addcolp = To_LastCol->Next;
+ To_LastCol->Next = NULL;
+
// Call the sort program, it returns the number of distinct values
if ((Ndif = Qsort(g, Num_K)) < 0)
goto err; // Error during sort
+ if (trace)
+ htrc("Make: Nk=%d n=%d Num_K=%d Ndif=%d addcolp=%p BlkFil=%p X=%p\n",
+ Nk, n, Num_K, Ndif, addcolp, Tdbp->To_BlkFil, X);
+
// Check whether the unique index is unique indeed
if (!Mul)
if (Ndif < Num_K) {
strcpy(g->Message, MSG(INDEX_NOT_UNIQ));
+ brc = true;
goto err;
} else
PlgDBfree(Offset); // Not used anymore
+ // Restore kcp list
+ To_LastCol->Next = addcolp;
+
// Use the index to physically reorder the xindex
Srtd = Reorder(g);
@@ -487,7 +584,7 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
} else {
Mul = false; // Current index is unique
PlgDBfree(Offset); // Not used anymore
- MaxSame = 1; // Reset it when remaking an index
+ MaxSame = 1; // Reset it when remaking an index
} // endif Ndif
/*********************************************************************/
@@ -502,7 +599,7 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
/* except if the subset originally contains unique values. */
/*********************************************************************/
// Update progress information
- dup->Step = STEP(REDUCE_INDEX);
+//dup->Step = STEP(REDUCE_INDEX);
ndf = Ndif;
To_LastCol->Mxs = MaxSame;
@@ -550,9 +647,11 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
/*********************************************************************/
/* For sorted columns and fixed record size, file position can be */
/* calculated, so the Record array can be discarted. */
+ /* Not true for DBF tables because of eventual soft deleted lines. */
/* Note: for Num_K = 1 any non null value is Ok. */
/*********************************************************************/
- if (Srtd && Tdbp->Ftype != RECFM_VAR) {
+ if (Srtd && !filp && Tdbp->Ftype != RECFM_VAR
+ && Tdbp->Txfp->GetAmType() != TYPE_AM_DBF) {
Incr = (Num_K > 1) ? To_Rec[1] : Num_K;
PlgDBfree(Record);
} // endif Srtd
@@ -579,14 +678,24 @@ bool XINDEX::Make(PGLOBAL g, PIXDEF sxp)
Cur_K = Num_K;
/*********************************************************************/
- /* Save the index so it has not to be recalculated. */
+ /* Save the xindex so it has not to be recalculated. */
/*********************************************************************/
- if (!SaveIndex(g, sxp))
- brc = false;
+ if (X) {
+ if (SaveIndex(g, sxp))
+ brc = true;
+
+ } else { // Dynamic index
+ // Indicate that key column values can be found from KEYCOL's
+ for (kcp = To_KeyCol; kcp; kcp = kcp->Next)
+ kcp->Colp->SetKcol(kcp);
+
+ Tdbp->SetFilter(NULL); // Not used anymore
+ } // endif X
err:
// We don't need the index anymore
- Close();
+ if (X || brc)
+ Close();
if (brc)
printf("%s\n", g->Message);
@@ -630,6 +739,7 @@ bool XINDEX::Reorder(PGLOBAL g)
register int i, j, k, n;
bool sorted = true;
PXCOL kcp;
+#if 0
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
if (Num_K > 500000) {
@@ -639,6 +749,7 @@ bool XINDEX::Reorder(PGLOBAL g)
dup->ProgCur = 0;
} else
dup = NULL;
+#endif // 0
if (!Pex)
return Srtd;
@@ -647,8 +758,8 @@ bool XINDEX::Reorder(PGLOBAL g)
if (Pex[i] == Num_K) { // Already moved
continue;
} else if (Pex[i] == i) { // Already placed
- if (dup)
- dup->ProgCur++;
+// if (dup)
+// dup->ProgCur++;
continue;
} // endif's Pex
@@ -677,8 +788,8 @@ bool XINDEX::Reorder(PGLOBAL g)
To_Rec[j] = To_Rec[k];
} // endif k
- if (dup)
- dup->ProgCur++;
+// if (dup)
+// dup->ProgCur++;
} // endfor j
@@ -706,11 +817,11 @@ bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
bool sep, rc = false;
PXCOL kcp = To_KeyCol;
PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- PDBUSER dup = PlgGetUser(g);
+//PDBUSER dup = PlgGetUser(g);
- dup->Step = STEP(SAVING_INDEX);
- dup->ProgMax = 15 + 16 * Nk;
- dup->ProgCur = 0;
+//dup->Step = STEP(SAVING_INDEX);
+//dup->ProgMax = 15 + 16 * Nk;
+//dup->ProgCur = 0;
switch (Tdbp->Ftype) {
case RECFM_VAR: ftype = ".dnx"; break;
@@ -755,31 +866,32 @@ bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
/*********************************************************************/
/* Write the index values on the index file. */
/*********************************************************************/
- n[0] = ID; // To check validity
+ n[0] = ID + MAX_INDX; // 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;
+ n[7] = Srtd ? 1 : 0; // Values are sorted in the file
-#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
+ if (trace) {
+ htrc("Saving index %s\n", Xdp->GetName());
+ htrc("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d Srtd=%d\n",
+ ID, Nk, nof, Num_K, Incr, Nblk, Sblk, Srtd);
+ } // endif trace
size = X->Write(g, n, NZ, sizeof(int), rc);
- dup->ProgCur = 1;
+//dup->ProgCur = 1;
if (Mul) // Write the offset array
size += X->Write(g, Pof, nof, sizeof(int), rc);
- dup->ProgCur = 5;
+//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;
+//dup->ProgCur = 15;
for (; kcp; kcp = kcp->Next) {
n[0] = kcp->Ndf; // Number of distinct sub-values
@@ -789,25 +901,24 @@ bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp)
n[4] = kcp->Type; // To be checked later
size += X->Write(g, n, NW, sizeof(int), rc);
- dup->ProgCur += 1;
+// dup->ProgCur += 1;
if (n[2])
size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc);
- dup->ProgCur += 5;
+// dup->ProgCur += 5;
size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc);
- dup->ProgCur += 5;
+// dup->ProgCur += 5;
if (n[1])
size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc);
- dup->ProgCur += 5;
+// dup->ProgCur += 5;
} // endfor kcp
-#if defined(TRACE)
- printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size);
-#endif // TRACE
+ if (trace)
+ htrc("Index %s saved, Size=%d\n", Xdp->GetName(), size);
end:
X->Close(fn, id);
@@ -896,9 +1007,8 @@ bool XINDEX::Init(PGLOBAL g)
PlugSetPath(fn, fn, Tdbp->GetPath());
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
+ if (trace)
+ htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/
/* Open the index file and check its validity. */
@@ -907,21 +1017,31 @@ bool XINDEX::Init(PGLOBAL g)
goto err; // No saved values
// Now start the reading process.
- if (X->Read(g, nv, NZ, sizeof(int)))
+ if (X->Read(g, nv, NZ - 1, 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
+ if (nv[0] >= MAX_INDX) {
+ // New index format
+ if (X->Read(g, nv + 7, 1, sizeof(int)))
+ goto err;
+
+ Srtd = nv[7] != 0;
+ nv[0] -= MAX_INDX;
+ } else
+ Srtd = false;
+
+ if (trace)
+ htrc("nv=%d %d %d %d %d %d %d (%d)\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd);
// The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
+ // 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
+
+ if (trace)
+ htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+
goto err;
} // endif
@@ -1140,9 +1260,8 @@ bool XINDEX::MapInit(PGLOBAL g)
PlugSetPath(fn, fn, Tdbp->GetPath());
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
+ if (trace)
+ htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/
/* Get a view on the part of the index file containing this index. */
@@ -1157,24 +1276,33 @@ bool XINDEX::MapInit(PGLOBAL g)
// 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
+ if (nv[0] >= MAX_INDX) {
+ // New index format
+ Srtd = nv[7] != 0;
+ nv[0] -= MAX_INDX;
+ mbase += NZ * sizeof(int);
+ } else {
+ Srtd = false;
+ mbase += (NZ - 1) * sizeof(int);
+ } // endif nv
+
+ if (trace)
+ htrc("nv=%d %d %d %d %d %d %d %d\n",
+ nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd);
// The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
+ // 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
+
+ if (trace)
+ htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+
goto err;
} // endif nv
@@ -1272,16 +1400,19 @@ err:
/***********************************************************************/
/* Get Ndif and Num_K from the index file. */
/***********************************************************************/
-bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
+bool XINDEX::GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk)
{
char *ftype;
char fn[_MAX_PATH];
- int n, nv[NZ], id = -1;
- bool estim = false;
+ int nv[NZ], id = -1; // n
+//bool estim = false;
+ bool rc = true;
PDOSDEF defp = (PDOSDEF)Tdbp->To_Def;
- ndif = numk = 0;
+// ndif = numk = 0;
+ numk = 0;
+#if 0
/*********************************************************************/
/* Get the estimated table size. */
/* Note: for fixed tables we must use cardinality to avoid the call */
@@ -1309,6 +1440,7 @@ bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
strcpy(g->Message, MSG(NO_KEY_COL));
return true; // Error
} // endif Nk
+#endif // 0
switch (Tdbp->Ftype) {
case RECFM_VAR: ftype = ".dnx"; break;
@@ -1341,9 +1473,8 @@ bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
PlugSetPath(fn, fn, Tdbp->GetPath());
-#if defined(TRACE)
- printf("Index %s file: %s\n", Xdp->GetName(), fn);
-#endif // TRACE
+ if (trace)
+ htrc("Index %s file: %s\n", Xdp->GetName(), fn);
/*********************************************************************/
/* Open the index file and check its validity. */
@@ -1359,20 +1490,21 @@ bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
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
+ if (trace)
+ htrc("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]);
// The test on ID was suppressed because MariaDB can change an index ID
- // when other indexes are added or deleted
+ // 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
+
+ if (trace)
+ htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk);
+
goto err;
} // endif
+#if 0
if (nv[2]) {
Mul = true;
Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1
@@ -1388,9 +1520,11 @@ bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
sprintf(g->Message, MSG(OPT_NOT_MATCH), fn);
goto err;
} // endif
+#endif // 0
Num_K = nv[3];
+#if 0
if (Nk > 1) {
if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR))
goto err;
@@ -1411,17 +1545,18 @@ bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk)
Ndif = nv[0];
} // endif Nk
+#endif // 0
/*********************************************************************/
/* Set size values. */
/*********************************************************************/
- ndif = Ndif;
+//ndif = Ndif;
numk = Num_K;
- return false;
+ rc = false;
err:
X->Close();
- return true;
+ return rc;
} // end of GetAllSizes
/***********************************************************************/
@@ -1455,7 +1590,7 @@ int XINDEX::Range(PGLOBAL g, int limit, bool incl)
n = k;
// if (limit)
// n = (Mul) ? k : kp->Val_K;
-// else
+// else
// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
} else {
@@ -1642,9 +1777,8 @@ int XINDEX::Fetch(PGLOBAL g)
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 (trace > 1)
+ htrc("looking for next same value\n");
if (NextVal(true)) {
Op = OP_EQ;
@@ -1690,9 +1824,9 @@ int XINDEX::Fetch(PGLOBAL g)
Nth++;
-#if defined(TRACE)
- printf("Fetch: Looking for new value\n");
-#endif // TRACE
+ if (trace > 1)
+ htrc("Fetch: Looking for new value\n");
+
Cur_K = FastFind(Nval);
if (Cur_K >= Num_K)
@@ -1848,8 +1982,7 @@ int XINDEX::FastFind(int nv)
XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp)
: XINDEX(tdbp, xdp, pxp, cp, xp)
{
-//Srtd = To_Cols[0]->GetOpt() < 0; // ?????
- Srtd = false;
+ Srtd = To_Cols[0]->GetOpt() == 2;
} // end of XINDXS constructor
/***********************************************************************/
@@ -1891,7 +2024,7 @@ int XINDXS::Range(PGLOBAL g, int limit, bool incl)
if (k < Num_K || Op != OP_EQ)
if (limit)
n = (Mul) ? k : kp->Val_K;
- else
+ else
n = (Mul) ? Pof[kp->Val_K + 1] - k : 1;
} else {
@@ -1987,10 +2120,9 @@ int XINDXS::Fetch(PGLOBAL g)
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
+ case OP_SAME: // Read next same
+ if (trace > 1)
+ htrc("looking for next same value\n");
if (!Mul || NextVal(true)) {
Op = OP_EQ;
@@ -2023,18 +2155,17 @@ int XINDXS::Fetch(PGLOBAL g)
/* 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)
+ if (To_KeyCol->InitFind(g, To_Vals[0]))
+ return -1; // No more constant values
+ else
+ Nth++;
+
+ if (trace > 1)
+ htrc("Fetch: Looking for new value\n");
+
+ Cur_K = FastFind(1);
+
+ if (Cur_K >= Num_K)
// Rank not whithin index table, signal record not found
return -2;
else if (Mul)
@@ -2119,7 +2250,10 @@ int XINDXS::FastFind(int nk)
n = 0;
} // endif sup
- kcp->Val_K = i; // Used by FillValue
+ // Loop on kcp because of dynamic indexing
+ for (; kcp; kcp = kcp->Next)
+ kcp->Val_K = i; // Used by FillValue
+
return ((n) ? Num_K : (Mul) ? Pof[i] : i);
} // end of FastFind
@@ -2195,7 +2329,7 @@ bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
return true;
} // endif
-
+
NewOff.Low = (int)ftell(Xfile);
} else if (mode == MODE_WRITE) {
if (id >= 0) {
@@ -2218,7 +2352,7 @@ bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode)
sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek");
return true;
} // endif
-
+
} // endif mode
return false;
@@ -2336,7 +2470,7 @@ void *XFILE::FileView(PGLOBAL g, char *fn)
/***********************************************************************/
bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
{
- IOFF noff[MAX_INDX];
+ IOFF noff[MAX_INDX];
if (Hfile != INVALID_HANDLE_VALUE) {
sprintf(g->Message, MSG(FILE_OPEN_YET), filename);
@@ -2344,7 +2478,7 @@ bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
} // endif
if (trace)
- htrc(" Xopen: filename=%s mode=%d\n", filename, mode);
+ htrc(" Xopen: filename=%s id=%d mode=%d\n", filename, id, mode);
#if defined(WIN32)
LONG high = 0;
@@ -2424,19 +2558,19 @@ bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
} // endif rc
// Position the cursor at the offset of this index
- rc = SetFilePointer(Hfile, noff[id].Low,
+ 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;
+ mode_t pmod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
/*********************************************************************/
/* Create the file object according to access mode */
@@ -2447,7 +2581,7 @@ bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
break;
case MODE_WRITE:
oflag |= O_WRONLY | O_CREAT | O_TRUNC;
- pmod = S_IREAD | S_IWRITE;
+// pmod = S_IREAD | S_IWRITE;
break;
case MODE_INSERT:
oflag |= (O_WRONLY | O_APPEND);
@@ -2479,7 +2613,10 @@ bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek");
return true;
} // endif
-
+
+ if (trace)
+ htrc("INSERT: NewOff=%lld\n", NewOff.Val);
+
} else if (mode == MODE_WRITE) {
if (id >= 0) {
// New not sep index file. Write the header.
@@ -2487,19 +2624,27 @@ bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode)
NewOff.Low = write(Hfile, &noff, sizeof(noff));
} // endif id
+ if (trace)
+ htrc("WRITE: NewOff=%lld\n", NewOff.Val);
+
} 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
+ } // endif read
+
+ if (trace)
+ htrc("noff[%d]=%lld\n", id, noff[id].Val);
// 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");
+ if (lseek64(Hfile, noff[id].Val, SEEK_SET) < 0) {
+ sprintf(g->Message, "(XHUGE)lseek64: %s (%lld)", strerror(errno), noff[id].Val);
+ printf("%s\n", g->Message);
+// sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek");
return true;
- } // endif
-
+ } // endif lseek64
+
} // endif mode
#endif // UNIX
@@ -2526,15 +2671,15 @@ bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin)
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
+
+ if (trace)
+ htrc("lseek64 error %d\n", errno);
+
return true;
} // endif lseek64
-#if defined(TRACE)
- printf("Seek: low=%d high=%d\n", low, high);
-#endif // TRACE
+ if (trace)
+ htrc("Seek: low=%d high=%d\n", low, high);
#endif // UNIX
return false;
@@ -2632,12 +2777,15 @@ int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc)
/***********************************************************************/
void XHUGE::Close(char *fn, int id)
{
+ if (trace)
+ htrc("XHUGE::Close: fn=%s id=%d NewOff=%lld\n", fn, id, NewOff.Val);
+
#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) {
@@ -2649,10 +2797,18 @@ void XHUGE::Close(char *fn, int id)
} // 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));
+ if (Hfile != INVALID_HANDLE_VALUE) {
+ if (lseek64(Hfile, id * sizeof(IOFF), SEEK_SET) >= 0) {
+ ssize_t nbw = write(Hfile, &NewOff, sizeof(IOFF));
+
+ if (nbw != (signed)sizeof(IOFF))
+ htrc("Error writing index file header: %s\n", strerror(errno));
+
+ } else
+ htrc("(XHUGE::Close)lseek64: %s (%d)\n", strerror(errno), id);
+
+ } else
+ htrc("(XHUGE)error reopening %s: %s\n", fn, strerror(errno));
} // endif id
#endif // !WIN32
@@ -2678,6 +2834,7 @@ void *XHUGE::FileView(PGLOBAL g, char *fn)
/***********************************************************************/
XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false)
{
+ Srtd = true;
Tdbp = tdbp;
Valp = NULL;
} // end of XXROW constructor
@@ -2788,7 +2945,7 @@ int XXROW::FastFind(int nk)
/***********************************************************************/
/* KXYCOL public constructor. */
/***********************************************************************/
-KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
+KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp),
To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp)
{
Next = NULL;
@@ -2821,7 +2978,7 @@ 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()) {
+ if (colp->IsNullable() && kln) {
sprintf(g->Message, "Cannot index nullable column %s", colp->GetName());
return true;
} // endif nullable
@@ -2860,8 +3017,7 @@ bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln)
// Store this information to avoid sorting when already done
if (Asc)
-// IsSorted = colp->GetOpt() == 2;
- IsSorted = false;
+ IsSorted = colp->GetOpt() == 2;
//SetNulls(colp->IsNullable()); for when null columns will be indexable
Colp = colp;
@@ -2885,9 +3041,9 @@ BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m)
Type = colp->GetResultType();
- if (trace)
- htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
- this, colp, Type, n[0], len, m);
+ if (trace)
+ htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n",
+ this, colp, Type, n[0], len, m);
// Allocate the Value object used when moving items
Valp = AllocateValue(g, Type, len, prec, colp->IsUnsigned());
diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h
index 62430ffa0ad..c702baeec83 100644
--- a/storage/connect/xindex.h
+++ b/storage/connect/xindex.h
@@ -87,7 +87,9 @@ class DllExport INDEXDEF : public BLOCK { /* Index description block */
void SetNext(PIXDEF pxdf) {Next = pxdf;}
PSZ GetName(void) {return (PSZ)Name;}
bool IsUnique(void) {return Unique;}
+ bool IsDynamic(void) {return Dynamic;}
bool IsAuto(void) {return AutoInc;}
+ bool IsValid(void) {return !Invalid;}
void SetAuto(bool b) {AutoInc = b;}
void SetInvalid(bool b) {Invalid = b;}
int GetNparts(void) {return Nparts;}
@@ -115,6 +117,8 @@ class DllExport INDEXDEF : public BLOCK { /* Index description block */
bool Unique; /* true if defined as unique */
bool Invalid; /* true if marked as Invalid */
bool AutoInc; /* true if unique key in auto increment */
+ bool Dynamic; /* KINDEX style */
+ bool Mapped; /* Use file mapping */
int Nparts; /* Number of key parts */
int ID; /* Index ID number */
int MaxSame; /* Max number of same values */
@@ -174,6 +178,8 @@ class DllExport XXBASE : public CSORT, public BLOCK {
virtual void Reset(void) = 0;
virtual bool IsMul(void) {return false;}
virtual bool IsRandom(void) {return true;}
+ virtual bool IsDynamic(void) {return Dynamic;}
+ virtual void SetDynamic(bool dyn) {Dynamic = dyn;}
virtual bool HaveSame(void) {return false;}
virtual int GetCurPos(void) {return Cur_K;}
virtual void SetNval(int n) {assert(n == 1);}
@@ -186,12 +192,14 @@ class DllExport XXBASE : public CSORT, public BLOCK {
void SetNth(int n) {Nth = n;}
int *GetPof(void) {return Pof;}
int *GetPex(void) {return Pex;}
+ bool IsSorted(void) {return Srtd;}
void FreeIndex(void) {PlgDBfree(Index);}
// Methods
virtual void Print(PGLOBAL g, FILE *f, uint n);
virtual void Print(PGLOBAL g, char *ps, uint z);
virtual bool Init(PGLOBAL g) = 0;
+ virtual bool Make(PGLOBAL g, PIXDEF sxp) = 0;
#if defined(XMAP)
virtual bool MapInit(PGLOBAL g) = 0;
#endif // XMAP
@@ -223,6 +231,7 @@ class DllExport XXBASE : public CSORT, public BLOCK {
OPVAL Op; // Search operator
bool Mul; // true if multiple
bool Srtd; // true for sorted column
+ bool Dynamic; // true when dynamically made
int Val_K; // Index of current value
int Nblk; // Number of blocks
int Sblk; // Block size
@@ -268,9 +277,10 @@ class DllExport XINDEX : public XXBASE {
virtual bool Make(PGLOBAL g, PIXDEF sxp);
virtual bool SaveIndex(PGLOBAL g, PIXDEF sxp);
virtual bool Reorder(PGLOBAL g);
- bool GetAllSizes(PGLOBAL g, int &ndif, int &numk);
+ bool GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk);
protected:
+ bool AddColumns(PIXDEF xdp);
bool NextValDif(void);
// Members
@@ -417,6 +427,7 @@ class DllExport XXROW : public XXBASE {
virtual int MaxRange(void) {return 1;}
virtual int Range(PGLOBAL g, int limit = 0, bool incl = true);
virtual int Qcompare(int *, int *) {assert(false); return 0;}
+ virtual bool Make(PGLOBAL g, PIXDEF sxp) {return false;}
virtual void Close(void) {}
protected:
diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp
index 05b408da2d2..cdc2ef9bf62 100644
--- a/storage/connect/xobject.cpp
+++ b/storage/connect/xobject.cpp
@@ -1,175 +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
-
-/***********************************************************************/
-/* 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 15ba6d99f33..1621b4e82ff 100644
--- a/storage/connect/xobject.h
+++ b/storage/connect/xobject.h
@@ -1,118 +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;
-
- 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 SetValue(PVAL vp) {Value = vp;}
- 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 1f937bba6c1..628ab96135d 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -62,6 +62,8 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
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;}
@@ -72,16 +74,17 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
inline void SetDegree(int degree) {Degree = degree;}
inline void SetMode(MODE mode) {Mode = mode;}
- //Properties
+ // 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;}
+ virtual void SetAbort(bool b) {;}
// 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 Cardinality(PGLOBAL g) {return 0;}
virtual int GetMaxSize(PGLOBAL) = 0;
virtual int GetProgMax(PGLOBAL) = 0;
virtual int GetProgCur(void) = 0;
@@ -91,7 +94,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
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)
+ 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);
@@ -113,6 +116,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
// 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
@@ -122,6 +126,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
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
+ int Cardinal; // Table number of rows
}; // end of class TDB
/***********************************************************************/
@@ -142,6 +147,8 @@ class DllExport TDBASE : public TDB {
inline PKXBASE GetKindex(void) {return To_Kindex;}
inline PCOL GetSetCols(void) {return To_SetCols;}
inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
+ inline void SetKey_Col(PCOL *cpp) {To_Key_Col = cpp;}
+ inline void SetXdp(PIXDEF xdp) {To_Xdp = xdp;}
inline void SetKindex(PKXBASE kxp) {To_Kindex = kxp;}
// Properties
@@ -150,11 +157,12 @@ class DllExport TDBASE : public TDB {
// Methods
virtual bool IsUsingTemp(PGLOBAL g) {return false;}
+ virtual bool IsIndexed(void) {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 GetAffectedRows(void) {return -1;}
virtual int GetRecpos(void) = 0;
virtual bool SetRecpos(PGLOBAL g, int recpos);
virtual bool IsReadOnly(void) {return Read_Only;}
@@ -168,7 +176,7 @@ class DllExport TDBASE : public TDB {
virtual void ResetDB(void) {}
virtual void ResetSize(void) {MaxSize = -1;}
virtual void RestoreNrec(void) {}
- virtual int ResetTableOpt(PGLOBAL g, bool dox);
+ virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
virtual PSZ GetServer(void) {return "Current";}
// Database routines
@@ -181,17 +189,21 @@ class DllExport TDBASE : public TDB {
virtual int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
{strcpy(g->Message, "Remote index"); return RC_INFO;}
virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
- {assert(false); return true;}
-
+ {assert(false); return true;}
protected:
+ virtual bool PrepareWriting(PGLOBAL g) {strcpy(g->Message,
+ "This function should not be called for this table"); return true;}
+
// 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
+ PIXDEF To_Xdp; // To the index definition block
PCOL To_SetCols; // Points to updated columns
- int MaxSize; // Max size in number of lines
+ RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT)
+ 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;
@@ -213,7 +225,7 @@ class DllExport TDBCAT : public TDBASE {
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);
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
@@ -231,9 +243,9 @@ class DllExport TDBCAT : public TDBASE {
bool InitCol(PGLOBAL g);
// Members
- PQRYRES Qrp;
+ PQRYRES Qrp;
int N; // Row number
- bool Init;
+ bool Init;
}; // end of class TDBCAT
/***********************************************************************/