summaryrefslogtreecommitdiffstats
path: root/storage/connect/connect.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/connect.cc')
-rw-r--r--storage/connect/connect.cc965
1 files changed, 965 insertions, 0 deletions
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
new file mode 100644
index 00000000..ee62e0cd
--- /dev/null
+++ b/storage/connect/connect.cc
@@ -0,0 +1,965 @@
+/* Copyright (C) Olivier Bertrand 2004 - 2017
+ Copyright (C) MariaDB Corporation Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/***********************************************************************/
+/* Author Olivier BERTRAND bertrandop@gmail.com 2004-2019 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the CONNECT general purpose semantic routines. */
+/***********************************************************************/
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+/***********************************************************************/
+/* Include application header files */
+/* */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/***********************************************************************/
+#define DONT_DEFINE_VOID
+#include <my_global.h>
+#include "handler.h"
+#undef OFFSET
+
+#include "global.h"
+#include "plgdbsem.h"
+#include "xobject.h"
+#include "connect.h"
+#include "tabcol.h"
+#include "catalog.h"
+#include "ha_connect.h"
+
+#define my_strupr(p) my_caseup_str(default_charset_info, (p));
+#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
+#define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b))
+
+/***********************************************************************/
+/* Routines called internally by semantic routines. */
+/***********************************************************************/
+void CntEndDB(PGLOBAL);
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr= false);
+
+/***********************************************************************/
+/* MySQL routines called externally by semantic routines. */
+/***********************************************************************/
+int rename_file_ext(const char *from, const char *to,const char *ext);
+
+/***********************************************************************/
+/* CntExit: CONNECT termination routine. */
+/***********************************************************************/
+PGLOBAL CntExit(PGLOBAL g)
+ {
+ if (g) {
+ CntEndDB(g);
+
+ if (g->Activityp) {
+ delete g->Activityp;
+ g->Activityp = NULL;
+ } // endif Activityp
+
+ g= PlugExit(g);
+ } // endif g
+
+ return g;
+ } // end of CntExit
+
+/***********************************************************************/
+/* CntEndDB: DB termination semantic routine. */
+/***********************************************************************/
+void CntEndDB(PGLOBAL g)
+{
+ PDBUSER dbuserp= PlgGetUser(g);
+
+ if (dbuserp) {
+ if (dbuserp->Catalog)
+ delete dbuserp->Catalog;
+
+ free(dbuserp);
+
+ if (trace(1))
+ htrc("CntEndDB: Freeing Dup\n");
+
+ g->Activityp->Aptr = NULL;
+ } // endif dbuserp
+
+} // end of CntEndDB
+
+/***********************************************************************/
+/* CntCheckDB: Initialize a DB application session. */
+/* Note: because MySQL does not call a storage handler when a user */
+/* executes a use db command, a check must be done before an SQL */
+/* command is executed to check whether we are still working on the */
+/* current database, and if not to load the newly used database. */
+/***********************************************************************/
+bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
+ {
+ bool rc= false;
+ PDBUSER dbuserp= PlgGetUser(g);
+
+ if (trace(1)) {
+ printf("CntCheckDB: dbuserp=%p\n", dbuserp);
+ } // endif trace
+
+ if (!dbuserp || !handler)
+ return true;
+
+ if (trace(1))
+ printf("cat=%p oldhandler=%p newhandler=%p\n", dbuserp->Catalog,
+ (dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL,
+ handler);
+
+ // Set the database path for this table
+ if (handler->SetDataPath(g, pathname))
+ return true;
+
+ if (dbuserp->Catalog) {
+ return false; // Nothing else to do
+ } // endif Catalog
+
+ // Copy new database name in dbuser block
+ strncpy(dbuserp->Name, "???", sizeof(dbuserp->Name) - 1);
+
+ dbuserp->Vtdbno= 0; // Init of TDB numbers
+
+ /*********************************************************************/
+ /* Now allocate and initialize the Database Catalog. */
+ /*********************************************************************/
+ dbuserp->Step= MSG(READY);
+
+ if (!(dbuserp->Catalog= new MYCAT(handler)))
+ return true;
+
+ /*********************************************************************/
+ /* All is correct. */
+ /*********************************************************************/
+ sprintf(g->Message, MSG(DATABASE_LOADED), "???");
+
+ if (trace(1))
+ printf("msg=%s\n", g->Message);
+
+ return rc;
+ } // end of CntCheckDB
+
+/***********************************************************************/
+/* CntInfo: Get table info. */
+/* Returns valid: true if this is a table info. */
+/***********************************************************************/
+bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
+{
+ if (tp) {
+ bool b = (tp->GetFtype() == RECFM_NAF);
+ PTDBDOS tdbp = b ? NULL : (PTDBDOS)tp;
+
+ info->data_file_length = (b) ? 0 : (ulonglong)tdbp->GetFileLength(g);
+
+ if (b || info->data_file_length)
+ info->records= (unsigned)tp->Cardinality(g);
+// info->records= (unsigned)tp->GetMaxSize(g);
+ else
+ info->records= 0;
+
+// info->mean_rec_length= tdbp->GetLrecl();
+ info->mean_rec_length= 0;
+ info->data_file_name= (b) ? NULL : (char*)tdbp->GetFile(g);
+ return true;
+ } else {
+ info->data_file_length= 0;
+ info->records= 0;
+ info->mean_rec_length= 0;
+ info->data_file_name= NULL;
+ return false;
+ } // endif tdbp
+
+} // end of CntInfo
+
+/***********************************************************************/
+/* GetTDB: Get the table description block of a CONNECT table. */
+/***********************************************************************/
+PTDB CntGetTDB(PGLOBAL g, LPCSTR name, MODE mode, PHC h)
+{
+ PTDB tdbp = NULL;
+ PTABLE tabp;
+ PDBUSER dup = PlgGetUser(g);
+ volatile PCATLG cat = (dup) ? dup->Catalog : NULL; // Safe over throw
+
+ if (trace(1))
+ printf("CntGetTDB: name=%s mode=%d cat=%p\n", name, mode, cat);
+
+ if (!cat)
+ return NULL;
+
+ try {
+ // Get table object from the catalog
+ tabp = new(g) XTAB(name);
+
+ if (trace(1))
+ printf("CntGetTDB: tabp=%p\n", tabp);
+
+ // Perhaps this should be made thread safe
+ ((MYCAT*)cat)->SetHandler(h);
+
+ if (!(tdbp = cat->GetTable(g, tabp, mode)))
+ printf("CntGetTDB: %s\n", g->Message);
+
+ } catch (int n) {
+ if (trace(1))
+ htrc("Exception %d: %s\n", n, g->Message);
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ } // end catch
+
+ if (trace(1))
+ printf("Returning tdbp=%p mode=%d\n", tdbp, mode);
+
+ return tdbp;
+} // end of CntGetTDB
+
+/***********************************************************************/
+/* OPENTAB: Open a Table. */
+/***********************************************************************/
+bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
+ bool del, PHC)
+ {
+ char *p;
+ int i, n;
+ bool rcop= true;
+ PCOL colp;
+//PCOLUMN cp;
+ PDBUSER dup= PlgGetUser(g);
+
+ if (trace(1))
+ printf("CntOpenTable: tdbp=%p mode=%d\n", tdbp, mode);
+
+ if (!tdbp) {
+ strcpy(g->Message, "Null tdbp");
+ printf("CntOpenTable: %s\n", g->Message);
+ return true;
+ } // endif tdbp
+
+ try {
+ if (!c1) {
+// if (mode == MODE_INSERT) or CHECK TABLE
+ // Allocate all column blocks for that table
+ tdbp->ColDB(g, NULL, 0);
+
+ } else for (p = c1; *p; p += n) {
+ // Allocate only used column blocks
+ if (trace(1))
+ printf("Allocating column %s\n", p);
+
+ g->Message[0] = 0; // To check whether ColDB made an error message
+ colp = tdbp->ColDB(g, p, 0);
+
+ if (!colp && !(mode == MODE_INSERT && tdbp->IsSpecial(p))) {
+ if (g->Message[0] == 0)
+ sprintf(g->Message, MSG(COL_ISNOT_TABLE), p, tdbp->GetName());
+
+ throw 1;
+ } // endif colp
+
+ n = strlen(p) + 1;
+ } // endfor p
+
+ for (i = 0, colp = tdbp->GetColumns(); colp; i++, colp = colp->GetNext()) {
+ if (colp->InitValue(g))
+ throw 2;
+
+ if (mode == MODE_INSERT)
+ // Allow type conversion
+ if (colp->SetBuffer(g, colp->GetValue(), true, false))
+ throw 3;
+
+ colp->AddColUse(U_P); // For PLG tables
+ } // endfor colp
+
+ /*******************************************************************/
+ /* In Update mode, the updated column blocks must be distinct from */
+ /* the read column blocks. So make a copy of the TDB and allocate */
+ /* its column blocks in mode write (required by XML tables). */
+ /*******************************************************************/
+ if (mode == MODE_UPDATE) {
+ PTDB utp;
+
+ if (!(utp = tdbp->Duplicate(g))) {
+ sprintf(g->Message, MSG(INV_UPDT_TABLE), tdbp->GetName());
+ throw 4;
+ } // endif tp
+
+ if (!c2)
+ // Allocate all column blocks for that table
+ utp->ColDB(g, NULL, 0);
+ else for (p = c2; *p; p += n) {
+ // Allocate only used column blocks
+ colp = utp->ColDB(g, p, 0);
+ n = strlen(p) + 1;
+ } // endfor p
+
+ for (i = 0, colp = utp->GetColumns(); colp; i++, colp = colp->GetNext()) {
+ if (colp->InitValue(g))
+ throw 5;
+
+ if (colp->SetBuffer(g, colp->GetValue(), true, false))
+ throw 6;
+
+ } // endfor colp
+
+ // Attach the updated columns list to the main table
+ tdbp->SetSetCols(utp->GetColumns());
+ } else if (tdbp && mode == MODE_INSERT)
+ tdbp->SetSetCols(tdbp->GetColumns());
+
+ // Now do open the physical table
+ if (trace(1))
+ printf("Opening table %s in mode %d tdbp=%p\n",
+ tdbp->GetName(), mode, tdbp);
+
+ //tdbp->SetMode(mode);
+
+ if (del/* && (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((PTDB)1);
+ dup->Check &= ~CHK_DELETE;
+ } // endif del
+
+
+ if (trace(1))
+ printf("About to open the table: tdbp=%p\n", tdbp);
+
+ if (mode != MODE_ANY && mode != MODE_ALTER) {
+ if (tdbp->OpenDB(g)) {
+ printf("%s\n", g->Message);
+ throw 7;
+ } else
+ tdbp->SetNext(NULL);
+
+ } // endif mode
+
+ rcop = false;
+ } catch (int n) {
+ if (trace(1))
+ htrc("Exception %d: %s\n", n, g->Message);
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ } // end catch
+
+ return rcop;
+ } // end of CntOpenTable
+
+/***********************************************************************/
+/* Rewind a table by reopening it. */
+/***********************************************************************/
+bool CntRewindTable(PGLOBAL g, PTDB tdbp)
+{
+ if (!tdbp)
+ return true;
+
+ tdbp->OpenDB(g);
+ return false;
+} // end of CntRewindTable
+
+/***********************************************************************/
+/* Evaluate all columns after a record is read. */
+/***********************************************************************/
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr)
+{
+ RCODE rc= RC_OK;
+ PCOL colp;
+
+ try {
+ for (colp = tdbp->GetColumns(); rc == RC_OK && colp;
+ colp = colp->GetNext()) {
+ xtrc(2, "Going to read column %s of table %s\n",
+ colp->GetName(), tdbp->GetName());
+
+ if (reset)
+ colp->Reset();
+
+ // Virtual columns are computed by MariaDB
+ if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
+ if (colp->Eval(g))
+ rc = RC_FX;
+
+ } // endfor colp
+
+ } catch (int n) {
+ if (trace(1))
+ printf("Error %d reading columns: %s\n", n, g->Message);
+
+ rc = RC_FX;
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ rc = RC_NF;
+ } // end catch
+
+ return rc;
+} // end of EvalColumns
+
+/***********************************************************************/
+/* ReadNext: Read next record sequentially. */
+/***********************************************************************/
+RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
+{
+ RCODE rc;
+
+ if (!tdbp)
+ return RC_FX;
+ else if (tdbp->GetKindex()) {
+ // Reading sequencially an indexed table. This happens after the
+ // handler function records_in_range was called and MySQL decides
+ // to quit using the index (!!!) Drop the index.
+// for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
+// colp->SetKcol(NULL);
+
+ ((PTDBASE)tdbp)->ResetKindex(g, NULL);
+ } // endif index
+
+ try {
+ // Do it now to avoid double eval when filtering
+ for (PCOL colp = tdbp->GetColumns(); colp; colp = colp->GetNext())
+ colp->Reset();
+
+ do {
+ if ((rc = (RCODE)tdbp->ReadDB(g)) == RC_OK)
+ if (!ApplyFilter(g, tdbp->GetFilter()))
+ rc = RC_NF;
+
+ } while (rc == RC_NF);
+
+ if (rc == RC_OK)
+ rc = EvalColumns(g, tdbp, false);
+
+ } catch (int) {
+ rc = RC_FX;
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ rc = RC_FX;
+ } // end catch
+
+ return rc;
+} // end of CntReadNext
+
+/***********************************************************************/
+/* WriteRow: Insert a new row into a table. */
+/***********************************************************************/
+RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
+{
+ RCODE rc;
+ PCOL colp;
+ //PTDBASE tp= (PTDBASE)tdbp;
+
+ if (!tdbp)
+ return RC_FX;
+
+ try {
+ // Store column values in table write buffer(s)
+ for (colp = tdbp->GetSetCols(); colp; colp = colp->GetNext())
+ if (!colp->GetColUse(U_VIRTUAL))
+ colp->WriteColumn(g);
+
+ if (tdbp->IsIndexed())
+ // Index values must be sorted before updating
+ rc = (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, true);
+ else
+ // Return result code from write operation
+ rc = (RCODE)tdbp->WriteDB(g);
+
+ } catch (int n) {
+ printf("Exception %d: %s\n", n, g->Message);
+ rc = RC_FX;
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ rc = RC_FX;
+ } // end catch
+
+ return rc;
+} // end of CntWriteRow
+
+/***********************************************************************/
+/* UpdateRow: Update a row into a table. */
+/***********************************************************************/
+RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
+ {
+ if (!tdbp || tdbp->GetMode() != MODE_UPDATE)
+ return RC_FX;
+
+ // Return result code from write operation
+ return CntWriteRow(g, tdbp);
+ } // end of CntUpdateRow
+
+/***********************************************************************/
+/* DeleteRow: Delete a row from a table. */
+/***********************************************************************/
+RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
+ {
+ RCODE rc;
+//PTDBASE tp= (PTDBASE)tdbp;
+
+ if (!tdbp || tdbp->GetMode() != MODE_DELETE)
+ return RC_FX;
+ else if (tdbp->IsReadOnly())
+ return RC_NF;
+
+ if (all) {
+ if (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 (tdbp->GetKindex() && !((PTDBASE)tdbp)->GetKindex()->IsSorted() &&
+// ((PTDBASE)tdbp)->Txfp->GetAmType() != TYPE_AM_DBF) {
+ } else if(tdbp->IsIndexed()) {
+ // Index values must be sorted before updating
+ rc= (RCODE)((PTDBDOS)tdbp)->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, bool nox, bool abort)
+{
+ int rc = RC_OK;
+ //TDBASE *tbxp= (PTDBASE)tdbp;
+
+ 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(1))
+ 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 (tdbp->IsIndexed())
+ rc = ((PTDBDOS)tdbp)->GetTxfp()->DeleteSortedRows(g);
+
+ if (!rc)
+ rc = tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
+
+ } else if (tdbp->GetMode() == MODE_UPDATE && tdbp->IsIndexed())
+ rc = ((PTDBDOS)tdbp)->GetTxfp()->UpdateSortedRows(g);
+
+ switch (rc) {
+ case RC_FX:
+ abort = true;
+ break;
+ case RC_INFO:
+ PushWarning(g, tdbp);
+ break;
+ } // endswitch rc
+
+ try {
+ // 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);
+
+ if (trace(2))
+ printf("Table %s closed\n", tdbp->GetName());
+
+ if (!nox && tdbp->GetMode() != MODE_READ && tdbp->GetMode() != MODE_ANY) {
+ if (trace(2))
+ printf("About to reset opt\n");
+
+ if (!tdbp->IsRemote()) {
+ // Make all the eventual indexes
+ PTDBASE tbxp = (PTDBASE)tdbp;
+ tbxp->ResetKindex(g, NULL);
+ tbxp->SetKey_Col(NULL);
+ rc = tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1);
+ } // endif remote
+
+ } // endif nox
+
+ } catch (int) {
+ rc = RC_FX;
+ } catch (const char *msg) {
+ strcpy(g->Message, msg);
+ rc = RC_FX;
+ } // end catch
+
+ if (trace(2))
+ htrc("Done rc=%d\n", rc);
+
+ return (rc == RC_OK || rc == RC_INFO) ? 0 : rc;
+} // end of CntCloseTable
+
+/***********************************************************************/
+/* Load and initialize the use of an index. */
+/* This is the condition(s) for doing indexing. */
+/* Note: FIX table are not reset here to Nrec= 1. */
+/***********************************************************************/
+int CntIndexInit(PGLOBAL g, PTDB ptdb, int id, bool sorted)
+ {
+ PIXDEF xdp;
+ PTDBDOS tdbp;
+ DOSDEF *dfp;
+
+ if (!ptdb)
+ return -1;
+ else if (!ptdb->GetDef()->Indexable()) {
+ sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
+ return 0;
+ } else if (ptdb->GetDef()->Indexable() == 3) {
+ return 1;
+ } else
+ tdbp= (PTDBDOS)ptdb;
+
+ dfp= (DOSDEF*)tdbp->GetDef();
+
+//if (!(k= colp->GetKey()))
+// if (colp->GetOpt() >= 2) {
+// strcpy(g->Message, "Not a valid indexed column");
+// return -1;
+// } else
+ // This is a pseudo indexed sorted block optimized column
+// return 0;
+
+ if (tdbp->GetKindex())
+ {
+ if (((XXBASE*)tdbp->GetKindex())->GetID() == id) {
+ tdbp->GetKindex()->Reset(); // Same index
+ return (tdbp->GetKindex()->IsMul()) ? 2 : 1;
+ } else {
+ tdbp->GetKindex()->Close();
+ tdbp->SetKindex(NULL);
+ } // endif colp
+ }
+
+ for (xdp= dfp->GetIndx(); xdp; xdp= xdp->GetNext())
+ if (xdp->GetID() == id)
+ break;
+
+ if (!xdp) {
+ sprintf(g->Message, "Wrong index ID %d", id);
+ return 0;
+ } // endif xdp
+
+#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;
+
+ return (tdbp->GetKindex()->IsMul()) ? 2 : 1;
+ } // end of CntIndexInit
+
+#if defined(WORDS_BIGENDIAN)
+/***********************************************************************/
+/* Swap bytes of the key that are written in little endian order. */
+/***********************************************************************/
+static void SetSwapValue(PVAL valp, char *kp)
+{
+ if (valp->IsTypeNum() && valp->GetType() != TYPE_DECIM) {
+ uchar buf[8];
+ int i, k= valp->GetClen();
+
+ for (i = 0; k > 0;)
+ buf[i++]= kp[--k];
+
+
+
+ valp->SetBinValue((void*)buf);
+ } else
+ valp->SetBinValue((void*)kp);
+
+} // end of SetSwapValue
+#endif // WORDS_BIGENDIAN
+
+/***********************************************************************/
+/* IndexRead: fetch a record having the index value. */
+/***********************************************************************/
+RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
+ const key_range *kr, bool mrr)
+ {
+ int n, x;
+ RCODE rc;
+ XXBASE *xbp;
+ PTDBDOS tdbp;
+
+ if (!ptdb)
+ return RC_FX;
+ else
+ x= ptdb->GetDef()->Indexable();
+
+ if (!x) {
+ sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
+ return RC_FX;
+ } else if (x == 2) {
+ // Remote index. Only used in read mode
+ if ((ptdb->GetMode() == MODE_READ || ptdb->GetMode() == MODE_READX)
+ && op != OP_SAME && ptdb->ReadKey(g, op, kr))
+ return RC_FX;
+
+ goto rnd;
+ } else if (x == 3) {
+ if (kr)
+ ((PTDBASE)ptdb)->SetRecpos(g, *(int*)kr->key);
+
+ if (op == OP_SAME)
+ return RC_NF;
+
+ goto rnd;
+ } else
+ tdbp= (PTDBDOS)ptdb;
+
+ // Set reference values and index operator
+ if (!tdbp->GetLink() || !tdbp->GetKindex()) {
+// if (!tdbp->To_Xdp) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->GetName());
+ 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->GetKindex();
+
+ if (kr) {
+ char *kp= (char*)kr->key;
+ int len= kr->length;
+ short lg;
+ bool rcb;
+ PVAL valp;
+ PCOL colp;
+
+ for (n= 0; n < tdbp->GetKnum(); n++) {
+ colp= (PCOL)tdbp->Key(n);
+
+ if (colp->GetColUse(U_NULLS))
+ kp++; // Skip null byte
+
+ valp= tdbp->Link(n)->GetValue();
+
+ if (!valp->IsTypeNum()) {
+ if (colp->GetColUse(U_VAR)) {
+#if defined(WORDS_BIGENDIAN)
+ ((char*)&lg)[0]= ((char*)kp)[1];
+ ((char*)&lg)[1]= ((char*)kp)[0];
+#else // !WORDS_BIGENDIAN
+ lg= *(short*)kp;
+#endif //!WORDS_BIGENDIAN
+ kp+= sizeof(short);
+ rcb= valp->SetValue_char(kp, (int)lg);
+ } else
+ rcb= valp->SetValue_char(kp, valp->GetClen());
+
+ if (rcb) {
+ if (tdbp->RowNumber(g))
+ sprintf(g->Message, "Out of range value for column %s at row %d",
+ colp->GetName(), tdbp->RowNumber(g));
+ else
+ sprintf(g->Message, "Out of range value for column %s",
+ colp->GetName());
+
+ PushWarning(g, tdbp);
+ } // endif b
+
+ } else
+#if defined(WORDS_BIGENDIAN)
+ SetSwapValue(valp, kp);
+#else // !WORDS_BIGENDIAN
+ valp->SetBinValue((void*)kp);
+#endif //!WORDS_BIGENDIAN
+
+ kp+= valp->GetClen();
+
+ if (len == kp - (char*)kr->key) {
+ n++;
+ break;
+ } else if (len < kp - (char*)kr->key) {
+ strcpy(g->Message, "Key buffer is too small");
+ return RC_FX;
+ } // endif len
+
+ } // endfor n
+
+ xbp->SetNval(n);
+ } // endif key
+
+ xbp->SetOp(op);
+ xbp->SetNth(0);
+
+ rnd:
+ if ((rc= (RCODE)ptdb->ReadDB(g)) == RC_OK)
+ rc= EvalColumns(g, ptdb, true, mrr);
+
+ return rc;
+ } // end of CntIndexRead
+
+/***********************************************************************/
+/* Return the number of rows matching given values. */
+/***********************************************************************/
+int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
+ bool *incl, key_part_map *kmap)
+ {
+ const uchar *p, *kp;
+ int i, n, x, k[2];
+ short lg;
+ bool b, rcb;
+ PVAL valp;
+ PCOL colp;
+ PTDBDOS tdbp;
+ XXBASE *xbp;
+
+ if (!ptdb)
+ return -1;
+
+ x= ptdb->GetDef()->Indexable();
+
+ if (!x) {
+ sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
+ DBUG_PRINT("Range", ("%s", g->Message));
+ return -1;
+ } else if (x == 2) {
+ // Remote index
+ return 2;
+ } else if (x == 3) {
+ // Virtual index
+ for (i= 0; i < 2; i++)
+ if (key[i])
+ k[i] = *(int*)key[i] + (incl[i] ? 0 : 1 - 2 * i);
+ else
+ k[i] = (i) ? ptdb->Cardinality(g) : 1;
+
+ return k[1] - k[0] + 1;
+ } else
+ tdbp= (PTDBDOS)ptdb;
+
+ if (!tdbp->GetKindex() || !tdbp->GetLink()) {
+ if (!tdbp->GetXdp()) {
+ sprintf(g->Message, "Index not initialized for table %s", tdbp->GetName());
+ DBUG_PRINT("Range", ("%s", g->Message));
+ return -1;
+ } else // Dynamic index
+ return tdbp->GetXdp()->GetMaxSame(); // TODO a better estimate
+
+ } else
+ xbp= (XXBASE*)tdbp->GetKindex();
+
+ for (b= false, i= 0; i < 2; i++) {
+ p= kp= key[i];
+
+ if (kp) {
+ for (n= 0; n < tdbp->GetKnum(); n++) {
+ if (kmap[i] & (key_part_map)(1 << n)) {
+ if (b == true)
+ // Cannot do indexing with missing intermediate key
+ return -1;
+
+ colp= (PCOL)tdbp->Key(n);
+
+ if (colp->GetColUse(U_NULLS))
+ p++; // Skip null byte ???
+
+ valp= tdbp->Link(n)->GetValue();
+
+ if (!valp->IsTypeNum()) {
+ if (colp->GetColUse(U_VAR)) {
+#if defined(WORDS_BIGENDIAN)
+ ((char*)&lg)[0]= ((char*)p)[1];
+ ((char*)&lg)[1]= ((char*)p)[0];
+#else // !WORDS_BIGENDIAN
+ lg= *(short*)p;
+#endif //!WORDS_BIGENDIAN
+ p+= sizeof(short);
+ rcb= valp->SetValue_char((char*)p, (int)lg);
+ } else
+ rcb= valp->SetValue_char((char*)p, valp->GetClen());
+
+ if (rcb) {
+ if (tdbp->RowNumber(g))
+ sprintf(g->Message,
+ "Out of range value for column %s at row %d",
+ colp->GetName(), tdbp->RowNumber(g));
+ else
+ sprintf(g->Message, "Out of range value for column %s",
+ colp->GetName());
+
+ PushWarning(g, tdbp);
+ } // endif b
+
+ } else
+#if defined(WORDS_BIGENDIAN)
+ SetSwapValue(valp, (char*)p);
+#else // !WORDS_BIGENDIAN
+ valp->SetBinValue((void*)p);
+#endif // !WORDS_BIGENDIAN
+
+ if (trace(1)) {
+ char bf[32];
+ printf("i=%d n=%d key=%s\n", i, n, valp->GetCharString(bf));
+ } // endif trace
+
+ p+= valp->GetClen();
+
+ if (len[i] == (unsigned)(p - kp)) {
+ n++;
+ break;
+ } else if (len[i] < (unsigned)(p - kp)) {
+ strcpy(g->Message, "Key buffer is too small");
+ return -1;
+ } // endif len
+
+ } else
+ b= true;
+
+ } // endfor n
+
+ xbp->SetNval(n);
+
+ if (trace(1))
+ printf("xbp=%p Nval=%d i=%d incl=%d\n", xbp, n, i, incl[i]);
+
+ k[i]= xbp->Range(g, i + 1, incl[i]);
+ } else
+ k[i]= (i) ? xbp->GetNum_K() : 0;
+
+ } // endfor i
+
+ if (trace(1))
+ printf("k1=%d k0=%d\n", k[1], k[0]);
+
+ return k[1] - k[0];
+ } // end of CntIndexRange