Fix and Enhance remote indexing:

- Apply to ODBC tables as well as MYSQL tables
- Fix and enhance the CheckCond routine
  Make ReadKey and MakeKeyWhere handle all conditions
  modified:   storage/connect/.gitattributes
  modified:   storage/connect/.gitignore
  modified:   storage/connect/connect.cc
  modified:   storage/connect/connect.h
  modified:   storage/connect/ha_connect.cc
  modified:   storage/connect/ha_connect.h
  modified:   storage/connect/mycat.cc
  modified:   storage/connect/tabmysql.cpp
  modified:   storage/connect/tabmysql.h
  modified:   storage/connect/tabodbc.cpp
  modified:   storage/connect/tabodbc.h
  modified:   storage/connect/xobject.cpp
  modified:   storage/connect/xobject.h
  modified:   storage/connect/xtable.h

Add some new tests:
  storage/connect/mysql-test/connect/r/endian.result
  storage/connect/mysql-test/connect/r/mysql_index.result
  storage/connect/mysql-test/connect/t/endian.test
  storage/connect/mysql-test/connect/t/mysql_index.test
This commit is contained in:
Olivier Bertrand 2015-07-16 11:05:20 +02:00
parent 3a9a3b98b7
commit a392c79552
18 changed files with 896 additions and 321 deletions

View file

