Commit 187e4169 authored by Olivier Bertrand's avatar Olivier Bertrand

- Implement "remote" index (similar to FEDERATED ones) for MYSQL tables.

  Not yet done for ODBC tables.
modified:
  storage/connect/connect.cc
  storage/connect/ha_connect.cc
  storage/connect/ha_connect.h
  storage/connect/mycat.cc
  storage/connect/plgdbsem.h
  storage/connect/reldef.h
  storage/connect/tabdos.h
  storage/connect/tabmysql.cpp
  storage/connect/tabmysql.h
  storage/connect/tabodbc.cpp
  storage/connect/tabodbc.h
  storage/connect/xindex.cpp
  storage/connect/xtable.h

- Return error in "info" on Cardinality error.
modified:
  storage/connect/ha_connect.cc
parent 213ecbbb
......@@ -587,7 +587,7 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp)
tbxp= (TDBDOX*)tdbp;
tbxp->SetKindex(NULL);
tbxp->To_Key_Col= NULL;
rc= tbxp->ResetTableOpt(g, ((PTDBASE)tdbp)->GetDef()->Indexable());
rc= tbxp->ResetTableOpt(g, ((PTDBASE)tdbp)->GetDef()->Indexable() == 1);
err:
if (trace > 1)
......@@ -709,7 +709,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
const void *key, int len)
{
char *kp= (char*)key;
int n;
int n, x;
short lg;
bool rcb;
RCODE rc;
......@@ -720,9 +720,18 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
if (!ptdb)
return RC_FX;
if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
else
x= ((PTDBASE)ptdb)->GetDef()->Indexable();
if (!x) {
sprintf(g->Message, "CntIndexRead: Table %s is not indexable", ptdb->GetName());
return RC_FX;
} else if (x == 2) {
// Remote index
if (ptdb->ReadKey(g, op, key, len))
return RC_FX;
goto rnd;
} else
tdbp= (PTDBDOX)ptdb;
......@@ -782,8 +791,9 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
xbp->SetOp(op);
xbp->SetNth(0);
if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK)
rc= EvalColumns(g, tdbp);
rnd:
if ((rc= (RCODE)ptdb->ReadDB(g)) == RC_OK)
rc= EvalColumns(g, ptdb);
return rc;
} // end of CntIndexRead
......@@ -795,7 +805,7 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
bool *incl, key_part_map *kmap)
{
const uchar *p, *kp;
int i, n, k[2];
int i, n, x, k[2];
short lg;
bool b, rcb;
PVAL valp;
......@@ -805,10 +815,16 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
if (!ptdb)
return -1;
else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
x= ((PTDBASE)ptdb)->GetDef()->Indexable();
if (!x) {
sprintf(g->Message, "CntIndexRange: Table %s is not indexable", ptdb->GetName());
DBUG_PRINT("Range", ("%s", g->Message));
return -1;
} else if (x == 2) {
// Remote index
return 2;
} else
tdbp= (PTDBDOX)ptdb;
......
......@@ -1218,9 +1218,10 @@ PTDB ha_connect::GetTDB(PGLOBAL g)
if (!xp->CheckQuery(valid_query_id) && tdbp
&& !stricmp(tdbp->GetName(), table_name)
&& (tdbp->GetMode() == xmod
|| (tdbp->GetMode() == MODE_READ && xmod == MODE_READX)
|| tdbp->GetAmType() == TYPE_AM_XML)) {
tp= tdbp;
// tp->SetMode(xmod);
tp->SetMode(xmod);
} else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
valid_query_id= xp->last_query_id;
tp->SetMode(xmod);
......@@ -1613,6 +1614,88 @@ int ha_connect::CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf)
} // end of dummy CheckRecord
/***********************************************************************/
/* Return the where clause for remote indexed read. */
/***********************************************************************/
bool ha_connect::MakeKeyWhere(PGLOBAL g, char *qry, OPVAL op, char *q,
const void *key, int klen)
{
const uchar *ptr;
uint rem, len, stlen; //, prtlen;
bool nq, b= false;
Field *fp;
KEY *kfp;
KEY_PART_INFO *kpart;
if (active_index == MAX_KEY)
return 0;
strcat(qry, " WHERE (");
kfp= &table->key_info[active_index];
rem= kfp->user_defined_key_parts,
len= klen,
ptr= (const uchar *)key;
for (kpart= kfp->key_part; rem; rem--, kpart++) {
fp= kpart->field;
stlen= kpart->store_length;
// prtlen= min(stlen, len);
nq= fp->str_needs_quotes();
if (b)
strcat(qry, " AND ");
else
b= true;
strcat(strncat(strcat(qry, q), fp->field_name, strlen(fp->field_name)), q);
switch (op) {
case OP_EQ:
case OP_GT:
case OP_GE:
strcat(qry, GetValStr(op, false));
break;
default:
strcat(qry, " ??? ");
} // endwitch op
if (nq)
strcat(qry, "'");
if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
String varchar;
uint var_length= uint2korr(ptr);
varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH,
var_length, &my_charset_bin);
strncat(qry, varchar.ptr(), varchar.length());
} else {
char strbuff[MAX_FIELD_WIDTH];
String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
res= fp->val_str(&str, ptr);
strncat(qry, res->ptr(), res->length());
} // endif flag
if (nq)
strcat(qry, "'");
if (stlen >= len)
break;
len-= stlen;
/* For nullable columns, null-byte is already skipped before, that is
ptr was incremented by 1. Since store_length still counts null-byte,
we need to subtract 1 from store_length. */
ptr+= stlen - test(kpart->null_bit);
} // endfor kpart
strcat(qry, ")");
return false;
} // end of MakeKeyWhere
/***********************************************************************/
/* Return the string representing an operator. */
/***********************************************************************/
......@@ -1738,7 +1821,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
} else {
p1= p2 + strlen(p2);
strcpy(p1, GetValStr(vop, FALSE));
strcpy(p1, GetValStr(vop, false));
p2= p1 + strlen(p1);
} // endif CheckCond
......@@ -2092,7 +2175,9 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT* check_opt)
dup->Check |= CHK_OPT;
if (tdbp) {
if (!((PTDBASE)tdbp)->GetDef()->Indexable()) {
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;
......@@ -2306,6 +2391,18 @@ int ha_connect::index_init(uint idx, bool sorted)
if (xtrace)
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 (!(rc= rnd_init(0))) {
active_index= idx;
indexing= 2; // TO DO: mul?
} //endif rc
DBUG_RETURN(rc);
} // endif index type
if ((rc= rnd_init(0)))
return rc;
......@@ -2821,12 +2918,16 @@ int ha_connect::info(uint flag)
if (cat && table)
cat->SetDataPath(g, table->s->db.str);
else
return HA_ERR_INTERNAL_ERROR; // Should never happen
DBUG_RETURN(HA_ERR_INTERNAL_ERROR); // Should never happen
tdbp= GetTDB(g);
} // endif tdbp
valid_info= CntInfo(g, tdbp, &xinfo);
if (((signed)xinfo.records) < 0)
DBUG_RETURN(HA_ERR_INITIALIZATION); // Error in Cardinality
} // endif valid_info
if (flag & HA_STATUS_VARIABLE) {
......@@ -3086,7 +3187,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
newmode= MODE_ALTER;
break;
default:
htrc("Unsupported sql_command=%d", thd_sql_command(thd));
htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
strcpy(g->Message, "CONNECT Unsupported command");
my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
newmode= MODE_ERROR;
......@@ -3131,7 +3232,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
newmode= MODE_ALTER;
break;
default:
htrc("Unsupported sql_command=%d", thd_sql_command(thd));
htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
strcpy(g->Message, "CONNECT Unsupported command");
my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
newmode= MODE_ERROR;
......@@ -3255,8 +3356,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
// DBUG_RETURN(HA_ERR_INTERNAL_ERROR); causes assert error
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
DBUG_RETURN(0);
} // endif Indexable
} else if (((PTDBASE)tdbp)->GetDef()->Indexable() == 1) {
bool oldsep= ((PCHK)g->Xchk)->oldsep;
bool newsep= ((PCHK)g->Xchk)->newsep;
PTDBDOS tdp= (PTDBDOS)tdbp;
......@@ -3337,6 +3437,8 @@ int ha_connect::external_lock(THD *thd, int lock_type)
rc= 0;
} // endif MakeIndex
} // endif indexable
} // endif Tdbp
} // endelse Xchk
......@@ -4831,7 +4933,7 @@ int ha_connect::create(const char *name, TABLE *table_arg,
sprintf(g->Message, "Table type %s is not indexable", options->type);
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
rc= HA_ERR_UNSUPPORTED;
} // endif Indexable
} // endif index type
} // endif xdp
......@@ -4840,62 +4942,6 @@ int ha_connect::create(const char *name, TABLE *table_arg,
my_message(ER_UNKNOWN_ERROR,
"CONNECT index modification should be in-place", MYF(0));
DBUG_RETURN(HA_ERR_UNSUPPORTED);
#if 0
PIXDEF xdp= GetIndexInfo();
PCHK xcp= (PCHK)g->Xchk;
if (xdp) {
if (!IsTypeIndexable(type)) {
g->Xchk= NULL;
sprintf(g->Message, "Table type %s is not indexable", options->type);
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
rc= HA_ERR_INTERNAL_ERROR;
} else {
xcp->newpix= xdp;
xcp->newsep= GetBooleanOption("Sepindex", false);
} // endif Indexable
} else if (!xcp->oldpix)
g->Xchk= NULL;
if (xtrace && g->Xchk)
htrc("oldsep=%d newsep=%d oldpix=%p newpix=%p\n",
xcp->oldsep, xcp->newsep, xcp->oldpix, xcp->newpix);
// if (g->Xchk &&
// (sqlcom != SQLCOM_CREATE_INDEX && sqlcom != SQLCOM_DROP_INDEX)) {
if (g->Xchk) {
PIXDEF xp1, xp2;
bool b= false; // true if index changes
if (xcp->oldsep == xcp->newsep) {
for (xp1= xcp->newpix, xp2= xcp->oldpix;
xp1 || xp2;
xp1= xp1->Next, xp2= xp2->Next)
if (!xp1 || !xp2 || !IsSameIndex(xp1, xp2)) {
b= true;
break;
} // endif xp1
} else
b= true;
if (!b)
g->Xchk= NULL;
#if 0
if (b) {
// CONNECT does not support indexing via ALTER TABLE
my_message(ER_UNKNOWN_ERROR,
"CONNECT does not support index modification via ALTER TABLE",
MYF(0));
DBUG_RETURN(HA_ERR_UNSUPPORTED);
} // endif b
#endif // 0
} // endif Xchk
#endif // 0
} // endif Xchk
table= st;
......
......@@ -198,6 +198,8 @@ public:
int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
uint key_len= 0);
bool MakeKeyWhere(PGLOBAL g, char *qry, OPVAL op, char *q,
const void *key, int klen);
/** @brief
The name that will be used for display purposes.
......
......@@ -275,9 +275,10 @@ int GetIndexType(TABTYPE type)
xtyp= 1;
break;
case TAB_MYSQL:
// case TAB_ODBC:
xtyp= 2;
break;
case TAB_ODBC:
// xtyp= 2; Remote indexes not implemented yet
// break;
default:
xtyp= 0;
break;
......
......@@ -158,6 +158,7 @@ enum ALGMOD {AMOD_AUTO = 0, /* PLG chooses best algorithm */
enum MODE {MODE_ERROR = -1, /* Invalid mode */
MODE_ANY = 0, /* Unspecified mode */
MODE_READ = 10, /* Input/Output mode */
MODE_READX = 11, /* Read indexed mode */
MODE_WRITE = 20, /* Input/Output mode */
MODE_UPDATE = 30, /* Input/Output mode */
MODE_INSERT = 40, /* Input/Output mode */
......
......@@ -31,6 +31,7 @@ class DllExport RELDEF : public BLOCK { // Relation definition block
PSZ GetName(void) {return Name;}
PSZ GetDB(void) {return (PSZ)Database;}
PCOLDEF GetCols(void) {return To_Cols;}
PHC GetHandler(void) {return Hc;}
void SetCols(PCOLDEF pcd) {To_Cols = pcd;}
PCATLG GetCat(void) {return Cat;}
virtual const char *GetType(void) = 0;
......@@ -45,7 +46,7 @@ class DllExport RELDEF : public BLOCK { // Relation definition block
int GetSizeCatInfo(PSZ what, PSZ sdef);
int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size);
char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef);
virtual bool Indexable(void) {return false;}
virtual int Indexable(void) {return 0;}
virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) = 0;
virtual PTDB GetTable(PGLOBAL g, MODE mode) = 0;
......
......@@ -49,7 +49,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
int GetEnding(void) {return Ending;}
// Methods
virtual bool Indexable(void) {return Compressed != 1;}
virtual int Indexable(void) {return (Compressed != 1) ? 1 : 0;}
virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf);
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE mode);
......@@ -149,7 +149,7 @@ class DllExport TDBDOS : public TDBASE {
virtual int EstimatedLength(PGLOBAL g);
// Optimization routines
int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
virtual int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
protected:
// Members
......
......@@ -61,6 +61,7 @@
#include "tabmysql.h"
#include "valblk.h"
#include "tabutil.h"
#include "ha_connect.h"
#if defined(_CONSOLE)
void PrintResult(PGLOBAL, PSEM, PQRYRES);
......@@ -481,16 +482,16 @@ PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
/* Note: when implementing EOM filtering, column only used in local */
/* filter should be removed from column list. */
/***********************************************************************/
bool TDBMYSQL::MakeSelect(PGLOBAL g)
bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
{
char *tk = "`";
int rank = 0;
bool b = FALSE;
int len = 0, rank = 0;
bool b = false;
PCOL colp;
//PDBUSER dup = PlgGetUser(g);
if (Query)
return FALSE; // already done
return false; // already done
if (Srcdef) {
Query = Srcdef;
......@@ -506,12 +507,12 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g)
if (!colp->IsSpecial()) {
// if (colp->IsSpecial()) {
// strcpy(g->Message, MSG(NO_SPEC_COL));
// return TRUE;
// return true;
// } else {
if (b)
strcat(Query, ", ");
else
b = TRUE;
b = true;
strcat(strcat(strcat(Query, tk), colp->GetName()), tk);
((PMYCOL)colp)->Rank = rank++;
......@@ -526,16 +527,24 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g)
} // endif ncol
strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);
len = strlen(Query);
if (To_CondFil)
if (To_CondFil) {
if (!mx) {
strcat(strcat(Query, " WHERE "), To_CondFil->Body);
len = strlen(Query) + 1;
} else
len += (strlen(To_CondFil->Body) + 256);
} else
len += (mx ? 256 : 1);
if (trace)
htrc("Query=%s\n", Query);
// Now we know how much to suballocate
PlugSubAlloc(g, NULL, strlen(Query) + 1);
return FALSE;
PlugSubAlloc(g, NULL, len);
return false;
} // end of MakeSelect
/***********************************************************************/
......@@ -833,9 +842,9 @@ bool TDBMYSQL::OpenDB(PGLOBAL g)
/*********************************************************************/
/* Allocate whatever is used for getting results. */
/*********************************************************************/
if (Mode == MODE_READ) {
if (!MakeSelect(g))
m_Rc = Myc.ExecSQL(g, Query);
if (Mode == MODE_READ || Mode == MODE_READX) {
MakeSelect(g, Mode == MODE_READX);
m_Rc = (Mode == MODE_READ) ? Myc.ExecSQL(g, Query) : RC_OK;
#if 0
if (!Myc.m_Res || !Myc.m_Fields) {
......@@ -994,6 +1003,36 @@ int TDBMYSQL::SendCommand(PGLOBAL g)
} // end of SendCommand
/***********************************************************************/
/* Data Base indexed read routine for MYSQL access method. */
/***********************************************************************/
bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
{
int oldlen = strlen(Query);
bool rc;
if (op == OP_NEXT)
return false;
else if (op == OP_FIRST) {
if (To_CondFil)
strcat(strcat(Query, " WHERE "), To_CondFil->Body);
} else {
if (Myc.m_Res)
Myc.FreeResult();
rc = To_Def->GetHandler()->MakeKeyWhere(g, Query, op, "`", key, len);
if (To_CondFil)
strcat(strcat(strcat(Query, " AND ("), To_CondFil->Body), ")");
} // endif's op
m_Rc = Myc.ExecSQL(g, Query);
Query[oldlen] = 0;
return false;
} // end of ReadKey
/***********************************************************************/
/* Data Base read routine for MYSQL access method. */
/***********************************************************************/
......@@ -1451,7 +1490,7 @@ bool TDBMYEXC::OpenDB(PGLOBAL g)
Use = USE_OPEN; // Do it now in case we are recursively called
if (Mode != MODE_READ) {
if (Mode != MODE_READ && Mode != MODE_READX) {
strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
return true;
} // endif Mode
......
......@@ -39,6 +39,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
inline int GetPortnumber(void) {return Portnumber;}
// Methods
virtual int Indexable(void) {return 2;}
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
bool ParseURL(PGLOBAL g, char *url, bool b = true);
......@@ -96,6 +97,7 @@ class TDBMYSQL : public TDBASE {
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g);
virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len);
// Specific routines
bool SetColumnRanks(PGLOBAL g);
......@@ -104,7 +106,7 @@ class TDBMYSQL : public TDBASE {
protected:
// Internal functions
bool MakeSelect(PGLOBAL g);
bool MakeSelect(PGLOBAL g, bool mx);
bool MakeInsert(PGLOBAL g);
int BindColumns(PGLOBAL g);
int MakeCommand(PGLOBAL g);
......
......@@ -752,7 +752,7 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/*********************************************************************/
/* Make the command and allocate whatever is used for getting results. */
/*********************************************************************/
if (Mode == MODE_READ) {
if (Mode == MODE_READ || Mode == MODE_READX) {
if ((Query = MakeSQL(g, false))) {
for (PODBCCOL colp = (PODBCCOL)Columns; colp;
colp = (PODBCCOL)colp->GetNext())
......@@ -1315,7 +1315,7 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
Use = USE_OPEN; // Do it now in case we are recursively called
if (Mode != MODE_READ) {
if (Mode != MODE_READ && Mode != MODE_READX) {
strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables");
return true;
} // endif Mode
......
......@@ -90,6 +90,7 @@ class TDBODBC : public TDBASE {
virtual void ResetSize(void);
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);
......@@ -100,6 +101,8 @@ class TDBODBC : public TDBASE {
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g);
virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
{return true;}
protected:
// Internal functions
......
......@@ -79,7 +79,7 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
{
int rc;
PTABLE tablep;
PTDBDOS tdbp;
PTDBASE tdbp;
PCATLG cat = PlgGetCatalog(g, true);
/*********************************************************************/
......@@ -87,13 +87,13 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
/*********************************************************************/
tablep = new(g) XTAB(name);
if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep)))
if (!(tdbp = (PTDBASE)cat->GetTable(g, tablep)))
rc = RC_NF;
else if (!tdbp->GetDef()->Indexable()) {
sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
rc = RC_NF;
} else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
rc = RC_OK; // No index
rc = RC_OK; // No or remote index
return rc;
} // end of PlgMakeIndex
......
......@@ -107,6 +107,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
virtual int DeleteDB(PGLOBAL, int) = 0;
virtual void CloseDB(PGLOBAL) = 0;
virtual int CheckWrite(PGLOBAL g) {return 0;}
virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int) = 0;
protected:
// Members
......@@ -176,6 +177,11 @@ class DllExport TDBASE : public TDB {
virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
virtual void MarkDB(PGLOBAL g, PTDB tdb2);
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;}
protected:
// Members
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment