mariadb/storage/connect/odbconn.cpp
Alexander Barkov f19f8f0ee4 MDEV-5341 ConnectSE: discovery for ODBC tables does not work if tables
with the same names present in multiple schemas

The "TABNAME" option now supports qualified table names,
to connect to tables residing in a particular schema and catalog.

Qualified table names have the following format:

  [[CatalogName.]SchemaName.]TableName

Qualified table names can be used:

1. In "normal" tables:

CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC
CONNECTION='DSN=ConnectEng;UID=mtr;PWD=mtr'
TABNAME='schema1.t1';

2. In catalog tables (CATFUNC=Tables  and CATFUNC=Columns)

CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC
CONNECTION='DSN=postgresql;UID=user;PWD=password'
TABNAME='schema1.t1';

Note, the % and _ wildcards are supported in
the schema name and the table name parts:

CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC
CONNECTION='DSN=postgresql;UID=user;PWD=password'
TABNAME='%.t1';

CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC
CONNECTION='DSN=postgresql;UID=user;PWD=password'
TABNAME='schema1.%';
2013-12-11 18:47:46 +04:00

2314 lines
71 KiB
C++

/************ Odbconn C++ Functions Source Code File (.CPP) ************/
/* Name: ODBCONN.CPP Version 1.7 */
/* */
/* (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>
#include <direct.h> // for getcwd
#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"
#include "osutil.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;
case TYPE_TINY : tp = SQL_TINYINT; 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;
case TYPE_TINY : tp = SQL_C_TINYINT; 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, char& v)
{
int type;
switch (stp) {
case SQL_VARCHAR: // 12
v = 'V';
case SQL_CHAR: // 1
type = TYPE_STRING;
break;
case SQL_LONGVARCHAR: // (-1)
v = 'V';
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
type = TYPE_SHORT;
break;
case SQL_TINYINT: // (-6)
case SQL_BIT: // (-7)
type = TYPE_TINY;
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
#if defined(PROMPT_OK)
/***********************************************************************/
/* ODBCCheckConnection: Check completeness of connection string. */
/***********************************************************************/
char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop)
{
char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH];
int rc;
DWORD options = ODBConn::openReadOnly;
ODBConn *ocp = new(g) ODBConn(g, NULL);
(void) getcwd(dir, sizeof(dir) - 1);
switch (cop) {
case 1: options |= ODBConn::forceOdbcDialog; break;
case 2: options |= ODBConn::noOdbcDialog; break;
} // endswitch cop
if (ocp->Open(dsn, options) < 1)
newdsn = NULL;
else
newdsn = ocp->GetConnect();
(void) getcwd(buf, sizeof(buf) - 1);
// Some data sources change the current directory
if (strcmp(dir, buf))
rc = chdir(dir);
ocp->Close();
return newdsn; // Return complete connection string
} // end of ODBCCheckConnection
#endif // PROMPT_OK
/***********************************************************************/
/* 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(SQLLEN *));
for (i = 0; i < n; i++)
cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SQLLEN));
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. */
/***********************************************************************/
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 = NULL;
/************************************************************************/
/* Do an evaluation of the result size. */
/************************************************************************/
if (!info) {
ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog
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, false, 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;
/* Cleanup */
ocp->Close();
/************************************************************************/
/* Return the result pointer for use by GetData routines. */
/************************************************************************/
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. */
/**************************************************************************/
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, false, 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, false, 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 = NULL;
/************************************************************************/
/* 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 = 16384; // 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, false, 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, false, 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, false, 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
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. */
/***********************************************************************/
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_Transact = false;
m_IDQuoteChar[0] = '"';
m_IDQuoteChar[1] = 0;
//*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) {
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 = (HWND)1;
#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
// Get the quote char to use when constructing SQL
rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
m_IDQuoteChar, sizeof(m_IDQuoteChar), &nResult);
if (trace)
htrc("DBMS: %s, Version: %s, rc=%d\n",
GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER), rc);
} // 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 len, tp, ncol = 0;
ODBCCOL *colp;
RETCODE rc;
HSTMT hstmt;
try {
b = false;
if (m_hstmt) {
// All this did not seems to make sense and was been commented out
// if (IsOpen())
// Close(SQL_CLOSE);
rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
if (trace && !Check(rc))
htrc("Error: SQLFreeStmt rc=%d\n", rc);
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);
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, "SQLPrepare", hstmt);
if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
ThrowDBX(rc, "SQLNumResultCols", hstmt);
if (ncol == 0) {
strcpy(g->Message, "This Srcdef does not return a result set");
return -1;
} // endif ncol
// Ok, now we can proceed
do {
rc = SQLExecute(hstmt);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLExecute", hstmt);
} else {
do {
rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "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())
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->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;
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;
if (m_hstmt) {
RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
hstmt = m_hstmt;
m_hstmt = NULL;
if (m_Tdb->GetAmType() != TYPE_AM_XDBC)
ThrowDBX(MSG(SEQUENCE_ERROR));
} // endif m_hstmt
rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc))
ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
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(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
m_hstmt = hstmt;
return (int)nparm;
} // end of PrepareSQL
/***********************************************************************/
/* Execute a prepared statement. */
/***********************************************************************/
int ODBConn::ExecuteSQL(void)
{
PGLOBAL& g = m_G;
SWORD ncol = 0;
RETCODE rc;
SQLLEN afrw = -1;
try {
do {
rc = SQLExecute(m_hstmt);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLExecute", m_hstmt);
if (!Check(rc = SQLNumResultCols(m_hstmt, &ncol)))
ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
if (ncol) {
// This should never happen while inserting
strcpy(g->Message, "Logical error while inserting");
} else {
// Insert, Update or Delete statement
if (!Check(rc = SQLRowCount(m_hstmt, &afrw)))
ThrowDBX(rc, "SQLRowCount", m_hstmt);
} // endif ncol
} catch(DBX *x) {
strcpy(m_G->Message, x->GetErrorMessage(0));
SQLCancel(m_hstmt);
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
m_hstmt = NULL;
if (m_Transact) {
rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
m_Transact = false;
} // endif m_Transact
afrw = -1;
} // end try/catch
return (int)afrw;
} // 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 = 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
/***********************************************************************/
/* Execute an SQL command. */
/***********************************************************************/
bool ODBConn::ExecSQLcommand(char *sql)
{
char cmd[16];
bool b, rcd = false;
UINT txn = 0;
PGLOBAL& g = m_G;
SWORD ncol = 0;
SQLLEN afrw;
RETCODE rc;
HSTMT hstmt;
try {
b = FALSE;
// Check whether we should use transaction
if (sscanf(sql, " %15s ", cmd) == 1) {
if (!stricmp(cmd, "INSERT") || !stricmp(cmd, "UPDATE") ||
!stricmp(cmd, "DELETE") || !stricmp(cmd, "REPLACE")) {
// Does the data source support transactions
rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
if (Check(rc) && txn != SQL_TC_NONE) {
rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
if (!Check(rc))
ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
m_Transact = TRUE;
} // endif txn
} // endif cmd
} // endif sql
// Allocate the statement handle
rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc))
ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
OnSetOptions(hstmt);
b = true;
if (trace)
htrc("ExecSQLcommand hstmt=%p %.64s\n", hstmt, sql);
// Proceed with command execution
do {
rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLExecDirect", hstmt);
// Check whether this is a query returning a result set
if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
ThrowDBX(rc, "SQLNumResultCols", hstmt);
if (!ncol) {
if (!Check(SQLRowCount(hstmt, &afrw)))
ThrowDBX(rc, "SQLRowCount", hstmt);
m_Tdb->AftRows = (int)afrw;
strcpy(g->Message, "Affected rows");
} else {
m_Tdb->AftRows = (int)ncol;
strcpy(g->Message, "Result set column number");
} // endif ncol
} catch(DBX *x) {
if (trace)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
sprintf(g->Message, "Remote: %s", x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
m_Tdb->AftRows = -1;
rcd = true;
} // end try/catch
if (!Check(rc = SQLFreeStmt(hstmt, SQL_CLOSE)))
sprintf(g->Message, "SQLFreeStmt: rc=%d", rc);
if (m_Transact) {
// Terminate the transaction
if (!Check(rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc,
(rcd) ? SQL_ROLLBACK : SQL_COMMIT)))
sprintf(g->Message, "SQLEndTran: rc=%d", rc);
if (!Check(rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)))
sprintf(g->Message, "SQLSetConnectAttr: rc=%d", rc);
m_Transact = false;
} // endif m_Transact
return rcd;
} // end of ExecSQLcommand
/**************************************************************************/
/* GetMetaData: constructs the result blocks containing the */
/* description of all the columns of an SQL command. */
/**************************************************************************/
PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
{
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
TYPE_SHORT, TYPE_SHORT};
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
FLD_SCALE, FLD_NULL};
static unsigned int length[] = {0, 6, 10, 6, 6};
unsigned char cn[60];
int qcol = 5;
short nl, type, prec, nul, cns = (short)sizeof(cn);
PQRYRES qrp = NULL;
PCOLRES crp;
USHORT i;
SQLULEN n;
SWORD ncol;
RETCODE rc;
HSTMT hstmt;
if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog
return NULL;
try {
rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc))
ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
OnSetOptions(hstmt);
do {
rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
// rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLExecDirect", hstmt);
do {
rc = SQLNumResultCols(hstmt, &ncol);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLNumResultCols", hstmt);
if (ncol) for (i = 1; i <= ncol; i++) {
do {
rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLDescribeCol", hstmt);
length[0] = max(length[0], (UINT)nl);
} // endfor i
} catch(DBX *x) {
strcpy(g->Message, x->GetErrorMessage(0));
goto err;
} // end try/catch
if (!ncol) {
strcpy(g->Message, "Invalid Srcdef");
goto err;
} // endif ncol
/************************************************************************/
/* Allocate the structures used to refer to the result set. */
/************************************************************************/
qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, true);
// Some columns must be renamed
for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
switch (++i) {
case 3: crp->Name = "Precision"; break;
case 4: crp->Name = "Scale"; break;
case 5: crp->Name = "Nullable"; break;
} // endswitch i
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
try {
for (i = 0; i < ncol; i++) {
do {
rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
} while (rc == SQL_STILL_EXECUTING);
if (!Check(rc))
ThrowDBX(rc, "SQLDescribeCol", hstmt);
else
qrp->Nblin++;
crp = qrp->Colresp; // Column_Name
crp->Kdata->SetValue((char*)cn, i);
crp = crp->Next; // Data_Type
crp->Kdata->SetValue(type, i);
crp = crp->Next; // Precision (length)
crp->Kdata->SetValue((int)n, i);
crp = crp->Next; // Scale
crp->Kdata->SetValue(prec, i);
crp = crp->Next; // Nullable
crp->Kdata->SetValue(nul, i);
} // endfor i
} catch(DBX *x) {
strcpy(g->Message, x->GetErrorMessage(0));
qrp = NULL;
} // end try/catch
/* Cleanup */
err:
SQLCancel(hstmt);
rc = SQLFreeStmt(hstmt, SQL_DROP);
Close();
/************************************************************************/
/* Return the result pointer for use by GetData routines. */
/************************************************************************/
return qrp;
} // end of GetMetaData
/***********************************************************************/
/* Get the list of Data Sources and set it in qrp. */
/***********************************************************************/
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
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
Close();
return rv;
} // end of GetDrivers
/**
A helper class to split an optionally qualified table name into components.
These formats are understood:
"CatalogName.SchemaName.TableName"
"SchemaName.TableName"
"TableName"
*/
class SQLQualifiedName
{
static const uint max_parts= 3; /* Catalog.Schema.Table */
MYSQL_LEX_STRING m_part[max_parts];
char m_buf[512];
void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length)
{
S->str= str;
S->length= length;
}
void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
{
DBUG_ASSERT(offs <= S->length);
S->str+= offs;
S->length-= offs;
}
/*
Find the rightmost '.' delimiter and return the length
of the qualifier, including the rightmost '.' delimier.
For example, for the string {"a.b.c",5} it will return 4,
which is the length of the qualifier "a.b."
*/
size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S)
{
size_t i;
for (i= S->length; i > 0; i--)
{
if (S->str[i - 1] == '.')
{
S->str[i - 1]= '\0';
return i;
}
}
return 0;
}
public:
/*
Initialize to the given optionally qualified name.
NULL pointer in "name" is supported.
*/
SQLQualifiedName(const char *name)
{
size_t len, i= 0;
if (!name)
goto ret;
/* Initialize the first (rightmost) part */
lex_string_set(&m_part[0], m_buf,
strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf);
/* Initialize the other parts, if exist. */
for (i= 1; i < max_parts; i++)
{
if (!(len= lex_string_find_qualifier(&m_part[i - 1])))
break;
lex_string_set(&m_part[i], m_part[i - 1].str, len - 1);
lex_string_shorten_down(&m_part[i - 1], len);
}
ret:
/* Initialize the remaining parts */
for ( ; i < max_parts; i++)
lex_string_set(&m_part[i], NULL, 0);
}
SQLCHAR *ptr(uint i)
{
DBUG_ASSERT(i < max_parts);
return (SQLCHAR *) (m_part[i].length ? m_part[i].str : NULL);
}
size_t length(uint i)
{
DBUG_ASSERT(i < max_parts);
return m_part[i].length;
}
};
/***********************************************************************/
/* 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 = NULL;
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
{
SQLULEN tmp= m_RowsetSize;
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, &tmp, 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");
SQLQualifiedName name((const char *) cap->Tab);
// 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, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0),
cap->Pat, SQL_NTS);
break;
case CAT_COL:
// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
// (SQLPOINTER)true, 0);
fnc = "SQLColumns";
rc = SQLColumns(hstmt, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0),
cap->Pat, SQL_NTS);
break;
case CAT_KEY:
fnc = "SQLPrimaryKeys";
rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0));
break;
case CAT_STAT:
fnc = "SQLStatistics";
rc = SQLStatistics(hstmt, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0),
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(SQLLEN *));
} // 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 (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 (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) {
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)
htrc("Error: SQLDisconnect rc=%d\n", rc);
rc = SQLFreeConnect(m_hdbc);
if (trace && rc != SQL_SUCCESS)
htrc("Error: SQLFreeConnect rc=%d\n", rc);
m_hdbc = SQL_NULL_HDBC;
} // endif m_hdbc
if (m_henv != SQL_NULL_HENV) {
rc = SQLFreeEnv(m_henv);
if (trace && rc != SQL_SUCCESS) // Nothing we can do
htrc("Error: SQLFreeEnv failure ignored in Close\n");
m_henv = SQL_NULL_HENV;
} // endif m_henv
} // end of Close