@ -11,8 +11,10 @@
# Declare files that will always have LF line endings on checkout.
*.result text eol=lf
mysql-test/connect/std_data/*.txt text eol=lf
mysql-test/connect/std_data/*.dat text eol=lf
# Denote files that should not be modified.
mysql-test/connect/std_data/*.txt binary
mysql-test/connect/std_data/*.dat binary
# Denote all files that are truly binary and should not be modified.
*.png binary

View file

@ -82,6 +82,10 @@ RelWithDebInfo/
*.vcproj.*
*.vcproj.*.*
*.vcproj.*.*.*
*.vcxproj
*.vcxproj.*
*.vcxproj.*.*
*.vcxproj.*.*.*
# Build results
[Dd]ebug/

View file

@ -735,17 +735,12 @@ static void SetSwapValue(PVAL valp, char *kp)
/* IndexRead: fetch a record having the index value. */
/***********************************************************************/
RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
const void *key, int len, bool mrr)
const key_range *kr, bool mrr)
{
char *kp= (char*)key;
int n, x;
short lg;
bool rcb;
RCODE rc;
PVAL valp;
PCOL colp;
XXBASE *xbp;
PTDBDOX tdbp;
PTDBDOX tdbp;
if (!ptdb)
return RC_FX;
@ -757,13 +752,13 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
return RC_FX;
} else if (x == 2) {
// Remote index
if (ptdb->ReadKey(g, op, key, len))
if (ptdb->ReadKey(g, op, kr))
return RC_FX;
goto rnd;
} else if (x == 3) {
if (key)
((PTDBASE)ptdb)->SetRecpos(g, *(int*)key);
if (kr)
((PTDBASE)ptdb)->SetRecpos(g, *(int*)kr->key);
if (op == OP_SAME)
return RC_NF;
@ -790,7 +785,14 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
xbp= (XXBASE*)tdbp->To_Kindex;
if (key) {
if (kr) {
char *kp= (char*)kr->key;
int len= kr->length;
short lg;
bool rcb;
PVAL valp;
PCOL colp;
for (n= 0; n < tdbp->Knum; n++) {
colp= (PCOL)tdbp->To_Key_Col[n];
@ -832,10 +834,10 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
kp+= valp->GetClen();
if (len == kp - (char*)key) {
if (len == kp - (char*)kr->key) {
n++;
break;
} else if (len < kp - (char*)key) {
} else if (len < kp - (char*)kr->key) {
strcpy(g->Message, "Key buffer is too small");
return RC_FX;
} // endif len

View file

@ -36,7 +36,7 @@ bool CntRewindTable(PGLOBAL g, PTDB tdbp);
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, bool mrr);
RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const key_range *kr, bool mrr);
RCODE CntWriteRow(PGLOBAL g, PTDB tdbp);
RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp);
RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all);
@ -60,7 +60,7 @@ class TDBDOX: public TDBDOS {
friend int MakeIndex(PGLOBAL, PTDB, PIXDEF);
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 CntIndexRead(PGLOBAL, PTDB, OPVAL, const key_range*, bool);
friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool);
friend int CntIndexRange(PGLOBAL, PTDB, const uchar**, uint*,
bool*, key_part_map*);

View file

@ -169,9 +169,9 @@
#define JSONMAX 10 // JSON Default max grp size
extern "C" {
char version[]= "Version 1.03.0007 June 03, 2015";
char version[]= "Version 1.04.0001 June 29, 2015";
#if defined(__WIN__)
char compver[]= "Version 1.03.0007 " __DATE__ " " __TIME__;
char compver[]= "Version 1.04.0001 " __DATE__ " " __TIME__;
char slash= '\\';
#else // !__WIN__
char slash= '/';
@ -2188,97 +2188,157 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
/***********************************************************************/
int ha_connect::CheckRecord(PGLOBAL g, const uchar *, uchar *newbuf)
{
return ScanRecord(g, newbuf);
return ScanRecord(g, newbuf);
} // end of dummy CheckRecord
/***********************************************************************/
/* Return true if this field is used in current indexing. */
/***********************************************************************/
bool ha_connect::IsIndexed(Field *fp)
{
if (active_index < MAX_KEY) {
KEY_PART_INFO *kpart;
KEY *kfp= &table->key_info[active_index];
uint rem= kfp->user_defined_key_parts;
for (kpart= kfp->key_part; rem; rem--, kpart++)
if (kpart->field == fp)
return true;
} // endif active_index
return false;
} // end of IsIndexed
/***********************************************************************/
/* Return the where clause for remote indexed read. */
/***********************************************************************/
bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q,
const void *key, int klen)
bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q,
const key_range *kr)
{
const uchar *ptr;
uint rem, len, stlen; //, prtlen;
bool nq, oom, b= false;
Field *fp;
KEY *kfp;
KEY_PART_INFO *kpart;
const uchar *ptr;
uint i, rem, len, klen, stlen;
bool nq, both, oom= false;
OPVAL op;
Field *fp;
const key_range *ranges[2];
my_bitmap_map *old_map;
KEY *kfp;
KEY_PART_INFO *kpart;
if (active_index == MAX_KEY)
return false;
else if (!key) {
strcpy(g->Message, "MakeKeyWhere: No key");
return true;
} // endif key
oom= qry->Append(" WHERE (");
kfp= &table->key_info[active_index];
rem= kfp->user_defined_key_parts,
len= klen,
ptr= (const uchar *)key;
ranges[0]= kr;
ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL;
for (kpart= kfp->key_part; rem; rem--, kpart++) {
fp= kpart->field;
stlen= kpart->store_length;
// prtlen= MY_MIN(stlen, len);
nq= fp->str_needs_quotes();
if (!ranges[0] && !ranges[1]) {
strcpy(g->Message, "MakeKeyWhere: No key");
return true;
} else
both= ranges[0] && ranges[1];
if (b)
oom|= qry->Append(" AND ");
else
b= true;
kfp= &table->key_info[active_index];
old_map= dbug_tmp_use_all_columns(table, table->write_set);
oom|= qry->Append(q);
oom|= qry->Append((PSZ)fp->field_name);
oom|= qry->Append(q);
for (i = 0; i <= 1; i++) {
if (ranges[i] == NULL)
continue;
switch (op) {
case OP_EQ:
case OP_GT:
case OP_GE:
oom|= qry->Append((PSZ)GetValStr(op, false));
break;
default:
oom|= qry->Append(" ??? ");
} // endwitch op
if (both && i > 0)
oom|= qry->Append(") AND (");
else
oom|= qry->Append(" WHERE (");
if (nq)
oom|= qry->Append('\'');
klen= len= ranges[i]->length;
rem= kfp->user_defined_key_parts;
ptr= ranges[i]->key;
if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
String varchar;
uint var_length= uint2korr(ptr);
for (kpart= kfp->key_part; rem; rem--, kpart++) {
fp= kpart->field;
stlen= kpart->store_length;
nq= fp->str_needs_quotes();
varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH,
var_length, &my_charset_bin);
oom|= qry->Append(varchar.ptr(), varchar.length());
} else {
char strbuff[MAX_FIELD_WIDTH];
String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
if (kpart != kfp->key_part)
oom|= qry->Append(" AND ");
res= fp->val_str(&str, ptr);
oom|= qry->Append(res->ptr(), res->length());
} // endif flag
if (q) {
oom|= qry->Append(q);
oom|= qry->Append((PSZ)fp->field_name);
oom|= qry->Append(q);
} else
oom|= qry->Append((PSZ)fp->field_name);
if (nq)
oom|= qry->Append('\'');
switch (ranges[i]->flag) {
case HA_READ_KEY_EXACT:
// op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT)
// ? OP_EQ : OP_LIKE;
op= OP_EQ;
break;
case HA_READ_AFTER_KEY:
op= (stlen >= len) ? (!i ? OP_GT : OP_LE) : OP_GE;
break;
case HA_READ_KEY_OR_NEXT:
op= OP_GE;
break;
case HA_READ_BEFORE_KEY:
op= (stlen >= len) ? OP_LT : OP_LE;
break;
case HA_READ_KEY_OR_PREV:
op= OP_LE;
break;
default:
sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag);
goto err;
} // endswitch flag
if (stlen >= len)
break;
oom|= qry->Append((PSZ)GetValStr(op, false));
len-= stlen;
if (nq)
oom|= qry->Append('\'');
/* 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 - MY_TEST(kpart->null_bit);
} // endfor kpart
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);
oom|= qry->Append(varchar.ptr(), varchar.length(), nq);
} else {
char strbuff[MAX_FIELD_WIDTH];
String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
res= fp->val_str(&str, ptr);
oom|= qry->Append(res->ptr(), res->length(), nq);
} // endif flag
if (nq)
oom |= qry->Append('\'');
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 - MY_TEST(kpart->null_bit);
} // endfor kpart
} // endfor i
if ((oom|= qry->Append(")")))
strcpy(g->Message, "Out of memory");
return oom;
dbug_tmp_restore_column_map(table->write_set, old_map);
return oom;
err:
dbug_tmp_restore_column_map(table->write_set, old_map);
return true;
} // end of MakeKeyWhere
@ -2548,8 +2608,9 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
/***********************************************************************/
/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
/***********************************************************************/
PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
{
AMT tty = filp->Type;
char *body= filp->Body;
unsigned int i;
bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
@ -2582,7 +2643,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
List<Item>* arglist= cond_item->argument_list();
List_iterator<Item> li(*arglist);
Item *subitem;
const Item *subitem;
p1= body + strlen(body);
strcpy(p1, "(");
@ -2590,7 +2651,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
for (i= 0; i < arglist->elements; i++)
if ((subitem= li++)) {
if (!CheckCond(g, filp, tty, subitem)) {
if (!CheckCond(g, filp, subitem)) {
if (vop == OP_OR || nonul)
return NULL;
else
@ -2612,26 +2673,27 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
} else if (cond->type() == COND::FUNC_ITEM) {
unsigned int i;
// int n;
bool iscol, neg= FALSE;
Item_func *condf= (Item_func *)cond;
Item* *args= condf->arguments();
if (trace)
htrc("Func type=%d argnum=%d\n", condf->functype(),
condf->argument_count());
// neg= condf->
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::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::LIKE_FUNC: vop= OP_LIKE; break;
case Item_func::ISNOTNULL_FUNC:
neg = true;
case Item_func::ISNULL_FUNC: vop= OP_NULL; break;
case Item_func::IN_FUNC: vop= OP_IN;
case Item_func::BETWEEN:
ismul= true;
neg= ((Item_func_opt_neg *)condf)->negated;
@ -2644,7 +2706,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
else if (ismul && tty == TYPE_AM_WMI)
return NULL; // Not supported by WQL
if (x && (neg || !(vop == OP_EQ || vop == OP_IN)))
if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL)))
return NULL;
for (i= 0; i < condf->argument_count(); i++) {
@ -2665,9 +2727,10 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
if (x && i)
return NULL;
if (pField->field->table != table)
return NULL; // Field does not belong to this table
else if (pField->field->table != table)
return NULL; // Field does not belong to this table
else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
return NULL; // Will be handled by ReadKey
else
fop= GetFieldOptionStruct(pField->field);
@ -2698,7 +2761,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
strcat(body, fnm);
} else if (args[i]->type() == COND::FUNC_ITEM) {
if (tty == TYPE_AM_MYSQL) {
if (!CheckCond(g, filp, tty, args[i]))
if (!CheckCond(g, filp, args[i]))
return NULL;
} else
@ -2887,14 +2950,17 @@ const COND *ha_connect::cond_push(const COND *cond)
goto fin;
if (b) {
PCFIL filp= (PCFIL)PlugSubAlloc(g, NULL, sizeof(CONDFIL));
PCFIL filp;
if ((filp= tdbp->GetCondFil()) && filp->Cond == cond &&
filp->Idx == active_index && filp->Type == tty)
goto fin; // Already done
filp= new(g) CONDFIL(cond, active_index, tty);
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 (CheckCond(g, filp, cond)) {
if (trace)
htrc("cond_push: %s\n", filp->Body);
@ -3358,13 +3424,13 @@ int ha_connect::index_end()
/****************************************************************************/
/* This is internally called by all indexed reading functions. */
/****************************************************************************/
int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len)
int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const key_range *kr)
{
int rc;
//statistic_increment(ha_read_key_count, &LOCK_status);
switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) {
switch (CntIndexRead(xp->g, tdbp, op, kr, mrr)) {
case RC_OK:
xp->fnd++;
rc= MakeRecord((char*)buf);
@ -3430,7 +3496,12 @@ int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
htrc("%p index_read: op=%d\n", this, op);
if (indexing > 0) {
rc= ReadIndexed(buf, op, key, key_len);
start_key.key= key;
start_key.length= key_len;
start_key.flag= find_flag;
start_key.keypart_map= 0;
rc= ReadIndexed(buf, op, &start_key);
if (rc == HA_ERR_INTERNAL_ERROR) {
nox= true; // To block making indexes
@ -3736,7 +3807,7 @@ void ha_connect::position(const uchar *)
//if (((PTDBASE)tdbp)->GetDef()->Indexable())
my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos());
if (trace)
if (trace > 1)
htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos());
DBUG_VOID_RETURN;
@ -6648,10 +6719,10 @@ maria_declare_plugin(connect)
PLUGIN_LICENSE_GPL,
connect_init_func, /* Plugin Init */
connect_done_func, /* Plugin Deinit */
0x0103, /* version number (1.03) */
0x0104, /* version number (1.04) */
NULL, /* status variables */
connect_system_variables, /* system variables */
"1.03.0007", /* string version */
"1.04.0001", /* string version */
MariaDB_PLUGIN_MATURITY_BETA /* maturity */
}
maria_declare_plugin_end;

View file

@ -241,11 +241,12 @@ public:
int MakeRecord(char *buf);
int ScanRecord(PGLOBAL g, uchar *buf);
int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
uint key_len= 0);
int ReadIndexed(uchar *buf, OPVAL op, const key_range *kr= NULL);
bool IsIndexed(Field *fp);
bool MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q,
const void *key, int klen);
inline char *Strz(LEX_STRING &ls);
const key_range *kr);
inline char *Strz(LEX_STRING &ls);
key_range start_key;
/** @brief
@ -374,7 +375,7 @@ public:
condition stack.
*/
virtual const COND *cond_push(const COND *cond);
PCFIL CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond);
PCFIL CheckCond(PGLOBAL g, PCFIL filp, const Item *cond);
const char *GetValStr(OPVAL vop, bool neg);
PFIL CondFilter(PGLOBAL g, Item *cond);
//PFIL CheckFilter(PGLOBAL g);

