diff options
author | Olivier Bertrand <bertrandop@gmail.com> | 2013-10-11 13:57:56 +0200 |
---|---|---|
committer | Olivier Bertrand <bertrandop@gmail.com> | 2013-10-11 13:57:56 +0200 |
commit | ca4c54aaeed00e5a0b9f09c0e94ee12bf77cbf9d (patch) | |
tree | 63f98d1926b4eeed86ab5e5588027c5978b564d8 | |
parent | 8619da05d94410bd57254e52dbdcb372f989e355 (diff) | |
download | mariadb-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.cc | 19 | ||||
-rw-r--r-- | storage/connect/myconn.cpp | 2 | ||||
-rw-r--r-- | storage/connect/odbccat.h | 1 | ||||
-rw-r--r-- | storage/connect/odbconn.cpp | 282 | ||||
-rw-r--r-- | storage/connect/odbconn.h | 8 | ||||
-rwxr-xr-x | storage/connect/tabmul.cpp | 8 | ||||
-rw-r--r-- | storage/connect/tabmysql.cpp | 26 | ||||
-rw-r--r-- | storage/connect/tabodbc.cpp | 411 | ||||
-rw-r--r-- | storage/connect/tabodbc.h | 93 |
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 { |