mariadb/storage/connect/odbconn.cpp

2654 lines
82 KiB
C++

/***********************************************************************/
/* Name: ODBCONN.CPP Version 2.4 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1998-2021 */
/* */
/* This file contains the ODBC connection classes functions. */
/***********************************************************************/
/***********************************************************************/
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include <my_global.h>
#include <m_string.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 "xtable.h"
#include "tabext.h"
#include "odbccat.h"
#include "tabodbc.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
TYPCONV GetTypeConv();
int GetConvSize();
void OdbcClose(PGLOBAL g, PFBLOCK fp);
/***********************************************************************/
/* 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
/***********************************************************************/
/* 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_DOUBLE: tp = SQL_DOUBLE; break;
case TYPE_TINY: tp = SQL_TINYINT; break;
case TYPE_DECIM: tp = SQL_DECIMAL; 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_DOUBLE: tp = SQL_C_DOUBLE; break;
case TYPE_TINY : tp = SQL_C_TINYINT; break;
//#if (ODBCVER >= 0x0300)
// case TYPE_DECIM: tp = SQL_C_NUMERIC; break; (CRASH!!!)
//#else
case TYPE_DECIM: tp = SQL_C_CHAR; break;
//#endif
} // 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, bool& w)
{
int type;
switch (stp) {
case SQL_WVARCHAR: // (-9)
w = true;
/* fall through */
case SQL_VARCHAR: // 12
v = 'V';
type = TYPE_STRING;
break;
case SQL_WCHAR: // (-8)
w = true;
/* fall through */
case SQL_CHAR: // 1
type = TYPE_STRING;
break;
case SQL_WLONGVARCHAR: // (-10)
w = true;
/* fall through */
case SQL_LONGVARCHAR: // (-1)
if (GetTypeConv() == TPC_YES || GetTypeConv() == TPC_FORCE) {
v = 'V';
type = TYPE_STRING;
len = (len) ? MY_MIN(abs(len), GetConvSize()) : GetConvSize();
} else
type = TYPE_ERROR;
break;
case SQL_NUMERIC: // 2
case SQL_DECIMAL: // 3
// type = (prec || len > 20) ? TYPE_DOUBLE
// : (len > 10) ? TYPE_BIGINT : TYPE_INT;
type = TYPE_DECIM;
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_DOUBLE;
break;
case SQL_DATETIME: // 9
type = TYPE_DATE;
len = 19;
break;
case SQL_TYPE_DATE: // 91
type = TYPE_DATE;
len = 10;
v = 'D';
break;
case SQL_INTERVAL: // 10
case SQL_TYPE_TIME: // 92
type = TYPE_STRING;
len = 8 + ((prec) ? (prec+1) : 0);
v = 'T';
break;
case SQL_TIMESTAMP: // 11
case SQL_TYPE_TIMESTAMP: // 93
type = TYPE_DATE;
len = 19 + ((prec) ? (prec+1) : 0);
v = 'S';
break;
case SQL_BIGINT: // (-5)
type = TYPE_BIGINT;
break;
case SQL_BINARY: // (-2)
case SQL_VARBINARY: // (-3)
case SQL_LONGVARBINARY: // (-4)
if (GetTypeConv() == TPC_FORCE) {
v = 'V';
type = TYPE_STRING;
len = (len) ? MY_MIN(abs(len), GetConvSize()) : GetConvSize();
} else
type = TYPE_ERROR;
break;
case SQL_GUID: // (-11)
type = TYPE_STRING;
len = 36;
break;
case SQL_UNKNOWN_TYPE: // 0
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, PCSZ db,
PCSZ tab, PQRYRES qrp)
{
size_t i, m, n;
CATPARM *cap;
#if defined(_DEBUG)
assert(qrp);
#endif
try {
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->DB = db;
cap->Tab = 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));
} catch (int n) {
htrc("Exeption %d: %s\n", n, g->Message);
cap = NULL;
} catch (const char *msg) {
htrc(g->Message, msg);
printf("%s\n", g->Message);
cap = NULL;
} // end catch
return cap;
} // end of AllocCatInfo
#if 0
/***********************************************************************/
/* 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
#endif
/***********************************************************************/
/* Close an ODBC table after a thrown error (called by PlugCloseFile) */
/***********************************************************************/
void OdbcClose(PGLOBAL g, PFBLOCK fp) {
((ODBConn*)fp->File)->Close();
} // end of OdbcClose
/***********************************************************************/
/* ODBCColumns: constructs the result blocks containing all columns */
/* of an ODBC table that will be retrieved by GetData commands. */
/***********************************************************************/
PQRYRES ODBCColumns(PGLOBAL g, PCSZ dsn, PCSZ db, PCSZ table,
PCSZ colpat, int maxres, bool info, POPARM sop)
{
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};
XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_TABNAME, FLD_NAME,
FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH,
FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM};
unsigned int length[] = {0, 0, 0, 0, 6, 0, 10, 10, 6, 6, 6, 0};
bool b[] = {true,true,false,false,false,false,false,false,true,true,false,true};
int i, n, ncol = 12;
PCOLRES crp;
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, sop, 10) < 1) // openReadOnly + noODBCdialog
return NULL;
if (table && !strchr(table, '%')) {
// We fix a MySQL limit because some data sources return 32767
n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
maxres = (n) ? MY_MIN(n, 4096) : 4096;
} else if (!maxres)
maxres = 20000;
// n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
// length[0] = (n) ? (n + 1) : 0;
// n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
// length[1] = (n) ? (n + 1) : 0;
// n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
// length[2] = (n) ? (n + 1) : 0;
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;
length[5] = 30;
length[11] = 255;
} // endif ocp
if (trace(1))
htrc("ODBCColumns: max=%d len=%d,%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);
for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
if (b[i])
crp->Kdata->SetNullable(true);
if (info || !qrp) // Info table
return qrp;
if (trace(1))
htrc("Getting col results ncol=%d\n", qrp->Nbcol);
if (!(cap = AllocCatInfo(g, CAT_COL, db, table, qrp)))
return NULL;
cap->Pat = colpat;
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
if ((n = ocp->GetCatInfo(cap)) >= 0) {
qrp->Nblin = n;
// ResetNullValues(cap);
if (trace(1))
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, POPARM sop)
{
char *sqry;
ODBConn *ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noOdbcDialog
return NULL;
if (strstr(src, "%s")) {
// Place holder for an eventual where clause
sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 3);
sprintf(sqry, src, "1=1", "1=1"); // dummy where clause
} else
sqry = src;
return ocp->GetMetaData(g, dsn, sqry);
} // 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;
bool w = false;
// 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, NULL, tab, 0, 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 Schema 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, w);
crpt->Kdata->SetValue(type, i);
// Some data sources do not count prec in length
if (type == TYPE_DOUBLE)
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
/*************************************************************************/
/* 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, int maxres, bool info)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING};
XFLD fldtyp[] = {FLD_NAME, FLD_REM};
unsigned int length[] = {128, 256};
bool b[] = {false, true};
int i, ncol = 2;
PCOLRES crp;
PQRYRES qrp;
ODBConn *ocp = NULL;
/************************************************************************/
/* Do an evaluation of the result size. */
/************************************************************************/
if (!info) {
ocp = new(g) ODBConn(g, NULL);
if (!maxres)
maxres = 256; // Estimated max number of drivers
} else
maxres = 0;
if (trace(1))
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);
for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
if (b[i])
crp->Kdata->SetNullable(true);
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
if (!info && qrp && ocp->GetDrivers(qrp))
qrp = NULL;
/************************************************************************/
/* Return the result pointer for use by GetData routines. */
/************************************************************************/
return qrp;
} // end of ODBCDrivers
/*************************************************************************/
/* 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, int maxres, bool info)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING};
XFLD fldtyp[] = {FLD_NAME, FLD_REM};
unsigned int length[] = {0, 256};
bool b[] = {false, true};
int i, n = 0, ncol = 2;
PCOLRES crp;
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;
if (!maxres)
maxres = 512; // Estimated max number of data sources
} else {
length[0] = 256;
maxres = 0;
} // endif info
if (trace(1))
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);
for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
if (b[i])
crp->Kdata->SetNullable(true);
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
if (!info && qrp && ocp->GetDataSources(qrp))
qrp = NULL;
/************************************************************************/
/* Return the result pointer for use by GetData routines. */
/************************************************************************/
return qrp;
} // end of ODBCDataSources
/**************************************************************************/
/* ODBCTables: constructs the result blocks containing all tables in */
/* an ODBC database that will be retrieved by GetData commands. */
/**************************************************************************/
PQRYRES ODBCTables(PGLOBAL g, PCSZ dsn, PCSZ db, PCSZ tabpat, PCSZ tabtyp,
int maxres, bool info, POPARM sop)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
TYPE_STRING, TYPE_STRING};
XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_NAME,
FLD_TYPE, FLD_REM};
unsigned int length[] = {0, 0, 0, 16, 0};
bool b[] ={ true, true, false, false, true };
int i, n, ncol = 5;
PCOLRES crp;
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, sop, 2) < 1) // 2 is openReadOnly
return NULL;
if (!maxres)
maxres = 10000; // This is completely arbitrary
// n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
// length[0] = (n) ? (n + 1) : 0;
// n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
// length[1] = (n) ? (n + 1) : 0;
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;
length[4] = 255;
} // endif info
if (trace(1))
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);
for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
if (b[i])
crp->Kdata->SetNullable(true);
if (info || !qrp)
return qrp;
if (!(cap = AllocCatInfo(g, CAT_TAB, db, tabpat, qrp)))
return NULL;
cap->Pat = tabtyp;
if (trace(1))
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(1))
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_CATALOG_NAME_LEN);
length[0] = (n) ? (n + 1) : 128;
n = ocp->GetMaxValue(SQL_MAX_SCHEMA_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(1))
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(1))
htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
cap = AllocCatInfo(g, CAT_KEY, NULL, table, qrp);
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
if ((n = ocp->GetCatInfo(cap)) >= 0) {
qrp->Nblin = n;
// ResetNullValues(cap);
if (trace(1))
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_SCHEMA_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_CATALOG_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(1))
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(1))
htrc("Getting stat results ncol=%d\n", qrp->Nbcol);
cap = AllocCatInfo(g, CAT_STAT, NULL, 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(1))
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, PCSZ 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. */
/***********************************************************************/
bool 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_NO_DATA_FOUND)
return false;
else 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)PlugDup(g, (char*)msg);
if (trace(1))
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 true;
} else {
snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg,
MSG(BAD_HANDLE_VAL));
m_ErrMsg[0] = (PSZ)PlugDup(g, (char*)msg);
if (trace(1))
htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
return true;
} // endif rc
} else
m_ErrMsg[0] = "No connexion address provided";
if (trace(1))
htrc("%s: rc=%hd (%s)\n", SVP(m_Msg), m_RC, SVP(m_ErrMsg[0]));
return true;
} // 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_Rows = 0;
m_Fetch = 0;
m_Fp = NULL;
m_Connect = NULL;
m_User = NULL;
m_Pwd = NULL;
m_Updatable = true;
m_Transact = false;
m_Scrollable = (tdbp) ? tdbp->Scrollable : false;
m_Full = false;
m_UseCnc = false;
m_IDQuoteChar[0] = '"';
if (tdbp)
{
if (tdbp->Quoted && tdbp->Quote)
m_IDQuoteChar[0] = *tdbp->Quote;
}
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(1)) {
DBX x(rc);
if (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, PCSZ msg, HSTMT hstmt)
{
DBX* xp = new(m_G) DBX(rc, msg);
// Don't throw if no error
if (xp->BuildErrorMessage(this, hstmt))
throw xp;
} // end of ThrowDBX
void ODBConn::ThrowDBX(PCSZ msg)
{
DBX* xp = new(m_G) DBX(0, "Error");
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 = PlugDup(m_G, 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(PCSZ ConnectString, POPARM sop, 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;
m_User = sop->User;
m_Pwd = sop->Pwd;
m_LoginTimeout = sop->Cto;
m_QueryTimeout = sop->Qto;
m_UseCnc = sop->UseCnc;
// Allocate the HDBC and make connection
try {
/*PSZ ver;*/
AllocConnect(options);
/*ver = GetStringInfo(SQL_ODBC_VER);*/
if (!m_UseCnc) {
if (DriverConnect(options)) {
snprintf(g->Message, sizeof(g->Message), MSG(CONNECT_CANCEL));
return 0;
} // endif
} else // Connect using SQLConnect
Connect();
/*********************************************************************/
/* Link a Fblock. This make possible to automatically close it */
/* in case of error (throw). */
/*********************************************************************/
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
m_Fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
m_Fp->Type = TYPE_FB_ODBC;
m_Fp->Fname = NULL;
m_Fp->Next = dbuserp->Openlist;
dbuserp->Openlist = m_Fp;
m_Fp->Count = 1;
m_Fp->Length = 0;
m_Fp->Memory = NULL;
m_Fp->Mode = MODE_ANY;
m_Fp->File = this;
m_Fp->Handle = 0;
/*ver = GetStringInfo(SQL_DRIVER_ODBC_VER);*/
// Verify support for required functionality and cache info
// VerifyConnect(); Deprecated
GetConnectInfo();
// Still we want to use the set QChar
} catch(DBX *xp) {
snprintf(g->Message, sizeof(g->Message), "%s: %s", xp->m_Msg, xp->GetErrorMessage(0));
Close();
// Free();
return -1;
} // end try-catch
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, (SQLULEN)"xodbc.out");
SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
} // endif
#endif // _DEBUG
if ((signed)m_LoginTimeout >= 0) {
rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
if (trace(1) && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
htrc("Warning: Failure setting login timeout\n");
} // endif Timeout
if (!m_Updatable) {
rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY);
if (trace(1) && 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_DRIVER);
return;
} // end of AllocConnect
/***********************************************************************/
/* Connect to data source using SQLConnect. */
/***********************************************************************/
void ODBConn::Connect(void)
{
SQLRETURN rc;
SQLSMALLINT ul = (m_User ? SQL_NTS : 0);
SQLSMALLINT pl = (m_Pwd ? SQL_NTS : 0);
rc = SQLConnect(m_hdbc, (SQLCHAR*)m_Connect, SQL_NTS,
(SQLCHAR*)m_User, ul, (SQLCHAR*)m_Pwd, pl);
if (!Check(rc))
ThrowDBX(rc, "SQLConnect");
} // end of Connect
/***********************************************************************/
/* Connect to data source using SQLDriverConnect. */
/***********************************************************************/
bool ODBConn::DriverConnect(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
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 DriverConnect
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(1))
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(1))
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, k;
SWORD len, tp, ncol = 0;
ODBCCOL *colp;
RETCODE rc;
HSTMT hstmt;
try {
b = false;
if (m_hstmt) {
// This is a Requery
rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
if (!Check(rc))
ThrowDBX(rc, "SQLFreeStmt", m_hstmt);
m_hstmt = NULL;
} // endif m_hstmt
rc = SQLAllocStmt(m_hdbc, &hstmt);
if (!Check(rc))
ThrowDBX(rc, "SQLAllocStmt");
if (m_Scrollable) {
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
(void*)SQL_SCROLLABLE, 0);
if (!Check(rc))
ThrowDBX(rc, "Scrollable", hstmt);
} // endif m_Scrollable
OnSetOptions(hstmt);
b = true;
if (trace(1))
htrc("ExecDirect hstmt=%p %.256s\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) {
snprintf(g->Message, sizeof(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);
k = 0; // used for column number
} // 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 (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) {
snprintf(m_G->Message, sizeof(m_G->Message), MSG(INV_COLUMN_TYPE),
colp->GetResultType(), SVP(colp->GetName()));
ThrowDBX(m_G->Message);
} // endif tp
if (m_Tdb->Srcdef)
k = colp->GetIndex();
else
k++;
if (trace(1))
htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n",
k, tp, buffer, len, colp->GetStrLen());
rc = SQLBindCol(hstmt, k, tp, buffer, len, colp->GetStrLen());
if (!Check(rc))
ThrowDBX(rc, "SQLBindCol", hstmt);
} // endif colp
} catch(DBX *x) {
if (trace(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, 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(1))
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(int pos)
{
ASSERT(m_hstmt);
int irc;
SQLULEN crow;
RETCODE rc;
PGLOBAL& g = m_G;
try {
// do {
if (pos) {
rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_ABSOLUTE, pos, &crow, NULL);
} else 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(2))
htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n",
m_hstmt, m_RowsetSize, rc);
if (!Check(rc))
ThrowDBX(rc, "Fetching", m_hstmt);
if (rc == SQL_NO_DATA_FOUND) {
m_Full = (m_Fetch == 1);
irc = 0;
} else
irc = (int)crow;
m_Fetch++;
m_Rows += irc;
} catch(DBX *x) {
if (trace(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, 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(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, x->GetErrorMessage(0));
} // end try/catch
} // endif Mode
try {
b = false;
if (m_hstmt) {
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(1))
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(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, 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
snprintf(g->Message, sizeof(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) {
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, 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;
int buftype = colp->GetResultType();
SQLUSMALLINT n = colp->GetRank();
SQLSMALLINT ct, sqlt, dec, nul __attribute__((unused));
SQLULEN colsize;
SQLLEN len;
SQLLEN *strlen = colp->GetStrLen();
SQLRETURN rc;
#if 0
try {
// This function is often not or badly implemented by data sources
rc = SQLDescribeParam(m_hstmt, n, &sqlt, &colsize, &dec, &nul);
if (!Check(rc))
ThrowDBX(rc, "SQLDescribeParam", m_hstmt);
} catch(DBX *x) {
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, x->GetErrorMessage(0));
#endif // 0
colsize = colp->GetPrecision();
sqlt = GetSQLType(buftype);
dec = IsTypeNum(buftype) ? colp->GetScale() : 0;
nul = colp->IsNullable() ? SQL_NULLABLE : SQL_NO_NULLS;
//} // end try/catch
buf = colp->GetBuffer(0);
len = IsTypeChar(buftype) ? colp->GetBuflen() : 0;
ct = GetSQLCType(buftype);
*strlen = IsTypeChar(buftype) ? SQL_NTS : 0;
try {
rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt,
colsize, dec, buf, len, 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(1))
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;
snprintf(g->Message, sizeof(g->Message), "Affected rows");
} else {
m_Tdb->AftRows = (int)ncol;
snprintf(g->Message, sizeof(g->Message), "Result set column number");
} // endif ncol
} catch(DBX *x) {
if (trace(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(g->Message, sizeof(g->Message), "Remote %s: %s", x->m_Msg, x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
m_Tdb->AftRows = -1;
rcd = true;
} // end try/catch
if (!Check(rc = SQLFreeStmt(hstmt, SQL_CLOSE)))
snprintf(g->Message, sizeof(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)))
snprintf(g->Message, sizeof(g->Message), "SQLEndTran: rc=%d", rc);
if (!Check(rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)))
snprintf(g->Message, sizeof(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, PCSZ dsn, PCSZ 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;
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] = MY_MAX(length[0], (UINT)nl);
} // endfor i
} catch(DBX *x) {
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, x->GetErrorMessage(0));
goto err;
} // end try/catch
if (!ncol) {
snprintf(g->Message, sizeof(g->Message), "Invalid Srcdef");
goto err;
} // endif ncol
/************************************************************************/
/* Allocate the structures used to refer to the result set. */
/************************************************************************/
if (!(qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, true)))
return NULL;
// 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) {
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, 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) {
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, 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) {
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, 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;
} // end of lex_string_set
void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
{
DBUG_ASSERT(offs <= S->length);
S->str+= offs;
S->length-= offs;
} // end of lex_string_shorten_down
/*********************************************************************/
/* 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;
} // end of lex_string_find_qualifier
public:
/*********************************************************************/
/* Initialize to the given optionally qualified name. */
/* NULL pointer in "name" is supported. */
/* name qualifier has precedence over schema. */
/*********************************************************************/
SQLQualifiedName(CATPARM *cap)
{
const char *name = (const char *)cap->Tab;
char *db = (char *)cap->DB;
size_t len, i;
// Initialize the parts
for (i = 0 ; i < max_parts; i++)
lex_string_set(&m_part[i], NULL, 0);
if (name) {
// 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);
} // endfor i
} // endif name
// If it was not specified, set schema as the passed db name
if (db && !m_part[1].length)
lex_string_set(&m_part[1], db, strlen(db));
} // end of SQLQualifiedName
SQLCHAR *ptr(uint i)
{
DBUG_ASSERT(i < max_parts);
return (SQLCHAR *) (m_part[i].length ? m_part[i].str : NULL);
} // end of ptr
SQLSMALLINT length(uint i)
{
DBUG_ASSERT(i < max_parts);
return (SQLSMALLINT)m_part[i].length;
} // end of length
}; // end of class SQLQualifiedName
/***********************************************************************/
/* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
/***********************************************************************/
int ODBConn::GetCatInfo(CATPARM *cap)
{
PGLOBAL& g = m_G;
void *buffer;
int i, irc;
bool b;
PCSZ fnc = "Unknown";
UWORD n = 0;
SWORD ncol, len, tp;
SQLULEN crow = 0;
PQRYRES qrp = cap->Qrp;
PCOLRES crp;
RETCODE rc = 0;
HSTMT hstmt = NULL;
SQLLEN *vl, *vlen = NULL;
PVAL *pval = NULL;
char* *pbuf = 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;
// Currently m_Catver should be always 0 here
assert(!m_Catver); // This may be temporary
if (qrp->Maxres > 0)
m_RowsetSize = 1;
else
ThrowDBX("0-sized result");
SQLQualifiedName name(cap);
// Now do call the proper ODBC API
switch (cap->Id) {
case CAT_TAB:
fnc = "SQLTables";
rc = SQLTables(hstmt, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0),
(SQLCHAR *)cap->Pat,
cap->Pat ? SQL_NTS : 0);
break;
case CAT_COL:
fnc = "SQLColumns";
rc = SQLColumns(hstmt, name.ptr(2), name.length(2),
name.ptr(1), name.length(1),
name.ptr(0), name.length(0),
(SQLCHAR *)cap->Pat,
cap->Pat ? SQL_NTS : 0);
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");
break;
default:
ThrowDBX("Invalid SQL function id");
} // endswitch infotype
if (!Check(rc))
ThrowDBX(rc, fnc, hstmt);
// Some data source do not implement SQLNumResultCols
if (Check(SQLNumResultCols(hstmt, &ncol)))
// n because we no more ignore the first column
if ((n = (UWORD)qrp->Nbcol) > (UWORD)ncol)
ThrowDBX(MSG(COL_NUM_MISM));
// Unconditional to handle STRBLK's
pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL));
vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN));
pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
// Now bind the column buffers
for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) {
if ((tp = GetSQLCType(crp->Type)) == SQL_TYPE_NULL) {
snprintf(g->Message, sizeof(g->Message), MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
ThrowDBX(g->Message);
} // endif tp
if (!(len = GetTypeSize(crp->Type, crp->Length))) {
len = 255; // for STRBLK's
((STRBLK*)crp->Kdata)->SetSorted(true);
} // endif len
pval[n] = AllocateValue(g, crp->Type, len);
pval[n]->SetNullable(true);
if (crp->Type == TYPE_STRING) {
pbuf[n] = (char*)PlugSubAlloc(g, NULL, len);
buffer = pbuf[n];
} else
buffer = pval[n]->GetTo_Val();
vl = vlen + n;
// 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
// Extended fetch cannot be used because of STRBLK's
for (i = 0; i < qrp->Maxres; i++) {
if ((rc = SQLFetch(hstmt)) == SQL_NO_DATA_FOUND)
break;
else if (rc != SQL_SUCCESS) {
if (trace(2) || (trace(1) && rc != SQL_SUCCESS_WITH_INFO)) {
UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
UCHAR state[SQL_SQLSTATE_SIZE + 1];
RETCODE erc;
SDWORD native;
htrc("SQLFetch: row %d rc=%d\n", i+1, rc);
erc = SQLError(m_henv, m_hdbc, hstmt, state, &native, msg,
SQL_MAX_MESSAGE_LENGTH - 1, &len);
if (rc != SQL_INVALID_HANDLE)
// Skip non-errors
for (n = 0; n < MAX_NUM_OF_MSG
&& (erc == SQL_SUCCESS || erc == SQL_SUCCESS_WITH_INFO)
&& strcmp((char*)state, "00000"); n++) {
htrc("%s: %s, Native=%d\n", state, msg, native);
erc = SQLError(m_henv, m_hdbc, hstmt, state, &native,
msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
} // endfor n
} // endif trace
if (rc != SQL_SUCCESS_WITH_INFO)
qrp->BadLines++;
} // endif rc
for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
if (vlen[n] == SQL_NO_TOTAL)
ThrowDBX("Unexpected SQL_NO_TOTAL returned from SQLFetch");
else if (vlen[n] == SQL_NULL_DATA)
pval[n]->SetNull(true);
else if (crp->Type == TYPE_STRING/* && vlen[n] != SQL_NULL_DATA*/)
pval[n]->SetValue_char(pbuf[n], (int)vlen[n]);
else
pval[n]->SetNull(false);
crp->Kdata->SetValue(pval[n], i);
cap->Vlen[n][i] = vlen[n];
} // endfor crp
} // endfor i
#if 0
if ((crow = i) && (rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO))
rc = SQL_SUCCESS;
if (rc == SQL_NO_DATA_FOUND) {
if (cap->Pat)
snprintf(g->Message, sizeof(g->Message), MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat);
else
snprintf(g->Message, sizeof(g->Message), MSG(NO_TAB_DATA), cap->Tab);
ThrowDBX(g->Message);
} else if (rc == SQL_SUCCESS) {
if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
qrp->Truncated = true;
} else
ThrowDBX(rc, fnc, hstmt);
#endif // 0
if (!rc || rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO) {
if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
qrp->Truncated = true;
crow = i;
} else
ThrowDBX(rc, fnc, hstmt);
irc = (int)crow;
} catch(DBX *x) {
if (trace(1))
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
snprintf(g->Message, sizeof(g->Message), "%s: %s", x->m_Msg, 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
/***********************************************************************/
/* Allocate a CONNECT result structure from the ODBC result. */
/***********************************************************************/
PQRYRES ODBConn::AllocateResult(PGLOBAL g)
{
bool uns;
PODBCCOL colp;
PCOLRES *pcrp, crp;
PQRYRES qrp;
if (!m_Rows) {
snprintf(g->Message, sizeof(g->Message), "Void result");
return NULL;
} // endif m_Res
/*********************************************************************/
/* Allocate the result storage for future retrieval. */
/*********************************************************************/
qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
pcrp = &qrp->Colresp;
qrp->Continued = FALSE;
qrp->Truncated = FALSE;
qrp->Info = FALSE;
qrp->Suball = TRUE;
qrp->BadLines = 0;
qrp->Maxsize = m_Rows;
qrp->Maxres = m_Rows;
qrp->Nbcol = 0;
qrp->Nblin = 0;
qrp->Cursor = 0;
for (colp = (PODBCCOL)m_Tdb->Columns; colp;
colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial()) {
*pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
crp = *pcrp;
pcrp = &crp->Next;
memset(crp, 0, sizeof(COLRES));
crp->Ncol = ++qrp->Nbcol;
crp->Name = colp->GetName();
crp->Type = colp->GetResultType();
crp->Prec = colp->GetScale();
crp->Length = colp->GetLength();
crp->Clen = colp->GetBuflen();
uns = colp->IsUnsigned();
if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
crp->Clen, 0, FALSE, TRUE, uns))) {
snprintf(g->Message, sizeof(g->Message), MSG(INV_RESULT_TYPE),
GetFormatType(crp->Type));
return NULL;
} // endif Kdata
if (!colp->IsNullable())
crp->Nulls = NULL;
else {
crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
memset(crp->Nulls, ' ', m_Rows);
} // endelse Nullable
colp->SetCrp(crp);
} // endif colp
*pcrp = NULL;
//qrp->Nblin = n;
return qrp;
} // end of AllocateResult
/***********************************************************************/
/* Restart from beginning of result set */
/***********************************************************************/
int ODBConn::Rewind(char *sql, ODBCCOL *tocols)
{
int rc, rbuf = -1;
if (!m_hstmt)
rbuf = 0;
else if (m_Full)
rbuf = m_Rows; // No need to "rewind"
else if (m_Scrollable) {
SQLULEN crow;
try {
rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_FIRST, 1, &crow, NULL);
if (!Check(rc))
ThrowDBX(rc, "SQLExtendedFetch", m_hstmt);
rbuf = (int)crow;
} catch(DBX *x) {
snprintf(m_G->Message, sizeof(m_G->Message), "%s: %s", x->m_Msg, x->GetErrorMessage(0));
rbuf = -1;
} // end try/catch
} else if (ExecDirectSQL(sql, tocols) >= 0)
rbuf = 0;
return rbuf;
} // end of Rewind
/***********************************************************************/
/* 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(1) && rc != SQL_SUCCESS)
htrc("Error: SQLDisconnect rc=%d\n", rc);
rc = SQLFreeConnect(m_hdbc);
if (trace(1) && 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(1) && rc != SQL_SUCCESS) // Nothing we can do
htrc("Error: SQLFreeEnv failure ignored in Close\n");
m_henv = SQL_NULL_HENV;
} // endif m_henv
if (m_Fp)
m_Fp->Count = 0;
} // end of Close