View file

@ -299,13 +299,13 @@ int GetIndexType(TABTYPE type)
xtyp= 1;
break;
case TAB_MYSQL:
// case TAB_ODBC:
case TAB_ODBC:
xtyp= 2;
break;
case TAB_VIR:
xtyp= 3;
break;
case TAB_ODBC:
// case TAB_ODBC:
default:
xtyp= 0;
break;

View file

@ -0,0 +1,105 @@
SET time_zone='+00:00';
#
# Testing little endian table
#
CREATE TABLE t1
(
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='L',
id CHAR(5) NOT NULL FIELD_FORMAT='L2',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='LF',
dept INT(4) NOT NULL FIELD_FORMAT='L2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat';
SELECT * FROM t1;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555);
SELECT * FROM t1;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
DROP TABLE t1;
CREATE TABLE t1
(
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id CHAR(5) NOT NULL FIELD_FORMAT='S',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept INT(4) NOT NULL FIELD_FORMAT='S'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat' OPTION_LIST='Endian=Little';
SELECT * FROM t1;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
#
# Testing big endian table
#
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='B',
id CHAR(5) NOT NULL FIELD_FORMAT='BS',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF',
dept INT(4) NOT NULL FIELD_FORMAT='B2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin';
INSERT INTO t2 SELECT * FROM t1;
SELECT * FROM t2;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
DROP TABLE t2;
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id CHAR(5) NOT NULL FIELD_FORMAT='S',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept INT(4) NOT NULL FIELD_FORMAT='2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big';
SELECT * FROM t2;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
DROP TABLE t2;
CREATE TABLE t2 (
fig CHAR(4) NOT NULL,
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id SMALLINT(5) NOT NULL,
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept SMALLINT(4) NOT NULL
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big';
SELECT * FROM t2;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
DROP TABLE t2;
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='B',
id CHAR(5) NOT NULL FIELD_FORMAT='BS',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF',
dept SMALLINT(4) NOT NULL FIELD_FORMAT='B'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin';
SELECT * FROM t2;
fig name birth id salary dept
5500 ARCHIBALD 1980-01-25 3789 4380.50 318
123 OLIVER 1953-08-10 23456 3400.68 2158
3123 FOO 2002-07-23 888 0.00 318
5555 RONALD 1980-02-26 3333 4444.44 555
DROP TABLE t1, t2;

View file

@ -0,0 +1,114 @@
#
# Make remote table
#
CREATE TABLE t1 (
id int(11) NOT NULL,
msg char(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES(1,'Un'),(3,'Trois'),(5,'Cinq');
INSERT INTO t1 VALUES(2,'Two'),(4,'Four'),(6,'Six');
SELECT * FROM t1;
id msg
1 Un
3 Trois
5 Cinq
2 Two
4 Four
6 Six
#
# Make local MYSQL table with indexed id column
#
CREATE TABLE t2 (
id int(11) NOT NULL,
msg char(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=CONNECT DEFAULT CHARSET=latin1 TABLE_TYPE=MYSQL TABNAME=t1;
#
# Testing SELECT, etc.
#
SELECT * FROM t2;
id msg
1 Un
3 Trois
5 Cinq
2 Two
4 Four
6 Six
SELECT * FROM t2 WHERE id = 3;
id msg
3 Trois
SELECT * FROM t2 WHERE id IN (2,4);
id msg
2 Two
4 Four
SELECT * FROM t2 WHERE id IN (2,4) AND msg = 'Two';
id msg
2 Two
SELECT * FROM t2 WHERE id > 3;
id msg
5 Cinq
4 Four
6 Six
SELECT * FROM t2 WHERE id >= 3;
id msg
3 Trois
5 Cinq
4 Four
6 Six
SELECT * FROM t2 WHERE id < 3;
id msg
1 Un
2 Two
SELECT * FROM t2 WHERE id < 3 OR id > 4;
id msg
1 Un
2 Two
5 Cinq
6 Six
SELECT * FROM t2 WHERE id <= 3;
id msg
1 Un
2 Two
3 Trois
SELECT * FROM t2 WHERE id BETWEEN 3 AND 5;
id msg
3 Trois
4 Four
5 Cinq
SELECT * FROM t2 WHERE id > 2 AND id < 6;
id msg
3 Trois
4 Four
5 Cinq
SELECT * FROM t2 ORDER BY id;
id msg
1 Un
2 Two
3 Trois
4 Four
5 Cinq
6 Six
UPDATE t2 SET msg = 'Five' WHERE id = 5;
Warnings:
Note 1105 t1: 1 affected rows
SELECT * FROM t2;
id msg
1 Un
3 Trois
5 Five
2 Two
4 Four
6 Six
DELETE FROM t2 WHERE id = 4;
Warnings:
Note 1105 t1: 1 affected rows
SELECT * FROM t2;
id msg
1 Un
3 Trois
5 Five
2 Two
6 Six
DROP TABLE t2;
DROP TABLE t1;

View file

@ -0,0 +1,88 @@
let $MYSQLD_DATADIR= `select @@datadir`;
--copy_file $MTR_SUITE_DIR/std_data/Testbal.dat $MYSQLD_DATADIR/test/Testbal.dat
SET time_zone='+00:00';
--echo #
--echo # Testing little endian table
--echo #
CREATE TABLE t1
(
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='L',
id CHAR(5) NOT NULL FIELD_FORMAT='L2',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='LF',
dept INT(4) NOT NULL FIELD_FORMAT='L2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat';
SELECT * FROM t1;
INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555);
SELECT * FROM t1;
DROP TABLE t1;
CREATE TABLE t1
(
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id CHAR(5) NOT NULL FIELD_FORMAT='S',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept INT(4) NOT NULL FIELD_FORMAT='S'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat' OPTION_LIST='Endian=Little';
SELECT * FROM t1;
--echo #
--echo # Testing big endian table
--echo #
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='B',
id CHAR(5) NOT NULL FIELD_FORMAT='BS',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF',
dept INT(4) NOT NULL FIELD_FORMAT='B2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin';
INSERT INTO t2 SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t2;
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id CHAR(5) NOT NULL FIELD_FORMAT='S',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept INT(4) NOT NULL FIELD_FORMAT='2'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big';
SELECT * FROM t2;
DROP TABLE t2;
CREATE TABLE t2 (
fig CHAR(4) NOT NULL,
name CHAR(10) NOT NULL,
birth DATE NOT NULL,
id SMALLINT(5) NOT NULL,
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F',
dept SMALLINT(4) NOT NULL
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big';
SELECT * FROM t2;
DROP TABLE t2;
CREATE TABLE t2 (
fig INT(4) NOT NULL FIELD_FORMAT='C',
name CHAR(10) NOT NULL,
birth DATE NOT NULL FIELD_FORMAT='B',
id CHAR(5) NOT NULL FIELD_FORMAT='BS',
salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF',
dept SMALLINT(4) NOT NULL FIELD_FORMAT='B'
) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin';
SELECT * FROM t2;
DROP TABLE t1, t2;
#
# Clean up
#
--remove_file $MYSQLD_DATADIR/test/Testbal.dat
--remove_file $MYSQLD_DATADIR/test/Testbal.bin

View file

@ -0,0 +1,66 @@
-- source include/not_embedded.inc
#
# TODO: consider a possibility to run this test
# against some remote MySQL server
#
let $PORT= `select @@port`;
--disable_query_log
--replace_result $PORT PORT
--error 0,ER_UNKNOWN_ERROR
--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT'
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
AND ENGINE='CONNECT'
AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`)
{
Skip Need MySQL support;
}
DROP TABLE t1;
--enable_query_log
--echo #
--echo # Make remote table
--echo #
CREATE TABLE t1 (
id int(11) NOT NULL,
msg char(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES(1,'Un'),(3,'Trois'),(5,'Cinq');
INSERT INTO t1 VALUES(2,'Two'),(4,'Four'),(6,'Six');
SELECT * FROM t1;
--echo #
--echo # Make local MYSQL table with indexed id column
--echo #
CREATE TABLE t2 (
id int(11) NOT NULL,
msg char(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=CONNECT DEFAULT CHARSET=latin1 TABLE_TYPE=MYSQL TABNAME=t1;
--echo #
--echo # Testing SELECT, etc.
--echo #
SELECT * FROM t2;
SELECT * FROM t2 WHERE id = 3;
SELECT * FROM t2 WHERE id IN (2,4);
SELECT * FROM t2 WHERE id IN (2,4) AND msg = 'Two';
SELECT * FROM t2 WHERE id > 3;
SELECT * FROM t2 WHERE id >= 3;
SELECT * FROM t2 WHERE id < 3;
SELECT * FROM t2 WHERE id < 3 OR id > 4;
SELECT * FROM t2 WHERE id <= 3;
SELECT * FROM t2 WHERE id BETWEEN 3 AND 5;
SELECT * FROM t2 WHERE id > 2 AND id < 6;
SELECT * FROM t2 ORDER BY id;
UPDATE t2 SET msg = 'Five' WHERE id = 5;
SELECT * FROM t2;
DELETE FROM t2 WHERE id = 4;
SELECT * FROM t2;
DROP TABLE t2;
DROP TABLE t1;

View file

@ -1054,14 +1054,14 @@ int TDBMYSQL::SendCommand(PGLOBAL g)
/***********************************************************************/
/* Data Base indexed read routine for MYSQL access method. */
/***********************************************************************/
bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
{
bool oom;
int oldlen = Query->GetLength();
PHC hc = To_Def->GetHandler();
if (!key || op == OP_NEXT ||
Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!key && Mode == MODE_READX) {
if (!(kr || hc->end_range) || op == OP_NEXT ||
Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!kr && Mode == MODE_READX) {
// This is a false indexed read
m_Rc = Myc.ExecSQL(g, Query->GetStr());
Mode = MODE_READ;
@ -1069,37 +1069,39 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
} // endif key
return false;
} else if (op == OP_FIRST) {
if (To_CondFil) {
oom = Query->Append(" WHERE ");
if ((oom |= Query->Append(To_CondFil->Body))) {
strcpy(g->Message, "Readkey: Out of memory");
return true;
} // endif oom
} // endif To_Condfil
} else {
if (Myc.m_Res)
Myc.FreeResult();
To_Def->GetHandler()->MakeKeyWhere(g, Query, op, '`', key, len);
if (hc->MakeKeyWhere(g, Query, op, '`', kr))
return true;
if (To_CondFil) {
oom = Query->Append(" AND (");
oom |= Query->Append(To_CondFil->Body);
if (To_CondFil->Idx != hc->active_index) {
To_CondFil->Idx = hc->active_index;
To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
*To_CondFil->Body= 0;
if ((oom |= Query->Append(')'))) {
strcpy(g->Message, "Readkey: Out of memory");
return true;
} // endif oom
if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond)))
PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
} // endif To_Condfil
} // endif active_index
} // endif's op
if (To_CondFil)
if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
strcpy(g->Message, "Readkey: Out of memory");
return true;
} // endif Append
m_Rc = Myc.ExecSQL(g, Query->GetStr());
} // endif To_Condfil
Mode = MODE_READ;
} // endif's op
if (trace)
htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr());
m_Rc = Myc.ExecSQL(g, Query->GetStr());
Query->Truncate(oldlen);
return (m_Rc == RC_FX) ? true : false;
} // end of ReadKey

