mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
2809803ee2
2) Fix bug on bad sprintf 3) Fix bug on cast from pointer to int 4) Begin implementing the "info" tables. Already existing were the ODBC sata source table and the WMI column info table. A common way to handle them will permit to develop many other such tables. Implemented: The ODBC column info table. Modified: ha_connect.cc (4) odbconn.cpp (4) tabodbc.h (4) tabodbc.cpp (4) tabsys.h (3) rcmsg.c (4) tabfmt.cpp (2) tabtbl.cpp (1) resource.h (4) mycat.h (4)
2042 lines
62 KiB
C++
2042 lines
62 KiB
C++
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
|
|
/* Name: ODBCONN.CPP Version 1.6 */
|
|
/* */
|
|
/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
|
|
/* */
|
|
/* This file contains the ODBC connection classes functions. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#if defined(WIN32)
|
|
//nclude <io.h>
|
|
//nclude <fcntl.h>
|
|
#if defined(__BORLANDC__)
|
|
#define __MFC_COMPAT__ // To define min/max as macro
|
|
#endif
|
|
//#include <windows.h>
|
|
#else
|
|
#if defined(UNIX)
|
|
#include <errno.h>
|
|
#else
|
|
//nclude <io.h>
|
|
#endif
|
|
//nclude <fcntl.h>
|
|
#define NODW
|
|
#endif
|
|
|
|
/***********************************************************************/
|
|
/* Required objects includes. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "xobject.h"
|
|
//#include "kindex.h"
|
|
#include "xtable.h"
|
|
#include "tabodbc.h"
|
|
#include "plgcnx.h" // For DB types
|
|
#include "resource.h"
|
|
#include "valblk.h"
|
|
|
|
|
|
#if defined(WIN32)
|
|
/***********************************************************************/
|
|
/* For dynamic load of ODBC32.DLL */
|
|
/***********************************************************************/
|
|
#pragma comment(lib, "odbc32.lib")
|
|
extern "C" HINSTANCE s_hModule; // Saved module handle
|
|
#else // !WIN32
|
|
extern "C" int GetRcString(int id, char *buf, int bufsize);
|
|
#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
|
|
|
|
/***********************************************************************/
|
|
/* GetSQLType: returns the SQL_TYPE corresponding to a PLG type. */
|
|
/***********************************************************************/
|
|
static short GetSQLType(int type)
|
|
{
|
|
short tp = SQL_TYPE_NULL;
|
|
|
|
switch (type) {
|
|
case TYPE_STRING: tp = SQL_CHAR; break;
|
|
case TYPE_SHORT: tp = SQL_SMALLINT; break;
|
|
case TYPE_INT: tp = SQL_INTEGER; break;
|
|
case TYPE_DATE: tp = SQL_TIMESTAMP; break;
|
|
case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
|
|
case TYPE_FLOAT: tp = SQL_DOUBLE; break;
|
|
} // endswitch type
|
|
|
|
return tp;
|
|
} // end of GetSQLType
|
|
|
|
/***********************************************************************/
|
|
/* GetSQLCType: returns the SQL_C_TYPE corresponding to a PLG type. */
|
|
/***********************************************************************/
|
|
static int GetSQLCType(int type)
|
|
{
|
|
int tp = SQL_TYPE_NULL;
|
|
|
|
switch (type) {
|
|
case TYPE_STRING: tp = SQL_C_CHAR; break;
|
|
case TYPE_SHORT: tp = SQL_C_SHORT; break;
|
|
case TYPE_INT: tp = SQL_C_LONG; break;
|
|
case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
|
|
case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
|
|
case TYPE_FLOAT: tp = SQL_C_DOUBLE; break;
|
|
} // endswitch type
|
|
|
|
return tp;
|
|
} // end of GetSQLCType
|
|
|
|
/***********************************************************************/
|
|
/* TranslateSQLType: translate a SQL Type to a PLG type. */
|
|
/***********************************************************************/
|
|
int TranslateSQLType(int stp, int prec, int& len)
|
|
{
|
|
int type;
|
|
|
|
switch (stp) {
|
|
case SQL_CHAR: // 1
|
|
case SQL_VARCHAR: // 12
|
|
type = TYPE_STRING;
|
|
break;
|
|
case SQL_LONGVARCHAR: // (-1)
|
|
type = TYPE_STRING;
|
|
len = min(abs(len), 255);
|
|
break;
|
|
case SQL_NUMERIC: // 2
|
|
case SQL_DECIMAL: // 3
|
|
type = (prec) ? TYPE_FLOAT
|
|
: (len > 10) ? TYPE_BIGINT : TYPE_INT;
|
|
break;
|
|
case SQL_INTEGER: // 4
|
|
type = TYPE_INT;
|
|
break;
|
|
case SQL_SMALLINT: // 5
|
|
case SQL_TINYINT: // (-6)
|
|
case SQL_BIT: // (-7)
|
|
type = TYPE_SHORT;
|
|
break;
|
|
case SQL_FLOAT: // 6
|
|
case SQL_REAL: // 7
|
|
case SQL_DOUBLE: // 8
|
|
type = TYPE_FLOAT;
|
|
break;
|
|
case SQL_DATETIME: // 9
|
|
// case SQL_DATE: // 9
|
|
type = TYPE_DATE;
|
|
len = 10;
|
|
break;
|
|
case SQL_INTERVAL: // 10
|
|
// case SQL_TIME: // 10
|
|
type = TYPE_STRING;
|
|
len = 8 + ((prec) ? (prec+1) : 0);
|
|
break;
|
|
case SQL_TIMESTAMP: // 11
|
|
type = TYPE_DATE;
|
|
len = 19 + ((prec) ? (prec+1) : 0);
|
|
break;
|
|
case SQL_BIGINT: // (-5)
|
|
type = TYPE_BIGINT;
|
|
break;
|
|
case SQL_UNKNOWN_TYPE: // 0
|
|
case SQL_BINARY: // (-2)
|
|
case SQL_VARBINARY: // (-3)
|
|
case SQL_LONGVARBINARY: // (-4)
|
|
// case SQL_BIT: // (-7)
|
|
case SQL_GUID: // (-11)
|
|
default:
|
|
type = TYPE_ERROR;
|
|
len = 0;
|
|
} // endswitch type
|
|
|
|
return type;
|
|
} // end of TranslateSQLType
|
|
|
|
/***********************************************************************/
|
|
/* ODBConn static members initialization. */
|
|
/***********************************************************************/
|
|
HENV ODBConn::m_henv = SQL_NULL_HENV;
|
|
int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above
|
|
|
|
/**************************************************************************/
|
|
/* Allocate the result structure that will contain result data. */
|
|
/**************************************************************************/
|
|
PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
|
|
int *dbtype, int *buftyp, unsigned int *length,
|
|
bool blank = true, bool nonull = true);
|
|
|
|
/***********************************************************************/
|
|
/* Allocate the structure used to refer to the result set. */
|
|
/***********************************************************************/
|
|
CATPARM *AllocCatInfo(PGLOBAL g, CATINFO fid, char *tab, PQRYRES qrp)
|
|
{
|
|
size_t i, m, n;
|
|
CATPARM *cap;
|
|
|
|
#if defined(_DEBUG)
|
|
assert(qrp);
|
|
#endif
|
|
m = (size_t)qrp->Maxres;
|
|
n = (size_t)qrp->Nbcol;
|
|
cap = (CATPARM *)PlugSubAlloc(g, NULL, sizeof(CATPARM));
|
|
memset(cap, 0, sizeof(CATPARM));
|
|
cap->Id = fid;
|
|
cap->Qrp = qrp;
|
|
cap->Tab = (PUCHAR)tab;
|
|
cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SDWORD *));
|
|
|
|
for (i = 0; i < n; i++)
|
|
cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SDWORD));
|
|
|
|
cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD));
|
|
return cap;
|
|
} // end of AllocCatInfo
|
|
|
|
/***********************************************************************/
|
|
/* Check for nulls and reset them to Null (?) values. */
|
|
/***********************************************************************/
|
|
void ResetNullValues(CATPARM *cap)
|
|
{
|
|
int i, n, ncol;
|
|
PCOLRES crp;
|
|
PQRYRES qrp = cap->Qrp;
|
|
|
|
#if defined(_DEBUG)
|
|
assert(qrp);
|
|
#endif
|
|
|
|
ncol = qrp->Nbcol;
|
|
|
|
for (i = 0, crp = qrp->Colresp; i < ncol && crp; i++, crp = crp->Next)
|
|
for (n = 0; n < qrp->Nblin; n++)
|
|
if (cap->Vlen[i][n] == SQL_NULL_DATA)
|
|
crp->Kdata->Reset(n);
|
|
|
|
} // end of ResetNullValues
|
|
|
|
/***********************************************************************/
|
|
/* ODBCColumns: constructs the result blocks containing all columns */
|
|
/* of an ODBC table that will be retrieved by GetData commands. */
|
|
/* Note: The first two columns (Qualifier, Owner) are ignored. */
|
|
/***********************************************************************/
|
|
PQRYRES ODBCColumns(PGLOBAL g, ODBConn *ocp, char *dsn, char *table,
|
|
char *colpat)
|
|
{
|
|
static int dbtype[] = {DB_CHAR, DB_CHAR,
|
|
DB_CHAR, DB_SHORT, DB_CHAR,
|
|
DB_INT, DB_INT, DB_SHORT,
|
|
DB_SHORT, DB_SHORT, DB_CHAR};
|
|
static int buftyp[] = {TYPE_STRING, TYPE_STRING,
|
|
TYPE_STRING, TYPE_SHORT, TYPE_STRING,
|
|
TYPE_INT, TYPE_INT, TYPE_SHORT,
|
|
TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
|
|
static unsigned int length[] = {0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128};
|
|
int n, ncol = 11;
|
|
int maxres;
|
|
PQRYRES qrp;
|
|
CATPARM *cap;
|
|
|
|
/************************************************************************/
|
|
/* Do an evaluation of the result size. */
|
|
/************************************************************************/
|
|
if (ocp) {
|
|
n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
|
|
maxres = (n) ? (int)n : 250;
|
|
n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
|
|
length[0] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
|
|
length[1] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
|
|
length[2] = (n) ? (n + 1) : 128;
|
|
} else { // Info table
|
|
maxres = 0;
|
|
length[0] = 128;
|
|
length[1] = 128;
|
|
length[2] = 128;
|
|
} // endif ocp
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("ODBCColumns: max=%d len=%d,%d,%d\n",
|
|
maxres, length[0], length[1], length[2]);
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Allocate the structures used to refer to the result set. */
|
|
/************************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS + 1,
|
|
dbtype, buftyp, length);
|
|
|
|
if (!ocp) // Info table
|
|
return qrp;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Getting col results ncol=%d\n", qrp->Nbcol);
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
|
|
#endif
|
|
} else
|
|
qrp = NULL;
|
|
|
|
/************************************************************************/
|
|
/* Return the result pointer for use by GetData routines. */
|
|
/************************************************************************/
|
|
return qrp;
|
|
} // end of ODBCColumns
|
|
|
|
/**************************************************************************/
|
|
/* MyODBCCols: returns column info as required by ha_connect::pre_create. */
|
|
/**************************************************************************/
|
|
PQRYRES MyODBCCols(PGLOBAL g, char *tab, char *dsn, bool info)
|
|
{
|
|
int type, len, prec;
|
|
PCOLRES 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. */
|
|
/************************************************************************/
|
|
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
|
|
qrp->Nbcol = 7; // Was 11, skipped 4
|
|
return qrp;
|
|
} // end of MyODBCCols
|
|
|
|
/*************************************************************************/
|
|
/* 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)
|
|
{
|
|
static int dbtype[] = {DB_CHAR, DB_CHAR};
|
|
static int buftyp[] = {TYPE_STRING, TYPE_STRING};
|
|
static unsigned int length[] = {0, 256};
|
|
int n = 0, ncol = 2;
|
|
int maxres;
|
|
PQRYRES qrp;
|
|
ODBConn *ocp;
|
|
|
|
/************************************************************************/
|
|
/* 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
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("ODBCDataSources: max=%d len=%d\n", maxres, length[0]);
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Allocate the structures used to refer to the result set. */
|
|
/************************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC, dbtype, buftyp, length);
|
|
|
|
/************************************************************************/
|
|
/* 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
|
|
|
|
#if 0 // Currently not used by CONNECT
|
|
/***********************************************************************/
|
|
/* 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, ODBConn *op, char *dsn, char *tabpat,
|
|
char *tabtyp)
|
|
{
|
|
static int dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, DB_CHAR};
|
|
static int buftyp[] = {TYPE_STRING, TYPE_STRING,
|
|
TYPE_STRING, TYPE_STRING};
|
|
static unsigned int length[] = {0, 0, 16, 128};
|
|
int n, ncol = 4;
|
|
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. */
|
|
/************************************************************************/
|
|
maxres = 512; // This is completely arbitrary
|
|
n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
|
|
length[0] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
|
|
length[1] = (n) ? (n + 1) : 128;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("ODBCTables: max=%d len=%d,%d\n",
|
|
maxres, length[0], length[1]);
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Allocate the structures used to refer to the result set. */
|
|
/************************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES + 1,
|
|
dbtype, buftyp, length);
|
|
|
|
cap = AllocCatInfo(g, CAT_TAB, tabpat, qrp);
|
|
cap->Pat = (PUCHAR)tabtyp;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol);
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Now get the results into blocks. */
|
|
/************************************************************************/
|
|
if ((n = ocp->GetCatInfo(cap)) >= 0) {
|
|
qrp->Nblin = n;
|
|
ResetNullValues(cap);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
|
|
#endif
|
|
} else
|
|
qrp = NULL;
|
|
|
|
/************************************************************************/
|
|
/* Close any local connection. */
|
|
/************************************************************************/
|
|
if (!op)
|
|
ocp->Close();
|
|
|
|
/************************************************************************/
|
|
/* Return the result pointer for use by GetData routines. */
|
|
/************************************************************************/
|
|
return qrp;
|
|
} // end of ODBCTables
|
|
|
|
/**************************************************************************/
|
|
/* 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 dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, DB_SHORT, DB_CHAR};
|
|
static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
|
|
TYPE_SHORT, TYPE_STRING};
|
|
static unsigned int length[] = {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_USER_NAME_LEN);
|
|
length[0] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
|
|
length[1] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
|
|
length[2] = (n) ? (n + 1) : 128;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("ODBCPrimaryKeys: max=%d len=%d,%d,%d\n",
|
|
maxres, length[0], length[1], length[2]);
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Allocate the structure used to refer to the result set. */
|
|
/************************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY + 1,
|
|
dbtype, buftyp, length);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
|
|
#endif
|
|
|
|
cap = AllocCatInfo(g, CAT_KEY, table, qrp);
|
|
|
|
/************************************************************************/
|
|
/* Now get the results into blocks. */
|
|
/************************************************************************/
|
|
if ((n = ocp->GetCatInfo(cap)) >= 0) {
|
|
qrp->Nblin = n;
|
|
ResetNullValues(cap);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n",
|
|
qrp->Nbcol, qrp->Nblin);
|
|
#endif
|
|
} 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 dbtype[] = {DB_CHAR, DB_CHAR, DB_SHORT, DB_CHAR,
|
|
DB_CHAR, DB_SHORT, DB_SHORT, DB_CHAR,
|
|
DB_CHAR, DB_INT, DB_INT, DB_CHAR};
|
|
static int buftyp[] = {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 ,6 ,0 ,0 ,6 ,6 ,0 ,2 ,10 ,10 ,128};
|
|
int n, ncol = 12;
|
|
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[0] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
|
|
length[1] = length[4] = (n) ? (n + 1) : 128;
|
|
n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN);
|
|
length[3] = (n) ? (n + 1) : length[1];
|
|
n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
|
|
length[7] = (n) ? (n + 1) : 128;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("SemStatistics: max=%d pat=%s\n", maxres, SVP(pat));
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Allocate the structure used to refer to the result set. */
|
|
/************************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, maxres, IDS_STAT + 1,
|
|
dbtype, buftyp, length);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Getting stat results ncol=%d\n", qrp->Nbcol);
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Statistics: NBCOL=%d NBLIN=%d\n",
|
|
qrp->Nbcol, qrp->Nblin);
|
|
#endif
|
|
} 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
|
|
|
|
/***********************************************************************/
|
|
/* GetColumnInfo: used when defining a ODBC table. The issue is that */
|
|
/* some ODBC drivers give key information by SQLPrimaryKeys while */
|
|
/* others do not implement it but give info using SQLStatistics. */
|
|
/***********************************************************************/
|
|
PQRYRES GetColumnInfo(PGLOBAL g, char*& dsn,
|
|
char *table, int ver, PVBLK& vbp)
|
|
{
|
|
PCOLRES crp;
|
|
PQRYRES qrpc, qrp;
|
|
PVBLK vbp2;
|
|
ODBConn *ocp = new(g) ODBConn(g, NULL);
|
|
|
|
/**********************************************************************/
|
|
/* Open the connection with the ODBC data source. */
|
|
/**********************************************************************/
|
|
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
|
|
return NULL;
|
|
else if (ver > 0)
|
|
ocp->m_Catver = ver;
|
|
|
|
/**********************************************************************/
|
|
/* Get the information about the ODBC table columns. */
|
|
/**********************************************************************/
|
|
if ((qrpc = ODBCColumns(g, ocp, dsn, table, NULL)))
|
|
dsn = ocp->GetConnect(); // Complete connect string
|
|
else
|
|
return NULL;
|
|
|
|
if ((qrp = ODBCPrimaryKeys(g, ocp, dsn, table))) {
|
|
// Oracle, ...
|
|
if (qrp->Nblin) {
|
|
crp = qrp->Colresp->Next->Next;
|
|
vbp = crp->Kdata;
|
|
vbp->ReAlloc(vbp->GetValPointer(), qrp->Nblin);
|
|
} // endif Nblin
|
|
|
|
} else if ((qrp = ODBCStatistics(g, ocp, dsn, table, -1, -1))) {
|
|
// Case of Microsoft Jet Engine
|
|
if (qrp->Nblin) {
|
|
int i, n = 0;
|
|
PCOLRES crp2;
|
|
|
|
crp = qrp->Colresp->Next->Next->Next->Next;
|
|
crp2 = crp->Next->Next->Next;
|
|
|
|
// This test may have to be modified for other ODBC drivers
|
|
for (i = 0; i < qrp->Nblin; i++)
|
|
if (!strcmp(crp->Kdata->GetCharValue(i), "PrimaryKey"))
|
|
n++;
|
|
|
|
if (n) {
|
|
vbp2 = crp2->Kdata;
|
|
vbp = AllocValBlock(g, NULL, vbp2->GetType(), n,
|
|
vbp2->GetVlen(), 0, false, false);
|
|
|
|
for (i = 0, n = 0; i < qrp->Nblin; i++)
|
|
if (!strcmp(crp->Kdata->GetCharValue(i), "PrimaryKey"))
|
|
vbp->SetValue(vbp2, n++, i);
|
|
|
|
} // endif n
|
|
|
|
} // endif Nblin
|
|
|
|
} // endif qrp
|
|
|
|
/************************************************************************/
|
|
/* Close the local connection. */
|
|
/************************************************************************/
|
|
ocp->Close();
|
|
|
|
return qrpc;
|
|
} // end of GetColumnInfo
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Implementation of DBX class. */
|
|
/***********************************************************************/
|
|
DBX::DBX(RETCODE rc)
|
|
{
|
|
m_RC = rc;
|
|
|
|
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);
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("%s, Native=%d\n", msg, native);
|
|
#endif
|
|
|
|
rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state,
|
|
&native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
|
|
} // endfor i
|
|
|
|
else
|
|
m_ErrMsg[0] = MSG(BAD_HANDLE_VAL);
|
|
|
|
} else
|
|
m_ErrMsg[0] = "No connexion address provided";
|
|
|
|
} // end of BuildErrorMessage
|
|
|
|
/***********************************************************************/
|
|
/* ODBConn construction/destruction. */
|
|
/***********************************************************************/
|
|
ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
|
|
{
|
|
m_G = g;
|
|
m_Tdb = tdbp;
|
|
m_hdbc = SQL_NULL_HDBC;
|
|
//m_Recset = NULL
|
|
m_hstmt = SQL_NULL_HSTMT;
|
|
m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT;
|
|
m_QueryTimeout = DEFAULT_QUERY_TIMEOUT;
|
|
m_UpdateOptions = 0;
|
|
m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10);
|
|
m_Catver = (tdbp) ? tdbp->Catver : 0;
|
|
m_Connect = NULL;
|
|
m_Updatable = true;
|
|
//m_Transactions = false;
|
|
m_IDQuoteChar = '\'';
|
|
//*m_ErrMsg = '\0';
|
|
} // end of ODBConn
|
|
|
|
//ODBConn::~ODBConn()
|
|
// {
|
|
//if (Connected())
|
|
// EndCom();
|
|
|
|
// } // end of ~ODBConn
|
|
|
|
/***********************************************************************/
|
|
/* Screen for errors. */
|
|
/***********************************************************************/
|
|
bool ODBConn::Check(RETCODE rc)
|
|
{
|
|
switch (rc) {
|
|
case SQL_SUCCESS_WITH_INFO:
|
|
if (m_G->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, HSTMT hstmt)
|
|
{
|
|
DBX* xp = new(m_G) DBX(rc);
|
|
|
|
xp->BuildErrorMessage(this, hstmt);
|
|
throw xp;
|
|
} // end of ThrowDBX
|
|
|
|
void ODBConn::ThrowDBX(PSZ msg)
|
|
{
|
|
DBX* xp = new(m_G) DBX(0);
|
|
|
|
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); // Temporary
|
|
// *buffer = '\0';
|
|
|
|
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));
|
|
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); // Fatal
|
|
} // endif
|
|
|
|
} // endif m_henv
|
|
|
|
// Do the real thing, allocating connection data
|
|
rc = SQLAllocConnect(m_henv, &m_hdbc);
|
|
|
|
if (!Check(rc)) {
|
|
// AfxUnlockGlobals(CRIT_ODBC);
|
|
ThrowDBX(rc); // Fatal
|
|
} // endif
|
|
|
|
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);
|
|
|
|
#ifdef DEBTRACE
|
|
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
|
|
htrc("Warning: Failure setting login timeout\n");
|
|
#endif
|
|
|
|
if (!m_Updatable) {
|
|
rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
|
|
SQL_MODE_READ_ONLY);
|
|
#ifdef DEBTRACE
|
|
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
|
|
htrc("Warning: Failure setting read only access mode\n");
|
|
#endif
|
|
} // endif
|
|
|
|
// Turn on cursor lib support
|
|
if (Options & useCursorLib)
|
|
rc = SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC);
|
|
|
|
return;
|
|
} // end of AllocConnect
|
|
|
|
/***********************************************************************/
|
|
/* Connect to data source using SQLDriverConnect. */
|
|
/***********************************************************************/
|
|
bool ODBConn::Connect(DWORD Options)
|
|
{
|
|
RETCODE rc;
|
|
SWORD nResult;
|
|
PUCHAR ConnOut = (PUCHAR)PlugSubAlloc(m_G, NULL, MAX_CONNECT_LEN);
|
|
UWORD wConnectOption = SQL_DRIVER_COMPLETE;
|
|
#if defined(WIN32)
|
|
HWND hWndTop = GetForegroundWindow();
|
|
HWND hWnd = GetParent(hWndTop);
|
|
|
|
if (hWnd == NULL)
|
|
hWnd = GetDesktopWindow();
|
|
#else // !WIN32
|
|
HWND hWnd = NULL;
|
|
#endif // !WIN32
|
|
PGLOBAL& g = m_G;
|
|
PDBUSER dup = PlgGetUser(g);
|
|
|
|
if (Options & noOdbcDialog || dup->Remote)
|
|
wConnectOption = SQL_DRIVER_NOPROMPT;
|
|
else if (Options & forceOdbcDialog)
|
|
wConnectOption = SQL_DRIVER_PROMPT;
|
|
|
|
rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect,
|
|
SQL_NTS, ConnOut, MAX_CONNECT_LEN,
|
|
&nResult, wConnectOption);
|
|
|
|
#if defined(WIN32)
|
|
if (hWndTop)
|
|
EnableWindow(hWndTop, true);
|
|
#endif // WIN32
|
|
|
|
// If user hit 'Cancel'
|
|
if (rc == SQL_NO_DATA_FOUND) {
|
|
Free();
|
|
return true;
|
|
} // endif rc
|
|
|
|
if (!Check(rc)) {
|
|
#ifdef DEBTRACE
|
|
if (!hWnd == NULL)
|
|
htrc("Error: No default window for SQLDriverConnect\n");
|
|
#endif
|
|
ThrowDBX(rc);
|
|
} // endif Check
|
|
|
|
// 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);
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("Warning: data source is readonly\n");
|
|
#endif
|
|
} else // Make data source is !Updatable
|
|
rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
|
|
SQL_MODE_READ_ONLY);
|
|
#endif // 0
|
|
|
|
// Cache the quote char to use when constructing SQL
|
|
char QuoteChar[2];
|
|
|
|
rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
|
|
QuoteChar, sizeof(QuoteChar), &nResult);
|
|
|
|
if (Check(rc) && nResult == 1)
|
|
m_IDQuoteChar = QuoteChar[0];
|
|
else
|
|
m_IDQuoteChar = ' ';
|
|
|
|
#ifdef DEBTRACE
|
|
htrc("DBMS: %s, Version: %s",
|
|
GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER));
|
|
#endif // DEBTRACE
|
|
} // end of GetConnectInfo
|
|
|
|
/***********************************************************************/
|
|
/* Allocate record set and execute an SQL query. */
|
|
/***********************************************************************/
|
|
int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
|
|
{
|
|
PGLOBAL& g = m_G;
|
|
void *buffer;
|
|
bool b;
|
|
UWORD n;
|
|
SWORD ncol, len, tp;
|
|
SQLLEN afrw;
|
|
ODBCCOL *colp;
|
|
RETCODE rc;
|
|
HSTMT hstmt;
|
|
|
|
//m_Recset = new(m_G) RECSET(this);
|
|
//ASSERT(m_Recset);
|
|
|
|
try {
|
|
b = false;
|
|
|
|
if (m_hstmt) {
|
|
RETCODE rc;
|
|
|
|
// All this did not seems to make sense and was been commented out
|
|
// if (IsOpen())
|
|
// Close(SQL_CLOSE);
|
|
|
|
rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
|
|
hstmt = m_hstmt;
|
|
m_hstmt = NULL;
|
|
ThrowDBX(MSG(SEQUENCE_ERROR));
|
|
} else {
|
|
rc = SQLAllocStmt(m_hdbc, &hstmt);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(SQL_INVALID_HANDLE);
|
|
|
|
} // endif hstmt
|
|
|
|
OnSetOptions(hstmt);
|
|
b = true;
|
|
|
|
if (g->Trace) {
|
|
htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
|
|
fflush(debug);
|
|
} // endif Trace
|
|
|
|
do {
|
|
rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
|
|
} while (rc == SQL_STILL_EXECUTING);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
do {
|
|
rc = SQLNumResultCols(hstmt, &ncol);
|
|
} while (rc == SQL_STILL_EXECUTING);
|
|
|
|
if (ncol == 0) {
|
|
// Update or Delete statement
|
|
rc = SQLRowCount(hstmt, &afrw);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
return afrw;
|
|
} // endif ncol
|
|
|
|
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
|
|
if (!colp->IsSpecial())
|
|
n++;
|
|
|
|
// n can be 0 for query such as Select count(*) from table
|
|
if (n && n != (UWORD)ncol)
|
|
ThrowDBX(MSG(COL_NUM_MISM));
|
|
|
|
// Now bind the column buffers
|
|
for (n = 1, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
|
|
if (!colp->IsSpecial()) {
|
|
buffer = colp->GetBuffer(m_RowsetSize);
|
|
len = colp->GetBuflen();
|
|
tp = GetSQLCType(colp->GetResultType());
|
|
|
|
if (tp == SQL_TYPE_NULL) {
|
|
sprintf(m_G->Message, MSG(INV_COLUMN_TYPE),
|
|
colp->GetResultType(), SVP(colp->GetName()));
|
|
ThrowDBX(m_G->Message);
|
|
} // endif tp
|
|
|
|
if (g->Trace) {
|
|
htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n",
|
|
n, tp, buffer, len, colp->GetStrLen());
|
|
fflush(debug);
|
|
} // endif Trace
|
|
|
|
rc = SQLBindCol(hstmt, n, tp, buffer, len, colp->GetStrLen());
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
n++;
|
|
} // endif pcol
|
|
|
|
} catch(DBX *x) {
|
|
#ifdef DEBTRACE
|
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
|
htrc(x->m_ErrMsg[i]);
|
|
#endif
|
|
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, m_hstmt);
|
|
|
|
if (rc == SQL_NO_DATA_FOUND)
|
|
break;
|
|
|
|
} // endfor n
|
|
|
|
} catch(DBX *x) {
|
|
// strcpy(m_G->Message, x->m_ErrMsg[0]);
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
#ifdef DEBTRACE
|
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
|
htrc(x->m_ErrMsg[i]);
|
|
#endif
|
|
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 (g->Trace)
|
|
htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n",
|
|
m_hstmt, m_RowsetSize, rc);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, m_hstmt);
|
|
|
|
irc = (rc == SQL_NO_DATA_FOUND) ? 0 : (int)crow;
|
|
} catch(DBX *x) {
|
|
if (g->Trace)
|
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
|
htrc(x->m_ErrMsg[i]);
|
|
|
|
strcpy(g->Message, x->GetErrorMessage(0));
|
|
irc = -1;
|
|
} // end try/catch
|
|
|
|
return irc;
|
|
} // end of Fetch
|
|
|
|
/***********************************************************************/
|
|
/* Prepare an SQL statement for insert. */
|
|
/***********************************************************************/
|
|
int ODBConn::PrepareSQL(char *sql)
|
|
{
|
|
PGLOBAL& g = m_G;
|
|
bool b;
|
|
SWORD nparm;
|
|
RETCODE rc;
|
|
HSTMT hstmt;
|
|
|
|
try {
|
|
b = false;
|
|
|
|
if (m_hstmt) {
|
|
RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
|
|
|
|
hstmt = m_hstmt;
|
|
m_hstmt = NULL;
|
|
ThrowDBX(MSG(SEQUENCE_ERROR));
|
|
} else {
|
|
rc = SQLAllocStmt(m_hdbc, &hstmt);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(SQL_INVALID_HANDLE);
|
|
|
|
} // endif hstmt
|
|
|
|
OnSetOptions(hstmt);
|
|
b = true;
|
|
|
|
if (g->Trace) {
|
|
htrc("Prepare hstmt=%p %.64s\n", hstmt, sql);
|
|
fflush(debug);
|
|
} // endif Trace
|
|
|
|
do {
|
|
rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
|
|
} while (rc == SQL_STILL_EXECUTING);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
do {
|
|
rc = SQLNumParams(hstmt, &nparm);
|
|
} while (rc == SQL_STILL_EXECUTING);
|
|
|
|
} catch(DBX *x) {
|
|
#ifdef DEBTRACE
|
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
|
htrc(x->m_ErrMsg[i]);
|
|
#endif
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
|
|
if (b)
|
|
SQLCancel(hstmt);
|
|
|
|
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
|
m_hstmt = NULL;
|
|
return -1;
|
|
} // end try/catch
|
|
|
|
m_hstmt = hstmt;
|
|
return (int)nparm;
|
|
} // end of PrepareSQL
|
|
|
|
/***********************************************************************/
|
|
/* Bind a parameter for inserting. */
|
|
/***********************************************************************/
|
|
bool ODBConn::ExecuteSQL(void)
|
|
{
|
|
RETCODE rc;
|
|
|
|
try {
|
|
rc = SQLExecute(m_hstmt);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, m_hstmt);
|
|
|
|
} catch(DBX *x) {
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
SQLCancel(m_hstmt);
|
|
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
|
|
m_hstmt = NULL;
|
|
return true;
|
|
} // end try/catch
|
|
|
|
return false;
|
|
} // end of ExecuteSQL
|
|
|
|
/***********************************************************************/
|
|
/* Bind a parameter for inserting. */
|
|
/***********************************************************************/
|
|
bool ODBConn::BindParam(ODBCCOL *colp)
|
|
{
|
|
void *buf;
|
|
UWORD n = colp->GetRank();
|
|
SWORD ct, sqlt;
|
|
UDWORD len;
|
|
SQLLEN *strlen = colp->GetStrLen();
|
|
RETCODE rc;
|
|
|
|
#if 0
|
|
try {
|
|
SWORD dec, nul;
|
|
rc = SQLDescribeParam(m_hstmt, n, &sqlt, &len, &dec, &nul);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, m_hstmt);
|
|
|
|
} catch(DBX *x) {
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
} // end try/catch
|
|
#endif // 0
|
|
|
|
buf = colp->GetBuffer(0);
|
|
// len = colp->GetBuflen();
|
|
len = IsTypeNum(colp->GetResultType()) ? 0 : colp->GetBuflen();
|
|
ct = GetSQLCType(colp->GetResultType());
|
|
sqlt = GetSQLType(colp->GetResultType());
|
|
*strlen = IsTypeNum(colp->GetResultType()) ? 0 : SQL_NTS;
|
|
|
|
try {
|
|
rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt,
|
|
len, 0, buf, 0, strlen);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, m_hstmt);
|
|
|
|
} catch(DBX *x) {
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
SQLCancel(m_hstmt);
|
|
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
|
|
m_hstmt = NULL;
|
|
return true;
|
|
} // end try/catch
|
|
|
|
return false;
|
|
} // end of BindParam
|
|
|
|
/***********************************************************************/
|
|
/* Get the list of Data Sources and set it in qrp. */
|
|
/***********************************************************************/
|
|
bool ODBConn::GetDataSources(PQRYRES qrp)
|
|
{
|
|
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); // 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); // Fatal
|
|
|
|
qrp->Nblin++;
|
|
dir = SQL_FETCH_NEXT;
|
|
} // endfor i
|
|
|
|
} catch(DBX *x) {
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
SQLFreeEnv(m_henv);
|
|
return true;
|
|
} // end try/catch
|
|
|
|
SQLFreeEnv(m_henv);
|
|
return false;
|
|
} // end of GetDataSources
|
|
|
|
/***********************************************************************/
|
|
/* 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;
|
|
UWORD n;
|
|
SWORD ncol, len, tp;
|
|
SQLULEN crow;
|
|
PCOLRES crp;
|
|
RETCODE rc;
|
|
HSTMT hstmt = NULL;
|
|
SQLLEN *vl, *vlen;
|
|
PVAL *pval = NULL;
|
|
|
|
try {
|
|
b = false;
|
|
|
|
if (!m_hstmt) {
|
|
rc = SQLAllocStmt(m_hdbc, &hstmt);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(SQL_INVALID_HANDLE);
|
|
|
|
} else
|
|
ThrowDBX(MSG(SEQUENCE_ERROR));
|
|
|
|
b = true;
|
|
|
|
if ((m_RowsetSize = cap->Qrp->Maxres) > 0) {
|
|
if (m_Catver) {
|
|
// Attempt to set rowset size.
|
|
// In case of failure reset it to 0 to use Fetch.
|
|
if (m_Catver == 3) // ODBC Ver 3
|
|
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE,
|
|
(SQLPOINTER)m_RowsetSize, 0);
|
|
else
|
|
rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
|
|
|
|
if (!Check(rc))
|
|
m_RowsetSize = 1; // don't attempt it again
|
|
// ThrowDBX(rc, hstmt); // Temporary
|
|
|
|
if (m_Catver == 3) { // ODBC Ver 3
|
|
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, cap->Status, 0);
|
|
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &crow, 0);
|
|
} // endif m_Catver
|
|
|
|
} else // ORABUG
|
|
m_RowsetSize = 1;
|
|
|
|
} else
|
|
ThrowDBX("0-sized result");
|
|
|
|
// Now do call the proper ODBC API
|
|
switch (cap->Id) {
|
|
case CAT_TAB:
|
|
// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
|
|
// (SQLPOINTER)false, 0);
|
|
rc = SQLTables(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS,
|
|
cap->Pat, SQL_NTS);
|
|
break;
|
|
case CAT_COL:
|
|
// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
|
|
// (SQLPOINTER)true, 0);
|
|
rc = SQLColumns(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS,
|
|
cap->Pat, SQL_NTS);
|
|
break;
|
|
case CAT_KEY:
|
|
rc = SQLPrimaryKeys(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS);
|
|
break;
|
|
case CAT_STAT:
|
|
rc = SQLStatistics(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS,
|
|
cap->Unique, cap->Accuracy);
|
|
break;
|
|
case CAT_SPC:
|
|
ThrowDBX("SQLSpecialColumns not available yet");
|
|
} // endswitch infotype
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
rc = SQLNumResultCols(hstmt, &ncol);
|
|
|
|
// n + 1 because we ignore the first column
|
|
if ((n = (UWORD)cap->Qrp->Nbcol) + 1 > (UWORD)ncol)
|
|
ThrowDBX(MSG(COL_NUM_MISM));
|
|
|
|
if (m_RowsetSize == 1 && cap->Qrp->Maxres > 1) {
|
|
pval = (PVAL *)PlugSubAlloc(m_G, NULL, n * sizeof(PVAL));
|
|
vlen = (SQLLEN *)PlugSubAlloc(m_G, NULL, n * sizeof(SDWORD *));
|
|
} // endif
|
|
|
|
// Now bind the column buffers
|
|
for (n = 0, crp = cap->Qrp->Colresp; crp; crp = crp->Next) {
|
|
if (pval) {
|
|
pval[n] = AllocateValue(m_G, crp->Kdata->GetType(),
|
|
crp->Kdata->GetVlen(), 0);
|
|
buffer = pval[n]->GetTo_Val();
|
|
vl = vlen + n;
|
|
} else {
|
|
buffer = crp->Kdata->GetValPointer();
|
|
vl = cap->Vlen[n];
|
|
} // endif pval
|
|
|
|
len = GetTypeSize(crp->Type, crp->Clen);
|
|
tp = GetSQLCType(crp->Type);
|
|
|
|
if (tp == SQL_TYPE_NULL) {
|
|
sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
|
|
ThrowDBX(m_G->Message);
|
|
} // endif tp
|
|
|
|
// n + 2 because column numbers begin with 1 and because
|
|
// we ignore the first column
|
|
rc = SQLBindCol(hstmt, n + 2, tp, buffer, len, vl);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
n++;
|
|
} // endfor crp
|
|
|
|
// Now fetch the result
|
|
if (m_Catver != 3) {
|
|
if (m_RowsetSize > 1) {
|
|
rc = SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &crow, cap->Status);
|
|
} else if (pval) {
|
|
for (n = 0; n < cap->Qrp->Maxres; n++) {
|
|
if ((rc = SQLFetch(hstmt)) != SQL_SUCCESS)
|
|
break;
|
|
|
|
for (i = 0, crp = cap->Qrp->Colresp; crp; i++, crp = crp->Next) {
|
|
crp->Kdata->SetValue(pval[i], n);
|
|
cap->Vlen[i][n] = vlen[i];
|
|
} // endfor crp
|
|
|
|
} // endfor n
|
|
|
|
if ((crow = n) && rc == SQL_NO_DATA)
|
|
rc = SQL_SUCCESS;
|
|
|
|
} else {
|
|
rc = SQLFetch(hstmt);
|
|
crow = 1;
|
|
} // endif's
|
|
|
|
} else // ODBC Ver 3
|
|
rc = SQLFetch(hstmt);
|
|
|
|
// if (!Check(rc))
|
|
if (rc == SQL_NO_DATA_FOUND) {
|
|
if (cap->Pat)
|
|
sprintf(m_G->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat);
|
|
else
|
|
sprintf(m_G->Message, MSG(NO_TAB_DATA), cap->Tab);
|
|
|
|
ThrowDBX(m_G->Message);
|
|
} else if (rc != SQL_SUCCESS)
|
|
ThrowDBX(rc, hstmt);
|
|
|
|
irc = (int)crow;
|
|
} catch(DBX *x) {
|
|
#ifdef DEBTRACE
|
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
|
htrc(x->m_ErrMsg[i]);
|
|
#endif
|
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
|
irc = -1;
|
|
} // end try/catch
|
|
|
|
if (b)
|
|
SQLCancel(hstmt);
|
|
|
|
// All this (hstmt vs> m_hstmt) to be revisited
|
|
if (hstmt)
|
|
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
|
|
|
return irc;
|
|
} // end of GetCatInfo
|
|
|
|
/***********************************************************************/
|
|
/* Disconnect connection */
|
|
/***********************************************************************/
|
|
void ODBConn::Close()
|
|
{
|
|
RETCODE rc;
|
|
|
|
#if 0
|
|
// Close any open recordsets
|
|
AfxLockGlobals(CRIT_ODBC);
|
|
TRY
|
|
{
|
|
while (!m_listRecordsets.IsEmpty())
|
|
{
|
|
CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead();
|
|
pSet->Close(); // will implicitly remove from list
|
|
pSet->m_pDatabase = NULL;
|
|
}
|
|
}
|
|
CATCH_ALL(e)
|
|
{
|
|
AfxUnlockGlobals(CRIT_ODBC);
|
|
THROW_LAST();
|
|
}
|
|
END_CATCH_ALL
|
|
AfxUnlockGlobals(CRIT_ODBC);
|
|
#endif // 0
|
|
|
|
if (m_hstmt) {
|
|
// Is required for multiple tables
|
|
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
|
|
m_hstmt = NULL;
|
|
} // endif m_hstmt
|
|
|
|
if (m_hdbc != SQL_NULL_HDBC) {
|
|
rc = SQLDisconnect(m_hdbc);
|
|
rc = SQLFreeConnect(m_hdbc);
|
|
m_hdbc = SQL_NULL_HDBC;
|
|
|
|
// AfxLockGlobals(CRIT_ODBC);
|
|
ASSERT(m_nAlloc != 0);
|
|
m_nAlloc--;
|
|
// AfxUnlockGlobals(CRIT_ODBC);
|
|
} // endif m_hdbc
|
|
|
|
} // end of Close
|
|
|
|
// Silently disconnect and free all ODBC resources.
|
|
// Don't throw any exceptions
|
|
void ODBConn::Free()
|
|
{
|
|
// Trap failures upon close
|
|
try {
|
|
Close();
|
|
} catch(DBX*) {
|
|
// Nothing we can do
|
|
#ifdef DEBTRACE
|
|
htrc("Error: exception by Close ignored in Free\n");
|
|
#endif
|
|
// DELETE_EXCEPTION(x);
|
|
} // endcatch
|
|
|
|
// free henv if refcount goes to 0
|
|
//AfxLockGlobals(CRIT_ODBC);
|
|
if (m_henv != SQL_NULL_HENV) {
|
|
ASSERT(m_nAlloc >= 0);
|
|
|
|
if (m_nAlloc == 0) {
|
|
// free last connection - release HENV
|
|
#ifdef DEBTRACE
|
|
RETCODE rc = SQLFreeEnv(m_henv);
|
|
if (rc != SQL_SUCCESS) // Nothing we can do
|
|
htrc("Error: SQLFreeEnv failure ignored in Free\n");
|
|
#else
|
|
SQLFreeEnv(m_henv);
|
|
#endif
|
|
m_henv = SQL_NULL_HENV;
|
|
} // endif m_nAlloc
|
|
}
|
|
//AfxUnlockGlobals(CRIT_ODBC);
|
|
} // end of Free
|
|
|
|
#if 0
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CRecordset helpers
|
|
|
|
//id AFXAPI AfxSetCurrentRecord(int* plCurrentRecord, int nRows, RETCODE nRetCode);
|
|
//id AFXAPI AfxSetRecordCount(int* plRecordCount, int lCurrentRecord,
|
|
//bool bEOFSeen, RETCODE nRetCode);
|
|
|
|
/***********************************************************************/
|
|
/* RECSET class implementation */
|
|
/***********************************************************************/
|
|
RECSET::RECSET(ODBConn *dbcp)
|
|
{
|
|
m_pDB = dbcp;
|
|
m_hstmt = SQL_NULL_HSTMT;
|
|
m_OpenType = snapshot;
|
|
m_Options = none;
|
|
|
|
#if 0
|
|
m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN;
|
|
m_nEditMode = noMode;
|
|
m_nDefaultType = snapshot;
|
|
|
|
m_bAppendable = false;
|
|
m_bUpdatable = false;
|
|
m_bScrollable = false;
|
|
m_bRecordsetDb = false;
|
|
m_bRebindParams = false;
|
|
m_bLongBinaryColumns = false;
|
|
m_nLockMode = optimistic;
|
|
m_dwInitialGetDataLen = 0;
|
|
m_rgODBCFieldInfos = NULL;
|
|
m_rgFieldInfos = NULL;
|
|
m_rgRowStatus = NULL;
|
|
m_dwRowsetSize = 25;
|
|
m_dwAllocatedRowsetSize = 0;
|
|
|
|
m_nFields = 0;
|
|
m_nParams = 0;
|
|
m_nFieldsBound = 0;
|
|
m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
|
|
m_lRecordCount = 0;
|
|
m_bUseUpdateSQL = false;
|
|
m_bUseODBCCursorLib = false;
|
|
m_nResultCols = -1;
|
|
m_bCheckCacheForDirtyFields = true;
|
|
|
|
m_pbFieldFlags = NULL;
|
|
m_pbParamFlags = NULL;
|
|
m_plParamLength = NULL;
|
|
m_pvFieldProxy = NULL;
|
|
m_pvParamProxy = NULL;
|
|
m_nProxyFields = 0;
|
|
m_nProxyParams = 0;
|
|
|
|
m_hstmtUpdate = SQL_NULL_HSTMT;
|
|
#endif // 0
|
|
} // end of RECSET constructor
|
|
|
|
RECSET::~RECSET()
|
|
{
|
|
try {
|
|
if (m_hstmt) {
|
|
#ifdef DEBTRACE
|
|
if (m_dwOptions & useMultiRowFetch) {
|
|
htrc("WARNING: Close called implicitly from destructor\n");
|
|
htrc("Use of multi row fetch requires explicit call\n");
|
|
htrc("to Close or memory leaks will result\n");
|
|
}
|
|
#endif
|
|
Close();
|
|
} // endif m_hstmt
|
|
|
|
// if (m_bRecordsetDb)
|
|
// delete m_pDB; ??????
|
|
|
|
m_pDB = NULL;
|
|
} catch(DBX*) {
|
|
// Nothing we can do
|
|
#ifdef DEBTRACE
|
|
htrc("Error: Exception ignored in ~RECSET\n");
|
|
#endif
|
|
} // endtry/catch
|
|
|
|
} // end of ~RECSET
|
|
|
|
/***********************************************************************/
|
|
/* Open: this function does the following: */
|
|
/* Allocates the hstmt, */
|
|
/* Bind columns, */
|
|
/* Execute the SQL statement */
|
|
/***********************************************************************/
|
|
bool RECSET::Open(PSZ sql, uint Type, DWORD options)
|
|
{
|
|
ASSERT(m_pDB && m_pDB->IsOpen());
|
|
ASSERT(Type == DB_USE_DEFAULT_TYPE || Type == dynaset ||
|
|
Type == snapshot || Type == forwardOnly || Type == dynamic);
|
|
//ASSERT(!(options & readOnly && options & appendOnly));
|
|
|
|
// Cache state info and allocate hstmt
|
|
SetState(Type, sql, options);
|
|
|
|
try {
|
|
if (m_hstmt) {
|
|
if (IsOpen())
|
|
Close(SQL_CLOSE);
|
|
|
|
} else {
|
|
RETCODE rc = SQLAllocStmt(m_pDB->m_hdbc, &m_hstmt);
|
|
|
|
if (!Check(rc))
|
|
ThrowDBException(SQL_INVALID_HANDLE);
|
|
|
|
} // endif m_hstmt
|
|
|
|
m_pDB->OnSetOptions(m_hstmt);
|
|
|
|
// Allocate the field/param status arrays, if necessary
|
|
// bool bUnbound = false;
|
|
|
|
// if (m_nFields > 0 || m_nParams > 0)
|
|
// AllocStatusArrays();
|
|
// else
|
|
// bUnbound = true;
|
|
|
|
// Build SQL and prep/execute or just execute direct
|
|
// BuildSQL(sql);
|
|
PrepareAndExecute(sql);
|
|
|
|
// Cache some field info and prepare the rowset
|
|
AllocAndCacheFieldInfo();
|
|
AllocRowset();
|
|
|
|
// If late binding, still need to allocate status arrays
|
|
// if (bUnbound && (m_nFields > 0 || m_nParams > 0))
|
|
// AllocStatusArrays();
|
|
|
|
} catch(DBX *x) {
|
|
Close(SQL_DROP);
|
|
// strcpy(m_pDB->m_G->Message, x->GetErrorMessage[0]);
|
|
strcpy(m_pDB->m_G->Message, x->GetErrorMessage(0));
|
|
return true;
|
|
} // endtry/catch
|
|
|
|
return false;
|
|
} // end of Open
|
|
|
|
/***********************************************************************/
|
|
/* Close a hstmt. */
|
|
/***********************************************************************/
|
|
void RECSET::Close(SWORD option)
|
|
{
|
|
if (m_hstmt != SQL_NULL_HSTMT) {
|
|
RETCODE rc = SQLFreeStmt(m_hstmt, option);
|
|
|
|
if (option == SQL_DROP)
|
|
m_hstmt = SQL_NULL_HSTMT;
|
|
|
|
} // endif m_hstmt
|
|
|
|
#if 0
|
|
m_lOpen = RECORDSET_STATUS_CLOSED;
|
|
m_bBOF = true;
|
|
m_bEOF = true;
|
|
m_bDeleted = false;
|
|
m_bAppendable = false;
|
|
m_bUpdatable = false;
|
|
m_bScrollable = false;
|
|
m_bRebindParams = false;
|
|
m_bLongBinaryColumns = false;
|
|
m_nLockMode = optimistic;
|
|
m_nFieldsBound = 0;
|
|
m_nResultCols = -1;
|
|
#endif // 0
|
|
} // end of Close
|
|
#endif // 0
|