DBD-ODBC
view release on metacpan or search on metacpan
/*
* portions Copyright (c) 1994,1995,1996,1997 Tim Bunce
* portions Copyright (c) 1997 Thomas K. Wenrich
* portions Copyright (c) 1997-2001 Jeff Urlwin
* portions Copyright (c) 2007-2013 Martin J. Evans
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the Perl README file.
*
*/
/*
* NOTES:
*
* o Trace levels 1 and 2 are reserved for DBI (see DBI::DBD) so don't
* use them here in DBIc_TRACE_LEVEL tests.
* "Trace Levels" in DBI defines trace levels as:
* 0 - Trace disabled.
* 1 - Trace DBI method calls returning with results or errors.
* 2 - Trace method entry with parameters and returning with results.
* 3 - As above, adding some high-level information from the driver
* and some internal information from the DBI.
* 4 - As above, adding more detailed information from the driver.
* 5 to 15 - As above but with more and more obscure information.
*
* SV Manipulation Functions
* http://perl.active-venture.com/pod/perlapi-svfunctions.html
* Formatted Printing of IVs, UVs, and NVs
* http://perldoc.perl.org/perlguts.html#Formatted-Printing-of-IVs,-UVs,-and-NVs
* http://cpansearch.perl.org/src/RURBAN/illguts-0.44/index.html
* Internal replacements for standard C library functions:
* http://perldoc.perl.org/perlclib.html
* http://search.cpan.org/dist/Devel-PPPort/PPPort.pm
*
* MS ODBC 64 bit:
* http://msdn.microsoft.com/en-us/library/ms716287%28v=vs.85%29.aspx
*/
#include <limits.h>
#define NEED_newRV_noinc
#define NEED_sv_2pv_flags
#define NEED_my_snprintf
#include "ODBC.h"
#if defined(WITH_UNICODE)
# include "unicode_helper.h"
#endif
/* trap iODBC on Unicode builds */
#if defined(WITH_UNICODE) && (defined(_IODBCUNIX_H) || defined(_IODBCEXT_H))
#error DBD::ODBC will not run properly with iODBC in unicode mode as iODBC defines wide characters as being 4 bytes in size
#endif
/* DBI defines the following but not until 1.617 so we replicate here for now */
/* will remove when DBD::ODBC requires 1.617 or above */
#ifndef DBIf_TRACE_SQL
# define DBIf_TRACE_SQL 0x00000100
#endif
#ifndef DBIf_TRACE_CON
# define DBIf_TRACE_CON 0x00000200
#endif
#ifndef DBIf_TRACE_ENC
# define DBIf_TRACE_ENC 0x00000400
#endif
#ifndef DBIf_TRACE_DBD
# define DBIf_TRACE_DBD 0x00000800
#endif
#ifndef DBIf_TRACE_TXN
# define DBIf_TRACE_TXN 0x000001000
#endif
/* combined DBI trace connection and encoding flags with DBD::ODBC ones */
/* Historically DBD::ODBC had 2 flags before they were made DBI ones */
#define UNICODE_TRACING (0x02000000|DBIf_TRACE_ENC|DBIf_TRACE_DBD)
#define CONNECTION_TRACING (0x04000000|DBIf_TRACE_CON|DBIf_TRACE_DBD)
#define DBD_TRACING DBIf_TRACE_DBD
#define TRANSACTION_TRACING (DBIf_TRACE_TXN|DBIf_TRACE_DBD)
#define SQL_TRACING (DBIf_TRACE_SQL|DBIf_TRACE_DBD)
#define TRACE0(a,b) PerlIO_printf(DBIc_LOGPIO(a), (b))
#define TRACE1(a,b,c) PerlIO_printf(DBIc_LOGPIO(a), (b), (c))
#define TRACE2(a,b,c,d) PerlIO_printf(DBIc_LOGPIO(a), (b), (c), (d))
#define TRACE3(a,b,c,d,e) PerlIO_printf(DBIc_LOGPIO(a), (b), (c), (d), (e))
/* An error return reserved for our internal use and should not clash with
any ODBC error codes like SQL_ERROR, SQL_INVALID_HANDLE etc.
It is used so we can call dbd_error but indicate there is no point in
calling SQLError as the error is internal */
#define DBDODBC_INTERNAL_ERROR -999
static int taf_callback_wrapper (
void *handle,
int type,
int event);
static int get_row_diag(SQLSMALLINT recno,
imp_sth_t *imp_sth,
char *state,
SQLINTEGER *native,
char *msg,
size_t max_msg);
static SQLSMALLINT default_parameter_type(
char *why, imp_sth_t *imp_sth, phs_t *phs);
static int post_connect(pTHX_ SV *dbh, imp_dbh_t *imp_dbh, SV *attr);
static int set_odbc_version(pTHX_ SV *dbh, imp_dbh_t *imp_dbh, SV* attr);
static const char *S_SqlTypeToString (SWORD sqltype);
static const char *S_SqlCTypeToString (SWORD sqltype);
static const char *cSqlTables = "SQLTables(%s,%s,%s,%s)";
static const char *cSqlPrimaryKeys = "SQLPrimaryKeys(%s,%s,%s)";
static const char *cSqlStatistics = "SQLStatistics(%s,%s,%s,%d,%d)";
static const char *cSqlForeignKeys = "SQLForeignKeys(%s,%s,%s,%s,%s,%s)";
static const char *cSqlColumns = "SQLColumns(%s,%s,%s,%s)";
static const char *cSqlGetTypeInfo = "SQLGetTypeInfo(%d)";
static SQLRETURN bind_columns(SV *h, imp_sth_t *imp_sth);
static void AllODBCErrors(HENV henv, HDBC hdbc, HSTMT hstmt, int output,
PerlIO *logfp);
static int check_connection_active(pTHX_ SV *h);
static int build_results(pTHX_ SV *sth, imp_sth_t *imp_sth,
SV *dbh, imp_dbh_t *imp_dbh,
RETCODE orc);
static int rebind_param(pTHX_ SV *sth, imp_sth_t *imp_sth, imp_dbh_t *imp_dbh, phs_t *phs);
static void get_param_type(SV *sth, imp_sth_t *imp_sth, imp_dbh_t *imp_dbh, phs_t *phs);
static void check_for_unicode_param(imp_sth_t *imp_sth, phs_t *phs);
/* Function to get the console window handle which we may use in SQLDriverConnect on WIndows */
#ifdef WIN32
static HWND GetConsoleHwnd(void);
#endif
int dbd_describe(SV *sth, imp_sth_t *imp_sth, int more);
int dbd_db_login6_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *dbname,
SV *uid, SV *pwd, SV *attr);
int dbd_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname,
char *uid, char *pwd, SV *attr);
int dbd_st_finish(SV *sth, imp_sth_t *imp_sth);
IV dbd_st_execute_iv(SV *sth, imp_sth_t *imp_sth);
/* for sanity/ease of use with potentially null strings */
#define XXSAFECHAR(p) ((p) ? (p) : "(null)")
/* unique value for db attrib that won't conflict with SQL types, just
* increment by one if you are adding */
#define ODBC_IGNORE_NAMED_PLACEHOLDERS 0x8332
#define ODBC_DEFAULT_BIND_TYPE 0x8333
#define ODBC_ASYNC_EXEC 0x8334
#define ODBC_ERR_HANDLER 0x8335
#define ODBC_ROWCACHESIZE 0x8336
#define ODBC_ROWSINCACHE 0x8337
#define ODBC_FORCE_REBIND 0x8338
#define ODBC_EXEC_DIRECT 0x8339
#define ODBC_VERSION 0x833A
#define ODBC_CURSORTYPE 0x833B
#define ODBC_QUERY_TIMEOUT 0x833C
#define ODBC_HAS_UNICODE 0x833D
#define ODBC_PUTDATA_START 0x833E
#define ODBC_OUTCON_STR 0x833F
#define ODBC_COLUMN_DISPLAY_SIZE 0x8340
#define ODBC_UTF8_ON 0x8341
#define ODBC_FORCE_BIND_TYPE 0x8342
#define ODBC_DESCRIBE_PARAMETERS 0x8344
#define ODBC_DRIVER_COMPLETE 0x8345
#define ODBC_BATCH_SIZE 0x8346
#define ODBC_ARRAY_OPERATIONS 0x8347
#define ODBC_TAF_CALLBACK 0x8348
/* This is the bind type for parameters we fall back to if the bind_param
method was not given a parameter type and SQLDescribeParam is not supported
or failed.
It also defines the point we switch from VARCHAR to LONGVARCHAR */
#ifdef WITH_UNICODE
# define ODBC_BACKUP_BIND_TYPE_VALUE SQL_WVARCHAR
# define ODBC_SWITCH_TO_LONGVARCHAR 2000
#else
# define ODBC_BACKUP_BIND_TYPE_VALUE SQL_VARCHAR
# define ODBC_SWITCH_TO_LONGVARCHAR 4000
#endif
DBISTATE_DECLARE;
void dbd_init(dbistate_t *dbistate)
{
dTHX;
DBISTATE_INIT;
DBIc_ACTIVE_on(imp_sth); /* XXX should only set for select ? */
return 1;
}
int odbc_discon_all(SV *drh,
imp_drh_t *imp_drh)
{
dTHX;
/* The disconnect_all concept is flawed and needs more work */
if (!PL_dirty && !SvTRUE(get_sv("DBI::PERL_ENDING",0))) {
DBIh_SET_ERR_CHAR(drh, (imp_xxh_t*)imp_drh, Nullch, 1,
"disconnect_all not implemented", Nullch, Nullch);
return FALSE;
}
return FALSE;
}
/* error : <=(-2), ok row count : >=0, unknown count : (-1) */
SQLLEN dbd_db_execdirect(SV *dbh,
SV *statement )
{
dTHX;
D_imp_dbh(dbh);
SQLRETURN ret; /* SQLxxx return value */
SQLLEN rows;
SQLHSTMT stmt;
int dbh_active;
if ((dbh_active = check_connection_active(aTHX_ dbh)) == 0) return 0;
ret = SQLAllocHandle(SQL_HANDLE_STMT, imp_dbh->hdbc, &stmt );
if (!SQL_SUCCEEDED(ret)) {
dbd_error( dbh, ret, "Statement allocation error" );
return(-2);
}
/* if odbc_query_timeout has been set, set it in the driver */
if (imp_dbh->odbc_query_timeout != -1) {
ret = odbc_set_query_timeout(imp_dbh, stmt, imp_dbh->odbc_query_timeout);
if (!SQL_SUCCEEDED(ret)) {
dbd_error(dbh, ret, "execdirect set_query_timeout");
}
/* don't fail if the query timeout can't be set. */
}
if (DBIc_TRACE(imp_dbh, SQL_TRACING, 0, 3)) {
TRACE1(imp_dbh, " SQLExecDirect %s\n", SvPV_nolen(statement));
}
#ifdef WITH_UNICODE
if (SvOK(statement) && DO_UTF8(statement)) {
SQLWCHAR *wsql;
STRLEN wsql_len;
SV *sql_copy;
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing utf8 sql in unicode mode\n");
sql_copy = sv_mortalcopy(statement);
SV_toWCHAR(aTHX_ sql_copy);
wsql = (SQLWCHAR *)SvPV(sql_copy, wsql_len);
ret = SQLExecDirectW(stmt, wsql, wsql_len / sizeof(SQLWCHAR));
} else {
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing non utf8 sql in unicode mode\n");
ret = SQLExecDirect(stmt, (SQLCHAR *)SvPV_nolen(statement), SQL_NTS);
}
#else
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing sql in non-unicode mode\n");
ret = SQLExecDirect(stmt, (SQLCHAR *)SvPV_nolen(statement), SQL_NTS);
#endif
if (DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 3))
TRACE1(imp_dbh, " SQLExecDirect = %d\n", ret);
if (!SQL_SUCCEEDED(ret) && ret != SQL_NO_DATA) {
dbd_error2(dbh, ret, "Execute immediate failed",
imp_dbh->henv, imp_dbh->hdbc, stmt );
rows = -2; /* error */
} else {
if (ret == SQL_NO_DATA) {
rows = 0;
}
else if (ret != SQL_SUCCESS) {
dbd_error2(dbh, ret, "Execute immediate success with info",
imp_dbh->henv, imp_dbh->hdbc, stmt );
}
ret = SQLRowCount(stmt, &rows);
if (!SQL_SUCCEEDED(ret)) {
dbd_error( dbh, ret, "SQLRowCount failed" );
rows = -1;
}
}
ret = SQLFreeHandle(SQL_HANDLE_STMT,stmt);
if (!SQL_SUCCEEDED(ret)) {
dbd_error2(dbh, ret, "Statement destruction error",
imp_dbh->henv, imp_dbh->hdbc, stmt);
}
return rows;
}
void dbd_db_destroy(SV *dbh, imp_dbh_t *imp_dbh)
{
if (DBIc_ACTIVE(imp_dbh))
dbd_db_disconnect(dbh, imp_dbh);
/* Nothing in imp_dbh to be freed */
DBIc_IMPSET_off(imp_dbh);
if (DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 8))
TRACE0(imp_dbh, " DBD::ODBC Disconnected!\n");
}
/*
* quick dumb function to handle case insensitivity for DSN= or DRIVER=
* in DSN...note this is because strncmpi is not available on all
* platforms using that name (VC++, Debian, etc most notably).
* Note, also, strupr doesn't seem to have a standard name, either...
*/
int dsnHasDriverOrDSN(char *dsn) {
char upper_dsn[512];
char *cp = upper_dsn;
strncpy(upper_dsn, dsn, sizeof(upper_dsn)-1);
upper_dsn[sizeof(upper_dsn)-1] = '\0';
while (*cp != '\0') {
*cp = toupper(*cp);
cp++; /* see rt 79190 was a sequence point error*/
}
return (strncmp(upper_dsn, "DSN=", 4) == 0 ||
strncmp(upper_dsn, "DRIVER=", 7) == 0);
}
int dsnHasUIDorPWD(char *dsn) {
char upper_dsn[512];
char *cp = upper_dsn;
strncpy(upper_dsn, dsn, sizeof(upper_dsn)-1);
upper_dsn[sizeof(upper_dsn)-1] = '\0';
while (*cp != '\0') {
*cp = toupper(*cp);
cp++; /* see rt 79190 was a sequence point error*/
}
return (strstr(upper_dsn, "UID=") != 0 || strstr(upper_dsn, "PWD=") != 0);
}
/************************************************************************/
/* */
/* dbd_db_login */
/* ============ */
/* */
/* NOTE: This is the old 5 argument version with no attribs */
/* */
/************************************************************************/
int dbd_db_login(
SV *dbh,
imp_dbh_t *imp_dbh,
char *dbname,
char *uid,
char *pwd)
{
return dbd_db_login6(dbh, imp_dbh, dbname, uid, pwd, Nullsv);
}
/************************************************************************/
/* */
/* dbd_db_login6_sv */
/* ================ */
/* */
/* This API was introduced in DBI after 1.607 (subversion revision */
/* 11723) and is the same as dbd_db_login6 except the connection */
/* strings are SVs so we can detect unicode strings and call */
/* SQLDriveConnectW. */
/* */
/************************************************************************/
int dbd_db_login6_sv(
SV *dbh,
imp_dbh_t *imp_dbh,
SV *dbname,
SV *uid,
SV *pwd,
SV *attr)
{
dTHX;
#ifndef WITH_UNICODE
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE0(imp_dbh, "non-Unicode login6_sv\n");
return dbd_db_login6(dbh, imp_dbh, SvPV_nolen(dbname),
(SvOK(uid) ? SvPV_nolen(uid) : NULL),
(SvOK(pwd) ? SvPV_nolen(pwd) : NULL), attr);
#else
D_imp_drh_from_dbh;
SQLRETURN rc;
SV *wconstr; /* copy of connection string in wide chrs */
/* decoded connection string in wide characters and its length to work
around an issue in older unixODBCs */
SQLWCHAR dc_constr[512];
STRLEN dc_constr_len;
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0)) {
TRACE2(imp_dbh, "Unicode login6 dbname=%s, uid=%s, pwd=xxxxx\n",
SvPV_nolen(dbname), neatsvpv(uid, 0));
}
imp_dbh->out_connect_string = Nullsv;
if (!imp_drh->connects) {
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &imp_drh->henv);
dbd_error(dbh, rc, "db_login6_sv/SQLAllocHandle(env)");
if (!SQL_SUCCEEDED(rc)) return 0;
if (set_odbc_version(aTHX_ dbh, imp_dbh, attr) != 1) return 0;
}
imp_dbh->henv = imp_drh->henv; /* needed for dbd_error */
/* If odbc_trace_file set, set it in ODBC */
{
SV **attr_sv;
char *file;
if ((attr_sv =
DBD_ATTRIB_GET_SVP(attr, "odbc_trace_file",
(I32)strlen("odbc_trace_file"))) != NULL) {
if (SvPOK(*attr_sv)) {
file = SvPV_nolen(*attr_sv);
rc = SQLSetConnectAttr(NULL, SQL_ATTR_TRACEFILE,
file, strlen(file));
if (!SQL_SUCCEEDED(rc)) {
warn("Failed to set trace file");
}
}
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
PerlIO_printf(
DBIc_LOGPIO(imp_dbh),
" SQLStatistics call: cat = %s, schema = %s, table = %s"
", unique=%d, quick = %d\n",
XXSAFECHAR(catalog), XXSAFECHAR(schema), XXSAFECHAR(table),
odbc_unique, odbc_quick);
}
dbd_error(sth, rc, "st_statistics/SQLStatistics");
if (!SQL_SUCCEEDED(rc)) {
SQLFreeHandle(SQL_HANDLE_STMT,imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
return build_results(aTHX_ sth, imp_sth, dbh, imp_dbh, rc);
}
/************************************************************************/
/* */
/* odbc_st_prepare */
/* =============== */
/* */
/* dbd_st_prepare is the old API which is now replaced with */
/* dbd_st_prepare_sv (taking a perl scalar) so this is now: */
/* */
/* a) just a wrapper around dbd_st_prepare_sv and */
/* b) not used - see ODBC.c */
/* */
/************************************************************************/
int odbc_st_prepare(
SV *sth,
imp_sth_t *imp_sth,
char *statement,
SV *attribs)
{
dTHX;
SV *sql;
sql = sv_newmortal();
sv_setpvn(sql, statement, strlen(statement));
return dbd_st_prepare_sv(sth, imp_sth, sql, attribs);
}
/************************************************************************/
/* */
/* odbc_st_prepare_sv */
/* ================== */
/* */
/* dbd_st_prepare_sv is the newer version of dbd_st_prepare taking a */
/* a perl scalar for the sql statement instead of a char* so it may be */
/* unicode */
/* */
/************************************************************************/
int odbc_st_prepare_sv(
SV *sth,
imp_sth_t *imp_sth,
SV *statement,
SV *attribs)
{
dTHX;
D_imp_dbh_from_sth;
RETCODE rc;
int dbh_active;
char *sql;
sql = SvPV_nolen(statement);
imp_sth->done_desc = 0;
imp_sth->henv = imp_dbh->henv;
imp_sth->hdbc = imp_dbh->hdbc;
/* inherit from connection */
imp_sth->odbc_ignore_named_placeholders =
imp_dbh->odbc_ignore_named_placeholders;
imp_sth->odbc_default_bind_type = imp_dbh->odbc_default_bind_type;
imp_sth->odbc_force_bind_type = imp_dbh->odbc_force_bind_type;
imp_sth->odbc_force_rebind = imp_dbh->odbc_force_rebind;
imp_sth->odbc_query_timeout = imp_dbh->odbc_query_timeout;
imp_sth->odbc_putdata_start = imp_dbh->odbc_putdata_start;
imp_sth->odbc_column_display_size = imp_dbh->odbc_column_display_size;
imp_sth->odbc_utf8_on = imp_dbh->odbc_utf8_on;
imp_sth->odbc_exec_direct = imp_dbh->odbc_exec_direct;
imp_sth->odbc_describe_parameters = imp_dbh->odbc_describe_parameters;
imp_sth->odbc_batch_size = imp_dbh->odbc_batch_size;
imp_sth->odbc_array_operations = imp_dbh->odbc_array_operations;
imp_sth->param_status_array = NULL;
if (DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 5)) {
TRACE1(imp_dbh, " initializing sth query timeout to %ld\n",
(long)imp_dbh->odbc_query_timeout);
}
if ((dbh_active = check_connection_active(aTHX_ sth)) == 0) return 0;
rc = SQLAllocHandle(SQL_HANDLE_STMT, imp_dbh->hdbc, &imp_sth->hstmt);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "st_prepare/SQLAllocHandle(stmt)");
return 0;
}
{
/*
* allow setting of odbc_execdirect in prepare() or overriding
*/
SV **attr_sv;
/* if the attribute is there, let it override what the default
* value from the dbh is (set above).
* NOTE:
* There are unfortunately two possible attributes because of an early
* typo in DBD::ODBC which we keep for backwards compatibility.
*/
if ((attr_sv =
{ /* MS SQL Server query notification */
SV **attr_sv;
if ((attr_sv =
DBD_ATTRIB_GET_SVP(
attribs, "odbc_qn_msgtxt",
(I32)strlen("odbc_qn_msgtxt"))) != NULL) {
rc = SQLSetStmtAttr(imp_sth->hstmt,
1234 /*SQL_SOPT_SS_QUERYNOTIFICATION_MSGTEXT*/,
(SQLPOINTER)SvPV_nolen(*attr_sv), SQL_NTS);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "SQLSetStmtAttr(QUERYNOTIFICATION_MSGTXT)");
SQLFreeHandle(SQL_HANDLE_STMT, imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
}
if ((attr_sv =
DBD_ATTRIB_GET_SVP(
attribs, "odbc_qn_options",
(I32)strlen("odbc_qn_options"))) != NULL) {
rc = SQLSetStmtAttr(imp_sth->hstmt,
1235 /*SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS*/,
(SQLPOINTER)SvPV_nolen(*attr_sv), SQL_NTS);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "SQLSetStmtAttr(QUERYNOTIFICATION_OPTIONS)");
SQLFreeHandle(SQL_HANDLE_STMT, imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
}
if ((attr_sv =
DBD_ATTRIB_GET_SVP(
attribs, "odbc_qn_timeout",
(I32)strlen("odbc_qn_timeout"))) != NULL) {
rc = SQLSetStmtAttr(imp_sth->hstmt,
1233 /*SQL_SOPT_SS_QUERYNOTIFICATION_TIMEOUT*/,
(SQLPOINTER)SvIV(*attr_sv), SQL_NTS);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "SQLSetStmtAttr(QUERYNOTIFICATION_TIMEOUT)");
SQLFreeHandle(SQL_HANDLE_STMT, imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
}
}
/* scan statement for '?', ':1' and/or ':foo' style placeholders */
dbd_preparse(imp_sth, sql);
/* Hold this statement for subsequent call of dbd_execute */
if (!imp_sth->odbc_exec_direct) {
if (DBIc_TRACE(imp_dbh, SQL_TRACING, 0, 3)) {
TRACE1(imp_dbh, " SQLPrepare %s\n", imp_sth->statement);
}
#ifdef WITH_UNICODE
if (SvOK(statement) && DO_UTF8(statement)) {
SQLWCHAR *wsql;
STRLEN wsql_len;
SV *sql_copy;
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing utf8 sql in unicode mode for SQLPrepareW\n");
sql_copy = sv_newmortal();
sv_setpv(sql_copy, imp_sth->statement);
#ifdef sv_utf8_decode
sv_utf8_decode(sql_copy);
#else
SvUTF8_on(sql_copy);
#endif
SV_toWCHAR(aTHX_ sql_copy);
wsql = (SQLWCHAR *)SvPV(sql_copy, wsql_len);
rc = SQLPrepareW(imp_sth->hstmt, wsql, wsql_len / sizeof(SQLWCHAR));
} else {
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing non-utf8 sql in unicode mode\n");
rc = SQLPrepare(imp_sth->hstmt, imp_sth->statement, SQL_NTS);
}
#else /* !WITH_UNICODE */
if (DBIc_TRACE(imp_dbh, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_dbh, " Processing sql in non-unicode mode for SQLPrepare\n");
rc = SQLPrepare(imp_sth->hstmt, imp_sth->statement, SQL_NTS);
#endif
if (DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 3))
TRACE1(imp_dbh, " SQLPrepare = %d\n", rc);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "st_prepare/SQLPrepare");
SQLFreeHandle(SQL_HANDLE_STMT, imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
} else if (DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 3)) {
TRACE1(imp_dbh, " odbc_exec_direct=1, statement (%s) "
"held for later exec\n", imp_sth->statement);
}
/* init sth pointers */
imp_sth->henv = imp_dbh->henv;
imp_sth->hdbc = imp_dbh->hdbc;
imp_sth->fbh = NULL;
imp_sth->ColNames = NULL;
imp_sth->RowBuffer = NULL;
imp_sth->RowCount = -1;
/*
* If odbc_async_exec is set and odbc_async_type is SQL_AM_STATEMENT,
* we need to set the SQL_ATTR_ASYNC_ENABLE attribute.
*/
if (imp_dbh->odbc_async_exec &&
imp_dbh->odbc_async_type == SQL_AM_STATEMENT){
rc = SQLSetStmtAttr(imp_sth->hstmt,
SQL_ATTR_ASYNC_ENABLE,
(SQLPOINTER) SQL_ASYNC_ENABLE_ON,
SQL_IS_UINTEGER);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "st_prepare/SQLSetStmtAttr");
SQLFreeHandle(SQL_HANDLE_STMT, imp_sth->hstmt);
imp_sth->hstmt = SQL_NULL_HSTMT;
return 0;
}
}
/*
* If odbc_query_timeout is set (not -1)
* we need to set the SQL_ATTR_QUERY_TIMEOUT
*/
if (imp_sth->odbc_query_timeout != -1){
odbc_set_query_timeout(imp_dbh, imp_sth->hstmt, imp_sth->odbc_query_timeout);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "set_query_timeout");
}
/* don't fail if the query timeout can't be set. */
}
DBIc_IMPSET_on(imp_sth);
return 1;
}
/* truncation of other fields should always be an error since it's */
/* a sign of an internal error */
if (!DBIc_has(imp_sth, DBIcf_LongTruncOk)
/* && rc == SQL_SUCCESS_WITH_INFO */) {
/*
* Since we've detected the problem locally via the datalen,
* we don't need to worry about the value of rc.
*
* This used to make sure rc was set to SQL_SUCCESS_WITH_INFO
* but since it's an error and not SUCCESS, call dbd_error()
* with SQL_ERROR explicitly instead.
*/
#ifdef COULD_DO_THIS
DBIh_SET_ERR_CHAR(
sth, (imp_xxh_t*)imp_sth, Nullch, 1,
"st_fetch/SQLFetch (long truncated DBI attribute LongTruncOk "
"not set and/or LongReadLen too small)", Nullch, Nullch);
#endif
dbd_error(
sth, DBDODBC_INTERNAL_ERROR,
"st_fetch/SQLFetch (long truncated DBI attribute LongTruncOk "
"not set and/or LongReadLen too small)");
return Nullav;
}
/* LongTruncOk true, just ensure perl has the right length
* for the truncated data.
*/
sv_setpvn(sv, (char*)fbh->data, fbh->ColDisplaySize);
} else {
switch(fbh->ftype) {
#ifdef TIMESTAMP_STRUCT /* iODBC doesn't define this */
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP:
{
TIMESTAMP_STRUCT *ts;
ts = (TIMESTAMP_STRUCT *)fbh->data;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE0(imp_dbh, " adjusting timestamp\n");
my_snprintf(cvbuf, sizeof(cvbuf),
"%04d-%02d-%02d %02d:%02d:%02d",
ts->year, ts->month, ts->day,
ts->hour, ts->minute, ts->second, ts->fraction);
sv_setpv(sv, cvbuf);
break;
}
#endif
#if defined(WITH_UNICODE)
case SQL_C_WCHAR:
if (ChopBlanks && fbh->ColSqlType == SQL_WCHAR &&
fbh->datalen > 0)
{
SQLWCHAR *p = (SQLWCHAR*)fbh->data;
SQLWCHAR blank = 0x20;
SQLLEN orig_len = fbh->datalen;
while(fbh->datalen && p[fbh->datalen/sizeof(SQLWCHAR)-1] == blank) {
--fbh->datalen;
}
if (DBIc_TRACE(imp_sth, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE2(imp_sth, " Unicode ChopBlanks orig len=%ld, new len=%ld\n",
orig_len, fbh->datalen);
}
sv_setwvn(aTHX_ sv, (SQLWCHAR*)fbh->data,
fbh->datalen/sizeof(SQLWCHAR));
if (DBIc_TRACE(imp_sth, UNICODE_TRACING, 0, 0)) { /* odbcunicode */
/* unsigned char dlog[256]; */
/* unsigned char *src; */
/* char *dst = dlog; */
/* unsigned int n; */
/* STRLEN len; */
/* src = SvPV(sv, len); */
/* dst += sprintf(dst, "0x"); */
/* for (n = 0; (n < 126) && (n < len); n++, src++) { */
/* dst += sprintf(dst, "%2.2x", *src); */
/* } */
/*TRACE1(imp_sth, " SQL_C_WCHAR data = %s\n", dlog);*/
TRACE1(imp_sth, " SQL_C_WCHAR data = %.100s\n", neatsvpv(sv, 100));
}
break;
#endif /* WITH_UNICODE */
case SQL_INTEGER:
sv_setiv(sv, *((SQLINTEGER *)fbh->data));
break;
default:
if (ChopBlanks && fbh->datalen > 0 &&
((fbh->ColSqlType == SQL_CHAR) ||
(fbh->ColSqlType == SQL_WCHAR))) {
char *p = (char*)fbh->data;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5))
TRACE0(imp_sth, " chopping blanks\n");
while(fbh->datalen && p[fbh->datalen - 1]==' ')
--fbh->datalen;
}
sv_setpvn(sv, (char*)fbh->data, fbh->datalen);
if (imp_sth->odbc_utf8_on && fbh->ftype != SQL_C_BINARY ) {
if (DBIc_TRACE(imp_sth, UNICODE_TRACING, 0, 0)) /* odbcunicode */
TRACE0(imp_sth, " odbc_utf8 - decoding UTF-8");
#ifdef sv_utf8_decode
sv_utf8_decode(sv);
#else
SvUTF8_on(sv);
#endif
}
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE2(imp_sth, " %s(%ld)\n", neatsvpv(sv, fbh->datalen+5),
fbh->datalen);
}
}
#if DBIXS_REVISION > 13590
/* If a bind type was specified we use DBI's sql_type_cast
to cast it - currently only number types are handled */
if (
/*(fbh->req_type == SQL_INTEGER) || not needed as we've already done a sv_setiv*/
(fbh->req_type == SQL_NUMERIC) ||
(fbh->req_type == SQL_DECIMAL)) {
int sts;
char errstr[256];
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE3(imp_sth, " sql_type_case %s %"IVdf" %lx\n", neatsvpv(sv, fbh->datalen+5), fbh->req_type, fbh->bind_flags);
sts = DBIc_DBISTATE(imp_sth)->sql_type_cast_svpv(
aTHX_ sv, fbh->req_type, (U32)fbh->bind_flags, NULL);
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE1(imp_sth, " sql_type_cast=%d\n", sts);
if (sts == 0) {
sprintf(errstr,
"over/under flow converting column %d to type %"IVdf"",
i+1, fbh->req_type);
DBIh_SET_ERR_CHAR(sth, (imp_xxh_t*)imp_sth, Nullch, 1,
errstr, Nullch, Nullch);
return Nullav;
}
else if (sts == -2) {
sprintf(errstr,
"unsupported bind type %"IVdf" for column %d in sql_type_cast_svpv",
fbh->req_type, i+1);
DBIh_SET_ERR_CHAR(sth, (imp_xxh_t*)imp_sth, Nullch, 1,
errstr, Nullch, Nullch);
return Nullav;
}
}
#endif /* DBIXS_REVISION > 13590 */
} /* end of loop through bound columns */
return av;
}
/* /\* SHOULD BE ABLE TO DELETE BOTH OF THESE NOW AND dbd_st_rows macro in dbdimp.h *\/ */
/* int dbd_st_rows(SV *sth, imp_sth_t *imp_sth) */
/* { */
DBIc_IMPSET_off(imp_sth); /* let DBI know we've done it */
}
/************************************************************************/
/* */
/* get_param_type */
/* ============== */
/* */
/* Sets the following fields for a parameter in the phs_st: */
/* */
/* sql_type - the SQL type to use when binding this parameter */
/* describe_param_called - set to 1 if we called SQLDescribeParam */
/* describe_param_status - set to result of SQLDescribeParam if */
/* SQLDescribeParam called */
/* described_sql_type - the sql type returned by SQLDescribeParam */
/* param_size - the parameter size returned by SQLDescribeParam */
/* */
/* The sql_type field is set to one of the following: */
/* value passed in bind method call if specified */
/* if SQLDescribeParam not supported: */
/* value of odbc_default_bind_type attribute if set else */
/* SQL_VARCHAR */
/* if SQLDescribeParam supported: */
/* if SQLDescribeParam succeeds: */
/* parameter type returned by SQLDescribeParam */
/* else if SQLDescribeParam fails: */
/* value of odbc_default_bind_type attribute if set else */
/* SQL_VARCHAR */
/* */
/* NOTE: Just because an ODBC driver says it supports SQLDescribeParam */
/* does not mean you can call it successfully e.g., MS SQL Server */
/* implements SQLDescribeParam by examining your SQL and rewriting it */
/* to be a select statement so it can find the column types etc. This */
/* fails horribly when the statement does not contain a table */
/* e.g., "select ?, LEN(?)" and so do most other SQL Server drivers. */
/* */
/************************************************************************/
static void get_param_type(
SV *sth,
imp_sth_t *imp_sth,
imp_dbh_t *imp_dbh,
phs_t *phs)
{
SWORD fNullable;
SWORD ibScale;
RETCODE rc;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE2(imp_sth, " +get_param_type(%p,%s)\n", sth, phs->name);
if (imp_sth->odbc_force_bind_type != 0) {
phs->sql_type = imp_sth->odbc_force_bind_type;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE1(imp_dbh, " forced param type to %d\n", phs->sql_type);
} else if (imp_dbh->odbc_sqldescribeparam_supported != 1) {
/* As SQLDescribeParam is not supported by the ODBC driver we need to
default a SQL type to bind the parameter as. The default is either
the value set with odbc_default_bind_type or a fallback of
SQL_VARCHAR/SQL_WVARCHAR depending on your data and whether we are unicode build. */
phs->sql_type = default_parameter_type(
"SQLDescribeParam not supported", imp_sth, phs);
} else if (!imp_sth->odbc_describe_parameters) {
phs->sql_type = default_parameter_type(
"SQLDescribeParam disabled", imp_sth, phs);
} else if (!phs->describe_param_called) {
/* If we haven't had a go at calling SQLDescribeParam before for this
parameter, have a go now. If it fails we'll default the sql type
as above when driver does not have SQLDescribeParam */
rc = SQLDescribeParam(imp_sth->hstmt,
phs->idx, &phs->described_sql_type,
&phs->param_size, &ibScale,
&fNullable);
phs->describe_param_called = 1;
phs->describe_param_status = rc;
if (!SQL_SUCCEEDED(rc)) {
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE1(imp_dbh, " Parameter %d\n", phs->idx);
phs->sql_type = default_parameter_type(
"SQLDescribeParam failed", imp_sth, phs);
/* show any odbc errors in log */
AllODBCErrors(imp_sth->henv, imp_sth->hdbc, imp_sth->hstmt,
DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3),
DBIc_LOGPIO(imp_sth));
} else if (phs->described_sql_type == 0) { /* unknown SQL type */
/* pretend it failed */
phs->describe_param_status = SQL_ERROR;
phs->sql_type = default_parameter_type(
"SQLDescribeParam returned unknown SQL type", imp_sth, phs);
} else {
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5))
PerlIO_printf(DBIc_LOGPIO(imp_dbh),
" SQLDescribeParam %s: SqlType=%s(%d) "
"param_size=%ld Scale=%d Nullable=%d\n",
phs->name,
S_SqlTypeToString(phs->described_sql_type),
phs->described_sql_type,
(unsigned long)phs->param_size, ibScale,
fNullable);
/*
* for non-integral numeric types, let the driver/database handle
* the conversion for us
*/
switch(phs->described_sql_type) {
case SQL_NUMERIC:
case SQL_DECIMAL:
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5))
TRACE3(imp_dbh,
" Param %s is numeric SQL type %s "
"(param size:%lu) changed to SQL_VARCHAR\n",
phs->name,
S_SqlTypeToString(phs->described_sql_type),
(unsigned long)phs->param_size);
phs->sql_type = SQL_VARCHAR;
break;
default: {
check_for_unicode_param(imp_sth, phs);
break;
}
}
}
} else if (phs->describe_param_called) {
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5))
TRACE1(imp_dbh,
" SQLDescribeParam already run and returned rc=%d\n",
phs->describe_param_status);
check_for_unicode_param(imp_sth, phs);
}
if (phs->requested_type != 0) {
phs->sql_type = phs->requested_type;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5))
TRACE1(imp_dbh, " Overriding sql type with requested type %d\n",
phs->requested_type);
}
#if defined(WITH_UNICODE)
/* for Unicode string types, change value_type to SQL_C_WCHAR*/
switch (phs->sql_type) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
case MS_SQLS_XML_TYPE: /* SQL Server XML Type */
phs->value_type = SQL_C_WCHAR;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 8)) {
TRACE0(imp_dbh,
" get_param_type: modified value type to SQL_C_WCHAR\n");
}
break;
}
#endif /* WITH_UNICODE */
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 8))
TRACE0(imp_dbh, " -get_param_type\n");
}
/*======================================================================*/
/* */
/* rebind_param */
/* ============ */
/* */
/*======================================================================*/
static int rebind_param(
pTHX_
SV *sth,
imp_sth_t *imp_sth,
imp_dbh_t *imp_dbh,
phs_t *phs)
{
SQLRETURN rc;
SQLULEN default_column_size;
STRLEN value_len = 0;
/* args of SQLBindParameter() call */
SQLSMALLINT param_io_type; /* SQL_PARAM_INPUT_OUTPUT || SQL_PARAM_INPUT */
SQLSMALLINT value_type; /* C data type of parameter */
UCHAR *value_ptr; /* ptr to actual parameter data */
SQLULEN column_size; /* size of column/expression of the parameter */
SQLSMALLINT d_digits; /* decimal digits of parameter */
SQLLEN buffer_length; /* length in bytes of parameter buffer */
SQLLEN strlen_or_ind; /* parameter length or indicator */
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
PerlIO_printf(
DBIc_LOGPIO(imp_dbh),
" +rebind_param %s %.100s (size SvCUR=%"UVuf"/SvLEN=%"UVuf"/max=%"IVdf") "
"svtype:%u, value type:%d, sql type:%d\n",
(unsigned long)value_len);
d_digits = 0; /* not relevant to lobs */
strlen_or_ind = SQL_LEN_DATA_AT_EXEC(vl);
value_ptr = (UCHAR*) phs;
}
#if THE_FOLLOWING_CODE_IS_FLAWED_AND_BROKEN
/*
* value_ptr is not null terminated - it is a byte array so PVallocW
* won't work as it works on null terminated strings
*/
#if defined(WITH_UNICODE)
if (value_type==SQL_C_WCHAR) {
char * c1;
c1 = PVallocW((SQLWCHAR *)value_ptr);
TRACE1(imp_dbh, " Param value = L'%s'\n", c1);
PVfreeW(c1);
}
#endif /* WITH_UNICODE */
#endif
/*
* The following code is a workaround for a problem in SQL Server
* when inserting more than 400K into varbinary(max) or varchar(max)
* columns. The older SQL Server driver (not the native client driver):
*
* o reports the size of xxx(max) columns as 2147483647 bytes in size
* when in reality they can be a lot bigger than that.
* o if you bind more than 400K you get the following errors:
* (HY000, 0, [Microsoft][ODBC SQL Server Driver]
* Warning: Partial insert/update. The insert/update of a text or
* image column(s) did not succeed.)
* (42000, 7125, [Microsoft][ODBC SQL Server Driver][SQL Server]
* The text, ntext, or image pointer value conflicts with the column
* name specified.)
*
* There appear to be 2 workarounds but I was not prepared to do the first.
* The first is simply to set the indicator to SQL_LEN_DATA_AT_EXEC(409600)
* if the parameter was larger than 409600 - miraculously it works but
* shouldn't according to MSDN.
* The second workaround (used here) is to set the indicator to
* SQL_LEN_DATA_AT_EXEC(0) and the buffer_length to 0.
*
*/
if ((imp_dbh->driver_type == DT_SQL_SERVER) &&
((phs->sql_type == SQL_LONGVARCHAR) ||
(phs->sql_type == SQL_LONGVARBINARY) ||
(phs->sql_type == SQL_WLONGVARCHAR)) &&
/*(column_size == 2147483647) && (strlen_or_ind < 0) &&*/
((-strlen_or_ind + SQL_LEN_DATA_AT_EXEC_OFFSET) >= 409600)) {
strlen_or_ind = SQL_LEN_DATA_AT_EXEC(0);
buffer_length = 0;
}
#if defined(WITH_UNICODE)
/*
* rt43384 - MS Access does not seem to like us binding parameters as
* wide characters and then SQLBindParameter column_size to byte length.
* e.g., if you have a text(255) column and try and insert 190 ascii chrs
* then the unicode enabled version of DBD::ODBC will convert those 190
* ascii chrs to wide chrs and hence double the size to 380. If you pass
* 380 to Access for column_size it just returns an invalid precision
* value. This changes to column_size to chrs instead of bytes but
* only if column_size is not reduced to 0 - which also produces
* an access error e.g., in the empty string '' case.
*/
else if (((imp_dbh->driver_type == DT_MS_ACCESS_JET) ||
(imp_dbh->driver_type == DT_MS_ACCESS_ACE)) &&
(value_type == SQL_C_WCHAR) && (column_size > 1)) {
column_size = column_size / 2;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE0(imp_dbh, " MSAccess - setting chrs not bytes\n");
}
#endif
/*
* workaround bug in SQL Server ODBC driver where it can describe some
* parameters (especially in SQL using sub selects) the wrong way.
* If this is a varchar then the column_size must be at least as big
* as the buffer size but if SQL Server associated the wrong column with
* our parameter it could get a totally different size. Without this
* a varchar(10) column can be desribed as a varchar(n) where n is less
* than 10 and this leads to data truncation errors - see rt 39841.
*/
if (((imp_dbh->driver_type == DT_SQL_SERVER) ||
(imp_dbh->driver_type == DT_SQL_SERVER_NATIVE_CLIENT)) &&
(phs->sql_type == SQL_VARCHAR) &&
(column_size < buffer_length)) {
column_size = buffer_length;
}
/*
* Yet another workaround for SQL Server native client.
* If you have a varbinary(max), varchar(max) or nvarchar(max) you have to
* pass 0 for the column_size or you get HY104 "Invalid precision value".
* See rt_38977.t which causes this.
* The versions of native client I've seen this with are:
* 2007.100.1600.22 sqlncli10.dll driver version = ?
* 2005.90.1399.00 SQLNCLI.DLL driver version = 09.00.1399
*
* Update, for nvarchar(max) it does not seem to simply be a driver issue
* as with the Easysoft SQL Server ODBC Driver going to Microsoft SQL Server
* 09.00.1399 we got the following error for all sizes between 4001 and 8000
* (inclusive).
* [SQL Server]The size (4001) given to the parameter '@P1' exceeds the
* maximum allowed (4000)
*
* Update, see RT100186 - same applies to VARBINARY(MAX)
*
* So to sum up for the native client when the parameter size is 0 or
* when the database is sql server and wchar and sql type not overwritten
* we need to use column size 0. We cannot do this if the requested_type
* was specified as if someone specifies a bind type we haven't called
* SQLDescribeParam and it looks like param_size = 0 even when it is
* not a xxx(max). e.g., the 40UnicodeRoundTrip tests will fail with
* MS SQL Server because they override the type.
*/
if ((phs->param_size == 0) &&
(SQL_SUCCEEDED(phs->describe_param_status)) &&
(imp_sth->odbc_describe_parameters)) { /* SQLDescribeParam not disabled */
/* no point in believing param_size = 0 if SQLDescribeParam failed */
SvCUR_set(bufsv, destoffset+retl);
*SvEND(bufsv) = '\0'; /* consistent with perl sv_setpvn etc */
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4))
TRACE1(imp_sth, " blob_read: SvCUR=%"UVuf"\n", (UV)SvCUR(bufsv));
return 1;
}
/*======================================================================*/
/* */
/* S_db_storeOptions */
/* ================= */
/* S_db_fetchOptions */
/* ================= */
/* */
/* An array of options/attributes we support on database handles for */
/* storing and fetching. */
/* */
/*======================================================================*/
enum paramdir { PARAM_READ = 1, PARAM_WRITE = 2, PARAM_READWRITE = 3 };
enum gettype {PARAM_TYPE_CUSTOM = 0, PARAM_TYPE_UINT, PARAM_TYPE_STR, PARAM_TYPE_BOOL};
typedef struct {
const char *str;
UWORD fOption;
enum paramdir dir;
enum gettype type;
UDWORD atrue;
UDWORD afalse;
} db_params;
static db_params S_db_options[] = {
{ "AutoCommit", SQL_AUTOCOMMIT, PARAM_READWRITE, PARAM_TYPE_BOOL, SQL_AUTOCOMMIT_ON, SQL_AUTOCOMMIT_OFF },
{ "ReadOnly", SQL_ATTR_ACCESS_MODE, PARAM_READWRITE, PARAM_TYPE_BOOL, SQL_MODE_READ_ONLY, SQL_MODE_READ_WRITE},
{ "RowCacheSize", ODBC_ROWCACHESIZE, PARAM_READ, PARAM_TYPE_CUSTOM },
#if 0 /* not defined by DBI/DBD specification */
{ "TRANSACTION",
SQL_ACCESS_MODE, PARAM_READWRITE, PARAM_TYPE_BOOL, SQL_MODE_READ_ONLY, SQL_MODE_READ_WRITE },
{ "solid_timeout", SQL_LOGIN_TIMEOUT, PARAM_READWRITE, PARAM_TYPE_UINT },
{ "ISOLATION", PARAM_READWRITE, PARAM_TYPE_UINT, SQL_TXN_ISOLATION },
#endif
{ "odbc_SQL_DBMS_NAME", SQL_DBMS_NAME, PARAM_READ, PARAM_TYPE_CUSTOM, },
{ "odbc_SQL_DRIVER_ODBC_VER", SQL_DRIVER_ODBC_VER, PARAM_READ, PARAM_TYPE_CUSTOM },
{ "odbc_SQL_ROWSET_SIZE", SQL_ROWSET_SIZE, PARAM_READWRITE, PARAM_TYPE_UINT },
{ "odbc_ignore_named_placeholders", ODBC_IGNORE_NAMED_PLACEHOLDERS, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_default_bind_type", ODBC_DEFAULT_BIND_TYPE, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_force_bind_type", ODBC_FORCE_BIND_TYPE, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_force_rebind", ODBC_FORCE_REBIND, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_async_exec", ODBC_ASYNC_EXEC, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_err_handler", ODBC_ERR_HANDLER, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_exec_direct", ODBC_EXEC_DIRECT, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_version", ODBC_VERSION, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_cursortype", ODBC_CURSORTYPE, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_query_timeout", ODBC_QUERY_TIMEOUT, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_putdata_start", ODBC_PUTDATA_START, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_column_display_size", ODBC_COLUMN_DISPLAY_SIZE, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_utf8_on", ODBC_UTF8_ON, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_has_unicode", ODBC_HAS_UNICODE, PARAM_READ, PARAM_TYPE_CUSTOM },
{ "odbc_out_connect_string", ODBC_OUTCON_STR, PARAM_READ, PARAM_TYPE_CUSTOM},
{ "odbc_describe_parameters", ODBC_DESCRIBE_PARAMETERS, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_batch_size", ODBC_BATCH_SIZE, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_array_operations", ODBC_ARRAY_OPERATIONS, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{ "odbc_taf_callback", ODBC_TAF_CALLBACK, PARAM_READWRITE, PARAM_TYPE_CUSTOM },
{"odbc_trace", SQL_ATTR_TRACE, PARAM_READWRITE, PARAM_TYPE_BOOL, SQL_OPT_TRACE_ON, SQL_OPT_TRACE_OFF},
{"odbc_trace_file", SQL_ATTR_TRACEFILE, PARAM_READWRITE, PARAM_TYPE_STR, },
{ NULL },
};
/*======================================================================*/
/* */
/* S_dbOption */
/* ========== */
/* */
/* Given a string and a length, locate this option in the specified */
/* array of valid options. Typically used by STORE and FETCH methods */
/* to decide if this option/attribute is supported by us. */
/* */
/*======================================================================*/
static const db_params *
S_dbOption(const db_params *pars, char *key, STRLEN len)
{
/* search option to set */
while (pars->str != NULL) {
if (strncmp(pars->str, key, len) == 0
&& len == strlen(pars->str))
break;
pars++;
}
if (pars->str == NULL) {
return NULL;
}
return pars;
}
/*======================================================================*/
/* */
/* dbd_db_STORE_attrib */
/* =================== */
/* */
/* This function handles: */
/* */
/* $dbh->{$key} = $value */
/* */
/* Method to handle the setting of driver specific attributes and DBI */
/* attributes AutoCommit and ChopBlanks (no other DBI attributes). */
/* */
/* Return TRUE if the attribute was handled, else FALSE. */
/* */
/*======================================================================*/
int dbd_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
{
dTHX;
RETCODE rc;
STRLEN kl;
char *key = SvPV(keysv,kl);
int on;
if (!(pars->dir & PARAM_READ))
return Nullsv;
switch (pars->fOption) {
case ODBC_OUTCON_STR:
if (!imp_dbh->out_connect_string) {
retsv = &PL_sv_undef;
} else {
retsv = newSVsv(imp_dbh->out_connect_string);
}
break;
case SQL_DRIVER_ODBC_VER:
retsv = newSVpv(imp_dbh->odbc_ver, 0);
break;
case SQL_DBMS_NAME:
retsv = newSVpv(imp_dbh->odbc_dbms_name, 0);
break;
case ODBC_IGNORE_NAMED_PLACEHOLDERS:
retsv = newSViv(imp_dbh->odbc_ignore_named_placeholders);
break;
case ODBC_ARRAY_OPERATIONS:
retsv = newSViv(imp_dbh->odbc_array_operations);
break;
case ODBC_QUERY_TIMEOUT:
/*
* fetch current value of query timeout
*
* -1 is our internal flag saying odbc_query_timeout has never been
* set so we map it back to the default for ODBC which is 0
*/
if (imp_dbh->odbc_query_timeout == -1) {
retsv = newSViv(0);
} else {
retsv = newSViv(imp_dbh->odbc_query_timeout);
}
break;
case ODBC_PUTDATA_START:
retsv = newSViv(imp_dbh->odbc_putdata_start);
break;
case ODBC_BATCH_SIZE:
retsv = newSViv(imp_dbh->odbc_batch_size);
break;
case ODBC_COLUMN_DISPLAY_SIZE:
retsv = newSViv(imp_dbh->odbc_column_display_size);
break;
case ODBC_UTF8_ON:
retsv = newSViv(imp_dbh->odbc_utf8_on);
break;
case ODBC_HAS_UNICODE:
retsv = newSViv(imp_dbh->odbc_has_unicode);
break;
case ODBC_DEFAULT_BIND_TYPE:
retsv = newSViv(imp_dbh->odbc_default_bind_type);
break;
case ODBC_FORCE_BIND_TYPE:
retsv = newSViv(imp_dbh->odbc_force_bind_type);
break;
case ODBC_FORCE_REBIND:
retsv = newSViv(imp_dbh->odbc_force_rebind);
break;
case ODBC_EXEC_DIRECT:
retsv = newSViv(imp_dbh->odbc_exec_direct);
break;
case ODBC_DRIVER_COMPLETE:
retsv = newSViv(imp_dbh->odbc_driver_complete);
break;
case ODBC_DESCRIBE_PARAMETERS:
retsv = newSViv(imp_dbh->odbc_describe_parameters);
break;
case ODBC_ASYNC_EXEC:
/*
* fetch current value of asynchronous execution (should be
* either 0 or 1).
*/
retsv = newSViv(imp_dbh->odbc_async_exec);
break;
case ODBC_ERR_HANDLER:
/* fetch current value of the error handler (a coderef). */
if(imp_dbh->odbc_err_handler) {
retsv = newSVsv(imp_dbh->odbc_err_handler);
} else {
retsv = &PL_sv_undef;
}
break;
case ODBC_ROWCACHESIZE:
retsv = newSViv(imp_dbh->RowCacheSize);
break;
default:
{
enum gettype type = pars->type;
char strval[256];
SQLUINTEGER uval = 0;
SQLINTEGER retstrlen;
if ((pars->fOption == SQL_ATTR_ACCESS_MODE) &&
(imp_dbh->read_only != -1)) {
retsv = newSViv(imp_dbh->read_only);
break;
}
#ifdef WITH_UNICODE
if (col_type == SQL_C_WCHAR) {
char *c1;
c1 = PVallocW((SQLWCHAR *)buf);
buf = SvGROW(data, strlen(c1) + 1);
retlen = retlen / sizeof(SQLWCHAR);
strcpy(buf, c1);
PVfreeW(c1);
# ifdef sv_utf8_decode
sv_utf8_decode(data);
# else
SvUTF8_on(data);
# endif
}
# endif
return retlen;
}
/************************************************************************/
/* */
/* odbc_col_attributes */
/* =================== */
/* */
/************************************************************************/
SV *odbc_col_attributes(SV *sth, int colno, int desctype)
{
dTHX;
D_imp_sth(sth);
RETCODE rc;
SV *retsv = NULL;
unsigned char str_attr[512];
SWORD str_attr_len = 0;
SQLLEN num_attr = 0;
memset(str_attr, '\0', sizeof(str_attr));
if ( !DBIc_ACTIVE(imp_sth) ) {
dbd_error(sth, DBDODBC_INTERNAL_ERROR, "no statement executing");
return Nullsv;
}
/*
* At least on Win95, calling this with colno==0 would "core" dump/GPF.
* protect, even though it's valid for some values of desctype
* (e.g. SQL_COLUMN_COUNT, since it doesn't depend on the colcount)
*/
if (colno == 0) {
dbd_error(sth, DBDODBC_INTERNAL_ERROR,
"cannot obtain SQLColAttributes for column 0");
return Nullsv;
}
/*
* workaround a problem in unixODBC 2.2.11 which can write off the
* end of the str_attr buffer when built with unicode - lie about
* buffer size - we've got more than we admit to.
*/
rc = SQLColAttributes(imp_sth->hstmt, (SQLUSMALLINT)colno,
(SQLUSMALLINT)desctype,
str_attr, sizeof(str_attr)/2,
&str_attr_len, &num_attr);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "odbc_col_attributes/SQLColAttributes");
return Nullsv;
} else if (SQL_SUCCESS_WITH_INFO == rc) {
warn("SQLColAttributes has truncated returned data");
}
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3)) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" SQLColAttributes: colno=%d, desctype=%d, str_attr=%s, "
"str_attr_len=%d, num_attr=%ld",
colno, desctype, str_attr, str_attr_len, (long)num_attr);
}
switch (desctype) {
case SQL_COLUMN_AUTO_INCREMENT:
case SQL_COLUMN_CASE_SENSITIVE:
case SQL_COLUMN_COUNT:
case SQL_COLUMN_DISPLAY_SIZE:
case SQL_COLUMN_LENGTH:
case SQL_COLUMN_MONEY:
case SQL_COLUMN_NULLABLE:
case SQL_COLUMN_PRECISION:
case SQL_COLUMN_SCALE:
case SQL_COLUMN_SEARCHABLE:
case SQL_COLUMN_TYPE:
case SQL_COLUMN_UNSIGNED:
case SQL_COLUMN_UPDATABLE:
{
retsv = newSViv(num_attr);
break;
}
case SQL_COLUMN_LABEL:
case SQL_COLUMN_NAME:
case SQL_COLUMN_OWNER_NAME:
case SQL_COLUMN_QUALIFIER_NAME:
case SQL_COLUMN_TABLE_NAME:
case SQL_COLUMN_TYPE_NAME:
{
/*
* NOTE: in unixODBC 2.2.11, if you called SQLDriverConnectW and
* then called SQLColAttributes for a string type it would often
* return half the number of characters it had written to
* str_attr in str_attr_len.
*/
retsv = newSVpv(str_attr, strlen(str_attr));
break;
}
default:
{
dbd_error(sth, DBDODBC_INTERNAL_ERROR,
"driver-specific column attributes not supported");
/* find catalog usage */
{
char yesno[10];
rc = SQLGetInfo(imp_dbh->hdbc, SQL_CATALOG_NAME,
yesno,
(SQLSMALLINT) sizeof(yesno), &dbvlen);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(dbh, rc, "post_connect/SQLGetInfo(SQL_CATALOG_NAME)");
imp_dbh->catalogs_supported = 0;
} else if (yesno[0] == 'Y') {
imp_dbh->catalogs_supported = 1;
} else {
imp_dbh->catalogs_supported = 0;
}
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh, "SQL_CATALOG_NAME = %d\n",
imp_dbh->catalogs_supported);
}
/* find schema usage */
{
rc = SQLGetInfo(imp_dbh->hdbc, SQL_SCHEMA_USAGE,
&imp_dbh->schema_usage,
(SQLSMALLINT) sizeof(imp_dbh->schema_usage), &dbvlen);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(dbh, rc, "post_connect/SQLGetInfo(SQL_SCHEMA_USAGE)");
imp_dbh->schema_usage = 0;
}
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh, "SQL_SCHEMA_USAGE = %lu\n",
(unsigned long)imp_dbh->schema_usage);
}
#ifdef WITH_UNICODE
imp_dbh->max_column_name_len = imp_dbh->max_column_name_len *
sizeof(SQLWCHAR) + 2;
#endif
if (imp_dbh->max_column_name_len > 512) {
imp_dbh->max_column_name_len = 512;
DBIh_SET_ERR_CHAR(
dbh, (imp_xxh_t*)imp_drh, "0", 1,
"Max column name length pegged at 512", Nullch, Nullch);
}
/* default ignoring named parameters and array operations to false */
imp_dbh->odbc_ignore_named_placeholders = 0;
imp_dbh->odbc_array_operations = 0;
#ifdef DEFAULT_IS_OFF_NOW_SO_THIS_IS_NOT_REQUIRED
/* Disable array operations by default for some drivers as no version
I've ever seen works and it annoys the dbix-class guys */
if (imp_dbh->driver_type == DT_FREETDS ||
imp_dbh->driver_type == DT_MS_ACCESS_JET ||
imp_dbh->driver_type == DT_MS_ACCESS_ACE) {
imp_dbh->odbc_array_operations = 0;
}
#endif
#ifdef WITH_UNICODE
imp_dbh->odbc_has_unicode = 1;
#else
imp_dbh->odbc_has_unicode = 0;
#endif
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh, "DBD::ODBC is unicode built : %s\n",
imp_dbh->odbc_has_unicode ? "YES" : "NO");
imp_dbh->odbc_default_bind_type = 0;
imp_dbh->odbc_force_bind_type = 0;
#ifdef SQL_ROWSET_SIZE_DEFAULT
imp_dbh->rowset_size = SQL_ROWSET_SIZE_DEFAULT;
#else
/* it should be 1 anyway so above should be redundant but included
here partly to remind me what it is */
imp_dbh->rowset_size = 1;
#endif
/* flag to see if SQLDescribeParam is supported */
imp_dbh->odbc_sqldescribeparam_supported = -1;
/* flag to see if SQLDescribeParam is supported */
imp_dbh->odbc_sqlmoreresults_supported = -1;
imp_dbh->odbc_defer_binding = 0;
imp_dbh->odbc_force_rebind = 0;
/* default value for query timeout is -1 which means do not set the
query timeout at all. */
imp_dbh->odbc_query_timeout = -1;
imp_dbh->odbc_putdata_start = 32768;
imp_dbh->odbc_batch_size = 10;
imp_dbh->read_only = -1; /* show not set yet */
/*printf("odbc_batch_size defaulted to %d\n", imp_dbh->odbc_batch_size);*/
imp_dbh->odbc_column_display_size = 2001;
imp_dbh->odbc_utf8_on = 0;
imp_dbh->odbc_exec_direct = 0; /* default to not having SQLExecDirect used */
imp_dbh->odbc_describe_parameters = 1;
imp_dbh->RowCacheSize = 1; /* default value for now */
#ifdef WE_DONT_DO_THIS_ANYMORE
if (!strcmp(imp_dbh->odbc_dbms_name, "Microsoft SQL Server")) {
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE0(imp_dbh, "Deferring Binding\n");
imp_dbh->odbc_defer_binding = 1;
}
#endif
/* check to see if SQLMoreResults is supported */
rc = SQLGetFunctions(imp_dbh->hdbc, SQL_API_SQLMORERESULTS, &supported);
if (SQL_SUCCEEDED(rc)) {
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh, "SQLMoreResults supported: %d\n", supported);
imp_dbh->odbc_sqlmoreresults_supported = supported ? 1 : 0;
} else {
imp_dbh->odbc_sqlmoreresults_supported = 0;
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE0(imp_dbh,
" !!SQLGetFunctions(SQL_API_SQLMORERESULTS) failed:\n");
AllODBCErrors(imp_dbh->henv, imp_dbh->hdbc, 0,
DBIc_TRACE(imp_dbh, DBD_TRACING, 0, 3), DBIc_LOGPIO(imp_dbh));
}
/* call only once per connection / DBH -- may want to do
* this during the connect to avoid potential threading
* issues */
/* check to see if SQLDescribeParam is supported */
rc = SQLGetFunctions(imp_dbh->hdbc, SQL_API_SQLDESCRIBEPARAM, &supported);
if (SQL_SUCCEEDED(rc)) {
strlen("odbc_putdata_start"), G_DISCARD);
}
}
/* odbc_column_display_size */
{
SV **svp;
IV column_display_size_value;
DBD_ATTRIB_GET_IV(
attr, "odbc_column_display_size",
strlen("odbc_column_display_size"),
svp, column_display_size_value);
if (svp) {
imp_dbh->odbc_column_display_size = column_display_size_value;
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh,
" Setting DBH default column display size to %d\n",
(int)column_display_size_value);
/* delete odbc_column_display_size so we don't see it again via STORE */
(void)hv_delete((HV*)SvRV(attr), "odbc_column_display_size",
strlen("odbc_column_display_size"), G_DISCARD);
}
}
/* odbc_utf8_on */
{
SV **svp;
IV utf8_on_value;
DBD_ATTRIB_GET_IV(
attr, "odbc_utf8_on",
strlen("odbc_utf8_on"),
svp, utf8_on_value);
if (svp) {
imp_dbh->odbc_utf8_on = utf8_on_value;
if (DBIc_TRACE(imp_dbh, CONNECTION_TRACING, 0, 0))
TRACE1(imp_dbh,
" Setting UTF8_ON to %d\n",
(int)utf8_on_value);
/* delete odbc_utf8_on so we don't see it again via STORE */
(void)hv_delete((HV*)SvRV(attr), "odbc_utf8_on",
strlen("odbc_utf8_on"), G_DISCARD);
}
}
return 1;
}
/*
* Called when we don't know what to bind a parameter as. This can happen for all sorts
* of reasons like:
*
* o SQLDescribeParam is not supported
* o odbc_describe_parameters is set to 0 (in other words telling us not to describe)
* o SQLDescribeParam was called and failed
* o SQLDescribeParam was called but returned an unrecognised parameter type
*
* If the data to bind is unicode (SvUTF8 is true) it is bound as SQL_WCHAR
* or SQL_WLONGVARCHAR depending on its size. Otherwise it is bound as
* SQL_VARCHAR/SQL_LONGVARCHAR.
*/
static SQLSMALLINT default_parameter_type(
char *why, imp_sth_t *imp_sth, phs_t *phs)
{
SQLSMALLINT sql_type;
struct imp_dbh_st *imp_dbh = NULL;
imp_dbh = (struct imp_dbh_st *)(DBIc_PARENT_COM(imp_sth));
if (imp_sth->odbc_default_bind_type != 0) {
sql_type = imp_sth->odbc_default_bind_type;
} else {
/* MS Access can return an invalid precision error in the 12blob
test unless the large value is bound as an SQL_LONGVARCHAR
or SQL_WLONGVARCHAR. Who knows what large is, but for now it is
4000 */
/*
Changed to 2000 for the varchar max switch as in a unicode build we
can change a string of 'x' x 2001 into 4002 wide chrs and SQL Server
will also return invalid precision in this case on a varchar(4000).
Of course, being SQL Server, it also has this problem with the
newer varchar(8000)! */
if (!SvOK(phs->sv)) {
sql_type = ODBC_BACKUP_BIND_TYPE_VALUE;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3))
TRACE2(imp_sth, "%s, sv is not OK, defaulting to %d\n",
why, sql_type);
} else if (SvCUR(phs->sv) > imp_dbh->switch_to_longvarchar) {
#if defined(WITH_UNICODE)
if (SvUTF8(phs->sv))
sql_type = SQL_WLONGVARCHAR;
else
#endif
sql_type = SQL_LONGVARCHAR;
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3))
TRACE3(imp_sth, "%s, sv=%"UVuf" bytes, defaulting to %d\n",
why, (UV)SvCUR(phs->sv), sql_type);
} else {
#if defined(WITH_UNICODE)
if (SvUTF8(phs->sv))
sql_type = SQL_WVARCHAR;
else
#endif
sql_type = SQL_VARCHAR;
/*return ODBC_BACKUP_BIND_TYPE_VALUE;*/
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3))
TRACE3(imp_sth, "%s, sv=%"UVuf" bytes, defaulting to %d\n",
why, (UV)SvCUR(phs->sv), sql_type);
}
}
return sql_type;
}
#ifdef WIN32
static HWND GetConsoleHwnd(void)
{
#define MY_BUFSIZE 1024 /* Buffer size for console window titles. */
HWND hwndFound; /* This is what is returned to the caller. */
char pszNewWindowTitle[MY_BUFSIZE]; /* Contains fabricated WindowTitle. */
char pszOldWindowTitle[MY_BUFSIZE]; /* Contains original WindowTitle */
/* Fetch current window title. */
GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);
/* Format a "unique" NewWindowTitle. */
wsprintf(pszNewWindowTitle,"%d/%d",
GetTickCount(),
GetCurrentProcessId());
/* Change current window title. */
SetConsoleTitle(pszNewWindowTitle);
/* Ensure window title has been updated. */
Sleep(40);
/* Look for NewWindowTitle. */
if (row == (SQLLEN)recno) return 1;
} else if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 3)) {
TRACE0(imp_sth, "SQLGetDiagField for SQL_DIAG_ROW_NUMBER failed");
}
i++; /* next record */
};
/* will be SQL_NO_DATA if we reach the end of diags without finding anything */
/* TO_DO some drivers are not going to support SQL_DIAG_COLUMN_NUMBER
so we should do better than below - maybe show the first/last error */
strcpy(state, "HY000");
*native = 1;
strcpy(msg, "failed to retrieve diags");
return 0;
}
/*
* taf_callback_wrapper is the function we pass to Oracle to be called
* when a connection fails. We asked the ODBC driver to pass our dbh
* handle in and it also gives us the type and event. We just pass all
* these args off to the registered Perl subroutine and return to
* the Oracle driver whatever that Perl sub returns to us. In this way
* the user's Perl dictates what happens in the failover process and not
* us.
*/
static int taf_callback_wrapper (
void *handle,
int type,
int event) {
dTHX;
int return_count;
int ret;
SV* dbh = (SV *)handle;
D_imp_dbh(dbh);
dSP;
PUSHMARK(SP);
XPUSHs(handle);
XPUSHs(sv_2mortal(newSViv(event)));
XPUSHs(sv_2mortal(newSViv(type)));
PUTBACK;
return_count = call_sv(imp_dbh->odbc_taf_callback, G_SCALAR);
SPAGAIN;
if (return_count != 1)
croak("Expected one scalar back from taf handler");
ret = POPi;
PUTBACK;
return ret;
}
static void check_for_unicode_param(
imp_sth_t *imp_sth,
phs_t *phs) {
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5)) {
TRACE2(imp_sth, "check_for_unicode_param - sql_type=%s, described=%s\n",
S_SqlTypeToString(phs->sql_type), S_SqlTypeToString(phs->described_sql_type));
}
/* If we didn't called SQLDescribeParam successfully, we've defaulted/guessed so just return
as sql_type will already be set */
if (!phs->described_sql_type) return;
if (SvUTF8(phs->sv)) {
if (phs->described_sql_type == SQL_CHAR) {
phs->sql_type = SQL_WCHAR;
} else if (phs->described_sql_type == SQL_VARCHAR) {
phs->sql_type = SQL_WVARCHAR;
} else if (phs->described_sql_type == SQL_LONGVARCHAR) {
phs->sql_type = SQL_WLONGVARCHAR;
} else {
phs->sql_type = phs->described_sql_type;
}
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 5) && (phs->sql_type != phs->described_sql_type))
TRACE1(imp_sth, " SvUTF8 parameter - changing to %s type\n",
S_SqlTypeToString(phs->sql_type));
} else {
if (phs->described_sql_type == SQL_NUMERIC ||
phs->described_sql_type == SQL_DECIMAL ||
phs->described_sql_type == SQL_FLOAT ||
phs->described_sql_type == SQL_REAL ||
phs->described_sql_type == SQL_DOUBLE) {
phs->sql_type = SQL_VARCHAR;
} else {
phs->sql_type = phs->described_sql_type;
}
}
}
AV* dbd_data_sources(SV *drh ) {
dTHX;
SQLUSMALLINT fDirection = SQL_FETCH_FIRST;
RETCODE rc;
SQLCHAR dsn[SQL_MAX_DSN_LENGTH+1+9 /* strlen("DBI:ODBC:") */];
SQLSMALLINT dsn_length;
SQLCHAR description[256];
SQLSMALLINT description_length;
AV *ds = newAV();
D_imp_drh(drh);
if (!imp_drh->connects) {
rc = SQLAllocEnv(&imp_drh->henv);
if (!SQL_ok(rc)) {
imp_drh->henv = SQL_NULL_HENV;
dbd_error(drh, rc, "data_sources/SQLAllocEnv");
return NULL;
}
}
strcpy(dsn, "dbi:ODBC:");
while (1) {
description[0] = '\0';
rc = SQLDataSources(imp_drh->henv, fDirection,
dsn+9, /* strlen("dbi:ODBC:") */
SQL_MAX_DSN_LENGTH,
( run in 1.063 second using v1.01-cache-2.11-cpan-39bf76dae61 )