summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqldump.c1
-rwxr-xr-xmysql-test/mysql-test-run.pl1
-rw-r--r--mysql-test/valgrind.supp92
-rw-r--r--storage/connect/colblk.cpp2
-rw-r--r--storage/connect/domdoc.cpp16
-rw-r--r--storage/connect/domdoc.h3
-rw-r--r--storage/connect/ha_connect.cc187
-rw-r--r--storage/connect/ha_connect.h3
-rw-r--r--storage/connect/libdoc.cpp173
-rw-r--r--storage/connect/myconn.cpp4
-rw-r--r--storage/connect/myconn.h1
-rw-r--r--storage/connect/mysql-test/connect/r/mysql.result21
-rw-r--r--storage/connect/mysql-test/connect/r/xml.result2
-rw-r--r--storage/connect/mysql-test/connect/t/mysql.test16
-rw-r--r--storage/connect/mysql-test/connect/t/mysql_grant.test156
-rw-r--r--storage/connect/odbccat.h2
-rw-r--r--storage/connect/odbconn.cpp327
-rw-r--r--storage/connect/odbconn.h8
-rw-r--r--storage/connect/os.h2
-rw-r--r--storage/connect/plgdbsem.h2
-rw-r--r--storage/connect/plgdbutl.cpp28
-rw-r--r--storage/connect/plgxml.h1
-rw-r--r--storage/connect/tabfmt.cpp2
-rwxr-xr-xstorage/connect/tabmul.cpp8
-rw-r--r--storage/connect/tabmysql.cpp268
-rw-r--r--storage/connect/tabmysql.h88
-rw-r--r--storage/connect/tabodbc.cpp424
-rw-r--r--storage/connect/tabodbc.h93
-rw-r--r--storage/connect/tabutil.cpp13
-rw-r--r--storage/connect/tabutil.h3
-rw-r--r--storage/connect/tabxcl.cpp6
-rw-r--r--storage/connect/tabxcl.h2
-rw-r--r--storage/connect/tabxml.cpp38
-rw-r--r--storage/connect/tabxml.h4
-rw-r--r--storage/connect/valblk.cpp9
35 files changed, 1703 insertions, 303 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 2c84b89b3e2..1d57b29c897 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -5147,6 +5147,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
if (!opt_no_data &&
(!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") ||
!strcmp(table_type,"MRG_ISAM") ||
+ !strcmp(table_type,"CONNECT") ||
!strcmp(table_type,"FEDERATED")))
result= IGNORE_DATA;
}
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index 1400f088870..a7a53166563 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -379,6 +379,7 @@ sub main {
mtr_report("Logging: $0 ", join(" ", @ARGV));
$DEFAULT_SUITES.= ',' . join(',', qw(
+ connect
query_response_time
sequence
spider
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index b09ab0904e7..754991e9014 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -1117,3 +1117,95 @@
fun:_nss_dns_gethostbyaddr_r
fun:gethostbyaddr_r
}
+
+
+{
+ ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ ...
+ obj:*/libodbc.so*
+ fun:_ZN7ODBConn10GetDriversEP7_qryres
+}
+
+{
+ ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers
+ Memcheck:Leak
+ fun:calloc
+ ...
+ obj:*/libodbc.so*
+ fun:_ZN7ODBConn10GetDriversEP7_qryres
+}
+
+{
+ ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ ...
+ obj:*/libodbc.so*
+ fun:_ZN7ODBConn14GetDataSourcesEP7_qryres
+}
+
+
+{
+ ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers
+ Memcheck:Leak
+ fun:calloc
+ ...
+ obj:*/libodbc.so*
+ fun:_ZN7ODBConn14GetDataSourcesEP7_qryres
+}
+
+
+{
+ ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ ...
+ obj:*/libodbc.so*
+ fun:SQLDriverConnect
+ fun:_ZN7ODBConn7ConnectEj
+ fun:_ZN7ODBConn4OpenEPcj
+ fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
+ fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
+}
+
+{
+ ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
+ Memcheck:Leak
+ fun:calloc
+ ...
+ obj:*/libodbc.so*
+ fun:SQLDriverConnect
+ fun:_ZN7ODBConn7ConnectEj
+ fun:_ZN7ODBConn4OpenEPcj
+ fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
+ fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
+}
+
+{
+ ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
+ Memcheck:Leak
+ fun:malloc
+ ...
+ obj:*/libodbc.so*
+ fun:SQLDriverConnect
+ fun:_ZN7ODBConn7ConnectEj
+ fun:_ZN7ODBConn4OpenEPcj
+ fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
+ fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
+}
+
+{
+ ConnectSE: unixODBC dlopen leaves some "still reachable"
+ Memcheck:Leak
+ fun:malloc
+ fun:expand_dynamic_string_token
+ ...
+ obj:*/libltdl.so*
+ ...
+ obj:*/libodbc.so*
+}
diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp
index a3206243cc1..64cccfced7f 100644
--- a/storage/connect/colblk.cpp
+++ b/storage/connect/colblk.cpp
@@ -178,7 +178,7 @@ bool COLBLK::InitValue(PGLOBAL g)
(To_Tdb) ? To_Tdb->GetCat() : NULL)))
return true;
- Status = BUF_READY;
+ AddStatus(BUF_READY);
Value->SetNullable(Nullable);
#ifdef DEBTRACE
diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp
index 95426be97bc..0272a1d709a 100644
--- a/storage/connect/domdoc.cpp
+++ b/storage/connect/domdoc.cpp
@@ -592,6 +592,18 @@ PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
} // end of GetItem
+/******************************************************************/
+/* Reset the pointer on the deleted item. */
+/******************************************************************/
+bool DOMNODELIST::DropItem(PGLOBAL g, int n)
+ {
+ if (Listp == NULL || Listp->length <= n)
+ return true;
+
+//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set'
+ return false;
+ } // end of DeleteItem
+
/* ----------------------- class DOMATTR ------------------------ */
/******************************************************************/
@@ -618,8 +630,8 @@ bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len)
Len = len;
} // endif len
- if (!MultiByteToWideChar(CP_ACP, 0, txtp, strlen(txtp) + 1,
- Ws, Len + 1)) {
+ if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
+ Ws, Len + 1)) {
sprintf(g->Message, MSG(WS_CONV_ERR), txtp);
return true;
} // endif
diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h
index 0fd0a58ffdb..b0bcc1478a5 100644
--- a/storage/connect/domdoc.h
+++ b/storage/connect/domdoc.h
@@ -104,8 +104,9 @@ class DOMNODELIST : public XMLNODELIST {
friend class DOMNODE;
public:
// Methods
- virtual int GetLength(void) {return Listp->length;}
+ virtual int GetLength(void) {return Listp->length;}
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
+ virtual bool DropItem(PGLOBAL g, int n);
protected:
// Constructor
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index 57770567cd6..a066c48bf0e 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -165,7 +165,7 @@ extern "C" char nmfile[];
extern "C" char pdebug[];
extern "C" {
- char version[]= "Version 1.01.0007 July 26, 2013";
+ char version[]= "Version 1.01.0008 August 18, 2013";
#if defined(XMSG)
char msglang[]; // Default message language
@@ -1093,7 +1093,8 @@ PTDB ha_connect::GetTDB(PGLOBAL g)
if (!xp->CheckQuery(valid_query_id) && tdbp
&& !stricmp(tdbp->GetName(), table_name)
- && tdbp->GetMode() == xmod) {
+ && (tdbp->GetMode() == xmod
+ || tdbp->GetAmType() == TYPE_AM_XML)) {
tp= tdbp;
tp->SetMode(xmod);
} else if ((tp= CntGetTDB(g, table_name, xmod, this)))
@@ -1132,13 +1133,14 @@ bool ha_connect::OpenTable(PGLOBAL g, bool del)
break;
} // endswitch xmode
- if (xmod != MODE_INSERT) {
+ if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC
+ || tdbp->GetAmType() == TYPE_AM_MYSQL) {
// Get the list of used fields (columns)
char *p;
unsigned int k1, k2, n1, n2;
Field* *field;
Field* fp;
- MY_BITMAP *map= table->read_set;
+ MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
k1= k2= 0;
@@ -1373,7 +1375,8 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
fp->option_struct->special)
continue; // Is a virtual column possible here ???
- if (xmod == MODE_INSERT ||
+ if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
+ && tdbp->GetAmType() != TYPE_AM_ODBC) ||
bitmap_is_set(table->write_set, fp->field_index)) {
for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
if (!stricmp(colp->GetName(), fp->field_name))
@@ -2658,8 +2661,9 @@ int ha_connect::delete_all_rows()
PGLOBAL g= xp->g;
DBUG_ENTER("ha_connect::delete_all_rows");
- // Close and reopen the table so it will be deleted
- rc= CloseTable(g);
+ if (tdbp && tdbp->GetAmType() != TYPE_AM_XML)
+ // Close and reopen the table so it will be deleted
+ rc= CloseTable(g);
if (!(OpenTable(g))) {
if (CntDeleteRow(g, tdbp, true)) {
@@ -3462,7 +3466,7 @@ static bool add_field(String *sql, const char *field_name, int typ, int len,
{
bool error= false;
const char *type= PLGtoMYSQLtype(typ, dbf);
- type= PLGtoMYSQLtype(typ, true);
+// type= PLGtoMYSQLtype(typ, true); ?????
error|= sql->append('`');
error|= sql->append(field_name);
@@ -3612,6 +3616,7 @@ static int init_table_share(THD *thd,
static int init_table_share(THD* thd,
TABLE_SHARE *table_s,
HA_CREATE_INFO *create_info,
+// char *dsn,
String *sql)
{
bool oom= false;
@@ -3670,10 +3675,12 @@ static int init_table_share(THD* thd,
} // endfor opt
if (create_info->connect_string.length) {
+//if (dsn) {
oom|= sql->append(' ');
oom|= sql->append("CONNECTION='");
oom|= sql->append_for_single_quote(create_info->connect_string.str,
create_info->connect_string.length);
+// oom|= sql->append_for_single_quote(dsn, strlen(dsn));
oom|= sql->append('\'');
if (oom)
@@ -3715,6 +3722,25 @@ static void add_option(THD* thd, HA_CREATE_INFO *create_info,
#endif // NEW_WAY
} // end of add_option
+// Used to check whether a MYSQL table is created on itself
+static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
+ const char *db, char *tab, const char *src, int port)
+{
+ if (src)
+ return false;
+ else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
+ return false;
+ else if (db && stricmp(db, s->db.str))
+ return false;
+ else if (tab && stricmp(tab, s->table_name.str))
+ return false;
+ else if (port && port != (signed)GetDefaultPort())
+ return false;
+
+ strcpy(g->Message, "This MySQL table is defined on itself");
+ return true;
+} // end of CheckSelf
+
/**
@brief
connect_assisted_discovery() is called when creating a table with no columns.
@@ -3733,13 +3759,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
{
char spc= ',', qch= 0;
const char *fncn= "?";
- const char *user, *fn, *db, *host, *pwd, *prt, *sep, *tbl, *src;
+ const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src;
const char *col, *ocl, *rnk, *pic, *fcl;
char *tab, *dsn;
#if defined(WIN32)
char *nsp= NULL, *cls= NULL;
#endif // WIN32
- int port= 0, hdr= 0, mxr= 0, rc= 0;
+ int port= 0, hdr= 0, mxr= 0, rc= 0, cop= 0;
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
bool bif, ok= false, dbf= false;
TABTYPE ttp= TAB_UNDEF;
@@ -3760,7 +3786,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (!g)
return HA_ERR_INTERNAL_ERROR;
- user= host= pwd= prt= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
+ user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
// Get the useful create options
ttp= GetTypeID(topt->type);
@@ -3778,23 +3804,23 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
col= topt->colist;
if (topt->oplist) {
- host= GetListOption(g,"host", topt->oplist, "localhost");
- user= GetListOption(g,"user", topt->oplist, "root");
+ host= GetListOption(g, "host", topt->oplist, "localhost");
+ user= GetListOption(g, "user", topt->oplist, "root");
// Default value db can come from the DBNAME=xxx option.
- db= GetListOption(g,"database", topt->oplist, db);
- col= GetListOption(g,"colist", topt->oplist, col);
- ocl= GetListOption(g,"occurcol", topt->oplist, NULL);
- pic= GetListOption(g,"pivotcol", topt->oplist, NULL);
- fcl= GetListOption(g,"fnccol", topt->oplist, NULL);
- rnk= GetListOption(g,"rankcol", topt->oplist, NULL);
- pwd= GetListOption(g,"password", topt->oplist);
- prt= GetListOption(g,"port", topt->oplist);
- port= (prt) ? atoi(prt) : 0;
+ db= GetListOption(g, "database", topt->oplist, db);
+ col= GetListOption(g, "colist", topt->oplist, col);
+ ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
+ pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
+ fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
+ rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
+ pwd= GetListOption(g, "password", topt->oplist);
#if defined(WIN32)
- nsp= GetListOption(g,"namespace", topt->oplist);
- cls= GetListOption(g,"class", topt->oplist);
+ nsp= GetListOption(g, "namespace", topt->oplist);
+ cls= GetListOption(g, "class", topt->oplist);
#endif // WIN32
+ port= atoi(GetListOption(g, "port", topt->oplist, "0"));
mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
+ cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
} else {
host= "localhost";
user= "root";
@@ -3840,7 +3866,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} // endif p
} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
- tab= (char*)create_info->alias;
+ tab= table_s->table_name.str; // Default value
#if defined(NEW_WAY)
add_option(thd, create_info, "tabname", tab);
@@ -3850,8 +3876,18 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
switch (ttp) {
#if defined(ODBC_SUPPORT)
case TAB_ODBC:
- if (!(dsn= create_info->connect_string.str)
- && !(fnc & (FNC_DSN | FNC_DRIVER)))
+ dsn= create_info->connect_string.str;
+
+ if (fnc & (FNC_DSN | FNC_DRIVER))
+ ok= true;
+ else if (!stricmp(thd->main_security_ctx.host, "localhost")
+ && cop == 1) {
+ if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
+ thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
+ ok= true;
+ } // endif dsn
+
+ } else if (!dsn)
sprintf(g->Message, "Missing %s connection string", topt->type);
else
ok= true;
@@ -3873,31 +3909,46 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case TAB_MYSQL:
ok= true;
- if ((dsn= create_info->connect_string.str)) {
+ if (create_info->connect_string.str) {
+ int len= create_info->connect_string.length;
PMYDEF mydef= new(g) MYSQLDEF();
PDBUSER dup= PlgGetUser(g);
PCATLG cat= (dup) ? dup->Catalog : NULL;
- dsn= (char*)PlugSubAlloc(g, NULL, strlen(dsn) + 1);
- strncpy(dsn, create_info->connect_string.str,
- create_info->connect_string.length);
- dsn[create_info->connect_string.length]= 0;
+ dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
+ strncpy(dsn, create_info->connect_string.str, len);
+ dsn[len]= 0;
mydef->SetName(create_info->alias);
mydef->SetCat(cat);
- if (!mydef->ParseURL(g, dsn)) {
- host= mydef->GetHostname();
- user= mydef->GetUsername();
- pwd= mydef->GetPassword();
- db= mydef->GetDatabase();
- tab= mydef->GetTabname();
- port= mydef->GetPortnumber();
+ if (!mydef->ParseURL(g, dsn, false)) {
+ if (mydef->GetHostname())
+ host= mydef->GetHostname();
+
+ if (mydef->GetUsername())
+ user= mydef->GetUsername();
+
+ if (mydef->GetPassword())
+ pwd= mydef->GetPassword();
+
+ if (mydef->GetDatabase())
+ db= mydef->GetDatabase();
+
+ if (mydef->GetTabname())
+ tab= mydef->GetTabname();
+
+ if (mydef->GetPortnumber())
+ port= mydef->GetPortnumber();
+
} else
ok= false;
} else if (!user)
user= "root";
+ if (CheckSelf(g, table_s, host, db, tab, src, port))
+ ok= false;
+
break;
#endif // MYSQL_SUPPORT
#if defined(WIN32)
@@ -3946,7 +3997,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
else
return HA_ERR_INTERNAL_ERROR; // Should never happen
- if (src && ttp != TAB_PIVOT) {
+ if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
qrp= SrcColumns(g, host, db, user, pwd, src, port);
if (qrp && ttp == TAB_OCCUR)
@@ -3964,7 +4015,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
switch (fnc) {
case FNC_NO:
case FNC_COL:
- qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL);
+ if (src) {
+ qrp= ODBCSrcCols(g, dsn, (char*)src);
+ src= NULL; // for next tests
+ } else
+ qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL);
+
break;
case FNC_TABLE:
qrp= ODBCTables(g, dsn, (char *) tab, true);
@@ -4130,6 +4186,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
#else // !NEW_WAY
if (!rc)
rc= init_table_share(thd, table_s, create_info, &sql);
+// rc= init_table_share(thd, table_s, create_info, dsn, &sql);
#endif // !NEW_WAY
return rc;
@@ -4250,6 +4307,54 @@ int ha_connect::create(const char *name, TABLE *table_arg,
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
} // endif tabname
+ case TAB_MYSQL:
+ {const char *src= options->srcdef;
+ char *host, *db, *tab= (char*)options->tabname;
+ int port;
+
+ host= GetListOption(g, "host", options->oplist, NULL);
+ db= GetListOption(g, "database", options->oplist, NULL);
+ port= atoi(GetListOption(g, "port", options->oplist, "0"));
+
+ if (create_info->connect_string.str) {
+ char *dsn;
+ int len= create_info->connect_string.length;
+ PMYDEF mydef= new(g) MYSQLDEF();
+ PDBUSER dup= PlgGetUser(g);
+ PCATLG cat= (dup) ? dup->Catalog : NULL;
+
+ dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
+ strncpy(dsn, create_info->connect_string.str, len);
+ dsn[len]= 0;
+ mydef->SetName(create_info->alias);
+ mydef->SetCat(cat);
+
+ if (!mydef->ParseURL(g, dsn, false)) {
+ if (mydef->GetHostname())
+ host= mydef->GetHostname();
+
+ if (mydef->GetDatabase())
+ db= mydef->GetDatabase();
+
+ if (mydef->GetTabname())
+ tab= mydef->GetTabname();
+
+ if (mydef->GetPortnumber())
+ port= mydef->GetPortnumber();
+
+ } else {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif ParseURL
+
+ } // endif connect_string
+
+ if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif CheckSelf
+
+ }break;
default: /* do nothing */;
break;
} // endswitch ttp
diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h
index 8fc88098794..35daf2e5c19 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -208,7 +208,8 @@ public:
return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS |
HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
HA_NO_COPY_ON_ALTER | HA_CAN_VIRTUAL_COLUMNS |
- /*HA_NULL_IN_KEY |*/ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE);
+ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
+ /*HA_NULL_IN_KEY |*/ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN);
}
/** @brief
diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp
index 45f379350f5..31aa90da076 100644
--- a/storage/connect/libdoc.cpp
+++ b/storage/connect/libdoc.cpp
@@ -14,6 +14,10 @@
#include "my_global.h"
//#endif // !WIN32
+#if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
+#error "tree support not compiled in"
+#endif
+
#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED)
#error "XPath not supported"
#endif
@@ -47,8 +51,6 @@ typedef struct _x2block { /* Loaded XML file block */
short Type; /* TYPE_FB_XML */
int Retcode; /* Return code from Load */
xmlDocPtr Docp; /* Document interface pointer */
-// xmlXPathContextPtr Ctxp;
-// xmlXPathObjectPtr Xop;
} X2BLOCK, *PX2BLOCK;
/******************************************************************/
@@ -91,6 +93,8 @@ class LIBXMLDOC : public XMLDOCUMENT {
xmlNodeSetPtr Nlist;
xmlXPathContextPtr Ctxp;
xmlXPathObjectPtr Xop;
+ xmlXPathObjectPtr NlXop;
+ xmlErrorPtr Xerr;
char *Buf; // Temporary
bool Nofreelist;
}; // end of class LIBXMLDOC
@@ -141,6 +145,7 @@ class XML2NODELIST : public XMLNODELIST {
// Methods
virtual int GetLength(void);
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
+ virtual bool DropItem(PGLOBAL g, int n);
protected:
// Constructor
@@ -180,6 +185,23 @@ extern int trace;
} // "C"
#if defined(MEMORY_TRACE)
+static int m = 0;
+static char s[500];
+/**************************************************************************/
+/* Tracing output function. */
+/**************************************************************************/
+void xtrc(char const *fmt, ...)
+ {
+ va_list ap;
+ va_start (ap, fmt);
+
+//vfprintf(stderr, fmt, ap);
+ vsprintf(s, fmt, ap);
+ if (s[strlen(s)-1] == '\n')
+ s[strlen(s)-1] = 0;
+ va_end (ap);
+ } // end of htrc
+
static xmlFreeFunc Free;
static xmlMallocFunc Malloc;
static xmlMallocFunc MallocA;
@@ -188,42 +210,53 @@ static xmlStrdupFunc Strdup;
void xmlMyFree(void *mem)
{
- if (trace)
- htrc("Freeing at %p\n", mem);
+ if (trace) {
+ htrc("%.4d Freeing at %p %s\n", ++m, mem, s);
+ *s = 0;
+ } // endif trace
Free(mem);
} // end of xmlMyFree
void *xmlMyMalloc(size_t size)
{
void *p = Malloc(size);
- if (trace)
- htrc("Allocating %.5d at %p\n", size, p);
+ if (trace) {
+ htrc("%.4d Allocating %.5d at %p %s\n", ++m, size, p, s);
+ *s = 0;
+ } // endif trace
return p;
} // end of xmlMyMalloc
void *xmlMyMallocAtomic(size_t size)
{
void *p = MallocA(size);
- if (trace)
- htrc("Atom alloc %.5d at %p\n", size, p);
+ if (trace) {
+ htrc("%.4d Atom alloc %.5d at %p %s\n", ++m, size, p, s);
+ *s = 0;
+ } // endif trace
return p;
} // end of xmlMyMallocAtomic
void *xmlMyRealloc(void *mem, size_t size)
{
void *p = Realloc(mem, size);
- if (trace)
- htrc("ReAlloc %.5d to %p from %p\n", size, p, mem);
+ if (trace) {
+ htrc("%.4d ReAlloc %.5d to %p from %p %s\n", ++m, size, p, mem, s);
+ *s = 0;
+ } // endif trace
return p;
} // end of xmlMyRealloc
char *xmlMyStrdup(const char *str)
{
char *p = Strdup(str);
- if (trace)
- htrc("Duplicating to %p from %p %s\n", p, str, str);
+ if (trace) {
+ htrc("%.4d Duplicating to %p from %p %s %s\n", ++m, p, str, str, s);
+ *s = 0;
+ } // endif trace
return p;
} // end of xmlMyStrdup
+#define htrc xtrc
#endif // MEMORY_TRACE
/******************************************************************/
@@ -295,6 +328,8 @@ LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
Nlist = NULL;
Ctxp = NULL;
Xop = NULL;
+ NlXop = NULL;
+ Xerr = NULL;
Buf = NULL;
Nofreelist = false;
} // end of LIBXMLDOC constructor
@@ -321,9 +356,10 @@ bool LIBXMLDOC::ParseFile(char *fn)
Encoding = (char*)Docp->encoding;
return false;
- } else
- return true;
+ } else if ((Xerr = xmlGetLastError()))
+ xmlResetError(Xerr);
+ return true;
} // end of ParseFile
/******************************************************************/
@@ -344,8 +380,6 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
xp->Length = (m == MODE_READ) ? 1 : 0;
xp->Retcode = rc;
xp->Docp = Docp;
-// xp->Ctxp = Ctxp;
-// xp->Xop = Xop;
// Return xp as a fp
return (PFBLOCK)xp;
@@ -356,6 +390,9 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
/******************************************************************/
bool LIBXMLDOC::NewDoc(PGLOBAL g, char *ver)
{
+ if (trace)
+ htrc("NewDoc\n");
+
return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL);
} // end of NewDoc
@@ -462,8 +499,7 @@ int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn)
if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) {
xmlErrorPtr err = xmlGetLastError();
- strcpy(g->Message, (err) ? err->message : "Error saving XML doc"
- );
+ strcpy(g->Message, (err) ? err->message : "Error saving XML doc");
rc = -1;
} // endif Save
// rc = xmlDocDump(of, Docp);
@@ -497,17 +533,44 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
if (trace)
htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
- if (xp && xp->Count == 1) {
- if (Nlist)
+//if (xp && xp->Count == 1) {
+ if (Nlist) {
xmlXPathFreeNodeSet(Nlist);
- if (Xop)
+ if ((Xerr = xmlGetLastError()))
+ xmlResetError(Xerr);
+
+ Nlist = NULL;
+ } // endif Nlist
+
+ if (Xop) {
xmlXPathFreeObject(Xop);
- if (Ctxp)
+ if ((Xerr = xmlGetLastError()))
+ xmlResetError(Xerr);
+
+ Xop = NULL;
+ } // endif Xop
+
+ if (NlXop) {
+ xmlXPathFreeObject(NlXop);
+
+ if ((Xerr = xmlGetLastError()))
+ xmlResetError(Xerr);
+
+ NlXop = NULL;
+ } // endif NlXop
+
+ if (Ctxp) {
xmlXPathFreeContext(Ctxp);
- } // endif Count
+ if ((Xerr = xmlGetLastError()))
+ xmlResetError(Xerr);
+
+ Ctxp = NULL;
+ } // endif Ctxp
+
+// } // endif Count
CloseXML2File(g, xp, false);
} // end of Close
@@ -560,18 +623,29 @@ xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp)
} // endfor nsp
- } else {
+ } // endif Ctxp
+
+ if (Xop) {
if (trace)
- htrc("Calling xmlXPathFreeNodeSetList Xop=%p\n", Xop);
+ htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n",
+ Xop, Nofreelist);
if (Nofreelist) {
// Making Nlist that must not be freed yet
- xmlXPathFreeNodeSetList(Xop); // Caused memory leak
+// xmlXPathFreeNodeSetList(Xop); // Caused memory leak
+ assert(!NlXop);
+ NlXop = Xop; // Freed on closing
Nofreelist = false;
} else
xmlXPathFreeObject(Xop); // Caused node not found
- } // endif Ctxp
+ if ((Xerr = xmlGetLastError())) {
+ strcpy(g->Message, Xerr->message);
+ xmlResetError(Xerr);
+ return NULL;
+ } // endif Xerr
+
+ } // endif Xop
// Set the context to the calling node
Ctxp->node = np;
@@ -990,6 +1064,8 @@ void XML2NODE::AddText(PGLOBAL g, char *txtp)
/******************************************************************/
void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
{
+ xmlErrorPtr xerr;
+
if (trace)
htrc("DeleteChild: node=%p\n", dnp);
@@ -999,12 +1075,39 @@ void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
// This is specific to row nodes
if (text && text->type == XML_TEXT_NODE) {
xmlUnlinkNode(text);
+
+ if ((xerr = xmlGetLastError()))
+ goto err;
+
xmlFreeNode(text);
+
+ if ((xerr = xmlGetLastError()))
+ goto err;
+
} // endif type
xmlUnlinkNode(np);
+
+ if ((xerr = xmlGetLastError()))
+ goto err;
+
xmlFreeNode(np);
+
+ if ((xerr = xmlGetLastError()))
+ goto err;
+
Delete(dnp);
+
+ if ((xerr = xmlGetLastError()))
+ goto err;
+
+ return;
+
+err:
+ if (trace)
+ htrc("DeleteChild: errmsg=%s\n", xerr->message);
+
+ xmlResetError(xerr);
} // end of DeleteChild
/* -------------------- class XML2NODELIST ---------------------- */
@@ -1045,6 +1148,22 @@ PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
} // end of GetItem
+/******************************************************************/
+/* Reset the pointer on the deleted item. */
+/******************************************************************/
+bool XML2NODELIST::DropItem(PGLOBAL g, int n)
+ {
+ if (trace)
+ htrc("DropItem: n=%d\n", n);
+
+ // We should do something here
+ if (!Listp || Listp->nodeNr <= n)
+ return true;
+
+ Listp->nodeTab[n] = NULL; // This was causing Valgrind warning
+ return false;
+ } // end of DropItem
+
/* ---------------------- class XML2ATTR ------------------------ */
/******************************************************************/
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index ea0425d3fae..e2de93e8fc8 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -89,7 +89,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db,
static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32};
char *fld, *fmt, cmd[128];
int i, n, nf, ncol = sizeof(buftyp) / sizeof(int);
- int len, type, prec, rc, k = 0;
+ int len, type, prec, rc, k = 0;
PQRYRES qrp;
PCOLRES crp;
MYSQLC myc;
@@ -166,7 +166,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db,
// Get type, type name, and precision
fld = myc.GetCharField(1);
prec = 0;
- len = 256; // Default for text or blob
+ len = 255; // Default for text or blob
if ((nf = sscanf(fld, "%[^(](%d,%d", cmd, &len, &prec)) < 1) {
sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld);
diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h
index f8c8c3dcbae..10ff76c3273 100644
--- a/storage/connect/myconn.h
+++ b/storage/connect/myconn.h
@@ -54,6 +54,7 @@ uint GetDefaultPort(void);
class DllItem MYSQLC {
friend class TDBMYSQL;
friend class MYSQLCOL;
+ friend class TDBMYEXC;
// Construction
public:
MYSQLC(void);
diff --git a/storage/connect/mysql-test/connect/r/mysql.result b/storage/connect/mysql-test/connect/r/mysql.result
index 784162cae33..c0ef487c111 100644
--- a/storage/connect/mysql-test/connect/r/mysql.result
+++ b/storage/connect/mysql-test/connect/r/mysql.result
@@ -217,3 +217,24 @@ t2 CREATE TABLE `t2` (
SELECT * FROM t2;
a
DROP TABLE t2, t1;
+#
+# MDEV-4877 mysqldump dumps all data from a connect table
+#
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20),(30);
+CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:PORT/test/t1';
+SELECT * FROM t2;
+a
+10
+20
+30
+# Start of mysqldump ------
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `t2` (
+ `a` int(11) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='mysql://root@localhost:PORT/test/t1' `TABLE_TYPE`='MYSQL';
+/*!40101 SET character_set_client = @saved_cs_client */;
+# End of mysqldump ------
+DROP TABLE t2;
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result
index 19b5c936e6c..ea915234203 100644
--- a/storage/connect/mysql-test/connect/r/xml.result
+++ b/storage/connect/mysql-test/connect/r/xml.result
@@ -413,7 +413,7 @@ DROP TABLE t1;
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/t1.xml');
SELECT CAST(@a AS CHAR CHARACTER SET latin1);
CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Created by CONNECT Version 1.01.0007 July 26, 2013 -->
+<!-- Created by CONNECT Version 1.01.0008 August 18, 2013 -->
<t1>
<line>
<node>ÀÃÂÃ</node>
diff --git a/storage/connect/mysql-test/connect/t/mysql.test b/storage/connect/mysql-test/connect/t/mysql.test
index bb4b2e78e56..673df3fcb54 100644
--- a/storage/connect/mysql-test/connect/t/mysql.test
+++ b/storage/connect/mysql-test/connect/t/mysql.test
@@ -10,7 +10,7 @@ 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='t1' OPTION_LIST='host=localhost,user=root,port=$PORT'
+--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'
@@ -420,3 +420,17 @@ DROP TABLE t2, t1;
# TODO: add test for YEAR
# TODO: add tests for fractional seconds
+--echo #
+--echo # MDEV-4877 mysqldump dumps all data from a connect table
+--echo #
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20),(30);
+--replace_result $PORT PORT
+--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:$PORT/test/t1'
+SELECT * FROM t2;
+--echo # Start of mysqldump ------
+--replace_result $PORT PORT
+--exec $MYSQL_DUMP --compact test t2
+--echo # End of mysqldump ------
+DROP TABLE t2;
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/t/mysql_grant.test b/storage/connect/mysql-test/connect/t/mysql_grant.test
index e825e70a84b..8eb6a90c917 100644
--- a/storage/connect/mysql-test/connect/t/mysql_grant.test
+++ b/storage/connect/mysql-test/connect/t/mysql_grant.test
@@ -1,78 +1,78 @@
--- source include/not_embedded.inc
-
-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='t1' 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 # Testing FILE privilege
---echo #
-GRANT ALL PRIVILEGES ON *.* TO user@localhost;
-REVOKE FILE ON *.* FROM user@localhost;
---connect(user,localhost,user,,)
---connection user
-SELECT user();
---replace_result $PORT PORT
---error ER_ACCESS_DENIED_ERROR
---eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'
---connection default
-SELECT user();
-CREATE TABLE t1remote (a INT NOT NULL);
-INSERT INTO t1remote VALUES (10),(20),(30);
---replace_result $PORT PORT
---eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'
-SELECT * FROM t1;
---connection user
-SELECT user();
---error ER_ACCESS_DENIED_ERROR
-SELECT * FROM t1;
---error ER_ACCESS_DENIED_ERROR
-INSERT INTO t1 VALUES ('xxx');
---error ER_ACCESS_DENIED_ERROR
-DELETE FROM t1 WHERE a='xxx';
---error ER_ACCESS_DENIED_ERROR
-UPDATE t1 SET a='yyy' WHERE a='xxx';
---error ER_ACCESS_DENIED_ERROR
-TRUNCATE TABLE t1;
---error ER_ACCESS_DENIED_ERROR
-ALTER TABLE t1 READONLY=1;
---error ER_ACCESS_DENIED_ERROR
-CREATE VIEW v1 AS SELECT * FROM t1;
-
---echo # Testing a VIEW created with FILE privileges but accessed with no FILE
---connection default
-SELECT user();
-CREATE VIEW v1 AS SELECT * FROM t1;
---connection user
-SELECT user();
---error ER_ACCESS_DENIED_ERROR
-SELECT * FROM v1;
---error ER_ACCESS_DENIED_ERROR
-INSERT INTO v1 VALUES (2);
---error ER_ACCESS_DENIED_ERROR
-UPDATE v1 SET a=123;
---error ER_ACCESS_DENIED_ERROR
-DELETE FROM v1;
-
---disconnect user
---connection default
-SELECT user();
-DROP VIEW v1;
-DROP TABLE t1, t1remote;
-DROP USER user@localhost;
---echo #
---echo # Testing FILE privileges done
---echo #
-
+-- source include/not_embedded.inc
+
+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 # Testing FILE privilege
+--echo #
+GRANT ALL PRIVILEGES ON *.* TO user@localhost;
+REVOKE FILE ON *.* FROM user@localhost;
+--connect(user,localhost,user,,)
+--connection user
+SELECT user();
+--replace_result $PORT PORT
+--error ER_ACCESS_DENIED_ERROR
+--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'
+--connection default
+SELECT user();
+CREATE TABLE t1remote (a INT NOT NULL);
+INSERT INTO t1remote VALUES (10),(20),(30);
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'
+SELECT * FROM t1;
+--connection user
+SELECT user();
+--error ER_ACCESS_DENIED_ERROR
+SELECT * FROM t1;
+--error ER_ACCESS_DENIED_ERROR
+INSERT INTO t1 VALUES ('xxx');
+--error ER_ACCESS_DENIED_ERROR
+DELETE FROM t1 WHERE a='xxx';
+--error ER_ACCESS_DENIED_ERROR
+UPDATE t1 SET a='yyy' WHERE a='xxx';
+--error ER_ACCESS_DENIED_ERROR
+TRUNCATE TABLE t1;
+--error ER_ACCESS_DENIED_ERROR
+ALTER TABLE t1 READONLY=1;
+--error ER_ACCESS_DENIED_ERROR
+CREATE VIEW v1 AS SELECT * FROM t1;
+
+--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--connection default
+SELECT user();
+CREATE VIEW v1 AS SELECT * FROM t1;
+--connection user
+SELECT user();
+--error ER_ACCESS_DENIED_ERROR
+SELECT * FROM v1;
+--error ER_ACCESS_DENIED_ERROR
+INSERT INTO v1 VALUES (2);
+--error ER_ACCESS_DENIED_ERROR
+UPDATE v1 SET a=123;
+--error ER_ACCESS_DENIED_ERROR
+DELETE FROM v1;
+
+--disconnect user
+--connection default
+SELECT user();
+DROP VIEW v1;
+DROP TABLE t1, t1remote;
+DROP USER user@localhost;
+--echo #
+--echo # Testing FILE privileges done
+--echo #
+
diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h
index 3665d1b283e..ba4074bf276 100644
--- a/storage/connect/odbccat.h
+++ b/storage/connect/odbccat.h
@@ -1,8 +1,10 @@
/***********************************************************************/
/* ODBC catalog function prototypes. */
/***********************************************************************/
+char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop);
PQRYRES ODBCDataSources(PGLOBAL g, bool info);
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
char *colpat, bool info);
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src);
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info);
PQRYRES ODBCDrivers(PGLOBAL g, bool info);
diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp
index 6129c93d388..0a19d90a422 100644
--- a/storage/connect/odbconn.cpp
+++ b/storage/connect/odbconn.cpp
@@ -1,5 +1,5 @@
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
-/* Name: ODBCONN.CPP Version 1.6 */
+/* Name: ODBCONN.CPP Version 1.7 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
/* */
@@ -13,6 +13,7 @@
#if defined(WIN32)
//nclude <io.h>
//nclude <fcntl.h>
+#include <direct.h> // for getcwd
#if defined(__BORLANDC__)
#define __MFC_COMPAT__ // To define min/max as macro
#endif
@@ -76,8 +77,9 @@ static short GetSQLType(int type)
case TYPE_SHORT: tp = SQL_SMALLINT; break;
case TYPE_INT: tp = SQL_INTEGER; break;
case TYPE_DATE: tp = SQL_TIMESTAMP; break;
- case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
+ case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
case TYPE_FLOAT: tp = SQL_DOUBLE; break;
+ case TYPE_TINY : tp = SQL_TINYINT; break;
} // endswitch type
return tp;
@@ -97,6 +99,7 @@ static int GetSQLCType(int type)
case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
case TYPE_FLOAT: tp = SQL_C_DOUBLE; break;
+ case TYPE_TINY : tp = SQL_C_TINYINT; break;
} // endswitch type
return tp;
@@ -127,9 +130,11 @@ int TranslateSQLType(int stp, int prec, int& len)
type = TYPE_INT;
break;
case SQL_SMALLINT: // 5
+ type = TYPE_SHORT;
+ break;
case SQL_TINYINT: // (-6)
case SQL_BIT: // (-7)
- type = TYPE_SHORT;
+ type = TYPE_TINY;
break;
case SQL_FLOAT: // 6
case SQL_REAL: // 7
@@ -168,10 +173,36 @@ int TranslateSQLType(int stp, int prec, int& len)
} // end of TranslateSQLType
/***********************************************************************/
-/* ODBConn static members initialization. */
+/* ODBCCheckConnection: Check completeness of connection string. */
/***********************************************************************/
-//HENV ODBConn::m_henv = SQL_NULL_HENV;
-//int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above
+char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop)
+ {
+ char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH];
+ int rc;
+ DWORD options = ODBConn::openReadOnly;
+ ODBConn *ocp = new(g) ODBConn(g, NULL);
+
+ (void) getcwd(dir, sizeof(dir) - 1);
+
+ switch (cop) {
+ case 1: options |= ODBConn::forceOdbcDialog; break;
+ case 2: options |= ODBConn::noOdbcDialog; break;
+ } // endswitch cop
+
+ if (ocp->Open(dsn, options) < 1)
+ newdsn = NULL;
+ else
+ newdsn = ocp->GetConnect();
+
+ (void) getcwd(buf, sizeof(buf) - 1);
+
+ // Some data sources change the current directory
+ if (strcmp(dir, buf))
+ rc = chdir(dir);
+
+ ocp->Close();
+ return newdsn; // Return complete connection string
+ } // end of ODBCCheckConnection
/***********************************************************************/
/* Allocate the structure used to refer to the result set. */
@@ -225,7 +256,6 @@ static void ResetNullValues(CATPARM *cap)
/***********************************************************************/
/* ODBCColumns: constructs the result blocks containing all columns */
/* of an ODBC table that will be retrieved by GetData commands. */
-/* Note: The first two columns (Qualifier, Owner) are ignored. */
/***********************************************************************/
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
char *colpat, bool info)
@@ -251,7 +281,7 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
if (!info) {
ocp = new(g) ODBConn(g, NULL);
- if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
+ if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog
return NULL;
// We fix a MySQL limit because some data sources return 32767
@@ -314,6 +344,17 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
return qrp;
} // end of ODBCColumns
+/**************************************************************************/
+/* ODBCSrcCols: constructs the result blocks containing the */
+/* description of all the columns of a Srcdef option. */
+/**************************************************************************/
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src)
+ {
+ ODBConn *ocp = new(g) ODBConn(g, NULL);
+
+ return ocp->GetMetaData(g, dsn, src);
+ } // end of ODBCSrcCols
+
#if 0
/**************************************************************************/
/* MyODBCCols: returns column info as required by ha_connect::pre_create. */
@@ -800,6 +841,17 @@ void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
} // end of BuildErrorMessage
+const char *DBX::GetErrorMessage(int i)
+ {
+ if (i < 0 || i >= MAX_NUM_OF_MSG)
+ return "No ODBC error";
+ else if (m_ErrMsg[i])
+ return m_ErrMsg[i];
+ else
+ return (m_Msg) ? m_Msg : "Unknown error";
+
+ } // end of GetErrorMessage
+
/***********************************************************************/
/* ODBConn construction/destruction. */
/***********************************************************************/
@@ -818,7 +870,7 @@ ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
m_Catver = (tdbp) ? tdbp->Catver : 0;
m_Connect = NULL;
m_Updatable = true;
-//m_Transactions = false;
+ m_Transact = false;
m_IDQuoteChar = '\'';
//*m_ErrMsg = '\0';
} // end of ODBConn
@@ -1061,7 +1113,7 @@ bool ODBConn::Connect(DWORD Options)
if (hWnd == NULL)
hWnd = GetDesktopWindow();
#else // !WIN32
- HWND hWnd = NULL;
+ HWND hWnd = 1;
#endif // !WIN32
PGLOBAL& g = m_G;
PDBUSER dup = PlgGetUser(g);
@@ -1204,8 +1256,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
void *buffer;
bool b;
UWORD n;
- SWORD ncol, len, tp;
- SQLLEN afrw;
+ SWORD len, tp, ncol = 0;
ODBCCOL *colp;
RETCODE rc;
HSTMT hstmt;
@@ -1240,26 +1291,44 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
if (trace)
htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
- do {
- rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
- } while (rc == SQL_STILL_EXECUTING);
+ if (m_Tdb->Srcdef) {
+ // Be sure this is a query returning a result set
+ do {
+ rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
+ } while (rc == SQL_STILL_EXECUTING);
- if (!Check(rc))
- ThrowDBX(rc, "SQLExecDirect", hstmt);
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLPrepare", hstmt);
- do {
- rc = SQLNumResultCols(hstmt, &ncol);
- } while (rc == SQL_STILL_EXECUTING);
+ if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
+ ThrowDBX(rc, "SQLNumResultCols", hstmt);
+
+ if (ncol == 0) {
+ strcpy(g->Message, "This Srcdef does not return a result set");
+ return -1;
+ } // endif ncol
+
+ // Ok, now we can proceed
+ do {
+ rc = SQLExecute(hstmt);
+ } while (rc == SQL_STILL_EXECUTING);
- if (ncol == 0) {
- // Update or Delete statement
- rc = SQLRowCount(hstmt, &afrw);
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLExecute", hstmt);
+
+ } else {
+ do {
+ rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
+ } while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
- ThrowDBX(rc, "SQLRowCount", hstmt);
+ ThrowDBX(rc, "SQLExecDirect", hstmt);
- return afrw;
- } // endif ncol
+ do {
+ rc = SQLNumResultCols(hstmt, &ncol);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ } // endif Srcdef
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial())
@@ -1407,10 +1476,33 @@ int ODBConn::PrepareSQL(char *sql)
{
PGLOBAL& g = m_G;
bool b;
+ UINT txn = 0;
SWORD nparm;
RETCODE rc;
HSTMT hstmt;
+ if (m_Tdb->GetMode() != MODE_READ) {
+ // Does the data source support transactions
+ rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
+
+ if (Check(rc) && txn != SQL_TC_NONE) try {
+ rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
+ SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
+
+ if (!Check(rc))
+ ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
+
+ m_Transact = true;
+ } catch(DBX *x) {
+ if (trace)
+ for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
+ htrc(x->m_ErrMsg[i]);
+
+ strcpy(g->Message, x->GetErrorMessage(0));
+ } // end try/catch
+
+ } // endif Mode
+
try {
b = false;
@@ -1450,13 +1542,19 @@ int ODBConn::PrepareSQL(char *sql)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ strcpy(g->Message, x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
rc = SQLFreeStmt(hstmt, SQL_DROP);
m_hstmt = NULL;
+
+ if (m_Transact) {
+ rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
+ m_Transact = false;
+ } // endif m_Transact
+
return -1;
} // end try/catch
@@ -1465,27 +1563,59 @@ int ODBConn::PrepareSQL(char *sql)
} // end of PrepareSQL
/***********************************************************************/
-/* Bind a parameter for inserting. */
+/* Execute a prepared statement. */
/***********************************************************************/
-bool ODBConn::ExecuteSQL(void)
+int ODBConn::ExecuteSQL(bool x)
{
- RETCODE rc;
+ PGLOBAL& g = m_G;
+ SWORD ncol = 0;
+ RETCODE rc;
+ SQLLEN afrw = -1;
try {
- rc = SQLExecute(m_hstmt);
+ do {
+ rc = SQLExecute(m_hstmt);
+ } while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLExecute", m_hstmt);
+ if (!Check(SQLNumResultCols(m_hstmt, &ncol)))
+ ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
+
+ if (ncol) {
+ if (x) {
+ afrw = ncol;
+ strcpy(g->Message, "Result set column number");
+ } else {
+ // This should never happen while inserting
+ strcpy(g->Message, "Logical error while inserting");
+ } // endif ncol
+
+ } else {
+ // Insert, Update or Delete statement
+ if (!Check(SQLRowCount(m_hstmt, &afrw)))
+ ThrowDBX(rc, "SQLRowCount", m_hstmt);
+
+ if (x)
+ strcpy(g->Message, "Affected rows");
+
+ } // endif ncol
+
} catch(DBX *x) {
strcpy(m_G->Message, x->GetErrorMessage(0));
SQLCancel(m_hstmt);
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
m_hstmt = NULL;
- return true;
+
+ if (m_Transact) {
+ rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
+ m_Transact = false;
+ } // endif m_Transact
+
} // end try/catch
- return false;
+ return (int)afrw;
} // end of ExecuteSQL
/***********************************************************************/
@@ -1537,6 +1667,132 @@ bool ODBConn::BindParam(ODBCCOL *colp)
return false;
} // end of BindParam
+/**************************************************************************/
+/* GetMetaData: constructs the result blocks containing the */
+/* description of all the columns of an SQL command. */
+/**************************************************************************/
+PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
+ {
+ static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
+ TYPE_SHORT, TYPE_SHORT};
+ static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
+ FLD_SCALE, FLD_NULL};
+ static unsigned int length[] = {0, 6, 10, 6, 6};
+ unsigned char cn[60];
+ int qcol = 5;
+ short nl, type, prec, nul, cns = (short)sizeof(cn);
+ PQRYRES qrp = NULL;
+ PCOLRES crp;
+ USHORT i;
+ SQLULEN n;
+ SWORD ncol;
+ RETCODE rc;
+ HSTMT hstmt;
+
+ if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog
+ return NULL;
+
+ try {
+ rc = SQLAllocStmt(m_hdbc, &hstmt);
+
+ if (!Check(rc))
+ ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
+
+ OnSetOptions(hstmt);
+
+ do {
+ rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
+// rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLExecDirect", hstmt);
+
+ do {
+ rc = SQLNumResultCols(hstmt, &ncol);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLNumResultCols", hstmt);
+
+ if (ncol) for (i = 1; i <= ncol; i++) {
+ do {
+ rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLDescribeCol", hstmt);
+
+ length[0] = max(length[0], (UINT)nl);
+ } // endfor i
+
+ } catch(DBX *x) {
+ strcpy(g->Message, x->GetErrorMessage(0));
+ goto err;
+ } // end try/catch
+
+ if (!ncol) {
+ strcpy(g->Message, "Invalid Srcdef");
+ goto err;
+ } // endif ncol
+
+ /************************************************************************/
+ /* Allocate the structures used to refer to the result set. */
+ /************************************************************************/
+ qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
+ buftyp, fldtyp, length, false, true);
+
+ // Some columns must be renamed
+ for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
+ switch (++i) {
+ case 3: crp->Name = "Precision"; break;
+ case 4: crp->Name = "Scale"; break;
+ case 5: crp->Name = "Nullable"; break;
+ } // endswitch i
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ try {
+ for (i = 0; i < ncol; i++) {
+ do {
+ rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLDescribeCol", hstmt);
+ else
+ qrp->Nblin++;
+
+ crp = qrp->Colresp; // Column_Name
+ crp->Kdata->SetValue((char*)cn, i);
+ crp = crp->Next; // Data_Type
+ crp->Kdata->SetValue(type, i);
+ crp = crp->Next; // Precision (length)
+ crp->Kdata->SetValue((int)n, i);
+ crp = crp->Next; // Scale
+ crp->Kdata->SetValue(prec, i);
+ crp = crp->Next; // Nullable
+ crp->Kdata->SetValue(nul, i);
+ } // endfor i
+
+ } catch(DBX *x) {
+ strcpy(g->Message, x->GetErrorMessage(0));
+ qrp = NULL;
+ } // end try/catch
+
+ /* Cleanup */
+ err:
+ SQLCancel(hstmt);
+ rc = SQLFreeStmt(hstmt, SQL_DROP);
+ Close();
+
+ /************************************************************************/
+ /* Return the result pointer for use by GetData routines. */
+ /************************************************************************/
+ return qrp;
+ } // end of GetMetaData
+
/***********************************************************************/
/* Get the list of Data Sources and set it in qrp. */
/***********************************************************************/
@@ -1840,6 +2096,11 @@ void ODBConn::Close()
} // endif m_hstmt
if (m_hdbc != SQL_NULL_HDBC) {
+ if (m_Transact) {
+ rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
+ m_Transact = false;
+ } // endif m_Transact
+
rc = SQLDisconnect(m_hdbc);
if (trace && rc != SQL_SUCCESS)
diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h
index a84cb19b485..448ce2d428f 100644
--- a/storage/connect/odbconn.h
+++ b/storage/connect/odbconn.h
@@ -91,8 +91,7 @@ class DBX : public BLOCK {
// Implementation (use ThrowDBX to create)
RETCODE GetRC(void) {return m_RC;}
PSZ GetMsg(void) {return m_Msg;}
- const char *GetErrorMessage(int i)
- {return (i >=0 && i < MAX_NUM_OF_MSG) ? m_ErrMsg[i] : "No ODBC error";}
+ const char *GetErrorMessage(int i);
protected:
void BuildErrorMessage(ODBConn* pdb, HSTMT hstmt = SQL_NULL_HSTMT);
@@ -107,6 +106,7 @@ class DBX : public BLOCK {
/* ODBConn class. */
/***********************************************************************/
class ODBConn : public BLOCK {
+ friend class TDBODBC;
friend class DBX;
friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&);
private:
@@ -142,11 +142,12 @@ class ODBConn : public BLOCK {
int ExecDirectSQL(char *sql, ODBCCOL *tocols);
int Fetch(void);
int PrepareSQL(char *sql);
- bool ExecuteSQL(void);
+ int ExecuteSQL(bool x);
bool BindParam(ODBCCOL *colp);
int GetCatInfo(CATPARM *cap);
bool GetDataSources(PQRYRES qrp);
bool GetDrivers(PQRYRES qrp);
+ PQRYRES GetMetaData(PGLOBAL g, char *dsn, char *src);
public:
// Set special options
@@ -185,5 +186,6 @@ class ODBConn : public BLOCK {
int m_Catver;
PSZ m_Connect;
bool m_Updatable;
+ bool m_Transact;
char m_IDQuoteChar;
}; // end of ODBConn class definition
diff --git a/storage/connect/os.h b/storage/connect/os.h
index 792d1668a91..e3d452bf7b8 100644
--- a/storage/connect/os.h
+++ b/storage/connect/os.h
@@ -1,7 +1,7 @@
#ifndef _OS_H_INCLUDED
#define _OS_H_INCLUDED
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
typedef off_t off64_t;
#define lseek64(fd, offset, whence) lseek((fd), (offset), (whence))
#define open64(path, flags, mode) open((path), (flags), (mode))
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index 11fe5694bc6..550153921a5 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -111,6 +111,7 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
TYPE_AM_FNC = 122, /* PIVOT source column type no */
+ TYPE_AM_XML = 127, /* XML access method type no */
TYPE_AM_XTB = 130, /* SYS table access method type */
TYPE_AM_MAC = 137, /* MAC table access method type */
TYPE_AM_WMI = 139, /* WMI table access method type */
@@ -582,5 +583,6 @@ FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode);
int global_open(GLOBAL *g, int msgid, const char *filename, int flags);
int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode);
DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
+char *MakeEscape(PGLOBAL g, char* str, char q);
bool PushWarning(PGLOBAL, PTDBASE);
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index 5ea8457f25e..270a0381bad 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -732,6 +732,34 @@ bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
} /* end of EvalLikePattern */
/***********************************************************************/
+/* MakeEscape: Escape some characters in a string. */
+/***********************************************************************/
+char *MakeEscape(PGLOBAL g, char* str, char q)
+ {
+ char *bufp;
+ int i, k, n = 0, len = (int)strlen(str);
+
+ for (i = 0; i < len; i++)
+ if (str[i] == q || str[i] == '\\')
+ n++;
+
+ if (!n)
+ return str;
+ else
+ bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
+
+ for (i = k = 0; i < len; i++) {
+ if (str[i] == q || str[i] == '\\')
+ bufp[k++] = '\\';
+
+ bufp[k++] = str[i];
+ } // endfor i
+
+ bufp[k] = 0;
+ return bufp;
+ } /* end of MakeEscape */
+
+/***********************************************************************/
/* PlugConvertConstant: convert a Plug constant to an Xobject. */
/***********************************************************************/
void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h
index b8352c36c14..74a16fd4823 100644
--- a/storage/connect/plgxml.h
+++ b/storage/connect/plgxml.h
@@ -147,6 +147,7 @@ class XMLNODELIST : public BLOCK {
// Properties
virtual int GetLength(void) = 0;
virtual PXNODE GetItem(PGLOBAL, int, PXNODE = NULL) = 0;
+ virtual bool DropItem(PGLOBAL, int) = 0;
protected:
// Constructor
diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp
index b538383b766..1955597bb76 100644
--- a/storage/connect/tabfmt.cpp
+++ b/storage/connect/tabfmt.cpp
@@ -93,7 +93,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
PCOLRES crp;
if (info) {
- imax = 0;
+ imax = hmax = 0;
length[0] = 128;
goto skipit;
} // endif info
diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp
index 146e2891ec7..6f68cd5381a 100755
--- a/storage/connect/tabmul.cpp
+++ b/storage/connect/tabmul.cpp
@@ -884,9 +884,11 @@ void TDBDIR::CloseDB(PGLOBAL g)
_findclose(Hsearch);
Hsearch = -1;
#else // !WIN32
- // Close the DIR handle.
- closedir(Dir);
- Dir = NULL;
+ // Close the DIR handle
+ if (Dir) {
+ closedir(Dir);
+ Dir = NULL;
+ } // endif dir
#endif // !WIN32
iFile = 0;
} // end of CloseDB
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index 6d91a444d5e..f37cb6d349e 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -86,6 +86,7 @@ MYSQLDEF::MYSQLDEF(void)
Isview = FALSE;
Bind = FALSE;
Delayed = FALSE;
+ Xsrc = FALSE;
} // end of MYSQLDEF constructor
/***********************************************************************/
@@ -171,7 +172,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
/* true error */
/* */
/***********************************************************************/
-bool MYSQLDEF::ParseURL(PGLOBAL g, char *url)
+bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
{
if ((!strstr(url, "://") && (!strchr(url, '@')))) {
// No :// or @ in connection string. Must be a straight
@@ -248,34 +249,41 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url)
if ((Database = strchr(Hostname, '/'))) {
*Database++ = 0;
- if ((Tabname = strchr(Database, '/')))
+ if ((Tabname = strchr(Database, '/'))) {
*Tabname++ = 0;
- // Make sure there's not an extra /
- if ((strchr(Tabname, '/'))) {
- strcpy(g->Message, "Syntax error in URL");
- return true;
- } // endif /
+ // Make sure there's not an extra /
+ if ((strchr(Tabname, '/'))) {
+ strcpy(g->Message, "Syntax error in URL");
+ return true;
+ } // endif /
+
+ } // endif Tabname
} // endif database
if ((sport = strchr(Hostname, ':')))
*sport++ = 0;
- Portnumber = (sport && sport[0]) ? atoi(sport) : GetDefaultPort();
+ // For unspecified values, get the values of old style options
+ // but only if called from MYSQLDEF, else set them to NULL
+ Portnumber = (sport && sport[0]) ? atoi(sport)
+ : (b) ? Cat->GetIntCatInfo("Port", GetDefaultPort()) : 0;
if (Username[0] == 0)
- Username = Cat->GetStringCatInfo(g, "User", "*");
+ Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL;
if (Hostname[0] == 0)
- Hostname = Cat->GetStringCatInfo(g, "Host", "localhost");
+ Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL;
if (!Database || !*Database)
- Database = Cat->GetStringCatInfo(g, "Database", "*");
+ Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL;
if (!Tabname || !*Tabname)
- Tabname = Name;
+ Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL;
+ if (!Password)
+ Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL;
} // endif URL
#if 0
@@ -347,6 +355,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
Isview = TRUE;
+ Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE);
return FALSE;
} // end of DefineAM
@@ -355,7 +364,9 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
/***********************************************************************/
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
{
- if (Catfunc == FNC_COL)
+ if (Xsrc)
+ return new(g) TDBMYEXC(this);
+ else if (Catfunc == FNC_COL)
return new(g) TDBMCL(this);
else
return new(g) TDBMYSQL(this);
@@ -507,6 +518,9 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g)
if (To_Filter)
strcat(strcat(Query, " WHERE "), To_Filter);
+ if (trace)
+ htrc("Query=%s\n", Query);
+
// Now we know how much to suballocate
PlugSubAlloc(g, NULL, strlen(Query) + 1);
return FALSE;
@@ -1040,18 +1054,20 @@ int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
/***********************************************************************/
void TDBMYSQL::CloseDB(PGLOBAL g)
{
- if (Mode == MODE_INSERT) {
- char cmd[64];
- int w;
- PDBUSER dup = PlgGetUser(g);
+ if (Myc.Connected()) {
+ if (Mode == MODE_INSERT) {
+ char cmd[64];
+ int w;
+ PDBUSER dup = PlgGetUser(g);
- dup->Step = "Enabling indexes";
- sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
- Myc.m_Rows = -1; // To execute the query
- m_Rc = Myc.ExecSQL(g, cmd, &w);
- } // endif m_Rc
+ dup->Step = "Enabling indexes";
+ sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
+ Myc.m_Rows = -1; // To execute the query
+ m_Rc = Myc.ExecSQL(g, cmd, &w);
+ } // endif m_Rc
- Myc.Close();
+ Myc.Close();
+ } // endif Myc
if (trace)
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
@@ -1275,6 +1291,212 @@ void MYSQLCOL::WriteColumn(PGLOBAL g)
} // end of WriteColumn
+/* ------------------------------------------------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBMYSQL class. */
+/***********************************************************************/
+
+// Is this really useful ???
+PTDB TDBMYEXC::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBMYEXC(g, this);
+
+ for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
+ cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
+
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate MYSQL column description block. */
+/***********************************************************************/
+PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
+
+ if (!colp->Flag)
+ Cmdcol = colp->GetName();
+
+ return colp;
+ } // end of MakeCol
+
+/***********************************************************************/
+/* MakeCMD: make the SQL statement to send to MYSQL connection. */
+/***********************************************************************/
+char *TDBMYEXC::MakeCMD(PGLOBAL g)
+ {
+ char *xcmd = NULL;
+
+ if (To_Filter) {
+ if (Cmdcol) {
+ char col[128], cmd[1024];
+ int n;
+
+ memset(cmd, 0, sizeof(cmd));
+ n = sscanf(To_Filter, "%s = '%1023c", col, cmd);
+
+ if (n == 2 && !stricmp(col, Cmdcol)) {
+ xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
+
+ strcpy(xcmd, cmd);
+ xcmd[strlen(xcmd) - 1] = 0;
+ } else
+ strcpy(g->Message, "Invalid command specification filter");
+
+ } else
+ strcpy(g->Message, "No command column in select list");
+
+ } else if (!Srcdef)
+ strcpy(g->Message, "No Srcdef default command");
+ else
+ xcmd = Srcdef;
+
+ return xcmd;
+ } // end of MakeCMD
+
+/***********************************************************************/
+/* EXC GetMaxSize: returns the maximum number of rows in the table. */
+/***********************************************************************/
+int TDBMYEXC::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0) {
+ MaxSize = 1;
+ } // endif MaxSize
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* MySQL Exec Access Method opening routine. */
+/***********************************************************************/
+bool TDBMYEXC::OpenDB(PGLOBAL g)
+ {
+ int rc;
+
+ if (Use == USE_OPEN) {
+ strcpy(g->Message, "Multiple execution is not allowed");
+ return true;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open a MySQL connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this server */
+ /* and if so to allocate just a new result set. But this only for */
+ /* servers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Myc.Connected())
+ if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ return true;
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ if (Mode != MODE_READ) {
+ strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
+ return true;
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Get the command to execute. */
+ /*********************************************************************/
+ if (!(Query = MakeCMD(g))) {
+ Myc.Close();
+ return true;
+ } // endif Query
+
+ if ((rc = Myc.ExecSQL(g, Query)) == RC_NF) {
+ strcpy(g->Message, "Affected rows");
+ AftRows = Myc.m_Rows;
+ } else if (rc == RC_OK) {
+ sprintf(g->Message, "Columns and %d rows", Myc.m_Rows);
+ AftRows = Myc.m_Fields;
+ } else
+ return true;
+
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* Data Base read routine for MYSQL access method. */
+/***********************************************************************/
+int TDBMYEXC::ReadDB(PGLOBAL g)
+ {
+ return (++N) ? RC_EF : RC_OK;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for Exec MYSQL access methods. */
+/***********************************************************************/
+int TDBMYEXC::WriteDB(PGLOBAL g)
+ {
+ strcpy(g->Message, "EXEC MYSQL tables are read only");
+ return RC_FX;
+ } // end of WriteDB
+
+// ------------------------- MYXCOL functions ---------------------------
+
+/***********************************************************************/
+/* MYXCOL public constructor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : MYSQLCOL(cdp, tdbp, cprec, i, am)
+ {
+ // Set additional EXEC MYSQL access method information for column.
+ Flag = cdp->GetOffset();
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYSQLCOL public constructor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
+ : MYSQLCOL(fld, tdbp, i, am)
+ {
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ } // end of MYSQLCOL constructor
+
+/***********************************************************************/
+/* MYXCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
+ {
+ Flag = col1->Flag;
+ } // end of MYXCOL copy constructor
+
+/***********************************************************************/
+/* ReadColumn: */
+/***********************************************************************/
+void MYXCOL::ReadColumn(PGLOBAL g)
+ {
+ PTDBMYX tdbp = (PTDBMYX)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;
+ } // endswitch Flag
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: should never be called. */
+/***********************************************************************/
+void MYXCOL::WriteColumn(PGLOBAL g)
+ {
+ assert(false);
+ } // end of WriteColumn
+
/* ---------------------------TDBMCL class --------------------------- */
/***********************************************************************/
diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h
index 2573259ec3c..bcac10dcaa7 100644
--- a/storage/connect/tabmysql.h
+++ b/storage/connect/tabmysql.h
@@ -3,8 +3,10 @@
typedef class MYSQLDEF *PMYDEF;
typedef class TDBMYSQL *PTDBMY;
-typedef class MYSQLC *PMYC;
typedef class MYSQLCOL *PMYCOL;
+typedef class TDBMYEXC *PTDBMYX;
+typedef class MYXCOL *PMYXCOL;
+typedef class MYSQLC *PMYC;
/* ------------------------- MYSQL classes --------------------------- */
@@ -38,7 +40,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
// Methods
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
- bool ParseURL(PGLOBAL g, char *url);
+ bool ParseURL(PGLOBAL g, char *url, bool b = true);
bool GetServerInfo(PGLOBAL g, const char *server_name);
protected:
@@ -54,6 +56,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
bool Isview; /* TRUE if this table is a MySQL view */
bool Bind; /* Use prepared statement on insert */
bool Delayed; /* Delayed insert */
+ bool Xsrc; /* Execution type */
}; // end of MYSQLDEF
/***********************************************************************/
@@ -159,6 +162,87 @@ class MYSQLCOL : public COLBLK {
}; // end of class MYSQLCOL
/***********************************************************************/
+/* This is the class declaration for the exec command MYSQL table. */
+/***********************************************************************/
+class TDBMYEXC : public TDBMYSQL {
+ friend class MYXCOL;
+ public:
+ // Constructor
+ TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) {Cmdcol = NULL;}
+ TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
+ {Cmdcol = tdbp->Cmdcol;}
+
+ // Implementation
+//virtual AMT GetAmType(void) {return TYPE_AM_MYSQL;}
+ virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYEXC(g, this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+//virtual int GetAffectedRows(void) {return AftRows;}
+//virtual int GetRecpos(void) {return N;}
+//virtual int GetProgMax(PGLOBAL g);
+//virtual void ResetDB(void) {N = 0;}
+//virtual int RowNumber(PGLOBAL g, bool b = FALSE);
+ virtual bool IsView(void) {return Isview;}
+//virtual PSZ GetServer(void) {return Server;}
+// void SetDatabase(LPCSTR db) {Database = (char*)db;}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+//virtual int DeleteDB(PGLOBAL g, int irc);
+//virtual void CloseDB(PGLOBAL g);
+
+ // Specific routines
+// bool SetColumnRanks(PGLOBAL g);
+// PCOL MakeFieldColumn(PGLOBAL g, char *name);
+// PSZ FindFieldColumn(char *name);
+
+ protected:
+ // Internal functions
+ char *MakeCMD(PGLOBAL g);
+//bool MakeSelect(PGLOBAL g);
+//bool MakeInsert(PGLOBAL g);
+//int BindColumns(PGLOBAL g);
+
+ // Members
+ char *Cmdcol; // The name of the Xsrc command column
+ }; // end of class TDBMYEXC
+
+/***********************************************************************/
+/* Class MYXCOL: MySQL exec command table column. */
+/***********************************************************************/
+class MYXCOL : public MYSQLCOL {
+ friend class TDBMYEXC;
+ public:
+ // Constructors
+ MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "MYSQL");
+ MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am = "MYSQL");
+ MYXCOL(MYXCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+//virtual int GetAmType(void) {return TYPE_AM_MYSQL;}
+// void InitBind(PGLOBAL g);
+
+ // Methods
+//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+// bool FindRank(PGLOBAL g);
+
+ protected:
+ // Default constructor not to be used
+ MYXCOL(void) {}
+
+ // Members
+ char *Buffer; // To get returned message
+ int Flag; // Column content desc
+ }; // end of class MYXCOL
+
+/***********************************************************************/
/* This is the class declaration for the MYSQL column catalog table. */
/***********************************************************************/
class TDBMCL : public TDBCAT {
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index ae6817344b8..d9d794ef4e4 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -1,7 +1,7 @@
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
-/* Version 2.5 */
+/* Version 2.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
@@ -90,8 +90,9 @@ extern int num_read, num_there, num_eq[2]; // Statistics
/***********************************************************************/
ODBCDEF::ODBCDEF(void)
{
- Connect = Tabname = Tabowner = Tabqual = Qchar = NULL;
- Catver = Options = 0;
+ Connect = Tabname = Tabowner = Tabqual = Srcdef = Qchar = NULL;
+ Catver = Options = 0;
+ Xsrc = false;
} // end of ODBCDEF constructor
/***********************************************************************/
@@ -99,17 +100,17 @@ ODBCDEF::ODBCDEF(void)
/***********************************************************************/
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
- int dop = ODBConn::noOdbcDialog; // Default for options
-
Desc = Connect = Cat->GetStringCatInfo(g, "Connect", "");
Tabname = Cat->GetStringCatInfo(g, "Name",
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname);
Tabowner = Cat->GetStringCatInfo(g, "Owner", "");
Tabqual = Cat->GetStringCatInfo(g, "Qualifier", "");
+ Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL);
Qchar = Cat->GetStringCatInfo(g, "Qchar", "");
Catver = Cat->GetIntCatInfo("Catver", 2);
- Options = Cat->GetIntCatInfo("Options", dop);
+ Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE);
+ Options = ODBConn::noOdbcDialog;
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -125,7 +126,9 @@ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
/* Allocate a TDB of the proper type. */
/* Column blocks will be allocated only when needed. */
/*********************************************************************/
- switch (Catfunc) {
+ if (Xsrc)
+ tdbp = new(g) TDBXDBC(this);
+ else switch (Catfunc) {
case FNC_COL:
tdbp = new(g) TDBOCL(this);
break;
@@ -161,19 +164,21 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
Cnp = NULL;
if (tdp) {
- Connect = tdp->GetConnect();
- TableName = tdp->GetTabname();
- Owner = tdp->GetTabowner();
- Qualifier = tdp->GetTabqual();
+ Connect = tdp->Connect;
+ TableName = tdp->Tabname;
+ Owner = tdp->Tabowner;
+ Qualifier = tdp->Tabqual;
+ Srcdef = tdp->Srcdef;
Quote = tdp->GetQchar();
- Options = tdp->GetOptions();
+ Options = tdp->Options;
Rows = tdp->GetElemt();
- Catver = tdp->GetCatver();
+ Catver = tdp->Catver;
} else {
Connect = NULL;
TableName = NULL;
Owner = NULL;
Qualifier = NULL;
+ Srcdef = NULL;
Quote = NULL;
Options = 0;
Rows = 0;
@@ -201,6 +206,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
TableName = tdbp->TableName;
Owner = tdbp->Owner;
Qualifier = tdbp->Qualifier;
+ Srcdef = tdbp->Srcdef;
Quote = tdbp->Quote;
Query = tdbp->Query;
Count = tdbp->Count;
@@ -299,7 +305,6 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn)
DBQ = fn;
} // end of SetFile
-
/******************************************************************/
/* Convert an UTF-8 string to latin characters. */
/******************************************************************/
@@ -314,7 +319,6 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n)
return 0;
} // end of Decode
-
/***********************************************************************/
/* MakeSQL: make the SQL statement use with ODBC connection. */
/* Note: when implementing EOM filtering, column only used in local */
@@ -329,6 +333,9 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
PTABLE tablep = To_Table;
PCOL colp;
+ if (Srcdef)
+ return Srcdef;
+
if (!cnt) {
// Normal SQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
@@ -431,6 +438,83 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
} // end of MakeSQL
/***********************************************************************/
+/* MakeInsert: make the Insert statement used with ODBC connection. */
+/***********************************************************************/
+bool TDBODBC::MakeInsert(PGLOBAL g)
+ {
+ char *colist, *valist;
+// char *tk = "`";
+ int len = 0;
+ bool b = FALSE;
+ PCOL colp;
+
+ if (Query)
+ return false; // already done
+
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (colp->IsSpecial()) {
+ strcpy(g->Message, MSG(NO_ODBC_SPECOL));
+ return true;
+ } else {
+ len += (strlen(colp->GetName()) + 4);
+ ((PODBCCOL)colp)->Rank = ++Nparm;
+ } // endif colp
+
+ colist = (char*)PlugSubAlloc(g, NULL, len);
+ *colist = '\0';
+ valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
+ *valist = '\0';
+
+ for (colp = Columns; colp; colp = colp->GetNext()) {
+ if (b) {
+ strcat(colist, ", ");
+ strcat(valist, ",");
+ } else
+ b = true;
+
+ if (Quote)
+ strcat(strcat(strcat(colist, Quote), colp->GetName()), Quote);
+ else
+ strcat(colist, colp->GetName());
+
+ strcat(valist, "?"); // Parameter marker
+ } // endfor colp
+
+ // Below 32 is enough to contain the fixed part of the query
+ len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32);
+ Query = (char*)PlugSubAlloc(g, NULL, len);
+ strcpy(Query, "INSERT INTO ");
+
+ if (Quote)
+ strcat(strcat(strcat(Query, Quote), TableName), Quote);
+ else
+ strcat(Query, TableName);
+
+ strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
+ strcat(strcat(Query, valist), ")");
+
+ return false;
+ } // end of MakeInsert
+
+/***********************************************************************/
+/* ODBC Bind Parameter function. */
+/***********************************************************************/
+bool TDBODBC::BindParameters(PGLOBAL g)
+ {
+ PODBCCOL colp;
+
+ for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
+ colp->AllocateBuffers(g, 0);
+
+ if (Ocp->BindParam(colp))
+ return true;
+
+ } // endfor colp
+
+ return false;
+ } // end of BindParameters
+
+/***********************************************************************/
/* ResetSize: call by TDBMUL when calculating size estimate. */
/***********************************************************************/
void TDBODBC::ResetSize(void)
@@ -448,6 +532,16 @@ void TDBODBC::ResetSize(void)
int TDBODBC::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
+ // Make MariaDB happy
+ MaxSize = 100;
+#if 0
+ // This is unuseful and takes time
+ if (Srcdef) {
+ // Return a reasonable guess
+ MaxSize = 100;
+ return MaxSize;
+ } // endif Srcdef
+
if (!Ocp)
Ocp = new(g) ODBConn(g, this);
@@ -466,19 +560,18 @@ int TDBODBC::GetMaxSize(PGLOBAL g)
if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0)
return -3;
-
+#endif // 0
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
-/* Return 0 in mode DELETE or UPDATE to tell that it is done. */
+/* Return max size value. */
/***********************************************************************/
int TDBODBC::GetProgMax(PGLOBAL g)
{
- return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0
- : GetMaxSize(g);
+ return GetMaxSize(g);
} // end of GetProgMax
/***********************************************************************/
@@ -546,14 +639,24 @@ bool TDBODBC::OpenDB(PGLOBAL g)
if (!colp->IsSpecial())
colp->AllocateBuffers(g, Rows);
- } else
- rc = true;
+ } else {
+ Ocp->Close();
+ return true;
+ } // endif Query
if (!rc)
rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
+ } else if (Mode == MODE_INSERT) {
+ if (!(rc = MakeInsert(g)))
+ if (Nparm != Ocp->PrepareSQL(Query)) {
+ strcpy(g->Message, MSG(PARM_CNT_MISS));
+ rc = true;
+ } else
+ rc = BindParameters(g);
+
} else {
- strcpy(g->Message, "ODBC tables are read only in this version");
+ strcpy(g->Message, "No DELETE/UPDATE of ODBC tablesd");
return true;
} // endelse
@@ -592,30 +695,6 @@ int TDBODBC::ReadDB(PGLOBAL g)
// Direct access of ODBC tables is not implemented yet
strcpy(g->Message, MSG(NO_ODBC_DIRECT));
longjmp(g->jumper[g->jump_level], GetAmType());
-
-#if 0
- /*******************************************************************/
- /* Reading is by an index table. */
- /*******************************************************************/
- int recpos = To_Kindex->Fetch(g);
-
- switch (recpos) {
- case -1: // End of file reached
- return RC_EF;
- case -2: // No match for join
- return RC_NF;
- case -3: // Same record as current one
- num_there++;
- return RC_OK;
- default:
- /***************************************************************/
- /* Set the cursor position according to record to read. */
- /***************************************************************/
-//--------------------------------- TODO --------------------------------
- break;
- } // endswitch recpos
-#endif // 0
-
} // endif To_Kindex
/*********************************************************************/
@@ -641,8 +720,15 @@ int TDBODBC::ReadDB(PGLOBAL g)
/***********************************************************************/
int TDBODBC::WriteDB(PGLOBAL g)
{
- strcpy(g->Message, "ODBC tables are read only");
- return RC_FX;
+ int n = Ocp->ExecuteSQL(false);
+
+ if (n < 0) {
+ AftRows = n;
+ return RC_FX;
+ } else
+ AftRows += n;
+
+ return RC_OK;
} // end of WriteDB
/***********************************************************************/
@@ -664,7 +750,8 @@ void TDBODBC::CloseDB(PGLOBAL g)
// To_Kindex = NULL;
// } // endif
- Ocp->Close();
+ if (Ocp)
+ Ocp->Close();
if (trace)
htrc("ODBC CloseDB: closing %s\n", Name);
@@ -892,7 +979,7 @@ void ODBCCOL::WriteColumn(PGLOBAL g)
/* Do convert the column value if necessary. */
/*********************************************************************/
if (Value != To_Val)
- Value->SetValue_pval(To_Val, false); // Convert the inserted value
+ Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
if (Buf_Type == TYPE_DATE) {
struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
@@ -903,8 +990,245 @@ void ODBCCOL::WriteColumn(PGLOBAL g)
Sqlbuf->day = dbtime->tm_mday;
Sqlbuf->month = dbtime->tm_mon + 1;
Sqlbuf->year = dbtime->tm_year + 1900;
+ Sqlbuf->fraction = 0;
} // endif Buf_Type
+ if (Nullable)
+ *StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
+ (IsTypeNum(Buf_Type)) ? 0 : SQL_NTS;
+
+ } // end of WriteColumn
+
+/* -------------------------- Class TDBXDBC -------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBODBC class. */
+/***********************************************************************/
+PTDB TDBXDBC::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PXSRCCOL cp1, cp2;
+ PGLOBAL g = t->G; // Is this really useful ???
+
+ tp = new(g) TDBXDBC(this);
+
+ for (cp1 = (PXSRCCOL)Columns; cp1; cp1 = (PXSRCCOL)cp1->GetNext()) {
+ cp2 = new(g) XSRCCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate XSRC column description block. */
+/***********************************************************************/
+PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n);
+
+ if (!colp->Flag)
+ Cmdcol = colp->GetName();
+
+ return colp;
+ } // end of MakeCol
+
+/***********************************************************************/
+/* MakeCMD: make the SQL statement to send to ODBC connection. */
+/***********************************************************************/
+char *TDBXDBC::MakeCMD(PGLOBAL g)
+ {
+ char *xcmd = NULL;
+
+ if (To_Filter) {
+ if (Cmdcol) {
+ char col[128], cmd[1024];
+ int n;
+
+ memset(cmd, 0, sizeof(cmd));
+ n = sscanf(To_Filter, "%s = '%1023c", col, cmd);
+
+ if (n == 2 && !stricmp(col, Cmdcol)) {
+ xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
+
+ strcpy(xcmd, cmd);
+ xcmd[strlen(xcmd) - 1] = 0;
+ } else
+ strcpy(g->Message, "Invalid command specification filter");
+
+ } else
+ strcpy(g->Message, "No command column in select list");
+
+ } else if (!Srcdef)
+ strcpy(g->Message, "No Srcdef default command");
+ else
+ xcmd = Srcdef;
+
+ return xcmd;
+ } // end of MakeCMD
+
+#if 0
+/***********************************************************************/
+/* ODBC Bind Parameter function. */
+/***********************************************************************/
+bool TDBXDBC::BindParameters(PGLOBAL g)
+ {
+ PODBCCOL colp;
+
+ for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
+ colp->AllocateBuffers(g, 0);
+
+ if (Ocp->BindParam(colp))
+ return true;
+
+ } // endfor colp
+
+ return false;
+ } // end of BindParameters
+#endif // 0
+
+/***********************************************************************/
+/* XDBC GetMaxSize: returns table size (always one row). */
+/***********************************************************************/
+int TDBXDBC::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0)
+ MaxSize = 1;
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* ODBC Access Method opening routine. */
+/* New method now that this routine is called recursively (last table */
+/* first in reverse order): index blocks are immediately linked to */
+/* join block of next table if it exists or else are discarted. */
+/***********************************************************************/
+bool TDBXDBC::OpenDB(PGLOBAL g)
+ {
+ bool rc = false;
+
+ if (g->Trace)
+ htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
+ this, Tdb_No, Use, Mode);
+
+ if (Use == USE_OPEN) {
+ strcpy(g->Message, "Multiple execution is not allowed");
+ return true;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open an ODBC connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this datasource */
+ /* and if so to allocate just a new result set. But this only for */
+ /* drivers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Ocp)
+ Ocp = new(g) ODBConn(g, this);
+ else if (Ocp->IsOpen())
+ Ocp->Close();
+
+ if (Ocp->Open(Connect, Options) < 1)
+ return true;
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ if (Mode != MODE_READ) {
+ strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables");
+ return true;
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Get the command to execute. */
+ /*********************************************************************/
+ if (!(Query = MakeCMD(g))) {
+ Ocp->Close();
+ return true;
+ } // endif Query
+
+ Rows = 1;
+
+ if (Ocp->PrepareSQL(Query)) {
+ strcpy(g->Message, "Parameters not supported");
+ AftRows = -1;
+ } else
+ AftRows = 0;
+
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* ReadDB: Data Base read routine for xdbc access method. */
+/***********************************************************************/
+int TDBXDBC::ReadDB(PGLOBAL g)
+ {
+ if (trace)
+ htrc("XDBC ReadDB: query=%s\n", SVP(Query));
+
+ if (Rows--) {
+ if (!AftRows)
+ AftRows = Ocp->ExecuteSQL(true);
+
+ } else
+ return RC_EF;
+
+ Fpos++; // Used for progress info
+ return RC_OK;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* Data Base delete line routine for ODBC access method. */
+/***********************************************************************/
+int TDBXDBC::WriteDB(PGLOBAL g)
+ {
+ strcpy(g->Message, "Execsrc tables are read only");
+ return RC_FX;
+ } // end of DeleteDB
+
+/* --------------------------- XSRCCOL ------------------------------- */
+
+/***********************************************************************/
+/* XSRCCOL public constructor. */
+/***********************************************************************/
+XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : ODBCCOL(cdp, tdbp, cprec, i, am)
+ {
+ // Set additional ODBC access method information for column.
+ Flag = cdp->GetOffset();
+ } // end of XSRCCOL constructor
+
+/***********************************************************************/
+/* XSRCCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp)
+ {
+ Flag = col1->Flag;
+ } // end of XSRCCOL copy constructor
+
+/***********************************************************************/
+/* ReadColumn: set column value according to Flag. */
+/***********************************************************************/
+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;
+ } // endswitch Flag
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: Should never be called. */
+/***********************************************************************/
+void XSRCCOL::WriteColumn(PGLOBAL g)
+ {
+ // Should never be called
} // end of WriteColumn
/* ---------------------------TDBSRC class --------------------------- */
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index a470655bd11..b3577bce5be 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -1,5 +1,5 @@
/*************** Tabodbc H Declares Source Code File (.H) **************/
-/* Name: TABODBC.H Version 1.5 */
+/* Name: TABODBC.H Version 1.6 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
/* */
@@ -11,6 +11,8 @@
typedef class ODBCDEF *PODEF;
typedef class TDBODBC *PTDBODBC;
typedef class ODBCCOL *PODBCCOL;
+typedef class TDBXDBC *PTDBXDBC;
+typedef class XSRCCOL *PXSRCCOL;
typedef class TDBOIF *PTDBOIF;
typedef class OIFCOL *POIFCOL;
typedef class TDBSRC *PTDBSRC;
@@ -19,6 +21,8 @@ typedef class TDBSRC *PTDBSRC;
/* ODBC table. */
/***********************************************************************/
class DllExport ODBCDEF : public TABDEF { /* Logical table description */
+ friend class TDBODBC;
+ friend class TDBXDBC;
public:
// Constructor
ODBCDEF(void);
@@ -29,6 +33,7 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
PSZ GetTabname(void) {return Tabname;}
PSZ GetTabowner(void) {return Tabowner;}
PSZ GetTabqual(void) {return Tabqual;}
+ PSZ GetSrcdef(void) {return Srcdef;}
PSZ GetQchar(void) {return (Qchar && *Qchar) ? Qchar : NULL;}
int GetCatver(void) {return Catver;}
int GetOptions(void) {return Options;}
@@ -43,9 +48,11 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
PSZ Tabname; /* External table name */
PSZ Tabowner; /* External table owner */
PSZ Tabqual; /* External table qualifier */
+ PSZ Srcdef; /* The source table SQL definition */
PSZ Qchar; /* Identifier quoting character */
int Catver; /* ODBC version for catalog functions */
int Options; /* Open connection options */
+ bool Xsrc; /* Execution type */
}; // end of ODBCDEF
#if !defined(NODBC)
@@ -92,10 +99,10 @@ class TDBODBC : public TDBASE {
int Decode(char *utf, char *buf, size_t n);
char *MakeSQL(PGLOBAL g, bool cnt);
//bool MakeUpdate(PGLOBAL g, PSELECT selist);
-//bool MakeInsert(PGLOBAL g);
+ bool MakeInsert(PGLOBAL g);
//bool MakeDelete(PGLOBAL g);
//bool MakeFilter(PGLOBAL g, bool c);
-//bool BindParameters(PGLOBAL g);
+ bool BindParameters(PGLOBAL g);
// Members
ODBConn *Ocp; // Points to an ODBC connection class
@@ -104,6 +111,7 @@ class TDBODBC : public TDBASE {
char *TableName; // Points to ODBC table name
char *Owner; // Points to ODBC table Owner
char *Qualifier; // Points to ODBC table Qualifier
+ 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
@@ -122,7 +130,7 @@ class TDBODBC : public TDBASE {
}; // end of class TDBODBC
/***********************************************************************/
-/* Class ODBCCOL: DOS access method column descriptor. */
+/* Class ODBCCOL: ODBC access method column descriptor. */
/* This A.M. is used for ODBC tables. */
/***********************************************************************/
class ODBCCOL : public COLBLK {
@@ -153,17 +161,86 @@ class ODBCCOL : public COLBLK {
ODBCCOL(void);
// Members
- TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
- void *Bufp; // To extended buffer
+ TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
+ void *Bufp; // To extended buffer
PVBLK Blkp; // To Value Block
//char F_Date[12]; // Internal Date format
PVAL To_Val; // To value used for Insert
SQLLEN *StrLen; // As returned by ODBC
- SQLLEN Slen; // Used with Fetch
- int Rank; // Rank (position) number in the query
+ SQLLEN Slen; // Used with Fetch
+ int Rank; // Rank (position) number in the query
}; // end of class ODBCCOL
/***********************************************************************/
+/* This is the ODBC Access Method class declaration that send */
+/* commands to be executed by other DB ODBC drivers. */
+/***********************************************************************/
+class TDBXDBC : public TDBODBC {
+ friend class XSRCCOL;
+ friend class ODBConn;
+ public:
+ // Constructor
+ TDBXDBC(PODEF tdp = NULL) : TDBODBC(tdp) {Cmdcol = NULL;}
+ TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp) {Cmdcol = tdbp->Cmdcol;}
+
+ // Implementation
+//virtual AMT GetAmType(void) {return TYPE_AM_ODBC;}
+ virtual PTDB Duplicate(PGLOBAL g)
+ {return (PTDB)new(g) TDBXDBC(this);}
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+//virtual int GetRecpos(void);
+//virtual PSZ GetFile(PGLOBAL g);
+//virtual void SetFile(PGLOBAL g, PSZ fn);
+//virtual void ResetSize(void);
+//virtual int GetAffectedRows(void) {return AftRows;}
+//virtual PSZ GetServer(void) {return "ODBC";}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+//virtual int GetProgMax(PGLOBAL g);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+//virtual int DeleteDB(PGLOBAL g, int irc);
+//virtual void CloseDB(PGLOBAL g);
+
+ protected:
+ // Internal functions
+ char *MakeCMD(PGLOBAL g);
+//bool BindParameters(PGLOBAL g);
+
+ // Members
+ char *Cmdcol; // The name of the Xsrc command column
+ }; // end of class TDBXDBC
+
+/***********************************************************************/
+/* Used by table in source execute mode. */
+/***********************************************************************/
+class XSRCCOL : public ODBCCOL {
+ friend class TDBXDBC;
+ public:
+ // Constructors
+ XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "ODBC");
+ XSRCCOL(XSRCCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+//virtual int GetAmType(void) {return TYPE_AM_ODBC;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+// void Print(PGLOBAL g, FILE *, uint);
+
+ protected:
+ // Members
+ char *Buffer; // To get returned message
+ int Flag; // Column content desc
+ }; // end of class XSRCCOL
+
+/***********************************************************************/
/* This is the class declaration for the Data Sources catalog table. */
/***********************************************************************/
class TDBSRC : public TDBCAT {
diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp
index 110c53c33b2..2405b1853a2 100644
--- a/storage/connect/tabutil.cpp
+++ b/storage/connect/tabutil.cpp
@@ -592,6 +592,17 @@ bool PRXCOL::Init(PGLOBAL g)
} // end of Init
/***********************************************************************/
+/* Reset the column descriptor to non evaluated yet. */
+/***********************************************************************/
+void PRXCOL::Reset(void)
+ {
+ if (Colp)
+ Colp->Reset();
+
+ Status &= ~BUF_READ;
+ } // end of Reset
+
+/***********************************************************************/
/* ReadColumn: */
/***********************************************************************/
void PRXCOL::ReadColumn(PGLOBAL g)
@@ -600,7 +611,7 @@ void PRXCOL::ReadColumn(PGLOBAL g)
htrc("PRX ReadColumn: name=%s\n", Name);
if (Colp) {
- Colp->ReadColumn(g);
+ Colp->Eval(g);
Value->SetValue_pval(To_Val);
// Set null when applicable
diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h
index 9bfa4b726d1..a6202950390 100644
--- a/storage/connect/tabutil.h
+++ b/storage/connect/tabutil.h
@@ -70,7 +70,7 @@ class DllExport TDBPRX : public TDBASE {
virtual int GetRecpos(void) {return Tdbp->GetRecpos();}
virtual void ResetDB(void) {Tdbp->ResetDB();}
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
- virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (char*) "?";}
+ virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (PSZ)"?";}
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
@@ -105,6 +105,7 @@ class DllExport PRXCOL : public COLBLK {
virtual int GetAmType(void) {return TYPE_AM_PRX;}
// Methods
+ virtual void Reset(void);
virtual bool IsSpecial(void) {return Pseudo;}
virtual void ReadColumn(PGLOBAL g);
bool Init(PGLOBAL g);
diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp
index eb9e9c0e4dc..33ec8984219 100644
--- a/storage/connect/tabxcl.cpp
+++ b/storage/connect/tabxcl.cpp
@@ -253,7 +253,7 @@ XCLCOL::XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
void XCLCOL::ReadColumn(PGLOBAL g)
{
if (((PTDBXCL)To_Tdb)->New) {
- Colp->ReadColumn(g);
+ Colp->Eval(g);
strcpy(Cbuf, To_Val->GetCharValue());
Cp = Cbuf;
} // endif New
@@ -272,9 +272,11 @@ void XCLCOL::ReadColumn(PGLOBAL g)
} else if (Nullable) {
Value->Reset();
Value->SetNull(true);
- } else
+ } else {
// Skip that row
((PTDBXCL)To_Tdb)->RowFlag = 2;
+ Colp->Reset();
+ } // endif Cp
if (Cp && *Cp)
// More to come from the same row
diff --git a/storage/connect/tabxcl.h b/storage/connect/tabxcl.h
index 28fbf85d509..0189775cd8d 100644
--- a/storage/connect/tabxcl.h
+++ b/storage/connect/tabxcl.h
@@ -90,7 +90,7 @@ class XCLCOL : public PRXCOL {
XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
// Methods
- virtual void Reset(void) {} // Evaluated only by TDBXCL
+ virtual void Reset(void) {Colp->Reset();} // Evaluated only by TDBXCL
virtual void ReadColumn(PGLOBAL g);
protected:
diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp
index 69ad6711638..3feba14ca5e 100644
--- a/storage/connect/tabxml.cpp
+++ b/storage/connect/tabxml.cpp
@@ -78,7 +78,9 @@ XMLDEF::XMLDEF(void)
DefNs = NULL;
Attrib = NULL;
Hdattr = NULL;
+ Coltype = 1;
Limit = 0;
+ Header = 0;
Xpand = false;
Usedom = false;
} // end of XMLDEF constructor
@@ -338,17 +340,14 @@ PCOL TDBXML::InsertSpecialColumn(PGLOBAL g, PCOL colp)
/***********************************************************************/
/* LoadTableFile: Load and parse an XML file. */
/***********************************************************************/
-int TDBXML::LoadTableFile(PGLOBAL g)
+int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
{
- char filename[_MAX_PATH];
int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2;
PFBLOCK fp = NULL;
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
- /*********************************************************************/
- /* We used the file name relative to recorded datapath. */
- /*********************************************************************/
- PlugSetPath(filename, Xfile, GetPath());
+ if (Docp)
+ return rc; // Already done
if (trace)
htrc("TDBXML: loading %s\n", filename);
@@ -397,6 +396,7 @@ int TDBXML::LoadTableFile(PGLOBAL g)
} else
rc = (errno == ENOENT) ? RC_NF : RC_INFO;
+ // Cannot make a Xblock until document is made
return rc;
} // endif Docp
@@ -418,9 +418,8 @@ int TDBXML::LoadTableFile(PGLOBAL g)
/***********************************************************************/
bool TDBXML::Initialize(PGLOBAL g)
{
- char tabpath[64];
- int rc;
- PXMLCOL colp;
+ int rc;
+ PXMLCOL colp;
if (Void)
return false;
@@ -440,8 +439,13 @@ bool TDBXML::Initialize(PGLOBAL g)
#else
if (!Root) {
#endif
+ char tabpath[64], filename[_MAX_PATH];
+
+ // We used the file name relative to recorded datapath
+ PlugSetPath(filename, Xfile, GetPath());
+
// Load or re-use the table file
- rc = LoadTableFile(g);
+ rc = LoadTableFile(g, filename);
if (rc == RC_OK) {
// Get root node
@@ -503,6 +507,9 @@ bool TDBXML::Initialize(PGLOBAL g)
goto error;
} // endif NewDoc
+ // Now we can link the Xblock
+ To_Xb = Docp->LinkXblock(g, Mode, rc, filename);
+
// Add a CONNECT comment node
// sprintf(buf, MSG(CREATED_PLUGDB), version);
sprintf(buf, " Created by CONNECT %s ", version);
@@ -893,12 +900,21 @@ int TDBXML::DeleteDB(PGLOBAL g, int irc)
if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
sprintf(g->Message, MSG(MISSING_ROWNODE), Irow);
return RC_FX;
- } else
+ } else {
TabNode->DeleteChild(g, RowNode);
+ if (Nlist->DropItem(g, Irow))
+ return RC_FX;
+
+ } // endif RowNode
+
Changed = true;
} else if (irc != RC_EF) {
TabNode->DeleteChild(g, RowNode);
+
+ if (Nlist->DropItem(g, Irow))
+ return RC_FX;
+
Changed = true;
} // endif's irc
diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h
index 1bce0824e49..5aa038530c7 100644
--- a/storage/connect/tabxml.h
+++ b/storage/connect/tabxml.h
@@ -6,8 +6,6 @@
/* */
/* This file contains the XML table classes declares. */
/***********************************************************************/
-#define TYPE_AM_XML (AMT)127
-
typedef class XMLDEF *PXMLDEF;
typedef class TDBXML *PTDBXML;
typedef class XMLCOL *PXMLCOL;
@@ -81,7 +79,7 @@ class DllExport TDBXML : public TDBASE {
virtual void ResetDB(void) {N = 0;}
virtual void ResetSize(void) {MaxSize = -1;}
virtual int RowNumber(PGLOBAL g, bool b = false);
- int LoadTableFile(PGLOBAL g);
+ int LoadTableFile(PGLOBAL g, char *filename);
bool Initialize(PGLOBAL g);
bool SetTabNode(PGLOBAL g);
void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node);
diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp
index 55f9da7571b..06c7776d2f5 100644
--- a/storage/connect/valblk.cpp
+++ b/storage/connect/valblk.cpp
@@ -447,13 +447,12 @@ int TYPBLK<TYPE>::Find(PVAL vp)
template <class TYPE>
int TYPBLK<TYPE>::GetMaxLength(void)
{
- char buf[12];
- int i, n;
+ char buf[32];
+ int i, n, m;
for (i = n = 0; i < Nval; i++) {
- sprintf(buf, Fmt, Typp[i]);
-
- n = max(n, (signed)strlen(buf));
+ m = sprintf(buf, Fmt, Typp[i]);
+ n = max(n, m);
} // endfor i
return n;