summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2013-10-11 13:57:56 +0200
committerOlivier Bertrand <bertrandop@gmail.com>2013-10-11 13:57:56 +0200
commitca4c54aaeed00e5a0b9f09c0e94ee12bf77cbf9d (patch)
tree63f98d1926b4eeed86ab5e5588027c5978b564d8
parent8619da05d94410bd57254e52dbdcb372f989e355 (diff)
downloadmariadb-git-ca4c54aaeed00e5a0b9f09c0e94ee12bf77cbf9d.tar.gz
- Fix bug when closing some table types
modified: storage/connect/tabmul.cpp storage/connect/tabmysql.cpp storage/connect/tabodbc.cpp - Add Insert support for ODBC table Add the send command feature to ODBC tables (not documented yet) modified: storage/connect/ha_connect.cc storage/connect/myconn.cpp storage/connect/odbccat.h storage/connect/odbconn.cpp storage/connect/odbconn.h storage/connect/tabodbc.cpp storage/connect/tabodbc.h
-rw-r--r--storage/connect/ha_connect.cc19
-rw-r--r--storage/connect/myconn.cpp2
-rw-r--r--storage/connect/odbccat.h1
-rw-r--r--storage/connect/odbconn.cpp282
-rw-r--r--storage/connect/odbconn.h8
-rwxr-xr-xstorage/connect/tabmul.cpp8
-rw-r--r--storage/connect/tabmysql.cpp26
-rw-r--r--storage/connect/tabodbc.cpp411
-rw-r--r--storage/connect/tabodbc.h93
9 files changed, 745 insertions, 105 deletions
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index a44310fd52b..00ab4d8124e 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1133,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;
@@ -1374,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))
@@ -3464,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);
@@ -3948,7 +3950,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)
@@ -3966,7 +3968,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);
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 5f121cf1d9d..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;
diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h
index 3665d1b283e..b3a170f4efe 100644
--- a/storage/connect/odbccat.h
+++ b/storage/connect/odbccat.h
@@ -4,5 +4,6 @@
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 397b7c2a2f4..990a212117e 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 */
/* */
@@ -229,7 +229,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)
@@ -318,6 +317,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. */
@@ -804,6 +814,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. */
/***********************************************************************/
@@ -822,7 +843,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
@@ -1208,8 +1229,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;
@@ -1244,26 +1264,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) {
- // Update or Delete statement
- rc = SQLRowCount(hstmt, &afrw);
+ 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 (!Check(rc))
- ThrowDBX(rc, "SQLRowCount", hstmt);
+ ThrowDBX(rc, "SQLExecute", hstmt);
- return afrw;
- } // endif ncol
+ } else {
+ do {
+ rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
+ } while (rc == SQL_STILL_EXECUTING);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLExecDirect", hstmt);
+
+ 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())
@@ -1411,10 +1449,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;
@@ -1454,13 +1515,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
@@ -1469,27 +1536,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
/***********************************************************************/
@@ -1541,6 +1640,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;
+ ULONG n;
+ SWORD ncol;
+ RETCODE rc;
+ HSTMT hstmt;
+
+ if (Open(dsn, 2) < 1) // 2 is openReadOnly
+ 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. */
/***********************************************************************/
@@ -1844,6 +2069,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/tabmul.cpp b/storage/connect/tabmul.cpp
index 146e2891ec7..14ebd8d891a 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 2ccb594439f..b1353a47013 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1043,18 +1043,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);
-
- 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();
+ 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
+
+ Myc.Close();
+ } // endif Myc
if (trace)
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index ae6817344b8..34157a19f10 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
/***********************************************************************/
@@ -107,9 +108,11 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
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);
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -125,7 +128,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 +166,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 +208,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 +307,6 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn)
DBQ = fn;
} // end of SetFile
-
/******************************************************************/
/* Convert an UTF-8 string to latin characters. */
/******************************************************************/
@@ -314,7 +321,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 +335,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 +440,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 +534,12 @@ void TDBODBC::ResetSize(void)
int TDBODBC::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
+ if (Srcdef) {
+ // Give a reasonable guess
+ MaxSize = 100;
+ return MaxSize;
+ } // endif Srcdef
+
if (!Ocp)
Ocp = new(g) ODBConn(g, this);
@@ -473,12 +565,11 @@ int TDBODBC::GetMaxSize(PGLOBAL g)
} // 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 +637,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 +693,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 +718,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 +748,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 +977,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 +988,242 @@ 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 = sscanf(To_Filter, "%s = '%[^\0]", 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 {