mariadb/storage/connect/connect.cc
2023-01-10 21:04:17 +01:00

973 lines
28 KiB
C++

/* 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. */
/*********************************************************************/
snprintf(g->Message, sizeof(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 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)
snprintf(g->Message, sizeof(g->Message), MSG(COL_ISNOT_TABLE), p, tdbp->GetName());
throw 1;
} // endif colp
n = strlen(p) + 1;
} // endfor p
for (colp = tdbp->GetColumns(); colp; 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))) {
/* If table type is of type virtual retrieve global parameter as it was.*/
if (tdbp->GetAmType() == TYPE_AM_VIR) {
if (tdbp->OpenDB(g)) {
printf("%s\n", g->Message);
throw 7;
}
}
snprintf(g->Message, sizeof(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 (colp = utp->GetColumns(); colp; 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->GetAmType() == TYPE_AM_JSN)
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()) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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)) {
snprintf(g->Message, sizeof(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))
snprintf(g->Message, sizeof(g->Message), "Out of range value for column %s at row %d",
colp->GetName(), tdbp->RowNumber(g));
else
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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()) {
snprintf(g->Message, sizeof(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))
snprintf(g->Message, sizeof(g->Message),
"Out of range value for column %s at row %d",
colp->GetName(), tdbp->RowNumber(g));
else
snprintf(g->Message, sizeof(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