DBD-ODBC

 view release on metacpan or  search on metacpan

dbdimp.c  view on Meta::CPAN

/*
 * 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;

dbdimp.c  view on Meta::CPAN


   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");
               }
           }

dbdimp.c  view on Meta::CPAN


   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 =

dbdimp.c  view on Meta::CPAN

   {                                            /* 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;
}



dbdimp.c  view on Meta::CPAN

            /* 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) */
/* { */

dbdimp.c  view on Meta::CPAN

    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",

dbdimp.c  view on Meta::CPAN

                   (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 */

dbdimp.c  view on Meta::CPAN


   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;

dbdimp.c  view on Meta::CPAN

    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;
          }

dbdimp.c  view on Meta::CPAN


#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");

dbdimp.c  view on Meta::CPAN

    /* 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)) {

dbdimp.c  view on Meta::CPAN

                            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. */

dbdimp.c  view on Meta::CPAN

            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 )