You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apr.apache.org by td...@apache.org on 2008/06/12 19:54:16 UTC
svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win aprutil.dsw
dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp include/apu.hw
include/private/apr_dbd_odbc_v2.h
Author: tdonovan
Date: Thu Jun 12 10:54:15 2008
New Revision: 667182
URL: http://svn.apache.org/viewvc?rev=667182&view=rev
Log:
apr_dbd_odbc driver
Added:
apr/apr-util/trunk/dbd/apr_dbd_odbc.c
apr/apr-util/trunk/dbd/apr_dbd_odbc.dsp
apr/apr-util/trunk/include/private/apr_dbd_odbc_v2.h
Modified:
apr/apr-util/trunk/Makefile.win
apr/apr-util/trunk/aprutil.dsw
apr/apr-util/trunk/dbd/apr_dbd.c
apr/apr-util/trunk/include/apu.hw
Modified: apr/apr-util/trunk/Makefile.win
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/Makefile.win?rev=667182&r1=667181&r2=667182&view=diff
==============================================================================
--- apr/apr-util/trunk/Makefile.win (original)
+++ apr/apr-util/trunk/Makefile.win Thu Jun 12 10:54:15 2008
@@ -190,16 +190,18 @@
cd ldap
$(MAKE) $(MAKEOPT) -f apr_ldap.mak CFG="apr_ldap - $(ARCH)" RECURSE=0 $(CTARGET)
cd ..
-!IFDEF DBD_LIST
cd dbd
+ $(MAKE) $(MAKEOPT) -f apr_dbd_%d.mak CFG="apr_dbd_odbc - $(ARCH)" RECURSE=0 $(CTARGET)
+!IFDEF DBD_LIST
for %d in ($(DBD_LIST)) do \
$(MAKE) $(MAKEOPT) -f apr_dbd_%d.mak CFG="apr_dbd_%d - $(ARCH)" RECURSE=0 $(CTARGET)
- cd ..
!ENDIF
+ cd ..
!ELSEIF $(USESLN) == 1
clean:
+ devenv aprutil.sln /useenv /clean "$(SLNARCH)" /project apr_dbd_odbc
!IFDEF DBD_LIST
-for %d in ($(DBD_LIST)) do \
devenv aprutil.sln /useenv /clean "$(SLNARCH)" /project apr_dbd_%d
@@ -224,6 +226,7 @@
devenv aprutil.sln /useenv /build "$(SLNARCH)" /project aprutil
devenv aprutil.sln /useenv /build "$(SLNARCH)" /project libaprutil
devenv aprutil.sln /useenv /build "$(SLNARCH)" /project apr_ldap
+ devenv aprutil.sln /useenv /build "$(SLNARCH)" /project apr_dbd_odbc
!IFDEF DBD_LIST
for %d in ($(DBD_LIST)) do \
devenv aprutil.sln /useenv /build "$(SLNARCH)" /project apr_dbd_%d
@@ -233,6 +236,7 @@
# $(USEDSP) == 1
clean:
+ msdev aprutil.dsw /USEENV /MAKE "apr_dbd_odbc - $(ARCH)" /CLEAN
!IFDEF DBD_LIST
-for %d in ($(DBD_LIST)) do \
msdev aprutil.dsw /USEENV /MAKE "apr_dbd_%d - $(ARCH)" /CLEAN
@@ -257,6 +261,7 @@
@msdev aprutil.dsw /USEENV /MAKE "libaprapp - $(ARCH)"
@msdev aprutil.dsw /USEENV /MAKE "libaprutil - $(ARCH)"
@msdev aprutil.dsw /USEENV /MAKE "apr_ldap - $(ARCH)"
+ msdev aprutil.dsw /USEENV /MAKE "apr_dbd_odbc - $(ARCH)"
!IFDEF DBD_LIST
@for %d in ($(DBD_LIST)) do \
msdev aprutil.dsw /USEENV /MAKE "apr_dbd_%d - $(ARCH)"
@@ -317,6 +322,8 @@
copy $(APU_PATH)\$(ARCHPATH)\libaprutil-1.pdb "$(PREFIX)\bin\" <.y
copy $(APU_PATH)\ldap\$(ARCHPATH)\apr_ldap-1.dll "$(PREFIX)\bin\" <.y
copy $(APU_PATH)\ldap\$(ARCHPATH)\apr_ldap-1.pdb "$(PREFIX)\bin\" <.y
+ copy $(APU_PATH)\dbd\$(ARCHPATH)\apr_dbd_odbc-1.dll "$(PREFIX)\bin\" <.y && \
+ copy $(APU_PATH)\dbd\$(ARCHPATH)\apr_dbd_odbc-1.pdb "$(PREFIX)\bin\" <.y \
!IFDEF DBD_LIST
for %d in ($(DBD_LIST)) do ( \
copy $(APU_PATH)\dbd\$(ARCHPATH)\apr_dbd_%d-1.dll "$(PREFIX)\bin\" <.y && \
Modified: apr/apr-util/trunk/aprutil.dsw
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/aprutil.dsw?rev=667182&r1=667181&r2=667182&view=diff
==============================================================================
--- apr/apr-util/trunk/aprutil.dsw (original)
+++ apr/apr-util/trunk/aprutil.dsw Thu Jun 12 10:54:15 2008
@@ -15,7 +15,7 @@
###############################################################################
-Project: "aprapp"="..\apr\build\aprapp.dsp" - Package Owner=<4>
+Project: "apr_dbd_freetds"=".\dbd\apr_dbd_freetds.dsp" - Package Owner=<4>
Package=<5>
{{{
@@ -24,13 +24,16 @@
Package=<4>
{{{
Begin Project Dependency
- Project_Dep_Name preaprapp
+ Project_Dep_Name libapr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libaprutil
End Project Dependency
}}}
###############################################################################
-Project: "apr_dbd_freetds"=".\dbd\apr_dbd_freetds.dsp" - Package Owner=<4>
+Project: "apr_dbd_mysql"=".\dbd\apr_dbd_mysql.dsp" - Package Owner=<4>
Package=<5>
{{{
@@ -48,7 +51,7 @@
###############################################################################
-Project: "apr_dbd_mysql"=".\dbd\apr_dbd_mysql.dsp" - Package Owner=<4>
+Project: "apr_dbd_odbc"=".\dbd\apr_dbd_odbc.dsp" - Package Owner=<4>
Package=<5>
{{{
@@ -57,10 +60,10 @@
Package=<4>
{{{
Begin Project Dependency
- Project_Dep_Name libapr
+ Project_Dep_Name libaprutil
End Project Dependency
Begin Project Dependency
- Project_Dep_Name libaprutil
+ Project_Dep_Name libapr
End Project Dependency
}}}
@@ -156,6 +159,21 @@
###############################################################################
+Project: "aprapp"="..\apr\build\aprapp.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name preaprapp
+ End Project Dependency
+}}}
+
+###############################################################################
+
Project: "apriconv"="..\apr-iconv\apriconv.dsp" - Package Owner=<4>
Package=<5>
Modified: apr/apr-util/trunk/dbd/apr_dbd.c
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd.c?rev=667182&r1=667181&r2=667182&view=diff
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd.c (original)
+++ apr/apr-util/trunk/dbd/apr_dbd.c Thu Jun 12 10:54:15 2008
@@ -97,10 +97,10 @@
/* Top level pool scope, need process-scope lifetime */
for (parent = pool; parent; parent = apr_pool_parent_get(pool))
pool = parent;
-
+#if APU_DSO_BUILD
/* deprecate in 2.0 - permit implicit initialization */
apu_dso_init(pool);
-
+#endif
drivers = apr_hash_make(pool);
#if APR_HAS_THREADS
@@ -146,14 +146,17 @@
#endif
apr_status_t rv;
+#if APU_DSO_BUILD
rv = apu_dso_mutex_lock();
if (rv) {
return rv;
}
-
+#endif
*driver = apr_hash_get(drivers, name, APR_HASH_KEY_STRING);
if (*driver) {
+#if APU_DSO_BUILD
apu_dso_mutex_unlock();
+#endif
return APR_SUCCESS;
}
Added: apr/apr-util/trunk/dbd/apr_dbd_odbc.c
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_odbc.c?rev=667182&view=auto
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_odbc.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_odbc.c Thu Jun 12 10:54:15 2008
@@ -0,0 +1,1615 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+#if APU_HAVE_ODBC
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_env.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_dbd_internal.h"
+#include "apr_thread_proc.h"
+#include "apu_version.h"
+
+/* If library is ODBC-V2, use macros for limited ODBC-V2 support
+ * No random access in V2.
+ */
+#ifdef ODBCV2
+#define ODBCVER 0x0200
+#include "apr_dbd_odbc_v2.h"
+#endif
+
+/* standard ODBC include files */
+#include <sql.h>
+#include <sqlext.h>
+
+ /* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver'
+ * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
+ * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.
+ */
+#ifndef ODBC_DRIVER_NAME
+#define ODBC_DRIVER_NAME odbc
+#endif
+#define STRINGIFY(x) #x
+#define NAMIFY2(n) apr_dbd_##n##_driver
+#define NAMIFY1(n) NAMIFY2(n)
+#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
+#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
+
+/* Required APR version for this driver */
+#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
+#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
+
+
+static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
+
+/* Use a CHECK_ERROR macro so we can grab the source line numbers
+ * for error reports */
+static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
+ SQLSMALLINT type, SQLHANDLE h, int line);
+#define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__)
+
+#define SOURCE_FILE __FILE__ /* source file for error messages */
+#define MAX_ERROR_STRING 1024 /* max length of message in dbc */
+#define MAX_COLUMN_NAME 256 /* longest column name recognized */
+#define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */
+
+#define MAX_PARAMS 20
+#define DEFAULTSEPS " \t\r\n,="
+#define CSINGLEQUOTE '\''
+#define SSINGLEQUOTE "\'"
+
+#define TEXTMODE 1 /* used for text (APR 1.2) mode params */
+#define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */
+
+/* Identify datatypes which are LOBs
+ * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB */
+#define IS_LOB(t) (t == SQL_LONGVARCHAR \
+ || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
+ || t == -98 || t == -99)
+/* These types are CLOBs
+ * - DB2 DRDA driver uses undefined type -98 for CLOB */
+#define IS_CLOB(t) \
+ (t == SQL_LONGVARCHAR || t == -98)
+
+/* Convert a SQL result to an APR result */
+#define APR_FROM_SQL_RESULT(rc) \
+ (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
+
+/* DBD opaque structures */
+struct apr_dbd_t
+{
+ SQLHANDLE dbc; /* SQL connection handle - NULL after close */
+ apr_pool_t *pool; /* connection lifetime pool */
+ char *dbname; /* ODBC datasource */
+ int lasterrorcode;
+ int lineNumber;
+ char lastError[MAX_ERROR_STRING];
+ int defaultBufferSize; /* used for CLOBs in text mode,
+ * and when fld size is indeterminate */
+ int transaction_mode;
+ int dboptions; /* driver options re SQLGetData */
+ int default_transaction_mode;
+ int can_commit; /* controls end_trans behavior */
+};
+
+struct apr_dbd_results_t
+{
+ SQLHANDLE stmt; /* parent sql statement handle */
+ SQLHANDLE dbc; /* parent sql connection handle */
+ apr_pool_t *pool; /* pool from query or select */
+ apr_dbd_t *apr_dbd; /* parent DBD connection handle */
+ int random; /* random access requested */
+ int ncols; /* number of columns */
+ int isclosed; /* cursor has been closed */
+ char **colnames; /* array of column names (NULL until used) */
+ SQLPOINTER *colptrs; /* pointers to column data */
+ SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
+ SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
+ SQLSMALLINT *coltypes; /* array of SQL data types for columns */
+ SQLLEN *colinds; /* array of SQL data indicator/strlens */
+ int *colstate; /* array of column states
+ * - avail, bound, present, unavail
+ */
+ int *all_data_fetched; /* flags data as all fetched, for LOBs */
+ void *data; /* buffer for all data for one row */
+};
+enum /* results column states */
+{
+ COL_AVAIL, /* data may be retrieved with SQLGetData */
+ COL_PRESENT, /* data has been retrieved with SQLGetData */
+ COL_BOUND, /* column is bound to colptr */
+ COL_RETRIEVED, /* all data from column has been returned */
+ COL_UNAVAIL /* column is unavailable because ODBC driver
+ * requires that columns be retrieved
+ * in ascending order and a higher col
+ * was accessed */
+};
+
+struct apr_dbd_row_t {
+ SQLHANDLE stmt; /* parent ODBC statement handle */
+ SQLHANDLE dbc; /* parent ODBC connection handle */
+ apr_pool_t *pool; /* pool from get_row */
+ apr_dbd_results_t *res;
+};
+
+struct apr_dbd_transaction_t {
+ SQLHANDLE dbc; /* parent ODBC connection handle */
+ apr_dbd_t *apr_dbd; /* parent DBD connection handle */
+};
+
+struct apr_dbd_prepared_t {
+ SQLHANDLE stmt; /* ODBC statement handle */
+ SQLHANDLE dbc; /* parent ODBC connection handle */
+ apr_dbd_t *apr_dbd;
+ int nargs;
+ int nvals;
+ int *types; /* array of DBD data types */
+
+};
+
+static void odbc_lob_bucket_destroy(void *data);
+static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
+static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block);
+
+/* the ODBC LOB bucket type */
+static const apr_bucket_type_t odbc_bucket_type = {
+ "ODBC_LOB", 5, APR_BUCKET_DATA,
+ odbc_lob_bucket_destroy,
+ odbc_lob_bucket_read,
+ odbc_lob_bucket_setaside,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
+
+
+/* ODBC LOB bucket data */
+typedef struct {
+ /** Ref count for shared bucket */
+ apr_bucket_refcount refcount;
+ const apr_dbd_row_t *row;
+ int col;
+ SQLSMALLINT type;
+} odbc_bucket;
+
+
+/* SQL datatype mappings to DBD datatypes
+ * These tables must correspond *exactly* to the apr_dbd_type_e enum
+ * in apr_dbd_internal.h
+ */
+
+/* ODBC "C" types to DBD datatypes */
+static SQLSMALLINT const sqlCtype[] = {
+ SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
+ SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
+ SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
+ SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
+ SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
+ SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
+ SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
+ SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
+ SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
+ SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
+ SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
+ SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
+ SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
+ SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
+ SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
+ SQL_C_CHAR, /*SQL_C_TYPE_TIME, /* APR_DBD_TYPE_TIME, \%pDi */
+ SQL_C_CHAR, /*SQL_C_TYPE_DATE, /* APR_DBD_TYPE_DATE, \%pDd */
+ SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_DATETIME, \%pDa */
+ SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_TIMESTAMP, \%pDs */
+ SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
+ SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
+ SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
+ SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
+};
+
+/* ODBC Base types to DBD datatypes */
+static SQLSMALLINT const sqlBaseType[] = {
+ SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
+ SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
+ SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
+ SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
+ SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
+ SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
+ SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
+ SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
+ SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
+ SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
+ SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
+ SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
+ SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
+ SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
+ SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
+ SQL_CHAR, /*SQL_TIME, /* APR_DBD_TYPE_TIME, \%pDi */
+ SQL_CHAR, /*SQL_DATE, /* APR_DBD_TYPE_DATE, \%pDd */
+ SQL_CHAR, /*SQL_TIMESTAMP, /* APR_DBD_TYPE_DATETIME, \%pDa */
+ SQL_CHAR, /*SQL_TIMESTAMP, /* APR_DBD_TYPE_TIMESTAMP, \%pDs */
+ SQL_CHAR, /*SQL_TIMESTAMP, /* APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
+ SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
+ SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
+ SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
+};
+
+/* result sizes for DBD datatypes (-1 for null-terminated) */
+static int const sqlSizes[] = {
+ 0,
+ sizeof(char), /**< \%hhd out: char* */
+ sizeof(unsigned char), /**< \%hhu out: unsigned char* */
+ sizeof(short), /**< \%hd out: short* */
+ sizeof(unsigned short), /**< \%hu out: unsigned short* */
+ sizeof(int), /**< \%d out: int* */
+ sizeof(unsigned int), /**< \%u out: unsigned int* */
+ sizeof(long), /**< \%ld out: long* */
+ sizeof(unsigned long), /**< \%lu out: unsigned long* */
+ sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
+ sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
+ sizeof(float), /**< \%f out: float* */
+ sizeof(double), /**< \%lf out: double* */
+ -1, /**< \%s out: char** */
+ -1, /**< \%pDt out: char** */
+ -1, /**< \%pDi out: char** */
+ -1, /**< \%pDd out: char** */
+ -1, /**< \%pDa out: char** */
+ -1, /**< \%pDs out: char** */
+ -1, /**< \%pDz out: char** */
+ sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
+ sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
+ 0 /**< \%pDn : in: void*, out: void** */
+};
+
+/*
+* local functions
+*/
+
+/* close any open results for the connection */
+static apr_status_t odbc_close_results(void *d)
+{ apr_dbd_results_t *dbr = (apr_dbd_results_t *) d;
+ SQLRETURN rc = SQL_SUCCESS;
+
+ if (dbr && !dbr->isclosed) {
+ rc = SQLCloseCursor(dbr->stmt);
+ }
+ dbr->isclosed = 1;
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/* close the ODBC statement handle from a prepare */
+static apr_status_t odbc_close_pstmt(void *s)
+{
+ SQLRETURN rc = APR_SUCCESS;
+ apr_dbd_prepared_t *statement = s;
+ SQLHANDLE hstmt = statement->stmt;
+ /* stmt is closed if connection has already been closed */
+ if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
+ rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+ }
+ statement->stmt = NULL;
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/* close: close/release a connection obtained from open() */
+static apr_status_t odbc_close(apr_dbd_t *handle)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+
+ if (handle->dbc) {
+ rc = SQLDisconnect(handle->dbc);
+ CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
+ rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
+ CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
+ handle->dbc = NULL;
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/* odbc_close re-defined for passing to pool cleanup */
+static apr_status_t odbc_close_cleanup(void *handle)
+{
+ return odbc_close( (apr_dbd_t *) handle);
+}
+
+/* close the ODBC environment handle at process termination */
+static apr_status_t odbc_close_env(SQLHANDLE henv)
+{
+ SQLRETURN rc;
+
+ rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
+ henv = NULL;
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/* setup the arrays in results for all the returned columns */
+static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t * res,
+ SQLHANDLE stmt)
+{
+ SQLRETURN rc;
+ int maxsize, textsize, realsize, type, isunsigned = 1;
+
+ /* discover the sql type */
+ rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
+ (SQLPOINTER) &isunsigned);
+ isunsigned = (isunsigned == SQL_TRUE);
+
+ rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
+ (SQLPOINTER) &type);
+ if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE)
+ /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
+ rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
+ 0, NULL, (SQLPOINTER) &type);
+ if (!SQL_SUCCEEDED(rc))
+ /* if still unknown make it CHAR */
+ type = SQL_C_CHAR;
+
+ switch (type) {
+ case SQL_INTEGER:
+ case SQL_SMALLINT:
+ case SQL_TINYINT:
+ case SQL_BIGINT:
+ /* fix these numeric binary types up as signed/unsigned for C types */
+ type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
+ break;
+ /* LOB types are not changed to C types */
+ case SQL_LONGVARCHAR:
+ type = SQL_LONGVARCHAR;
+ break;
+ case SQL_LONGVARBINARY:
+ type = SQL_LONGVARBINARY;
+ break;
+ case SQL_FLOAT :
+ type = SQL_C_FLOAT;
+ break;
+ case SQL_DOUBLE :
+ type = SQL_C_DOUBLE;
+ break;
+
+ /* DBD wants times as strings */
+ case SQL_TIMESTAMP:
+ case SQL_DATE:
+ case SQL_TIME:
+ default:
+ type = SQL_C_CHAR;
+ }
+
+ res->coltypes[icol] = type;
+
+ /* size if retrieved as text */
+ rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
+ NULL, (SQLPOINTER) & textsize);
+ if (!SQL_SUCCEEDED(rc) || textsize < 0)
+ textsize = res->apr_dbd->defaultBufferSize;
+ /* for null-term, which sometimes isn't included */
+ textsize++;
+
+ /* real size */
+ rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
+ NULL, (SQLPOINTER) & realsize);
+ if (!SQL_SUCCEEDED(rc))
+ realsize = textsize;
+
+ maxsize = (textsize > realsize) ? textsize : realsize;
+ if ( IS_LOB(type) || maxsize <= 0) {
+ /* LOB types are never bound and have a NULL colptr for binary.
+ * Ingore their real (1-2gb) length & use a default - the larger
+ * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
+ * If not a LOB, but simply unknown length - always use defaultBufferSize.
+ */
+ maxsize = res->apr_dbd->defaultBufferSize;
+ if ( IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE )
+ maxsize = APR_BUCKET_BUFF_SIZE;
+
+ res->colptrs[icol] = NULL;
+ res->colstate[icol] = COL_AVAIL;
+ res->colsizes[icol] = maxsize;
+ rc = SQL_SUCCESS;
+ }
+ else {
+ res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
+ res->colsizes[icol] = maxsize;
+ if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
+ /* we are allowed to call SQLGetData if we need to */
+ rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
+ res->colptrs[icol], maxsize,
+ &(res->colinds[icol]) );
+ CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
+ stmt);
+ res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
+ }
+ else {
+ /* this driver won't allow us to call SQLGetData on bound
+ * columns - so don't bind any */
+ res->colstate[icol] = COL_AVAIL;
+ rc = SQL_SUCCESS;
+ }
+ }
+ return rc;
+}
+
+/* create and populate an apr_dbd_results_t for a select */
+static SQLRETURN odbc_create_results(apr_dbd_t * handle, SQLHANDLE hstmt,
+ apr_pool_t * pool, const int random,
+ apr_dbd_results_t ** res)
+{
+ SQLRETURN rc;
+ SQLSMALLINT ncols;
+
+ *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ (*res)->stmt = hstmt;
+ (*res)->dbc = handle->dbc;
+ (*res)->pool = pool;
+ (*res)->random = random;
+ (*res)->apr_dbd = handle;
+ rc = SQLNumResultCols(hstmt, &ncols);
+ CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
+ (*res)->ncols = ncols;
+
+ if SQL_SUCCEEDED(rc) {
+ int i;
+
+ (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
+ (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
+ (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
+ (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
+ (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
+ (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
+ (*res)->ncols = ncols;
+
+ for (i = 0 ; i < ncols ; i++)
+ odbc_set_result_column(i, (*res), hstmt);
+ }
+ return rc;
+}
+
+
+/* bind a parameter - input params only, does not support output parameters */
+static SQLRETURN odbc_bind_param(apr_pool_t * pool,
+ apr_dbd_prepared_t * statement, const int narg,
+ const SQLSMALLINT type, int *argp,
+ const void **args, const int textmode)
+{
+ SQLRETURN rc;
+ SQLSMALLINT baseType, cType;
+ void *ptr;
+ SQLUINTEGER len;
+ SQLINTEGER *indicator;
+ static SQLINTEGER nullValue = SQL_NULL_DATA;
+ static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
+
+ /* bind a NULL data value */
+ if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
+ baseType = SQL_CHAR;
+ cType = SQL_C_CHAR;
+ ptr = &nullValue;
+ len = sizeof(SQLINTEGER);
+ indicator = &nullValue;
+ (*argp)++;
+ }
+ /* bind a non-NULL data value */
+ else {
+ baseType = sqlBaseType[type];
+ cType = sqlCtype[type];
+ indicator = NULL;
+ /* LOBs */
+ if (IS_LOB(cType)) {
+ ptr = (void *) args[*argp];
+ len = (SQLUINTEGER) * (apr_size_t *) args[*argp + 1];
+ cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
+ (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
+ }
+ /* non-LOBs */
+ else {
+ switch (baseType) {
+ case SQL_CHAR:
+ case SQL_DATE:
+ case SQL_TIME:
+ case SQL_TIMESTAMP:
+ ptr = (void *) args[*argp];
+ len = (SQLUINTEGER) strlen(ptr);
+ break;
+ case SQL_TINYINT:
+ ptr = apr_palloc(pool, sizeof(unsigned char));
+ len = sizeof(unsigned char);
+ *(unsigned char *) ptr =
+ (textmode ?
+ atoi(args[*argp]) : *(unsigned char *) args[*argp]);
+ break;
+ case SQL_SMALLINT:
+ ptr = apr_palloc(pool, sizeof(short));
+ len = sizeof(short);
+ *(short *) ptr =
+ (textmode ? atoi(args[*argp]) : *(short *) args[*argp]);
+ break;
+ case SQL_INTEGER:
+ ptr = apr_palloc(pool, sizeof(int));
+ len = sizeof(int);
+ *(long *) ptr =
+ (textmode ? atol(args[*argp]) : *(long *) args[*argp]);
+ break;
+ case SQL_FLOAT:
+ ptr = apr_palloc(pool, sizeof(float));
+ len = sizeof(float);
+ *(float *) ptr =
+ (textmode ?
+ (float) atof(args[*argp]) : *(float *) args[*argp]);
+ break;
+ case SQL_DOUBLE:
+ ptr = apr_palloc(pool, sizeof(double));
+ len = sizeof(double);
+ *(double *) ptr =
+ (textmode ? atof(args[*argp]) : *(double *)
+ args[*argp]);
+ break;
+ case SQL_BIGINT:
+ ptr = apr_palloc(pool, sizeof(apr_int64_t));
+ len = sizeof(apr_int64_t);
+ *(apr_int64_t *) ptr =
+ (textmode ?
+ apr_atoi64(args[*argp]) : *(apr_int64_t *) args[*argp]);
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ (*argp)++; /* non LOBs consume one argument */
+ }
+ }
+ rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
+ baseType, len, 0, ptr, len, indicator);
+ CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ return rc;
+}
+
+/* LOB / Bucket Brigade functions */
+
+
+
+/* bucket type specific destroy */
+static void odbc_lob_bucket_destroy(void *data)
+{
+ odbc_bucket *bd = data;
+
+ if (apr_bucket_shared_destroy(bd))
+ apr_bucket_free(bd);
+}
+
+/* set aside a bucket if possible */
+static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
+{
+ odbc_bucket *bd = (odbc_bucket *) e->data;
+
+ /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
+ if (apr_pool_is_ancestor(bd->row->pool, pool))
+ return APR_SUCCESS;
+
+ return apr_bucket_setaside_notimpl(e, pool);
+}
+
+/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
+static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ SQLRETURN rc;
+ SQLINTEGER len_indicator;
+ SQLSMALLINT type;
+ odbc_bucket *bd = (odbc_bucket *) e->data;
+ apr_bucket *nxt;
+ void *buf;
+ int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
+ int eos;
+
+ /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
+ type = bd->row->res->coltypes[bd->col];
+ type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
+
+ /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
+ * but they may be much bigger per the BUFSIZE parameter.
+ */
+ if (bufsize < APR_BUCKET_BUFF_SIZE)
+ bufsize = APR_BUCKET_BUFF_SIZE;
+
+ buf = apr_bucket_alloc(bufsize, e->list);
+ *str = NULL;
+ *len = 0;
+
+ rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
+ type, buf, bufsize,
+ &len_indicator);
+
+ CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
+ SQL_HANDLE_STMT, bd->row->res->stmt);
+
+ if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
+ len_indicator = 0;
+
+ if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
+
+ if (rc = SQL_SUCCESS_WITH_INFO
+ && ( len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize) ) {
+ /* not the last read = a full buffer. CLOBs have a null terminator */
+ *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
+
+ eos = 0;
+ }
+ else {
+ /* the last read - len_indicator is supposed to be the length,
+ * but some driver get this wrong and return the total length.
+ * We try to handle both interpretations.
+ */
+ *len = (len_indicator > bufsize
+ && len_indicator >= (SQLINTEGER) e->start)
+ ? (len_indicator - (SQLINTEGER) e->start) : len_indicator;
+
+ eos = 1;
+ }
+
+ if (!eos) {
+ /* Create a new LOB bucket to append and append it */
+ nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
+ APR_BUCKET_INIT(nxt);
+ nxt->length = -1;
+ nxt->data = e->data;
+ nxt->type = &odbc_bucket_type;
+ nxt->free = apr_bucket_free;
+ nxt->list = e->list;
+ nxt->start = e->start + *len;
+ APR_BUCKET_INSERT_AFTER(e, nxt);
+ }
+ else {
+ odbc_lob_bucket_destroy(e->data);
+ }
+ /* make current bucket into a heap bucket */
+ apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
+ *str = buf;
+
+ /* No data is success in this context */
+ rc = SQL_SUCCESS;
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/* Create a bucket brigade on the row pool for a LOB column */
+static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
+ SQLSMALLINT type, apr_bucket_brigade *bb)
+{
+ apr_bucket_alloc_t *list = bb->bucket_alloc;
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+ odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
+ apr_bucket *eos = apr_bucket_eos_create(list);
+
+
+ bd->row = row;
+ bd->col = col;
+ bd->type = type;
+
+
+ APR_BUCKET_INIT(b);
+ b->type = &odbc_bucket_type;
+ b->free = apr_bucket_free;
+ b->list = list;
+ /* LOB lengths are unknown in ODBC */
+ b = apr_bucket_shared_make(b, bd, 0, -1);
+
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ APR_BRIGADE_INSERT_TAIL(bb, eos);
+
+ return APR_SUCCESS;
+}
+
+/* returns a data pointer for a column, returns NULL for NULL value,
+ * return -1 if data not available */
+static void *odbc_get(const apr_dbd_row_t *row, const int col,
+ const SQLSMALLINT sqltype)
+{
+ SQLRETURN rc;
+ SQLINTEGER indicator;
+ int state = row->res->colstate[col];
+ int options = row->res->apr_dbd->dboptions;
+
+ switch (state) {
+ case (COL_UNAVAIL) : return (void *) -1;
+ case (COL_RETRIEVED) : return NULL;
+
+ case (COL_BOUND) :
+ case (COL_PRESENT) :
+ if (sqltype == row->res->coltypes[col]) {
+ /* same type and we already have the data */
+ row->res->colstate[col] = COL_RETRIEVED;
+ return (row->res->colinds[col] == SQL_NULL_DATA) ?
+ NULL : row->res->colptrs[col];
+ }
+ }
+
+ /* we need to get the data now */
+ if (!(options & SQL_GD_ANY_ORDER)) {
+ /* this ODBC driver requires columns to be retrieved in order,
+ * so we attempt to get every prior un-gotten non-LOB column */
+ int i;
+ for (i = 0; i < col; i++) {
+ if (row->res->colstate[i] == COL_AVAIL) {
+ if (IS_LOB(row->res->coltypes[i]))
+ row->res->colstate[i] = COL_UNAVAIL;
+ else {
+ odbc_get(row, i, row->res->coltypes[i]);
+ row->res->colstate[i] = COL_PRESENT;
+ }
+ }
+ }
+ }
+
+ if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
+ /* this driver won't let us re-get bound columns */
+ return (void *) -1;
+
+ /* a LOB might not have a buffer allocated yet - so create one */
+ if (!row->res->colptrs[col])
+ row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
+
+ rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
+ row->res->colsizes[col], &indicator);
+ CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
+ row->res->stmt);
+ if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
+ return NULL;
+
+ if (SQL_SUCCEEDED(rc)) {
+ /* whatever it was originally, it is now this sqltype */
+ row->res->coltypes[col] = sqltype;
+ /* this allows getting CLOBs in text mode by calling get_entry
+ * until it returns NULL */
+ row->res->colstate[col] =
+ (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
+ return row->res->colptrs[col];
+ }
+ else return (void *) -1;
+}
+
+/* Parse the parameter string for open */
+static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
+ int *connect, SQLCHAR **datasource,
+ SQLCHAR **user, SQLCHAR **password,
+ int *defaultBufferSize, int *nattrs,
+ int **attrs, int **attrvals)
+{
+ char *seps, *last, *name[MAX_PARAMS], *val[MAX_PARAMS];
+ int nparams=0, i, j;
+
+ *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
+ *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(int));
+ *nattrs = 0;
+ seps = DEFAULTSEPS;
+ name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
+ do {
+ if (last[strspn(last, seps)] == CSINGLEQUOTE) {
+ last += strspn(last, seps);
+ seps=SSINGLEQUOTE;
+ }
+ val[nparams] = apr_strtok(NULL, seps, &last);
+ seps = DEFAULTSEPS;
+ name[++nparams] = apr_strtok(NULL, seps, &last);
+ } while ( nparams <= MAX_PARAMS && name[nparams] != NULL
+ && val[nparams] != NULL);
+
+ for(j=i=0 ; i< nparams ; i++)
+ { if (!apr_strnatcasecmp(name[i], "CONNECT"))
+ { *datasource = apr_pstrdup(pool, val[i]);
+ *connect=1;
+ }
+ else if (!apr_strnatcasecmp(name[i], "DATASOURCE"))
+ { *datasource = apr_pstrdup(pool, val[i]);
+ *connect=0;
+ }
+ else if (!apr_strnatcasecmp(name[i], "USER"))
+ *user = apr_pstrdup(pool, val[i]);
+ else if (!apr_strnatcasecmp(name[i], "PASSWORD"))
+ *password = apr_pstrdup(pool, val[i]);
+ else if (!apr_strnatcasecmp(name[i], "BUFSIZE"))
+ *defaultBufferSize = atoi(val[i]);
+ else if (!apr_strnatcasecmp(name[i], "ACCESS"))
+ { if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
+ (*attrvals)[j] = SQL_MODE_READ_ONLY;
+ else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
+ (*attrvals)[j] = SQL_MODE_READ_WRITE;
+ else return SQL_ERROR;
+ (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
+ }
+ else if (!apr_strnatcasecmp(name[i], "CTIMEOUT"))
+ { (*attrvals)[j] = atoi(val[i]);
+ (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
+ }
+ else if (!apr_strnatcasecmp(name[i], "STIMEOUT"))
+ { (*attrvals)[j] = atoi(val[i]);
+ (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
+ }
+ else if (!apr_strnatcasecmp(name[i], "TXMODE"))
+ { if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
+ (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
+ else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
+ (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
+ else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
+ (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
+ else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
+ (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
+ else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
+ continue;
+ else return SQL_ERROR;
+ (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
+ }
+ else return SQL_ERROR;
+ }
+ *nattrs = j;
+ return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
+}
+
+/* common handling after ODBC calls - save error info (code and text) in dbc */
+static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
+ SQLSMALLINT type, SQLHANDLE h, int line)
+{
+ SQLCHAR buffer[512];
+ SQLCHAR sqlstate[128];
+ SQLINTEGER native;
+ SQLSMALLINT reslength;
+ char *res, *p, *end, *logval=NULL;
+ int i;
+ apr_status_t r;
+
+ /* set info about last error in dbc - fast return for SQL_SUCCESS */
+ if (rc == SQL_SUCCESS) {
+ char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
+ dbc->lasterrorcode = SQL_SUCCESS;
+ strcpy(dbc->lastError, successMsg);
+ strcpy(dbc->lastError+sizeof(successMsg)-1, step);
+ return;
+ }
+ switch (rc) {
+ case SQL_INVALID_HANDLE : { res = "SQL_INVALID_HANDLE"; break; }
+ case SQL_ERROR : { res = "SQL_ERROR"; break; }
+ case SQL_SUCCESS_WITH_INFO : { res = "SQL_SUCCESS_WITH_INFO"; break; }
+ case SQL_STILL_EXECUTING : { res = "SQL_STILL_EXECUTING"; break; }
+ case SQL_NEED_DATA : { res = "SQL_NEED_DATA"; break; }
+ case SQL_NO_DATA : { res = "SQL_NO_DATA"; break; }
+ default : { res = "unrecognized SQL return code"; }
+ }
+ /* these two returns are expected during normal execution */
+ if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA)
+ dbc->can_commit = 0;
+ p = dbc->lastError;
+ end = p + sizeof(dbc->lastError);
+ dbc->lasterrorcode = rc;
+ p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
+ step, res, rc, SOURCE_FILE, line-1);
+ for (i=1, rc=0 ; rc==0 ; i++) {
+ rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
+ sizeof(buffer), &reslength);
+ if (SQL_SUCCEEDED(rc) && (p < (end-280)))
+ p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
+ }
+ r = apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
+ /* if env var was set or call was init/open (no dbname) - log to stderr */
+ if (logval || !dbc->dbname ) {
+ char timestamp[APR_CTIME_LEN];
+ apr_file_t *se;
+ apr_ctime(timestamp, apr_time_now());
+ apr_file_open_stderr(&se, dbc->pool);
+ apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
+ }
+}
+
+/*
+* public functions per DBD driver API
+*/
+
+/** init: allow driver to perform once-only initialisation. **/
+static void odbc_init(apr_pool_t *pool)
+{
+ SQLRETURN rc;
+ char *step;
+ apr_version_t apuver;
+
+ apu_version(&apuver);
+ if (apuver.major != DRIVER_APU_VERSION_MAJOR
+ || apuver.minor != DRIVER_APU_VERSION_MINOR) {
+ apr_file_t *se;
+
+ apr_file_open_stderr(&se, pool);
+ apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
+ "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
+ DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
+ apuver.major, apuver.minor);
+ abort();
+ }
+
+ if (henv)
+ return;
+
+ step = "SQLAllocHandle (SQL_HANDLE_ENV)";
+ rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+ apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
+ if (SQL_SUCCEEDED(rc))
+ { step = "SQLSetEnvAttr";
+ rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3, 0);
+ }
+ else
+ { apr_dbd_t tmp_dbc;
+ SQLHANDLE err_h = henv;
+
+ tmp_dbc.pool = pool;
+ tmp_dbc.dbname = NULL;
+ CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
+ }
+}
+
+/** native_handle: return the native database handle of the underlying db **/
+static void* odbc_native_handle(apr_dbd_t *handle)
+{ return handle->dbc;
+}
+
+/** open: obtain a database connection from the server rec. **/
+
+/* It would be more efficient to allocate a single statement handle
+ here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
+ SQLPrepare, and we don't know whether random-access is
+ specified until SQLExecute so we cannot.
+*/
+
+static apr_dbd_t* odbc_open(apr_pool_t *pool, const char *params, const char **error)
+{
+ SQLRETURN rc;
+ SQLHANDLE hdbc = NULL;
+ apr_dbd_t *handle;
+ char *err_step;
+ int err_htype, i;
+ int defaultBufferSize=DEFAULT_BUFFER_SIZE;
+ SQLHANDLE err_h = NULL;
+ SQLCHAR *datasource="", *user="", *password="";
+ int nattrs=0, *attrs=NULL, *attrvals=NULL, connect=0;
+
+ err_step="SQLAllocHandle (SQL_HANDLE_DBC)";
+ err_htype = SQL_HANDLE_ENV;
+ err_h = henv;
+ rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+ if (SQL_SUCCEEDED(rc)) {
+ err_step="Invalid DBD Parameters - open";
+ err_htype = SQL_HANDLE_DBC;
+ err_h = hdbc;
+ rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
+ &password, &defaultBufferSize, &nattrs, &attrs,
+ &attrvals);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ for (i=0 ; i < nattrs && SQL_SUCCEEDED(rc); i++) {
+ err_step="SQLSetConnectAttr (from DBD Parameters)";
+ err_htype = SQL_HANDLE_DBC;
+ err_h = hdbc;
+ rc = SQLSetConnectAttr(hdbc, attrs[i], (void *) attrvals[i], 0);
+ }
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ if (connect) {
+ SQLCHAR out[1024];
+ SQLSMALLINT outlen;
+ err_step="SQLDriverConnect";
+ err_htype = SQL_HANDLE_DBC;
+ err_h = hdbc;
+ rc = SQLDriverConnect(hdbc, NULL, datasource,
+ (SQLSMALLINT) strlen(datasource),
+ out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
+ }
+ else {
+ err_step="SQLConnect";
+ err_htype = SQL_HANDLE_DBC;
+ err_h = hdbc;
+ rc = SQLConnect(hdbc, datasource, (SQLSMALLINT) strlen(datasource),
+ user, (SQLSMALLINT) strlen(user),
+ password, (SQLSMALLINT) strlen(password));
+ }
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
+ handle->dbname = apr_pstrdup(pool, datasource);
+ handle->dbc = hdbc;
+ handle->pool = pool;
+ handle->defaultBufferSize = defaultBufferSize;
+ CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
+ handle->default_transaction_mode = 0;
+ SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
+ &(handle->default_transaction_mode), sizeof(int), NULL);
+ handle->transaction_mode = handle->default_transaction_mode;
+ SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
+ sizeof(int), NULL);
+ apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
+ return handle;
+ }
+ else {
+ apr_dbd_t tmp_dbc;
+ tmp_dbc.pool = pool;
+ tmp_dbc.dbname = NULL;
+ CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
+ if (error)
+ *error = apr_pstrdup(pool, tmp_dbc.lastError);
+ if (hdbc)
+ SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
+ return NULL;
+ }
+}
+
+/** check_conn: check status of a database connection **/
+static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
+{
+ SQLUINTEGER isDead;
+ SQLRETURN rc;
+
+ rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
+ sizeof(SQLUINTEGER), NULL);
+ CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
+ SQL_HANDLE_DBC, handle->dbc);
+ /* if driver cannot check connection, say so */
+ if (rc != SQL_SUCCESS)
+ return APR_ENOTIMPL;
+
+ return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+
+/** set_dbname: select database name. May be a no-op if not supported. **/
+static int odbc_set_dbname(apr_pool_t* pool, apr_dbd_t *handle,
+ const char *name)
+{
+ if (apr_strnatcmp(name, handle->dbname)) {
+ return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
+ }
+ CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
+ handle->dbc);
+ return APR_SUCCESS; /* OK if it's the same name */
+}
+
+/** transaction: start a transaction. May be a no-op. **/
+static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+
+ if (handle->transaction_mode) {
+ rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, (void *)
+ handle->transaction_mode, 0);
+ CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
+ SQL_HANDLE_DBC, handle->dbc);
+ }
+ if SQL_SUCCEEDED(rc) {
+ /* turn off autocommit for transactions */
+ rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
+ SQL_AUTOCOMMIT_OFF, 0);
+ CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
+ SQL_HANDLE_DBC, handle->dbc);
+ }
+ if SQL_SUCCEEDED(rc) {
+ *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
+ (*trans)->dbc = handle->dbc;
+ (*trans)->apr_dbd = handle;
+ handle->can_commit = 1;
+ }
+ return APR_FROM_SQL_RESULT(rc);
+};
+
+
+/** end_transaction: end a transaction **/
+static int odbc_end_transaction(apr_dbd_transaction_t *trans)
+{
+ SQLRETURN rc;
+ int action = trans->apr_dbd->can_commit ? SQL_COMMIT : SQL_ROLLBACK;
+
+ rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, SQL_COMMIT);
+ CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
+ if SQL_SUCCEEDED(rc) {
+ rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
+ CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
+ rc, SQL_HANDLE_DBC, trans->dbc);
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** query: execute an SQL statement which doesn't return a result set **/
+static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
+{
+ SQLRETURN rc;
+ SQLHANDLE hstmt = NULL;
+ size_t len = strlen(statement);
+
+ rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
+ CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
+ handle->dbc);
+ if (!SQL_SUCCEEDED(rc))
+ return APR_FROM_SQL_RESULT(rc);
+
+ rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
+ CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
+
+ if SQL_SUCCEEDED(rc) {
+ rc = SQLRowCount(hstmt, (SQLINTEGER *) nrows);
+ CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
+ }
+
+ SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** select: execute an SQL statement which returns a result set **/
+static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_results_t **res, const char *statement,
+ int random)
+{
+ SQLRETURN rc;
+ SQLHANDLE hstmt;
+ apr_dbd_prepared_t *stmt;
+ size_t len = strlen(statement);
+
+ rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
+ CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
+ handle->dbc);
+ if (!SQL_SUCCEEDED(rc))
+ return APR_FROM_SQL_RESULT(rc);
+ /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
+ * is not a prepared statement. We want the same cleanup mechanism.
+ */
+ stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
+ stmt->apr_dbd = handle;
+ stmt->dbc = handle->dbc;
+ stmt->stmt = hstmt;
+ apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
+ if (random) {
+ rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
+ (SQLPOINTER) SQL_SCROLLABLE, 0);
+ CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
+ SQL_HANDLE_STMT, hstmt);
+ }
+ if SQL_SUCCEEDED(rc) {
+ rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
+ CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
+ }
+ if SQL_SUCCEEDED(rc) {
+ rc = odbc_create_results(handle, hstmt, pool, random, res);
+ apr_pool_cleanup_register(pool, *res,
+ odbc_close_results, apr_pool_cleanup_null);
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** num_cols: get the number of columns in a results set **/
+static int odbc_num_cols(apr_dbd_results_t *res)
+{
+ return res->ncols;
+}
+
+/** num_tuples: get the number of rows in a results set **/
+static int odbc_num_tuples(apr_dbd_results_t *res)
+{
+ SQLRETURN rc;
+ SQLINTEGER nrows;
+
+ rc = SQLRowCount(res->stmt, &nrows);
+ CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
+ return SQL_SUCCEEDED(rc) ? (int) nrows : -1;
+}
+
+/** get_row: get a row from a result set **/
+static int odbc_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
+ apr_dbd_row_t ** row, int rownum)
+{
+ SQLRETURN rc;
+ char *fetchtype;
+ int c;
+
+ *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
+ (*row)->stmt = res->stmt;
+ (*row)->dbc = res->dbc;
+ (*row)->res = res;
+ (*row)->pool = res->pool;
+
+ /* mark all the columns as needing SQLGetData unless they are bound */
+ for (c = 0; c < res->ncols; c++) {
+ if (res->colstate[c] != COL_BOUND)
+ res->colstate[c] = COL_AVAIL;
+ /* some drivers do not null-term zero-len CHAR data */
+ if (res->colptrs[c] )
+ * (char *) res->colptrs[c] = 0;
+ }
+
+ if (res->random && (rownum > 0)) {
+ fetchtype = "SQLFetchScroll";
+ rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
+ }
+ else {
+ fetchtype = "SQLFetch";
+ rc = SQLFetch(res->stmt);
+ }
+ CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
+ (*row)->stmt = res->stmt;
+ if (!SQL_SUCCEEDED(rc) && !res->random) {
+ /* early close on any error (usually SQL_NO_DATA) if fetching
+ * sequentially to release resources ASAP */
+ odbc_close_results(res);
+ return -1;
+ }
+ return SQL_SUCCEEDED(rc) ? 0 : -1;
+}
+
+/** datum_get: get a binary entry from a row **/
+static apr_status_t odbc_datum_get(const apr_dbd_row_t * row, int col,
+ apr_dbd_type_e dbdtype, void *data)
+{
+ SQLSMALLINT sqltype;
+ void *p;
+ int len = sqlSizes[dbdtype];
+
+ if (col >= row->res->ncols)
+ return APR_EGENERAL;
+
+ if (dbdtype < 0 || dbdtype >= sizeof(sqlCtype)) {
+ data = NULL; /* invalid type */
+ return APR_EGENERAL;
+ }
+ sqltype = sqlCtype[dbdtype];
+
+ /* must not memcpy a brigade, sentinals are relative to orig loc */
+ if (IS_LOB(sqltype))
+ return odbc_create_bucket(row, col, sqltype, data);
+
+ p = odbc_get(row, col, sqltype);
+ if (p == (void *) -1)
+ return APR_EGENERAL;
+
+ if (p == NULL)
+ return APR_ENOENT; /* SQL NULL value */
+
+ if (len < 0)
+ strcpy(data, p);
+ else
+ memcpy(data, p, len);
+
+ return APR_SUCCESS;
+
+}
+
+/** get_entry: get an entry from a row (string data) **/
+static const char *odbc_get_entry(const apr_dbd_row_t * row, int col)
+{
+ void *p;
+
+ if (col >= row->res->ncols)
+ return NULL;
+
+ p = odbc_get(row, col, SQL_C_CHAR);
+
+ if ((signed int) p > 0)
+ return apr_pstrdup(row->pool, p); /* row pool lifetime */
+ else
+ return p; /* NULL or invalid (-1) */
+}
+
+/** error: get current error message (if any) **/
+static const char* odbc_error(apr_dbd_t *handle, int errnum)
+{
+ return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
+}
+
+/** escape: escape a string so it is safe for use in query/select **/
+static const char* odbc_escape(apr_pool_t *pool, const char *s,
+ apr_dbd_t *handle)
+{
+ char *newstr, *src, *dst, *sq;
+ int qcount;
+
+ /* return the original if there are no single-quotes */
+ if (!(sq = strchr(s, '\'')))
+ return (char *) s;
+ /* count the single-quotes and allocate a new buffer */
+ for (qcount = 1; sq = strchr(sq + 1, '\''); )
+ qcount++;
+ newstr = apr_palloc(pool, strlen(s) + qcount + 1);
+
+ /* move chars, doubling all single-quotes */
+ src = (char *) s;
+ for (dst = newstr ; *src ; src++) {
+ if ((*dst++ = *src) == '\'')
+ *dst++ = '\'';
+ }
+ *dst = 0;
+ return newstr;
+}
+/** prepare: prepare a statement **/
+static int odbc_prepare(apr_pool_t * pool, apr_dbd_t * handle,
+ const char *query, const char *label, int nargs,
+ int nvals, apr_dbd_type_e * types,
+ apr_dbd_prepared_t ** statement)
+{
+ SQLRETURN rc;
+ size_t len = strlen(query);
+
+ *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
+ (*statement)->dbc = handle->dbc;
+ (*statement)->apr_dbd = handle;
+ (*statement)->nargs = nargs;
+ (*statement)->nvals = nvals;
+ (*statement)->types =
+ apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
+ rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
+ apr_pool_cleanup_register(pool, *statement,
+ odbc_close_pstmt, apr_pool_cleanup_null);
+ CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
+ SQL_HANDLE_DBC, handle->dbc);
+ rc = SQLPrepare((*statement)->stmt, (SQLCHAR *) query, (SQLINTEGER) len);
+ CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
+ (*statement)->stmt);
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pquery: query using a prepared statement + args **/
+static int odbc_pquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
+ apr_dbd_prepared_t * statement, const char **args)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+ int i, argp;
+
+ for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
+ rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
+ &argp, (const void **) args, TEXTMODE);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLExecute(statement->stmt);
+ CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
+ CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pvquery: query using a prepared statement + args **/
+static int odbc_pvquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
+ apr_dbd_prepared_t * statement, va_list args)
+{
+ const char **values;
+ int i;
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+ for (i = 0; i < statement->nvals; i++)
+ values[i] = va_arg(args, const char *);
+ return odbc_pquery(pool, handle, nrows, statement, values);
+}
+
+/** pselect: select using a prepared statement + args **/
+int odbc_pselect(apr_pool_t * pool, apr_dbd_t * handle,
+ apr_dbd_results_t ** res, apr_dbd_prepared_t * statement,
+ int random, const char **args)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+ int i, argp;
+
+ if (random) {
+ rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
+ (SQLPOINTER) SQL_SCROLLABLE, 0);
+ CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
+ rc, SQL_HANDLE_STMT, statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
+ rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
+ &argp, (const void **) args, TEXTMODE);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLExecute(statement->stmt);
+ CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = odbc_create_results(handle, statement->stmt, pool, random, res);
+ apr_pool_cleanup_register(pool, *res,
+ odbc_close_results, apr_pool_cleanup_null);
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pvselect: select using a prepared statement + args **/
+static int odbc_pvselect(apr_pool_t * pool, apr_dbd_t * handle,
+ apr_dbd_results_t ** res,
+ apr_dbd_prepared_t * statement, int random,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+ for (i = 0; i < statement->nvals; i++)
+ values[i] = va_arg(args, const char *);
+ return odbc_pselect(pool, handle, res, statement, random, values);
+}
+
+/** get_name: get a column title from a result set **/
+static const char *odbc_get_name(const apr_dbd_results_t * res, int col)
+{
+ SQLRETURN rc;
+ char buffer[MAX_COLUMN_NAME];
+ SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
+ SQLUINTEGER colsize;
+
+ if (col >= res->ncols)
+ return NULL; /* bogus column number */
+ if (res->colnames[col] != NULL)
+ return res->colnames[col]; /* we already retrieved it */
+ rc = SQLDescribeCol(res->stmt, col + 1,
+ buffer, sizeof(buffer), &colnamelength,
+ &coltype, &colsize, &coldecimal, &colnullable);
+ CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
+ SQL_HANDLE_STMT, res->stmt);
+ res->colnames[col] = apr_pstrdup(res->pool, buffer);
+ return res->colnames[col];
+}
+
+/** transaction_mode_get: get the mode of transaction **/
+static int odbc_transaction_mode_get(apr_dbd_transaction_t * trans)
+{
+ return (int) trans->apr_dbd->transaction_mode;
+}
+
+/** transaction_mode_set: set the mode of transaction **/
+static int odbc_transaction_mode_set(apr_dbd_transaction_t * trans, int mode)
+{
+ SQLRETURN rc;
+
+ int legal = (SQL_TXN_READ_UNCOMMITTED | SQL_TXN_READ_COMMITTED
+ | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE);
+
+ if ((mode & legal) != mode)
+ return APR_EGENERAL;
+
+ trans->apr_dbd->transaction_mode = mode;
+ rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_TXN_ISOLATION,
+ (void *) mode, 0);
+ CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)",
+ rc, SQL_HANDLE_DBC, trans->dbc);
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pbquery: query using a prepared statement + binary args **/
+static int odbc_pbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
+ apr_dbd_prepared_t * statement, const void **args)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+ int i, argp;
+
+ for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
+ rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
+ &argp, args, BINARYMODE);
+
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLExecute(statement->stmt);
+ CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
+ CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pbselect: select using a prepared statement + binary args **/
+static int odbc_pbselect(apr_pool_t * pool, apr_dbd_t * handle,
+ apr_dbd_results_t ** res,
+ apr_dbd_prepared_t * statement,
+ int random, const void **args)
+{
+ SQLRETURN rc = SQL_SUCCESS;
+ int i, argp;
+
+ if (random) {
+ rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
+ (SQLPOINTER) SQL_SCROLLABLE, 0);
+ CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
+ rc, SQL_HANDLE_STMT, statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
+ rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
+ &argp, args, BINARYMODE);
+ }
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = SQLExecute(statement->stmt);
+ CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
+ statement->stmt);
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ rc = odbc_create_results(handle, statement->stmt, pool, random, res);
+ apr_pool_cleanup_register(pool, *res,
+ odbc_close_results, apr_pool_cleanup_null);
+ }
+
+ return APR_FROM_SQL_RESULT(rc);
+}
+
+/** pvbquery: query using a prepared statement + binary args **/
+static int odbc_pvbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
+ apr_dbd_prepared_t * statement, va_list args)
+{
+ const char **values;
+ int i;
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+ for (i = 0; i < statement->nvals; i++)
+ values[i] = va_arg(args, const char *);
+ return odbc_pbquery(pool, handle, nrows, statement, (const void **) values);
+}
+
+/** pvbselect: select using a prepared statement + binary args **/
+static int odbc_pvbselect(apr_pool_t * pool, apr_dbd_t * handle,
+ apr_dbd_results_t ** res,
+ apr_dbd_prepared_t * statement,
+ int random, va_list args)
+{
+ const char **values;
+ int i;
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+ for (i = 0; i < statement->nvals; i++)
+ values[i] = va_arg(args, const char *);
+ return odbc_pbselect(pool, handle, res, statement, random, (const void **) values);
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
+ ODBC_DRIVER_STRING,
+ odbc_init,
+ odbc_native_handle,
+ odbc_open,
+ odbc_check_conn,
+ odbc_close,
+ odbc_set_dbname,
+ odbc_start_transaction,
+ odbc_end_transaction,
+ odbc_query,
+ odbc_select,
+ odbc_num_cols,
+ odbc_num_tuples,
+ odbc_get_row,
+ odbc_get_entry,
+ odbc_error,
+ odbc_escape,
+ odbc_prepare,
+ odbc_pvquery,
+ odbc_pvselect,
+ odbc_pquery,
+ odbc_pselect,
+ odbc_get_name,
+ odbc_transaction_mode_get,
+ odbc_transaction_mode_set,
+ "?",
+ odbc_pvbquery,
+ odbc_pvbselect,
+ odbc_pbquery,
+ odbc_pbselect,
+ odbc_datum_get
+};
+
+#endif
Added: apr/apr-util/trunk/dbd/apr_dbd_odbc.dsp
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_odbc.dsp?rev=667182&view=auto
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_odbc.dsp (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_odbc.dsp Thu Jun 12 10:54:15 2008
@@ -0,0 +1,112 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_odbc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=apr_dbd_odbc - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "apr_dbd_odbc.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "apr_dbd_odbc.mak" CFG="apr_dbd_odbc - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_odbc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_odbc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "apr_dbd_odbc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APR_DBD_ODBC_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX- /Zi /O2 /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DBD_DSO_BUILD" /D APU_HAVE_ODBC=1 /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"Release/apr_dbd_odbc-1.dll"
+
+!ELSEIF "$(CFG)" == "apr_dbd_odbc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APR_DBD_ODBC_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DBD_DSO_BUILD" /D APU_HAVE_ODBC=1 /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug/apr_dbd_odbc-1.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_odbc - Win32 Release"
+# Name "apr_dbd_odbc - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\apr_dbd_odbc.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ".h"
+# End Group
+# End Target
+# End Project
Modified: apr/apr-util/trunk/include/apu.hw
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/include/apu.hw?rev=667182&r1=667181&r2=667182&view=diff
==============================================================================
--- apr/apr-util/trunk/include/apu.hw (original)
+++ apr/apr-util/trunk/include/apu.hw Thu Jun 12 10:54:15 2008
@@ -116,6 +116,8 @@
#define APU_HAVE_ORACLE 0
#define APU_HAVE_FREETDS 0
#endif
+/* Windows always has ODBC */
+#define APU_HAVE_ODBC 1
#define APU_HAVE_APR_ICONV 1
#define APU_HAVE_ICONV 0
Added: apr/apr-util/trunk/include/private/apr_dbd_odbc_v2.h
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/include/private/apr_dbd_odbc_v2.h?rev=667182&view=auto
==============================================================================
--- apr/apr-util/trunk/include/private/apr_dbd_odbc_v2.h (added)
+++ apr/apr-util/trunk/include/private/apr_dbd_odbc_v2.h Thu Jun 12 10:54:15 2008
@@ -0,0 +1,119 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/* ONLY USED FOR ODBC Version 2 -DODBCV2
+*
+* Re-define everything to work (more-or-less) in an ODBC V2 environment
+* Random access to retrieved rows is not supported - i.e. calls to apr_dbd_select() cannot
+* have a 'random' argument of 1. apr_dbd_get_row() must always pass rownum as 0 (get next row)
+*
+*/
+
+#define SQLHANDLE SQLHENV // Presumes that ENV, DBC, and STMT handles are all the same datatype
+#define SQL_NULL_HANDLE 0
+#define SQL_HANDLE_STMT 1
+#define SQL_HANDLE_DBC 2
+#define SQL_HANDLE_ENV 3
+#define SQL_NO_DATA SQL_NO_DATA_FOUND
+
+#ifndef SQL_SUCCEEDED
+#define SQL_SUCCEEDED(rc) (((rc)&(~1))==0)
+#endif
+
+#undef SQLSetEnvAttr
+#define SQLSetEnvAttr(henv, Attribute, Value, StringLength) (0)
+
+#undef SQLAllocHandle
+#define SQLAllocHandle(type, parent, hndl) \
+( (type == SQL_HANDLE_STMT) ? SQLAllocStmt(parent, hndl) \
+ : (type == SQL_HANDLE_ENV) ? SQLAllocEnv(hndl) \
+ : SQLAllocConnect(parent, hndl) \
+)
+
+#undef SQLFreeHandle
+#define SQLFreeHandle(type, hndl) \
+( (type == SQL_HANDLE_STMT) ? SQLFreeStmt(hndl, SQL_DROP) \
+ : (type == SQL_HANDLE_ENV) ? SQLFreeEnv(hndl) \
+ : SQLFreeConnect(hndl) \
+)
+
+#undef SQLGetDiagRec
+#define SQLGetDiagRec(type, h, i, state, native, buffer, bufsize, reslen) \
+ SQLError( (type == SQL_HANDLE_ENV) ? h : NULL, \
+ (type == SQL_HANDLE_DBC) ? h : NULL, \
+ (type == SQL_HANDLE_STMT) ? h : NULL, \
+ state, native, buffer, bufsize, reslen)
+
+#undef SQLCloseCursor
+#define SQLCloseCursor(stmt) SQLFreeStmt(stmt, SQL_CLOSE)
+
+#undef SQLGetConnectAttr
+#define SQLGetConnectAttr(hdbc, fOption, ValuePtr, BufferLength, NULL) \
+ SQLGetConnectOption(hdbc, fOption, ValuePtr)
+
+#undef SQLSetConnectAttr
+#define SQLSetConnectAttr(hdbc, fOption, ValuePtr, BufferLength) \
+ SQLSetConnectOption(hdbc, fOption, (SQLUINTEGER) ValuePtr)
+
+#undef SQLSetStmtAttr
+#define SQLSetStmtAttr(hstmt, fOption, ValuePtr, BufferLength) (0); return APR_ENOTIMPL;
+
+#undef SQLEndTran
+#define SQLEndTran(hType, hdbc,type) SQLTransact(henv, hdbc, type)
+
+#undef SQLFetchScroll
+#define SQLFetchScroll(stmt, orient, rownum) (0); return APR_ENOTIMPL;
+
+#define SQL_DESC_TYPE SQL_COLUMN_TYPE
+#define SQL_DESC_CONCISE_TYPE SQL_COLUMN_TYPE
+#define SQL_DESC_DISPLAY_SIZE SQL_COLUMN_DISPLAY_SIZE
+#define SQL_DESC_OCTET_LENGTH SQL_COLUMN_LENGTH
+#define SQL_DESC_UNSIGNED SQL_COLUMN_UNSIGNED
+
+#undef SQLColAttribute
+#define SQLColAttribute(s, c, f, a, l, m, n) SQLColAttributes(s, c, f, a, l, m, n)
+
+#define SQL_ATTR_ACCESS_MODE SQL_ACCESS_MODE
+#define SQL_ATTR_AUTOCOMMIT SQL_AUTOCOMMIT
+#define SQL_ATTR_CONNECTION_TIMEOUT 113
+#define SQL_ATTR_CURRENT_CATALOG SQL_CURRENT_QUALIFIER
+#define SQL_ATTR_DISCONNECT_BEHAVIOR 114
+#define SQL_ATTR_ENLIST_IN_DTC 1207
+#define SQL_ATTR_ENLIST_IN_XA 1208
+
+#define SQL_ATTR_CONNECTION_DEAD 1209
+#define SQL_CD_TRUE 1L /* Connection is closed/dead */
+#define SQL_CD_FALSE 0L /* Connection is open/available */
+
+#define SQL_ATTR_LOGIN_TIMEOUT SQL_LOGIN_TIMEOUT
+#define SQL_ATTR_ODBC_CURSORS SQL_ODBC_CURSORS
+#define SQL_ATTR_PACKET_SIZE SQL_PACKET_SIZE
+#define SQL_ATTR_QUIET_MODE SQL_QUIET_MODE
+#define SQL_ATTR_TRACE SQL_OPT_TRACE
+#define SQL_ATTR_TRACEFILE SQL_OPT_TRACEFILE
+#define SQL_ATTR_TRANSLATE_LIB SQL_TRANSLATE_DLL
+#define SQL_ATTR_TRANSLATE_OPTION SQL_TRANSLATE_OPTION
+#define SQL_ATTR_TXN_ISOLATION SQL_TXN_ISOLATION
+
+#define SQL_ATTR_CURSOR_SCROLLABLE -1
+
+#define SQL_C_SBIGINT (SQL_BIGINT+SQL_SIGNED_OFFSET) /* SIGNED BIGINT */
+#define SQL_C_UBIGINT (SQL_BIGINT+SQL_UNSIGNED_OFFSET) /* UNSIGNED BIGINT */
+
+#define SQL_FALSE 0
+#define SQL_TRUE 1
+
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win aprutil.dsw
dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
> --- apr/apr-util/trunk/include/apu.hw (original)
> +++ apr/apr-util/trunk/include/apu.hw Thu Jun 12 10:54:15 2008
> @@ -116,6 +116,8 @@
> #define APU_HAVE_ORACLE 0
> #define APU_HAVE_FREETDS 0
> #endif
> +/* Windows always has ODBC */
> +#define APU_HAVE_ODBC 1
This didn't seem to be true; however I've added it to the .dsp's for both
the static and dynamic lib builds, so hopefully it is, now :)
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win
aprutil.dsw dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp
include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by Bojan Smojver <bo...@rexursive.com>.
On Thu, 2008-06-12 at 20:27 -0400, Tom Donovan wrote:
> I would be happy to take the /*'s out if it's confusing.
I already took them out.
> Is there a reason not to have /* in a comment? They don't nest in C (but, curiously - they're
> suppose to nest in SQL - although few dbs actually do that).
Compiler (GCC) is throwing warnings about that.
--
Bojan
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win
aprutil.dsw dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp
include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by Tom Donovan <do...@bellatlantic.net>.
Bojan Smojver wrote:
> On Thu, 2008-06-12 at 17:54 +0000, tdonovan@apache.org wrote:
>
>> + SQL_C_CHAR, /*SQL_C_TYPE_TIME, /* APR_DBD_TYPE_TIME, \%pDi */
>> + SQL_C_CHAR, /*SQL_C_TYPE_DATE, /* APR_DBD_TYPE_DATE, \%pDd */
>> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_DATETIME, \%pDa */
>> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_TIMESTAMP, \%pDs */
>> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
>
> Nested comment?
>
Actually just supposed to be a reminder that, while ODBC defines bona-fide "C" datatypes for date,
time, & timestamp - DBD currently treats them all as char for now.
I would be happy to take the /*'s out if it's confusing.
Is there a reason not to have /* in a comment? They don't nest in C (but, curiously - they're
suppose to nest in SQL - although few dbs actually do that).
-tom-
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win
aprutil.dsw dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp
include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by Bojan Smojver <bo...@rexursive.com>.
On Thu, 2008-06-12 at 17:54 +0000, tdonovan@apache.org wrote:
> + SQL_C_CHAR, /*SQL_C_TYPE_TIME, /* APR_DBD_TYPE_TIME, \%pDi */
> + SQL_C_CHAR, /*SQL_C_TYPE_DATE, /* APR_DBD_TYPE_DATE, \%pDd */
> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_DATETIME, \%pDa */
> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_TIMESTAMP, \%pDs */
> + SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, /* APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
Nested comment?
--
Bojan
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win
aprutil.dsw dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp
include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by Bojan Smojver <bo...@rexursive.com>.
On Fri, 2008-06-13 at 02:15 -0500, William A. Rowe, Jr. wrote:
> But this is a great fix (below) - care to backport to branches/1.3.x ?
Done in r667437.
--
Bojan
Re: svn commit: r667182 - in /apr/apr-util/trunk: Makefile.win aprutil.dsw
dbd/apr_dbd.c dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp include/apu.hw include/private/apr_dbd_odbc_v2.h
Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
tdonovan@apache.org wrote:
> Author: tdonovan
> Date: Thu Jun 12 10:54:15 2008
> New Revision: 667182
>
> URL: http://svn.apache.org/viewvc?rev=667182&view=rev
> Log:
> apr_dbd_odbc driver
Tom - please try not to bury bug fixes in an unrelated patch.
But this is a great fix (below) - care to backport to branches/1.3.x ?
Bill
> --- apr/apr-util/trunk/dbd/apr_dbd.c (original)
> +++ apr/apr-util/trunk/dbd/apr_dbd.c Thu Jun 12 10:54:15 2008
> @@ -97,10 +97,10 @@
> /* Top level pool scope, need process-scope lifetime */
> for (parent = pool; parent; parent = apr_pool_parent_get(pool))
> pool = parent;
> -
> +#if APU_DSO_BUILD
> /* deprecate in 2.0 - permit implicit initialization */
> apu_dso_init(pool);
> -
> +#endif
> drivers = apr_hash_make(pool);
>
> #if APR_HAS_THREADS
> @@ -146,14 +146,17 @@
> #endif
> apr_status_t rv;
>
> +#if APU_DSO_BUILD
> rv = apu_dso_mutex_lock();
> if (rv) {
> return rv;
> }
> -
> +#endif
> *driver = apr_hash_get(drivers, name, APR_HASH_KEY_STRING);
> if (*driver) {
> +#if APU_DSO_BUILD
> apu_dso_mutex_unlock();
> +#endif
> return APR_SUCCESS;
> }
>