View file

@ -99,7 +99,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);
virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr);
// Specific routines
bool SetColumnRanks(PGLOBAL g);

View file

@ -1,7 +1,7 @@
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
/* Version 2.9 */
/* Version 3.0 */
/* */
/* COPYRIGHT: */
/* ---------- */
@ -35,6 +35,7 @@
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
#include "sql_class.h"
#if defined(__WIN__)
#include <io.h>
#include <fcntl.h>
@ -72,6 +73,7 @@
#include "reldef.h"
#include "tabcol.h"
#include "valblk.h"
#include "ha_connect.h"
#include "sql_string.h"
@ -386,176 +388,209 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n)
/* Note: when implementing EOM filtering, column only used in local */
/* filter should be removed from column list. */
/***********************************************************************/
char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
{
char *colist, *tabname, *sql, buf[NAM_LEN * 3];
LPCSTR schmp = NULL, catp = NULL;
int len, ncol = 0;
bool first = true;
PTABLE tablep = To_Table;
PCOL colp;
char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
int len;
bool oom = false, first = true;
PTABLE tablep = To_Table;
PCOL colp;
if (Srcdef)
return Srcdef;
if (Srcdef) {
Query = new(g)STRING(g, 0, Srcdef);
return false;
} // endif Srcdef
if (!cnt) {
// Normal SQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial())
ncol++;
// Allocate the string used to contain the Query
Query = new(g)STRING(g, 1023, "SELECT ");
if (ncol) {
colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);
if (!cnt) {
if (Columns) {
// Normal SQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial()) {
if (!first)
oom |= Query->Append(", ");
else
first = false;
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial()) {
// Column name can be in UTF-8 encoding
/*rc=*/ Decode(colp->GetName(), buf, sizeof(buf));
// Column name can be encoded in UTF-8
Decode(colp->GetName(), buf, sizeof(buf));
if (Quote) {
if (first) {
strcat(strcat(strcpy(colist, Quote), buf), Quote);
first = false;
} else
strcat(strcat(strcat(strcat(colist, ", "),
Quote), buf), Quote);
if (Quote) {
// Put column name between identifier quotes in case in contains blanks
oom |= Query->Append(Quote);
oom |= Query->Append(buf);
oom |= Query->Append(Quote);
} else
oom |= Query->Append(buf);
} else {
if (first) {
strcpy(colist, buf);
first = false;
} else
strcat(strcat(colist, ", "), buf);
} // endif colp
} // endif Quote
} // endif !Special
} else {
// ncol == 0 can occur for queries such that sql count(*) from...
} else
// !Columns can occur for queries such that sql count(*) from...
// for which we will count the rows from sql * from...
colist = (char*)PlugSubAlloc(g, NULL, 2);
strcpy(colist, "*");
} // endif ncol
oom |= Query->Append('*');
} else {
} else
// SQL statement used to retrieve the size of the result
colist = (char*)PlugSubAlloc(g, NULL, 9);
strcpy(colist, "count(*)");
} // endif cnt
oom |= Query->Append("count(*)");
// Table name can be encoded in UTF-8
/*rc = */Decode(TableName, buf, sizeof(buf));
// Put table name between identifier quotes in case in contains blanks
tabname = (char*)PlugSubAlloc(g, NULL, strlen(buf) + 3);
if (Quote)
strcat(strcat(strcpy(tabname, Quote), buf), Quote);
else
strcpy(tabname, buf);
// Below 14 is length of 'select ' + length of ' from ' + 1
len = (strlen(colist) + strlen(buf) + 14);
len += (To_CondFil ? strlen(To_CondFil->Body) + 7 : 0);
oom |= Query->Append(" FROM ");
if (Catalog && *Catalog)
catp = Catalog;
if (catp)
len += (strlen(catp) + 2);
if (tablep->GetSchema())
schmp = tablep->GetSchema();
schmp = (char*)tablep->GetSchema();
else if (Schema && *Schema)
schmp = Schema;
if (schmp)
len += (strlen(schmp) + 1);
sql = (char*)PlugSubAlloc(g, NULL, len);
strcat(strcat(strcpy(sql, "SELECT "), colist), " FROM ");
if (catp) {
strcat(sql, catp);
oom |= Query->Append(catp);
if (schmp)
strcat(strcat(sql, "."), schmp);
else
strcat(sql, ".");
if (schmp) {
oom |= Query->Append('.');
oom |= Query->Append(schmp);
} // endif schmp
strcat(sql, ".");
} else if (schmp)
strcat(strcat(sql, schmp), ".");
oom |= Query->Append('.');
} else if (schmp) {
oom |= Query->Append(schmp);
oom |= Query->Append('.');
} // endif schmp
strcat(sql, tabname);
// Table name can be encoded in UTF-8
Decode(TableName, buf, sizeof(buf));
if (To_CondFil)
strcat(strcat(sql, " WHERE "), To_CondFil->Body);
if (trace)
htrc("sql: '%s'\n", sql);
if (Quote) {
// Put table name between identifier quotes in case in contains blanks
oom |= Query->Append(Quote);
oom |= Query->Append(buf);
oom |= Query->Append(Quote);
} else
oom |= Query->Append(buf);
return sql;
len = Query->GetLength();
if (To_CondFil) {
if (Mode == MODE_READ) {
oom |= Query->Append(" WHERE ");
oom |= Query->Append(To_CondFil->Body);
len = Query->GetLength() + 1;
} else
len += (strlen(To_CondFil->Body) + 256);
} else
len += ((Mode == MODE_READX) ? 256 : 1);
if (oom || Query->Resize(len)) {
strcpy(g->Message, "MakeSQL: Out of memory");
return true;
} // endif oom
if (trace)
htrc("Query=%s\n", Query->GetStr());
return false;
} // end of MakeSQL
/***********************************************************************/
/* MakeInsert: make the Insert statement used with ODBC connection. */
/***********************************************************************/
char *TDBODBC::MakeInsert(PGLOBAL g)
bool TDBODBC::MakeInsert(PGLOBAL g)
{
char *stmt, *colist, *valist, buf[NAM_LEN * 3];
// char *tk = "`";
int len = 0;
bool b = FALSE;
PCOL colp;
char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
int len = 0;
bool b = false, oom;
PTABLE tablep = To_Table;
PCOL colp;
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->IsSpecial()) {
strcpy(g->Message, MSG(NO_ODBC_SPECOL));
return NULL;
return true;
} else {
len += (strlen(colp->GetName()) + 4);
// Column name can be encoded in UTF-8
Decode(colp->GetName(), buf, sizeof(buf));
len += (strlen(buf) + 6); // comma + quotes + valist
((PODBCCOL)colp)->Rank = ++Nparm;
} // endif colp
colist = (char*)PlugSubAlloc(g, NULL, len);
*colist = '\0';
valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
*valist = '\0';
// Below 32 is enough to contain the fixed part of the query
if (Catalog && *Catalog)
catp = Catalog;
for (colp = Columns; colp; colp = colp->GetNext()) {
if (b) {
strcat(colist, ", ");
strcat(valist, ",");
} else
b = true;
if (catp)
len += strlen(catp) + 1;
// Column name can be in UTF-8 encoding
Decode(colp->GetName(), buf, sizeof(buf));
if (tablep->GetSchema())
schmp = (char*)tablep->GetSchema();
else if (Schema && *Schema)
schmp = Schema;
if (Quote)
strcat(strcat(strcat(colist, Quote), buf), Quote);
else
strcat(colist, buf);
if (schmp)
len += strlen(schmp) + 1;
strcat(valist, "?"); // Parameter marker
} // endfor colp
// Column name can be encoded in UTF-8
Decode(TableName, buf, sizeof(buf));
len += (strlen(buf) + 32);
Query = new(g) STRING(g, len, "INSERT INTO ");
// Below 32 is enough to contain the fixed part of the query
len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32);
stmt = (char*)PlugSubAlloc(g, NULL, len);
strcpy(stmt, "INSERT INTO ");
if (catp) {
oom |= Query->Append(catp);
if (Quote)
strcat(strcat(strcat(stmt, Quote), TableName), Quote);
else
strcat(stmt, TableName);
if (schmp) {
oom |= Query->Append('.');
oom |= Query->Append(schmp);
} // endif schmp
strcat(strcat(strcat(stmt, " ("), colist), ") VALUES (");
strcat(strcat(stmt, valist), ")");
oom |= Query->Append('.');
} else if (schmp) {
oom |= Query->Append(schmp);
oom |= Query->Append('.');
} // endif schmp
return stmt;
if (Quote) {
// Put table name between identifier quotes in case in contains blanks
oom |= Query->Append(Quote);
oom |= Query->Append(buf);
oom |= Query->Append(Quote);
} else
oom |= Query->Append(buf);
oom |= Query->Append('(');
for (colp = Columns; colp; colp = colp->GetNext()) {
if (b)
oom |= Query->Append(", ");
else
b = true;
// Column name can be in UTF-8 encoding
Decode(colp->GetName(), buf, sizeof(buf));
if (Quote) {
// Put column name between identifier quotes in case in contains blanks
oom |= Query->Append(Quote);
oom |= Query->Append(buf);
oom |= Query->Append(Quote);
} else
oom |= Query->Append(buf);
} // endfor colp
oom |= Query->Append(") VALUES (");
for (int i = 0; i < Nparm; i++)
oom |= Query->Append("?,");
if (oom)
strcpy(g->Message, "MakeInsert: Out of memory");
else
Query->RepLast(')');
return oom;
} // end of MakeInsert
/***********************************************************************/
@ -580,7 +615,7 @@ bool TDBODBC::BindParameters(PGLOBAL g)
/* MakeCommand: make the Update or Delete statement to send to the */
/* MySQL server. Limited to remote values and filtering. */
/***********************************************************************/
char *TDBODBC::MakeCommand(PGLOBAL g)
bool TDBODBC::MakeCommand(PGLOBAL g)
{
char *p, *stmt, name[68], *body = NULL, *qc = Ocp->GetQuoteChar();
char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
@ -638,7 +673,8 @@ char *TDBODBC::MakeCommand(PGLOBAL g)
return NULL;
} // endif p
return stmt;
Query = new(g) STRING(g, 0, stmt);
return (!Query->GetSize());
} // end of MakeCommand
#if 0
@ -815,10 +851,12 @@ bool TDBODBC::OpenDB(PGLOBAL g)
if (Memory < 3) {
// Method will depend on cursor type
if ((Rbuf = Ocp->Rewind(Query, (PODBCCOL)Columns)) < 0) {
Ocp->Close();
return true;
} // endif Rewind
if ((Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0)
if (Mode != MODE_READX) {
Ocp->Close();
return true;
} else
Rbuf = 0;
} else
Rbuf = Qrp->Nblin;
@ -853,15 +891,14 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/*********************************************************************/
if (Mode == MODE_READ || Mode == MODE_READX) {
if (Memory > 1 && !Srcdef) {
char *Sql;
int n;
int n;
if ((Sql = MakeSQL(g, true))) {
if (!MakeSQL(g, true)) {
// Allocate a Count(*) column
Cnp = new(g) ODBCCOL;
Cnp->InitValue(g);
if ((n = Ocp->GetResultSize(Sql, Cnp)) < 0) {
if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
strcpy(g->Message, "Cannot get result size");
return true;
} // endif n
@ -871,36 +908,36 @@ bool TDBODBC::OpenDB(PGLOBAL g)
if ((Qrp = Ocp->AllocateResult(g)))
Memory = 2; // Must be filled
else {
strcpy(g->Message, "Memory allocation failed");
strcpy(g->Message, "Result set memory allocation failed");
return true;
} // endif n
Ocp->m_Rows = 0;
} else {
strcpy(g->Message, "MakeSQL failed");
} else
return true;
} // endif Sql
} // endif Memory
if ((Query = MakeSQL(g, false))) {
if (!(rc = MakeSQL(g, false))) {
for (PODBCCOL colp = (PODBCCOL)Columns; colp;
colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial())
colp->AllocateBuffers(g, Rows);
rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
} // endif Query
rc = (Mode == MODE_READ)
? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0)
: false;
} // endif rc
} else if (Mode == MODE_INSERT) {
if ((Query = MakeInsert(g))) {
if (Nparm != Ocp->PrepareSQL(Query)) {
if (!(rc = MakeInsert(g))) {
if (Nparm != Ocp->PrepareSQL(Query->GetStr())) {
strcpy(g->Message, MSG(PARM_CNT_MISS));
rc = true;
} else
rc = BindParameters(g);
} // endif Query
} // endif rc
} else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
rc = false; // wait for CheckCond before calling MakeCommand(g);
@ -958,6 +995,59 @@ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
return false;
} // end of SetRecpos
/***********************************************************************/
/* Data Base indexed read routine for MYSQL access method. */
/***********************************************************************/
bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
{
char c = Quote ? *Quote : 0;
int oldlen = Query->GetLength();
PHC hc = To_Def->GetHandler();
if (!(kr || hc->end_range) || op == OP_NEXT ||
Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!kr && Mode == MODE_READX) {
// This is a false indexed read
Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
Mode = MODE_READ;
return (Rows < 0);
} // endif key
return false;
} else {
if (To_Def->GetHandler()->MakeKeyWhere(g, Query, op, c, kr))
return true;
if (To_CondFil) {
if (To_CondFil->Idx != hc->active_index) {
To_CondFil->Idx = hc->active_index;
To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
*To_CondFil->Body= 0;
if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond)))
PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
} // endif active_index
if (To_CondFil)
if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
strcpy(g->Message, "Readkey: Out of memory");
return true;
} // endif Append
} // endif To_Condfil
Mode = MODE_READ;
} // endif's op
if (trace)
htrc("ODBC ReadKey: Query=%s\n", Query->GetStr());
Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
Query->Truncate(oldlen);
return (Rows < 0);
} // end of ReadKey
/***********************************************************************/
/* VRDNDOS: Data Base read routine for odbc access method. */
/***********************************************************************/
@ -970,11 +1060,11 @@ int TDBODBC::ReadDB(PGLOBAL g)
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!Query && !(Query = MakeCommand(g)))
if (!Query && MakeCommand(g))
return RC_FX;
// Send the UPDATE/DELETE command to the remote table
if (!Ocp->ExecSQLcommand(Query)) {
if (!Ocp->ExecSQLcommand(Query->GetStr())) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
if (trace)
@ -1052,11 +1142,11 @@ int TDBODBC::WriteDB(PGLOBAL g)
int TDBODBC::DeleteDB(PGLOBAL g, int irc)
{
if (irc == RC_FX) {
if (!Query && !(Query = MakeCommand(g)))
if (!Query && MakeCommand(g))
return RC_FX;
// Send the DELETE (all) command to the remote table
if (!Ocp->ExecSQLcommand(Query)) {
if (!Ocp->ExecSQLcommand(Query->GetStr())) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
if (trace)
@ -1272,7 +1362,7 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
if (Value->IsZero())
Value->SetNull(Nullable);
if (trace) {
if (trace > 1) {
char buf[64];
htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
@ -1560,9 +1650,12 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
int TDBXDBC::ReadDB(PGLOBAL g)
{
if (Cmdlist) {
Query = Cmdlist->Cmd;
if (!Query)
Query = new(g)STRING(g, 0, Cmdlist->Cmd);
else
Query->Set(Cmdlist->Cmd);
if (Ocp->ExecSQLcommand(Query))
if (Ocp->ExecSQLcommand(Query->GetStr()))
Nerr++;
Fpos++; // Used for progress info
@ -1620,10 +1713,10 @@ void XSRCCOL::ReadColumn(PGLOBAL g)
PTDBXDBC tdbp = (PTDBXDBC)To_Tdb;
switch (Flag) {
case 0: Value->SetValue_psz(tdbp->Query); break;
case 1: Value->SetValue(tdbp->AftRows); break;
case 2: Value->SetValue_psz(g->Message); break;
default: Value->SetValue_psz("Invalid Flag"); break;
case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
case 1: Value->SetValue(tdbp->AftRows); break;
case 2: Value->SetValue_psz(g->Message); break;
default: Value->SetValue_psz("Invalid Flag"); break;
} // endswitch Flag
} // end of ReadColumn

View file

@ -42,7 +42,8 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
int GetOptions(void) {return Options;}
// Methods
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual int Indexable(void) {return 2;}
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
protected:
@ -111,15 +112,14 @@ 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;}
virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr);
protected:
// Internal functions
int Decode(char *utf, char *buf, size_t n);
char *MakeSQL(PGLOBAL g, bool cnt);
char *MakeInsert(PGLOBAL g);
char *MakeCommand(PGLOBAL g);
bool MakeSQL(PGLOBAL g, bool cnt);
bool MakeInsert(PGLOBAL g);
bool MakeCommand(PGLOBAL g);
//bool MakeFilter(PGLOBAL g, bool c);
bool BindParameters(PGLOBAL g);
//char *MakeUpdate(PGLOBAL g);
@ -129,14 +129,14 @@ class TDBODBC : public TDBASE {
ODBConn *Ocp; // Points to an ODBC connection class
ODBCCOL *Cnp; // Points to count(*) column
ODBCPARM Ops; // Additional parameters
char *Connect; // Points to connection string
PSTRG Query; // Constructed SQL query
char *Connect; // Points to connection string
char *TableName; // Points to ODBC table name
char *Schema; // Points to ODBC table Schema
char *User; // User connect info
char *Pwd; // Password connect info
char *Catalog; // Points to ODBC table Catalog
char *Srcdef; // The source table SQL definition
char *Query; // Points to SQL statement
char *Count; // Points to count(*) SQL statement
//char *Where; // Points to local where clause
char *Quote; // The identifier quoting character

View file

@ -290,14 +290,14 @@ bool STRING::Set(char *s, uint n)
} // end of Set
/***********************************************************************/
/* Append a char* to a STRING. */
/* Append a char* to a STRING. */
/***********************************************************************/
bool STRING::Append(const char *s, uint ln)
bool STRING::Append(const char *s, uint ln, bool nq)
{
if (!s)
return false;
uint len = Length + ln + 1;
uint i, len = Length + ln + 1;
if (len > Size) {
char *p = Realloc(len);
@ -311,8 +311,22 @@ bool STRING::Append(const char *s, uint ln)
} // endif n
strncpy(Strp + Length, s, ln);
Length = len - 1;
if (nq) {
for (i = 0; i < ln; i++)
switch (s[i]) {
case '\\': Strp[Length++] = '\\'; Strp[Length++] = '\\'; break;
case '\0': Strp[Length++] = '\\'; Strp[Length++] = '0'; break;
case '\'': Strp[Length++] = '\\'; Strp[Length++] = '\''; break;
case '\n': Strp[Length++] = '\\'; Strp[Length++] = 'n'; break;
case '\r': Strp[Length++] = '\\'; Strp[Length++] = 'r'; break;
case '\032': Strp[Length++] = '\\'; Strp[Length++] = 'Z'; break;
default: Strp[Length++] = s[i];
} // endswitch s[i]
} else
for (i = 0; i < ln && s[i]; i++)
Strp[Length++] = s[i];
Strp[Length] = 0;
return false;
} // end of Append

View file

@ -134,7 +134,7 @@ class DllExport STRING : public BLOCK {
inline void Reset(void) {*Strp = 0;}
bool Set(PSZ s);
bool Set(char *s, uint n);
bool Append(const char *s, uint ln);
bool Append(const char *s, uint ln, bool nq = false);
bool Append(PSZ s);
bool Append(STRING &str);
bool Append(char c);

View file

@ -19,6 +19,7 @@
#include "m_ctype.h"
typedef class CMD *PCMD;
typedef struct st_key_range key_range;
// Commands executed by XDBC and MYX tables
class CMD : public BLOCK {
@ -32,12 +33,24 @@ class CMD : public BLOCK {
}; // end of class CMD
// Condition filter structure
typedef struct _cond_filter {
char *Body;
OPVAL Op;
PCMD Cmds;
} CONDFIL, *PCFIL;
class CONDFIL : public BLOCK {
public:
// Constructor
CONDFIL(const Item *cond, uint idx, AMT type)
{
Cond = cond; Idx = idx; Type = type; Body = NULL; Op = OP_XX; Cmds = NULL;
}
// Members
const Item *Cond;
AMT Type;
uint Idx;
char *Body;
OPVAL Op;
PCMD Cmds;
}; // end of class CONDFIL
typedef class CONDFIL *PCFIL;
typedef class TDBCAT *PTDBCAT;
typedef class CATCOL *PCATCOL;
@ -109,7 +122,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
virtual int DeleteDB(PGLOBAL, int) = 0;
virtual void CloseDB(PGLOBAL) = 0;
virtual int CheckWrite(PGLOBAL) {return 0;}
virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int) = 0;
virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *) = 0;
protected:
// Members
@ -188,7 +201,7 @@ class DllExport TDBASE : public TDB {
virtual void MarkDB(PGLOBAL g, PTDB tdb2);
virtual int MakeIndex(PGLOBAL g, PIXDEF, bool)
{strcpy(g->Message, "Remote index"); return RC_INFO;}
virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int)
virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *)
{assert(false); return true;}
protected: