|
|
/************ Odbconn C++ Functions Source Code File (.CPP) ************//* Name: ODBCONN.CPP Version 1.6 *//* *//* (C) Copyright to the author Olivier BERTRAND 1998-2013 *//* *//* This file contains the ODBC connection classes functions. *//***********************************************************************/
/***********************************************************************//* Include relevant MariaDB header file. *//***********************************************************************/#include "my_global.h"
#if defined(WIN32)
//nclude <io.h>
//nclude <fcntl.h>
#if defined(__BORLANDC__)
#define __MFC_COMPAT__ // To define min/max as macro
#endif
//#include <windows.h>
#else
#if defined(UNIX)
#include <errno.h>
#else
//nclude <io.h>
#endif
//nclude <fcntl.h>
#define NODW
#endif
/***********************************************************************//* Required objects includes. *//***********************************************************************/#include "global.h"
#include "plgdbsem.h"
#include "xobject.h"
//#include "kindex.h"
#include "xtable.h"
#include "tabodbc.h"
#include "odbccat.h"
#include "plgcnx.h" // For DB types
#include "resource.h"
#include "valblk.h"
#if defined(WIN32)
/***********************************************************************//* For dynamic load of ODBC32.DLL *//***********************************************************************/#pragma comment(lib, "odbc32.lib")
extern "C" HINSTANCE s_hModule; // Saved module handle
#endif // WIN32
/***********************************************************************//* Some macro's (should be defined elsewhere to be more accessible) *//***********************************************************************/#if defined(_DEBUG)
#define ASSERT(f) assert(f)
#define DEBUG_ONLY(f) (f)
#else // !_DEBUG
#define ASSERT(f) ((void)0)
#define DEBUG_ONLY(f) ((void)0)
#endif // !_DEBUG
extern "C" int trace;
/***********************************************************************//* GetSQLType: returns the SQL_TYPE corresponding to a PLG type. *//***********************************************************************/static short GetSQLType(int type) { short tp = SQL_TYPE_NULL;
switch (type) { case TYPE_STRING: tp = SQL_CHAR; break; 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_FLOAT: tp = SQL_DOUBLE; break; } // endswitch type
return tp; } // end of GetSQLType
/***********************************************************************//* GetSQLCType: returns the SQL_C_TYPE corresponding to a PLG type. *//***********************************************************************/static int GetSQLCType(int type) { int tp = SQL_TYPE_NULL;
switch (type) { case TYPE_STRING: tp = SQL_C_CHAR; break; case TYPE_SHORT: tp = SQL_C_SHORT; break; case TYPE_INT: tp = SQL_C_LONG; break; case TYPE_DATE: tp = SQL_C_TIMESTAMP; break; case TYPE_BIGINT: tp = SQL_C_SBIGINT; break; case TYPE_FLOAT: tp = SQL_C_DOUBLE; break; } // endswitch type
return tp; } // end of GetSQLCType
/***********************************************************************//* TranslateSQLType: translate a SQL Type to a PLG type. *//***********************************************************************/int TranslateSQLType(int stp, int prec, int& len) { int type;
switch (stp) { case SQL_CHAR: // 1
case SQL_VARCHAR: // 12
type = TYPE_STRING; break; case SQL_LONGVARCHAR: // (-1)
type = TYPE_STRING; len = min(abs(len), 255); break; case SQL_NUMERIC: // 2
case SQL_DECIMAL: // 3
type = (prec) ? TYPE_FLOAT : (len > 10) ? TYPE_BIGINT : TYPE_INT; break; case SQL_INTEGER: // 4
type = TYPE_INT; break; case SQL_SMALLINT: // 5
case SQL_TINYINT: // (-6)
case SQL_BIT: // (-7)
type = TYPE_SHORT; break; case SQL_FLOAT: // 6
case SQL_REAL: // 7
case SQL_DOUBLE: // 8
type = TYPE_FLOAT; break; case SQL_DATETIME: // 9
// case SQL_DATE: // 9
type = TYPE_DATE; len = 10; break; case SQL_INTERVAL: // 10
// case SQL_TIME: // 10
type = TYPE_STRING; len = 8 + ((prec) ? (prec+1) : 0); break; case SQL_TIMESTAMP: // 11
type = TYPE_DATE; len = 19 + ((prec) ? (prec+1) : 0); break; case SQL_BIGINT: // (-5)
type = TYPE_BIGINT; break; case SQL_UNKNOWN_TYPE: // 0
case SQL_BINARY: // (-2)
case SQL_VARBINARY: // (-3)
case SQL_LONGVARBINARY: // (-4)
// case SQL_BIT: // (-7)
case SQL_GUID: // (-11)
default: type = TYPE_ERROR; len = 0; } // endswitch type
return type; } // end of TranslateSQLType
/***********************************************************************//* ODBConn static members initialization. *//***********************************************************************///HENV ODBConn::m_henv = SQL_NULL_HENV;
//int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above
/***********************************************************************//* Allocate the structure used to refer to the result set. *//***********************************************************************/static CATPARM *AllocCatInfo(PGLOBAL g, CATINFO fid, char *tab, PQRYRES qrp) { size_t i, m, n; CATPARM *cap;
#if defined(_DEBUG)
assert(qrp);#endif
m = (size_t)qrp->Maxres; n = (size_t)qrp->Nbcol; cap = (CATPARM *)PlugSubAlloc(g, NULL, sizeof(CATPARM)); memset(cap, 0, sizeof(CATPARM)); cap->Id = fid; cap->Qrp = qrp; cap->Tab = (PUCHAR)tab; cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SDWORD *));
for (i = 0; i < n; i++) cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SDWORD));
cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD)); return cap; } // end of AllocCatInfo
/***********************************************************************//* Check for nulls and reset them to Null (?) values. *//***********************************************************************/static void ResetNullValues(CATPARM *cap) { int i, n, ncol; PCOLRES crp; PQRYRES qrp = cap->Qrp;
#if defined(_DEBUG)
assert(qrp);#endif
ncol = qrp->Nbcol;
for (i = 0, crp = qrp->Colresp; i < ncol && crp; i++, crp = crp->Next) for (n = 0; n < qrp->Nblin; n++) if (cap->Vlen[i][n] == SQL_NULL_DATA) crp->Kdata->Reset(n);
} // end of ResetNullValues
/***********************************************************************//* 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) { static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_TABNAME, FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM}; static unsigned int length[] = {0, 0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128}; int n, ncol = 12; int maxres; PQRYRES qrp; CATPARM *cap; ODBConn *ocp;
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ if (!info) { ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
// We fix a MySQL limit because some data sources return 32767
n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); maxres = (n) ? min(n, 4096) : 4096; n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); length[0] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); length[1] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); length[2] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); length[3] = (n) ? (n + 1) : 128; } else { // Info table
maxres = 0; length[0] = 128; length[1] = 128; length[2] = 128; length[3] = 128; } // endif ocp
if (trace) htrc("ODBCColumns: max=%d len=%d,%d,%d\n", maxres, length[0], length[1], length[2], length[3]);
/************************************************************************/ /* Allocate the structures used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS, buftyp, fldtyp, length, true, true);
if (info) // Info table
return qrp;
if (trace) htrc("Getting col results ncol=%d\n", qrp->Nbcol);
cap = AllocCatInfo(g, CAT_COL, table, qrp); cap->Pat = (PUCHAR)colpat;
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if ((n = ocp->GetCatInfo(cap)) >= 0) { qrp->Nblin = n; ResetNullValues(cap);
if (trace) htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
} else qrp = NULL;
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of ODBCColumns
#if 0
/**************************************************************************//* MyODBCCols: returns column info as required by ha_connect::pre_create. *//**************************************************************************/PQRYRES MyODBCCols(PGLOBAL g, char *dsn, char *tab, bool info) {// int i, type, len, prec;
// PCOLRES crp, crpt, crpl, crpp;
PQRYRES qrp; ODBConn *ocp;
/**********************************************************************/ /* Open the connection with the ODBC data source. */ /**********************************************************************/ if (!info) { ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
} else ocp = NULL;
/**********************************************************************/ /* Get the information about the ODBC table columns. */ /**********************************************************************/ if ((qrp = ODBCColumns(g, ocp, dsn, tab, NULL)) && ocp) dsn = ocp->GetConnect(); // Complete connect string
/************************************************************************/ /* Close the local connection. */ /************************************************************************/ if (ocp) ocp->Close();
if (!qrp) return NULL; // Error in ODBCColumns
/************************************************************************/ /* Keep only the info used by ha_connect::pre_create. */ /************************************************************************/ qrp->Colresp = qrp->Colresp->Next->Next; // Skip Owner and Table names
crpt = qrp->Colresp->Next; // SQL type
crpl = crpt->Next->Next; // Length
crpp = crpl->Next->Next; // Decimals
for (int i = 0; i < qrp->Nblin; i++) { // Types must be PLG types, not SQL types
type = crpt->Kdata->GetIntValue(i); len = crpl->Kdata->GetIntValue(i); prec = crpp->Kdata->GetIntValue(i); type = TranslateSQLType(type, prec, len); crpt->Kdata->SetValue(type, i);
// Some data sources do not count prec in length
if (type == TYPE_FLOAT) len += (prec + 2); // To be safe
// Could have been changed for blobs or numeric
crpl->Kdata->SetValue(len, i); } // endfor i
crpp->Next = crpp->Next->Next->Next; // Should be Remark
// Renumber crp's for flag comparison
for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) crp->Ncol = ++i;
qrp->Nbcol = i; // Should be 7; was 11, skipped 4
return qrp; } // end of MyODBCCols
#endif // 0
/*************************************************************************//* ODBCDataSources: constructs the result blocks containing all ODBC *//* data sources available on the local host. *//* Called with info=true to have result column names. *//*************************************************************************/PQRYRES ODBCDataSources(PGLOBAL g, bool info) { static int buftyp[] = {TYPE_STRING, TYPE_STRING}; static XFLD fldtyp[] = {FLD_NAME, FLD_REM}; static unsigned int length[] = {0, 256}; int n = 0, ncol = 2; int maxres; PQRYRES qrp; ODBConn *ocp = NULL;
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ if (!info) { ocp = new(g) ODBConn(g, NULL); n = ocp->GetMaxValue(SQL_MAX_DSN_LENGTH); length[0] = (n) ? (n + 1) : 256; maxres = 512; // Estimated max number of data sources
} else { length[0] = 256; maxres = 0; } // endif info
if (trace) htrc("ODBCDataSources: max=%d len=%d\n", maxres, length[0]);
/************************************************************************/ /* Allocate the structures used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC, buftyp, fldtyp, length, true, true);
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if (!info && ocp->GetDataSources(qrp)) qrp = NULL;
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of ODBCDataSources
/*************************************************************************//* ODBCDrivers: constructs the result blocks containing all ODBC *//* drivers available on the local host. *//* Called with info=true to have result column names. *//*************************************************************************/PQRYRES ODBCDrivers(PGLOBAL g, bool info) { static int buftyp[] = {TYPE_STRING, TYPE_STRING}; static XFLD fldtyp[] = {FLD_NAME, FLD_REM}; static unsigned int length[] = {128, 256}; int ncol = 2; int maxres; PQRYRES qrp; ODBConn *ocp = NULL;
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ if (!info) { ocp = new(g) ODBConn(g, NULL); maxres = 256; // Estimated max number of drivers
} else maxres = 0;
if (trace) htrc("ODBCDrivers: max=%d len=%d\n", maxres, length[0]);
/************************************************************************/ /* Allocate the structures used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_DRIVER, buftyp, fldtyp, length, true, true);
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if (!info && ocp->GetDrivers(qrp)) qrp = NULL;
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of ODBCDrivers
/***********************************************************************//* ODBCTables: constructs the result blocks containing all tables in *//* an ODBC database that will be retrieved by GetData commands. *//* Note: The first two columns (Qualifier, Owner) are ignored. *//***********************************************************************/PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info) { static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING}; static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_NAME, FLD_TYPE, FLD_REM}; static unsigned int length[] = {0, 0, 0, 16, 128}; int n, ncol = 5; int maxres; PQRYRES qrp; CATPARM *cap; ODBConn *ocp;
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ if (!info) { /**********************************************************************/ /* Open the connection with the ODBC data source. */ /**********************************************************************/ ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
maxres = 512; // This is completely arbitrary
n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); length[0] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); length[1] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); length[2] = (n) ? (n + 1) : 128; } else { maxres = 0; length[0] = 128; length[1] = 128; length[2] = 128; } // endif info
if (trace) htrc("ODBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]);
/************************************************************************/ /* Allocate the structures used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, buftyp, fldtyp, length, true, true);
if (info) return qrp;
cap = AllocCatInfo(g, CAT_TAB, tabpat, qrp);//cap->Pat = (PUCHAR)tabtyp;
if (trace) htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol);
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if ((n = ocp->GetCatInfo(cap)) >= 0) { qrp->Nblin = n; ResetNullValues(cap);
if (trace) htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
} else qrp = NULL;
/************************************************************************/ /* Close any local connection. */ /************************************************************************/ ocp->Close();
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of ODBCTables
#if 0 // Currently not used by CONNECT
/**************************************************************************//* PrimaryKeys: constructs the result blocks containing all the *//* ODBC catalog information concerning primary keys. *//**************************************************************************/PQRYRES ODBCPrimaryKeys(PGLOBAL g, ODBConn *op, char *dsn, char *table) { static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING}; static unsigned int length[] = {0, 0, 0, 0, 6, 128}; int n, ncol = 5; int maxres; PQRYRES qrp; CATPARM *cap; ODBConn *ocp = op;
if (!op) { /**********************************************************************/ /* Open the connection with the ODBC data source. */ /**********************************************************************/ ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
} // endif op
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); maxres = (n) ? (int)n : 250; n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); length[0] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); length[1] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); length[2] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); length[3] = (n) ? (n + 1) : 128;
if (trace) htrc("ODBCPrimaryKeys: max=%d len=%d,%d,%d\n", maxres, length[0], length[1], length[2]);
/************************************************************************/ /* Allocate the structure used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY, buftyp, NULL, length, true, true);
if (trace) htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
cap = AllocCatInfo(g, CAT_KEY, table, qrp);
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if ((n = ocp->GetCatInfo(cap)) >= 0) { qrp->Nblin = n; ResetNullValues(cap);
if (trace) htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
} else qrp = NULL;
/************************************************************************/ /* Close any local connection. */ /************************************************************************/ if (!op) ocp->Close();
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of ODBCPrimaryKeys
/**************************************************************************//* Statistics: constructs the result blocks containing statistics *//* about one or several tables to be retrieved by GetData commands. *//**************************************************************************/PQRYRES ODBCStatistics(PGLOBAL g, ODBConn *op, char *dsn, char *pat, int un, int acc) { static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_STRING, TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_STRING}; static unsigned int length[] = {0, 0, 0 ,6 ,0 ,0 ,6 ,6 ,0 ,2 ,10 ,10 ,128}; int n, ncol = 13; int maxres; PQRYRES qrp; CATPARM *cap; ODBConn *ocp = op;
if (!op) { /**********************************************************************/ /* Open the connection with the ODBC data source. */ /**********************************************************************/ ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
} // endif op
/************************************************************************/ /* Do an evaluation of the result size. */ /************************************************************************/ n = 1 + ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_INDEX); maxres = (n) ? (int)n : 32; n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); length[1] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); length[2] = length[5] = (n) ? (n + 1) : 128; n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); length[0] = length[4] = (n) ? (n + 1) : length[2]; n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); length[7] = (n) ? (n + 1) : 128;
if (trace) htrc("SemStatistics: max=%d pat=%s\n", maxres, SVP(pat));
/************************************************************************/ /* Allocate the structure used to refer to the result set. */ /************************************************************************/ qrp = PlgAllocResult(g, ncol, maxres, IDS_STAT, buftyp, NULL, length, true, true);
if (trace) htrc("Getting stat results ncol=%d\n", qrp->Nbcol);
cap = AllocCatInfo(g, CAT_STAT, pat, qrp); cap->Unique = (un < 0) ? SQL_INDEX_UNIQUE : (UWORD)un; cap->Accuracy = (acc < 0) ? SQL_QUICK : (UWORD)acc;
/************************************************************************/ /* Now get the results into blocks. */ /************************************************************************/ if ((n = ocp->GetCatInfo(cap)) >= 0) { qrp->Nblin = n; ResetNullValues(cap);
if (trace) htrc("Statistics: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
} else qrp = NULL;
/************************************************************************/ /* Close any local connection. */ /************************************************************************/ if (!op) ocp->Close();
/************************************************************************/ /* Return the result pointer for use by GetData routines. */ /************************************************************************/ return qrp; } // end of Statistics
#endif // 0
/***********************************************************************//* Implementation of DBX class. *//***********************************************************************/DBX::DBX(RETCODE rc, PSZ msg) { m_RC = rc; m_Msg = msg;
for (int i = 0; i < MAX_NUM_OF_MSG; i++) m_ErrMsg[i] = NULL;
} // end of DBX constructor
/***********************************************************************//* This function is called by ThrowDBX. *//***********************************************************************/void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt) { if (pdb) { SWORD len; RETCODE rc; UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1]; UCHAR state[SQL_SQLSTATE_SIZE + 1]; SDWORD native; PGLOBAL g = pdb->m_G;
rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state, &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
if (rc != SQL_INVALID_HANDLE) { // Skip non-errors
for (int i = 0; i < MAX_NUM_OF_MSG && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) && strcmp((char*)state, "00000"); i++) { m_ErrMsg[i] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1); strcpy(m_ErrMsg[i], (char*)msg);
if (trace) htrc("%s: %s, Native=%d\n", state, msg, native);
rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state, &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
} // endfor i
return; } else { snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg, MSG(BAD_HANDLE_VAL)); m_ErrMsg[0] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1); strcpy(m_ErrMsg[0], (char*)msg);
if (trace) htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
return; } // endif rc
} else m_ErrMsg[0] = "No connexion address provided";
if (trace) htrc("%s: rc=%hd (%s)\n", SVP(m_Msg), m_RC, SVP(m_ErrMsg[0]));
} // end of BuildErrorMessage
/***********************************************************************//* ODBConn construction/destruction. *//***********************************************************************/ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp) { m_G = g; m_Tdb = tdbp; m_henv = SQL_NULL_HENV; m_hdbc = SQL_NULL_HDBC;//m_Recset = NULL
m_hstmt = SQL_NULL_HSTMT; m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT; m_QueryTimeout = DEFAULT_QUERY_TIMEOUT; m_UpdateOptions = 0; m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10); m_Catver = (tdbp) ? tdbp->Catver : 0; m_Connect = NULL; m_Updatable = true;//m_Transactions = false;
m_IDQuoteChar = '\'';//*m_ErrMsg = '\0';
} // end of ODBConn
//ODBConn::~ODBConn()
// {
//if (Connected())
// EndCom();
// } // end of ~ODBConn
/***********************************************************************//* Screen for errors. *//***********************************************************************/bool ODBConn::Check(RETCODE rc) { switch (rc) { case SQL_SUCCESS_WITH_INFO: if (trace > 1) { DBX x(rc);
x.BuildErrorMessage(this, m_hstmt); htrc("ODBC Success With Info, hstmt=%p %s\n", m_hstmt, x.GetErrorMessage(0)); } // endif trace
// Fall through
case SQL_SUCCESS: case SQL_NO_DATA_FOUND: return true; } // endswitch rc
return false; } // end of Check
/***********************************************************************//* DB exception throw routines. *//***********************************************************************/void ODBConn::ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt) { DBX* xp = new(m_G) DBX(rc, msg);
xp->BuildErrorMessage(this, hstmt); throw xp; } // end of ThrowDBX
void ODBConn::ThrowDBX(PSZ msg) { DBX* xp = new(m_G) DBX(0, msg);
xp->m_ErrMsg[0] = msg; throw xp; } // end of ThrowDBX
/***********************************************************************//* Utility routine. *//***********************************************************************/PSZ ODBConn::GetStringInfo(ushort infotype) {//ASSERT(m_hdbc != SQL_NULL_HDBC);
char *p, buffer[MAX_STRING_INFO]; SWORD result; RETCODE rc;
rc = SQLGetInfo(m_hdbc, infotype, buffer, sizeof(buffer), &result);
if (!Check(rc)) { ThrowDBX(rc, "SQLGetInfo"); // Temporary
// *buffer = '\0';
} // endif rc
p = (char *)PlugSubAlloc(m_G, NULL, strlen(buffer) + 1); strcpy(p, buffer); return p; } // end of GetStringInfo
/***********************************************************************//* Utility routine. *//***********************************************************************/int ODBConn::GetMaxValue(ushort infotype) {//ASSERT(m_hdbc != SQL_NULL_HDBC);
ushort maxval; RETCODE rc;
rc = SQLGetInfo(m_hdbc, infotype, &maxval, 0, NULL);
if (!Check(rc)) maxval = 0;
return (int)maxval; } // end of GetMaxValue
/***********************************************************************//* Utility routines. *//***********************************************************************/void ODBConn::OnSetOptions(HSTMT hstmt) { RETCODE rc; ASSERT(m_hdbc != SQL_NULL_HDBC);
if ((signed)m_QueryTimeout != -1) { // Attempt to set query timeout. Ignore failure
rc = SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT, m_QueryTimeout);
if (!Check(rc)) // don't attempt it again
m_QueryTimeout = (DWORD)-1;
} // endif m_QueryTimeout
if (m_RowsetSize > 0) { // Attempt to set rowset size.
// In case of failure reset it to 0 to use Fetch.
rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
if (!Check(rc)) // don't attempt it again
m_RowsetSize = 0;
} // endif m_RowsetSize
} // end of OnSetOptions
/***********************************************************************//* Open: connect to a data source. *//***********************************************************************/int ODBConn::Open(PSZ ConnectString, DWORD options) { PGLOBAL& g = m_G;//ASSERT_VALID(this);
//ASSERT(ConnectString == NULL || AfxIsValidString(ConnectString));
ASSERT(!(options & noOdbcDialog && options & forceOdbcDialog));
m_Updatable = !(options & openReadOnly); m_Connect = ConnectString;
// Allocate the HDBC and make connection
try { PSZ ver;
AllocConnect(options); ver = GetStringInfo(SQL_ODBC_VER);
if (Connect(options)) { strcpy(g->Message, MSG(CONNECT_CANCEL)); return 0; } // endif
ver = GetStringInfo(SQL_DRIVER_ODBC_VER); } catch(DBX *xp) {// strcpy(g->Message, xp->m_ErrMsg[0]);
strcpy(g->Message, xp->GetErrorMessage(0)); Close();// Free();
return -1; } // end try-catch
// Verify support for required functionality and cache info
VerifyConnect(); GetConnectInfo(); return 1; } // end of Open
/***********************************************************************//* Allocate an henv (first time called) and hdbc. *//***********************************************************************/void ODBConn::AllocConnect(DWORD Options) { if (m_hdbc != SQL_NULL_HDBC) return;
RETCODE rc;//AfxLockGlobals(CRIT_ODBC);
// Need to allocate an environment for first connection
if (m_henv == SQL_NULL_HENV) {// ASSERT(m_nAlloc == 0);
rc = SQLAllocEnv(&m_henv);
if (!Check(rc)) {// AfxUnlockGlobals(CRIT_ODBC);
ThrowDBX(rc, "SQLAllocEnv"); // Fatal
} // endif rc
} // endif m_henv
// Do the real thing, allocating connection data
rc = SQLAllocConnect(m_henv, &m_hdbc);
if (!Check(rc)) {// AfxUnlockGlobals(CRIT_ODBC);
ThrowDBX(rc, "SQLAllocConnect"); // Fatal
} // endif rc
//m_nAlloc++; // allocated at last
//AfxUnlockGlobals(CRIT_ODBC);
#if defined(_DEBUG)
if (Options & traceSQL) { SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE, (DWORD)"xodbc.out"); SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1); } // endif
#endif // _DEBUG
rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) htrc("Warning: Failure setting login timeout\n");
if (!m_Updatable) { rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY); if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) htrc("Warning: Failure setting read only access mode\n");
} // endif
// Turn on cursor lib support
if (Options & useCursorLib) rc = SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC);
return; } // end of AllocConnect
/***********************************************************************//* Connect to data source using SQLDriverConnect. *//***********************************************************************/bool ODBConn::Connect(DWORD Options) { RETCODE rc; SWORD nResult; PUCHAR ConnOut = (PUCHAR)PlugSubAlloc(m_G, NULL, MAX_CONNECT_LEN); UWORD wConnectOption = SQL_DRIVER_COMPLETE;#if defined(WIN32)
HWND hWndTop = GetForegroundWindow(); HWND hWnd = GetParent(hWndTop);
if (hWnd == NULL) hWnd = GetDesktopWindow();#else // !WIN32
HWND hWnd = NULL;#endif // !WIN32
PGLOBAL& g = m_G; PDBUSER dup = PlgGetUser(g);
if (Options & noOdbcDialog || dup->Remote) wConnectOption = SQL_DRIVER_NOPROMPT; else if (Options & forceOdbcDialog) wConnectOption = SQL_DRIVER_PROMPT;
rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect, SQL_NTS, ConnOut, MAX_CONNECT_LEN, &nResult, wConnectOption);
#if defined(WIN32)
if (hWndTop) EnableWindow(hWndTop, true);#endif // WIN32
// If user hit 'Cancel'
if (rc == SQL_NO_DATA_FOUND) { Close();// Free();
return true; } // endif rc
if (!Check(rc)) ThrowDBX(rc, "SQLDriverConnect");
// Save connect string returned from ODBC
m_Connect = (PSZ)ConnOut;
// All done
return false; } // end of Connect
void ODBConn::VerifyConnect() {#if defined(NEWMSG) || defined(XMSG)
PGLOBAL& g = m_G;#endif // NEWMSG || XMSG
RETCODE rc; SWORD result; SWORD conformance;
rc = SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE, &conformance, sizeof(conformance), &result);
if (!Check(rc)) ThrowDBX(rc, "SQLGetInfo");
if (conformance < SQL_OAC_LEVEL1) ThrowDBX(MSG(API_CONF_ERROR));
rc = SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE, &conformance, sizeof(conformance), &result);
if (!Check(rc)) ThrowDBX(rc, "SQLGetInfo");
if (conformance < SQL_OSC_MINIMUM) ThrowDBX(MSG(SQL_CONF_ERROR));
} // end of VerifyConnect
void ODBConn::GetConnectInfo() { RETCODE rc; SWORD nResult;#if 0 // Update not implemented yet
UDWORD DrvPosOp;
// Reset the database update options
m_UpdateOptions = 0;
// Check for SQLSetPos support
rc = SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS, &DrvPosOp, sizeof(DrvPosOp), &nResult);
if (Check(rc) && (DrvPosOp & SQL_POS_UPDATE) && (DrvPosOp & SQL_POS_DELETE) && (DrvPosOp & SQL_POS_ADD)) m_UpdateOptions = SQL_SETPOSUPDATES;
// Check for positioned update SQL support
UDWORD PosStatements;
rc = SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS, &PosStatements, sizeof(PosStatements), &nResult);
if (Check(rc) && (PosStatements & SQL_PS_POSITIONED_DELETE) && (PosStatements & SQL_PS_POSITIONED_UPDATE)) m_UpdateOptions |= SQL_POSITIONEDSQL;
if (m_Updatable) { // Make sure data source is Updatable
char ReadOnly[10];
rc = SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY, ReadOnly, sizeof(ReadOnly), &nResult);
if (Check(rc) && nResult == 1) m_Updatable = !!strcmp(ReadOnly, "Y"); else m_Updatable = false;
if (trace) htrc("Warning: data source is readonly\n");
} else // Make data source is !Updatable
rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY);#endif // 0
// Cache the quote char to use when constructing SQL
char QuoteChar[2];
rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR, QuoteChar, sizeof(QuoteChar), &nResult);
if (Check(rc) && nResult == 1) m_IDQuoteChar = QuoteChar[0]; else m_IDQuoteChar = ' ';
if (trace) htrc("DBMS: %s, Version: %s", GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER));
} // end of GetConnectInfo
/***********************************************************************//* Allocate record set and execute an SQL query. *//***********************************************************************/int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols) { PGLOBAL& g = m_G; void *buffer; bool b; UWORD n; SWORD ncol, len, tp; SQLLEN afrw; ODBCCOL *colp; RETCODE rc; HSTMT hstmt;
//m_Recset = new(m_G) RECSET(this);
//ASSERT(m_Recset);
try { b = false;
if (m_hstmt) { RETCODE rc;
// All this did not seems to make sense and was been commented out
// if (IsOpen())
// Close(SQL_CLOSE);
rc = SQLFreeStmt(m_hstmt, SQL_CLOSE); hstmt = m_hstmt; m_hstmt = NULL; ThrowDBX(MSG(SEQUENCE_ERROR)); } else { rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc)) ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
} // endif hstmt
OnSetOptions(hstmt); b = true;
if (trace) htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
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);
if (ncol == 0) { // Update or Delete statement
rc = SQLRowCount(hstmt, &afrw);
if (!Check(rc)) ThrowDBX(rc, "SQLRowCount", hstmt);
return afrw; } // endif ncol
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext()) if (!colp->IsSpecial()) n++;
// n can be 0 for query such as Select count(*) from table
if (n && n != (UWORD)ncol) ThrowDBX(MSG(COL_NUM_MISM));
// Now bind the column buffers
for (n = 1, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext()) if (!colp->IsSpecial()) { buffer = colp->GetBuffer(m_RowsetSize); len = colp->GetBuflen(); tp = GetSQLCType(colp->GetResultType());
if (tp == SQL_TYPE_NULL) { sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), colp->GetResultType(), SVP(colp->GetName())); ThrowDBX(m_G->Message); } // endif tp
if (trace) htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n", n, tp, buffer, len, colp->GetStrLen());
rc = SQLBindCol(hstmt, n, tp, buffer, len, colp->GetStrLen());
if (!Check(rc)) ThrowDBX(rc, "SQLBindCol", hstmt);
n++; } // endif pcol
} 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(m_G->Message, x->GetErrorMessage(0));
if (b) SQLCancel(hstmt);
rc = SQLFreeStmt(hstmt, SQL_DROP); m_hstmt = NULL; return -1; } // end try/catch
m_hstmt = hstmt; return (int)m_RowsetSize; // May have been reset in OnSetOptions
} // end of ExecDirectSQL
/***********************************************************************//* Get the number of lines of the result set. *//***********************************************************************/int ODBConn::GetResultSize(char *sql, ODBCCOL *colp) { int n = 0; RETCODE rc;
if (ExecDirectSQL(sql, colp) < 0) return -1;
try { for (n = 0; ; n++) { do { rc = SQLFetch(m_hstmt); } while (rc == SQL_STILL_EXECUTING);
if (!Check(rc)) ThrowDBX(rc, "SQLFetch", m_hstmt);
if (rc == SQL_NO_DATA_FOUND) break;
} // endfor n
} catch(DBX *x) {// strcpy(m_G->Message, x->m_ErrMsg[0]);
strcpy(m_G->Message, x->GetErrorMessage(0)); if (trace) for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) htrc(x->m_ErrMsg[i]);
SQLCancel(m_hstmt); n = -2; } // end try/catch
rc = SQLFreeStmt(m_hstmt, SQL_DROP); m_hstmt = NULL;
if (n != 1) return -3; else return colp->GetIntValue();
} // end of GetResultSize
/***********************************************************************//* Fetch next row. *//***********************************************************************/int ODBConn::Fetch() { ASSERT(m_hstmt); int irc; SQLULEN crow; RETCODE rc; PGLOBAL& g = m_G;
try {// do {
if (m_RowsetSize) { rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL); } else { rc = SQLFetch(m_hstmt); crow = 1; } // endif m_RowsetSize
// } while (rc == SQL_STILL_EXECUTING);
if (trace > 1) htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n", m_hstmt, m_RowsetSize, rc);
if (!Check(rc)) ThrowDBX(rc, "Fetch", m_hstmt);
irc = (rc == SQL_NO_DATA_FOUND) ? 0 : (int)crow; } 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)); irc = -1; } // end try/catch
return irc; } // end of Fetch
/***********************************************************************//* Prepare an SQL statement for insert. *//***********************************************************************/int ODBConn::PrepareSQL(char *sql) { PGLOBAL& g = m_G; bool b; SWORD nparm; RETCODE rc; HSTMT hstmt;
try { b = false;
if (m_hstmt) { RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
hstmt = m_hstmt; m_hstmt = NULL; ThrowDBX(MSG(SEQUENCE_ERROR)); } else { rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc)) ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
} // endif hstmt
OnSetOptions(hstmt); b = true;
if (trace) htrc("Prepare hstmt=%p %.64s\n", hstmt, sql);
do { rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS); } while (rc == SQL_STILL_EXECUTING);
if (!Check(rc)) ThrowDBX(rc, "SQLPrepare", hstmt);
do { rc = SQLNumParams(hstmt, &nparm); } while (rc == SQL_STILL_EXECUTING);
} 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(m_G->Message, x->GetErrorMessage(0));
if (b) SQLCancel(hstmt);
rc = SQLFreeStmt(hstmt, SQL_DROP); m_hstmt = NULL; return -1; } // end try/catch
m_hstmt = hstmt; return (int)nparm; } // end of PrepareSQL
/***********************************************************************//* Bind a parameter for inserting. *//***********************************************************************/bool ODBConn::ExecuteSQL(void) { RETCODE rc;
try { rc = SQLExecute(m_hstmt);
if (!Check(rc)) ThrowDBX(rc, "SQLExecute", m_hstmt);
} catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); SQLCancel(m_hstmt); rc = SQLFreeStmt(m_hstmt, SQL_DROP); m_hstmt = NULL; return true; } // end try/catch
return false; } // end of ExecuteSQL
/***********************************************************************//* Bind a parameter for inserting. *//***********************************************************************/bool ODBConn::BindParam(ODBCCOL *colp) { void *buf; UWORD n = colp->GetRank(); SWORD ct, sqlt; UDWORD len; SQLLEN *strlen = colp->GetStrLen(); RETCODE rc;
#if 0
try { SWORD dec, nul; rc = SQLDescribeParam(m_hstmt, n, &sqlt, &len, &dec, &nul);
if (!Check(rc)) ThrowDBX(rc, m_hstmt);
} catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); } // end try/catch
#endif // 0
buf = colp->GetBuffer(0);// len = colp->GetBuflen();
len = IsTypeNum(colp->GetResultType()) ? 0 : colp->GetBuflen(); ct = GetSQLCType(colp->GetResultType()); sqlt = GetSQLType(colp->GetResultType()); *strlen = IsTypeNum(colp->GetResultType()) ? 0 : SQL_NTS;
try { rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt, len, 0, buf, 0, strlen);
if (!Check(rc)) ThrowDBX(rc, "SQLBindParameter", m_hstmt);
} catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); SQLCancel(m_hstmt); rc = SQLFreeStmt(m_hstmt, SQL_DROP); m_hstmt = NULL; return true; } // end try/catch
return false; } // end of BindParam
/***********************************************************************//* Get the list of Data Sources and set it in qrp. *//***********************************************************************/bool ODBConn::GetDataSources(PQRYRES qrp) { bool rv = false; UCHAR *dsn, *des; UWORD dir = SQL_FETCH_FIRST; SWORD n1, n2, p1, p2; PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next; RETCODE rc;
n1 = crp1->Clen; n2 = crp2->Clen;
try { rc = SQLAllocEnv(&m_henv);
if (!Check(rc)) ThrowDBX(rc, "SQLAllocEnv"); // Fatal
for (int i = 0; i < qrp->Maxres; i++) { dsn = (UCHAR*)crp1->Kdata->GetValPtr(i); des = (UCHAR*)crp2->Kdata->GetValPtr(i); rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2);
if (rc == SQL_NO_DATA_FOUND) break; else if (!Check(rc)) ThrowDBX(rc, "SQLDataSources");
qrp->Nblin++; dir = SQL_FETCH_NEXT; } // endfor i
} catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); rv = true; } // end try/catch
//SQLFreeEnv(m_henv);
Close(); return rv; } // end of GetDataSources
/***********************************************************************//* Get the list of Drivers and set it in qrp. *//***********************************************************************/bool ODBConn::GetDrivers(PQRYRES qrp) { int i, n; bool rv = false; UCHAR *des, *att; UWORD dir = SQL_FETCH_FIRST; SWORD n1, n2, p1, p2; PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next; RETCODE rc;
n1 = crp1->Clen; n2 = crp2->Clen;
try { rc = SQLAllocEnv(&m_henv);
if (!Check(rc)) ThrowDBX(rc, "SQLAllocEnv"); // Fatal
for (n = 0; n < qrp->Maxres; n++) { des = (UCHAR*)crp1->Kdata->GetValPtr(n); att = (UCHAR*)crp2->Kdata->GetValPtr(n); rc = SQLDrivers(m_henv, dir, des, n1, &p1, att, n2, &p2);
if (rc == SQL_NO_DATA_FOUND) break; else if (!Check(rc)) ThrowDBX(rc, "SQLDrivers");
// The attributes being separated by '\0', set them to ';'
for (i = 0; i < p2; i++) if (!att[i]) att[i] = ';';
qrp->Nblin++; dir = SQL_FETCH_NEXT; } // endfor n
} catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); rv = true; } // end try/catch
//SQLFreeEnv(m_henv);
Close(); return rv; } // end of GetDrivers
/***********************************************************************//* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. *//***********************************************************************/int ODBConn::GetCatInfo(CATPARM *cap) {#if defined(NEWMSG) || defined(XMSG)
PGLOBAL& g = m_G;#endif // NEWMSG || XMSG
void *buffer; int i, irc; bool b; PSZ fnc = "Unknown"; UWORD n; SWORD ncol, len, tp; SQLULEN crow; PCOLRES crp; RETCODE rc; HSTMT hstmt = NULL; SQLLEN *vl, *vlen; PVAL *pval = NULL;
try { b = false;
if (!m_hstmt) { rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc)) ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
} else ThrowDBX(MSG(SEQUENCE_ERROR));
b = true;
if ((m_RowsetSize = cap->Qrp->Maxres) > 0) { if (m_Catver) { // Attempt to set rowset size.
// In case of failure reset it to 0 to use Fetch.
if (m_Catver == 3) // ODBC Ver 3
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)m_RowsetSize, 0); else rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
if (!Check(rc)) m_RowsetSize = 1; // don't attempt it again
// ThrowDBX(rc, hstmt); // Temporary
if (m_Catver == 3) { // ODBC Ver 3
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, cap->Status, 0); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &crow, 0); } // endif m_Catver
} else // ORABUG
m_RowsetSize = 1;
} else ThrowDBX("0-sized result");
// Now do call the proper ODBC API
switch (cap->Id) { case CAT_TAB:// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
// (SQLPOINTER)false, 0);
fnc = "SQLTables"; rc = SQLTables(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, cap->Pat, SQL_NTS); break; case CAT_COL:// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
// (SQLPOINTER)true, 0);
fnc = "SQLColumns"; rc = SQLColumns(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, cap->Pat, SQL_NTS); break; case CAT_KEY: fnc = "SQLPrimaryKeys"; rc = SQLPrimaryKeys(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS); break; case CAT_STAT: fnc = "SQLStatistics"; rc = SQLStatistics(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, cap->Unique, cap->Accuracy); break; case CAT_SPC: ThrowDBX("SQLSpecialColumns not available yet"); } // endswitch infotype
if (!Check(rc)) ThrowDBX(rc, fnc, hstmt);
rc = SQLNumResultCols(hstmt, &ncol);
// n because we no more ignore the first column
if ((n = (UWORD)cap->Qrp->Nbcol) > (UWORD)ncol) ThrowDBX(MSG(COL_NUM_MISM));
if (m_RowsetSize == 1 && cap->Qrp->Maxres > 1) { pval = (PVAL *)PlugSubAlloc(m_G, NULL, n * sizeof(PVAL)); vlen = (SQLLEN *)PlugSubAlloc(m_G, NULL, n * sizeof(SDWORD *)); } // endif
// Now bind the column buffers
for (n = 0, crp = cap->Qrp->Colresp; crp; crp = crp->Next) { if (pval) { pval[n] = AllocateValue(m_G, crp->Kdata->GetType(), crp->Kdata->GetVlen(), 0); buffer = pval[n]->GetTo_Val(); vl = vlen + n; } else { buffer = crp->Kdata->GetValPointer(); vl = cap->Vlen[n]; } // endif pval
len = GetTypeSize(crp->Type, crp->Clen); tp = GetSQLCType(crp->Type);
if (tp == SQL_TYPE_NULL) { sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name); ThrowDBX(m_G->Message); } // endif tp
// n + 1 because column numbers begin with 1
rc = SQLBindCol(hstmt, n + 1, tp, buffer, len, vl);
if (!Check(rc)) ThrowDBX(rc, "SQLBindCol", hstmt);
n++; } // endfor crp
fnc = "SQLFetch";
// Now fetch the result
if (m_Catver != 3) { if (m_RowsetSize > 1) { fnc = "SQLExtendedFetch"; rc = SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &crow, cap->Status); } else if (pval) { for (n = 0; n < cap->Qrp->Maxres; n++) { if ((rc = SQLFetch(hstmt)) != SQL_SUCCESS) break;
for (i = 0, crp = cap->Qrp->Colresp; crp; i++, crp = crp->Next) { crp->Kdata->SetValue(pval[i], n); cap->Vlen[i][n] = vlen[i]; } // endfor crp
} // endfor n
if ((crow = n) && rc == SQL_NO_DATA) rc = SQL_SUCCESS;
} else { rc = SQLFetch(hstmt); crow = 1; } // endif's
} else // ODBC Ver 3
rc = SQLFetch(hstmt);
// if (!Check(rc))
if (rc == SQL_NO_DATA_FOUND) { if (cap->Pat) sprintf(m_G->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat); else sprintf(m_G->Message, MSG(NO_TAB_DATA), cap->Tab);
ThrowDBX(m_G->Message); } else if (rc != SQL_SUCCESS) ThrowDBX(rc, fnc, hstmt);
irc = (int)crow; } 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(m_G->Message, x->GetErrorMessage(0)); irc = -1; } // end try/catch
if (b) SQLCancel(hstmt);
// All this (hstmt vs> m_hstmt) to be revisited
if (hstmt) rc = SQLFreeStmt(hstmt, SQL_DROP);
return irc; } // end of GetCatInfo
/***********************************************************************//* Disconnect connection *//***********************************************************************/void ODBConn::Close() { RETCODE rc;
#if 0
// Close any open recordsets
AfxLockGlobals(CRIT_ODBC); TRY { while (!m_listRecordsets.IsEmpty()) { CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead(); pSet->Close(); // will implicitly remove from list
pSet->m_pDatabase = NULL; } } CATCH_ALL(e) { AfxUnlockGlobals(CRIT_ODBC); THROW_LAST(); } END_CATCH_ALL AfxUnlockGlobals(CRIT_ODBC);#endif // 0
if (m_hstmt) { // Is required for multiple tables
rc = SQLFreeStmt(m_hstmt, SQL_DROP); m_hstmt = NULL; } // endif m_hstmt
if (m_hdbc != SQL_NULL_HDBC) { rc = SQLDisconnect(m_hdbc); rc = SQLFreeConnect(m_hdbc); m_hdbc = SQL_NULL_HDBC;
// AfxLockGlobals(CRIT_ODBC);
// ASSERT(m_nAlloc != 0);
// m_nAlloc--;
// AfxUnlockGlobals(CRIT_ODBC);
} // endif m_hdbc
if (m_henv != SQL_NULL_HENV) { if (trace) { RETCODE rc = SQLFreeEnv(m_henv); if (rc != SQL_SUCCESS) // Nothing we can do
htrc("Error: SQLFreeEnv failure ignored in Close\n"); } else SQLFreeEnv(m_henv);
m_henv = SQL_NULL_HENV; } // endif m_henv
} // end of Close
#if 0
// Silently disconnect and free all ODBC resources.
// Don't throw any exceptions
void ODBConn::Free() { // Trap failures upon close
try { Close(); } catch(DBX*) { // Nothing we can do
if (trace) htrc("Error: exception by Close ignored in Free\n");
// DELETE_EXCEPTION(x);
} // endcatch
// free henv if refcount goes to 0
//AfxLockGlobals(CRIT_ODBC);
if (m_henv != SQL_NULL_HENV) { ASSERT(m_nAlloc >= 0);
if (m_nAlloc == 0) { // free last connection - release HENV
if (trace) { RETCODE rc = SQLFreeEnv(m_henv); if (rc != SQL_SUCCESS) // Nothing we can do
htrc("Error: SQLFreeEnv failure ignored in Free\n"); } else SQLFreeEnv(m_henv);
m_henv = SQL_NULL_HENV; } // endif m_nAlloc
} // endif m_henv
//AfxUnlockGlobals(CRIT_ODBC);
} // end of Free
//////////////////////////////////////////////////////////////////////////////
// CRecordset helpers
//id AFXAPI AfxSetCurrentRecord(int* plCurrentRecord, int nRows, RETCODE nRetCode);
//id AFXAPI AfxSetRecordCount(int* plRecordCount, int lCurrentRecord,
//bool bEOFSeen, RETCODE nRetCode);
/***********************************************************************//* RECSET class implementation *//***********************************************************************/RECSET::RECSET(ODBConn *dbcp) { m_pDB = dbcp; m_hstmt = SQL_NULL_HSTMT; m_OpenType = snapshot; m_Options = none;
#if 0
m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN; m_nEditMode = noMode; m_nDefaultType = snapshot;
m_bAppendable = false; m_bUpdatable = false; m_bScrollable = false; m_bRecordsetDb = false; m_bRebindParams = false; m_bLongBinaryColumns = false; m_nLockMode = optimistic; m_dwInitialGetDataLen = 0; m_rgODBCFieldInfos = NULL; m_rgFieldInfos = NULL; m_rgRowStatus = NULL; m_dwRowsetSize = 25; m_dwAllocatedRowsetSize = 0;
m_nFields = 0; m_nParams = 0; m_nFieldsBound = 0; m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED; m_lRecordCount = 0; m_bUseUpdateSQL = false; m_bUseODBCCursorLib = false; m_nResultCols = -1; m_bCheckCacheForDirtyFields = true;
m_pbFieldFlags = NULL; m_pbParamFlags = NULL; m_plParamLength = NULL; m_pvFieldProxy = NULL; m_pvParamProxy = NULL; m_nProxyFields = 0; m_nProxyParams = 0;
m_hstmtUpdate = SQL_NULL_HSTMT;#endif // 0
} // end of RECSET constructor
RECSET::~RECSET() { try { if (m_hstmt) { if (trace && (m_dwOptions & useMultiRowFetch)) { htrc("WARNING: Close called implicitly from destructor\n"); htrc("Use of multi row fetch requires explicit call\n"); htrc("to Close or memory leaks will result\n"); } // endif trace
Close(); } // endif m_hstmt
// if (m_bRecordsetDb)
// delete m_pDB; ??????
m_pDB = NULL; } catch(DBX*) { // Nothing we can do
if (trace) htrc("Error: Exception ignored in ~RECSET\n");
} // endtry/catch
} // end of ~RECSET
/***********************************************************************//* Open: this function does the following: *//* Allocates the hstmt, *//* Bind columns, *//* Execute the SQL statement *//***********************************************************************/bool RECSET::Open(PSZ sql, uint Type, DWORD options) { ASSERT(m_pDB && m_pDB->IsOpen()); ASSERT(Type == DB_USE_DEFAULT_TYPE || Type == dynaset || Type == snapshot || Type == forwardOnly || Type == dynamic);//ASSERT(!(options & readOnly && options & appendOnly));
// Cache state info and allocate hstmt
SetState(Type, sql, options);
try { if (m_hstmt) { if (IsOpen()) Close(SQL_CLOSE);
} else { RETCODE rc = SQLAllocStmt(m_pDB->m_hdbc, &m_hstmt);
if (!Check(rc)) ThrowDBException(SQL_INVALID_HANDLE);
} // endif m_hstmt
m_pDB->OnSetOptions(m_hstmt);
// Allocate the field/param status arrays, if necessary
// bool bUnbound = false;
// if (m_nFields > 0 || m_nParams > 0)
// AllocStatusArrays();
// else
// bUnbound = true;
// Build SQL and prep/execute or just execute direct
// BuildSQL(sql);
PrepareAndExecute(sql);
// Cache some field info and prepare the rowset
AllocAndCacheFieldInfo(); AllocRowset();
// If late binding, still need to allocate status arrays
// if (bUnbound && (m_nFields > 0 || m_nParams > 0))
// AllocStatusArrays();
} catch(DBX *x) { Close(SQL_DROP);// strcpy(m_pDB->m_G->Message, x->GetErrorMessage[0]);
strcpy(m_pDB->m_G->Message, x->GetErrorMessage(0)); return true; } // endtry/catch
return false; } // end of Open
/***********************************************************************//* Close a hstmt. *//***********************************************************************/void RECSET::Close(SWORD option) { if (m_hstmt != SQL_NULL_HSTMT) { RETCODE rc = SQLFreeStmt(m_hstmt, option);
if (option == SQL_DROP) m_hstmt = SQL_NULL_HSTMT;
} // endif m_hstmt
#if 0
m_lOpen = RECORDSET_STATUS_CLOSED; m_bBOF = true; m_bEOF = true; m_bDeleted = false; m_bAppendable = false; m_bUpdatable = false; m_bScrollable = false; m_bRebindParams = false; m_bLongBinaryColumns = false; m_nLockMode = optimistic; m_nFieldsBound = 0; m_nResultCols = -1;#endif // 0
} // end of Close
#endif // 0
|