summaryrefslogtreecommitdiff
path: root/dbd
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-08-28 17:19:40 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-08-28 17:19:40 +0100
commit9eb41448747f514394eb3163b3019b13cb7455f4 (patch)
tree4109e781880c68c682821ec90df8a892f9e39048 /dbd
downloadlibapr-util-tarball-9eb41448747f514394eb3163b3019b13cb7455f4.tar.gz
Tarball conversion
Diffstat (limited to 'dbd')
-rw-r--r--dbd/NWGNUdbdfreetds296
-rw-r--r--dbd/NWGNUdbdmysql292
-rw-r--r--dbd/NWGNUdbdpgsql301
-rw-r--r--dbd/NWGNUdbdsqli2296
-rw-r--r--dbd/NWGNUdbdsqli3298
-rw-r--r--dbd/NWGNUmakefile262
-rw-r--r--dbd/apr_dbd.c574
-rw-r--r--dbd/apr_dbd_freetds.c805
-rw-r--r--dbd/apr_dbd_freetds.dsp207
-rw-r--r--dbd/apr_dbd_mysql.c1305
-rw-r--r--dbd/apr_dbd_mysql.dsp207
-rw-r--r--dbd/apr_dbd_odbc.c1734
-rw-r--r--dbd/apr_dbd_odbc.dsp191
-rw-r--r--dbd/apr_dbd_oracle.c2220
-rw-r--r--dbd/apr_dbd_oracle.dsp207
-rw-r--r--dbd/apr_dbd_pgsql.c1315
-rw-r--r--dbd/apr_dbd_pgsql.dsp207
-rw-r--r--dbd/apr_dbd_sqlite2.c566
-rw-r--r--dbd/apr_dbd_sqlite2.dsp207
-rw-r--r--dbd/apr_dbd_sqlite3.c914
-rw-r--r--dbd/apr_dbd_sqlite3.dsp207
21 files changed, 12611 insertions, 0 deletions
diff --git a/dbd/NWGNUdbdfreetds b/dbd/NWGNUdbdfreetds
new file mode 100644
index 0000000..fcac227
--- /dev/null
+++ b/dbd/NWGNUdbdfreetds
@@ -0,0 +1,296 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#include $(APR)\build\NWGNUcustom.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+# LINK_STATIC = 1
+
+# for now defined here - should finally go into build/NWGNUenvironment.inc
+FREETDS_INC = $(FREETDSSDK)/include
+FREETDS_IMP = $(FREETDSSDK)/lib/libfreetds.imp
+FREETDS_LIB = $(FREETDSSDK)/lib/libfreetds.lib
+FREETDS_NLM = libfreetds
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include/arch/netware \
+ $(APR)/include \
+ $(APU)/include \
+ $(APU)/include/private \
+ $(APR) \
+ $(FREETDS_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_HAVE_FREETDS=1 \
+ -DAPU_DSO_MODULE_BUILD \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+ifdef LINK_STATIC
+XLFLAGS += \
+ -l $(FREETDSSDK)/lib \
+ $(EOLIST)
+endif
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = dbdfreetds
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache Portability Runtime Library $(VERSION_STR) DBD FreeTDS Driver Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = dbdfreetds
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)\$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/apr_dbd_freetds.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifeq ($(LINK_STATIC),1)
+FILES_nlm_libs += \
+ $(FREETDS_LIB) \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_modules += \
+ $(FREETDS_NLM) \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @$(APR)/aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_Ximports += \
+ @$(FREETDS_IMP) \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ apr_dbd_freetds_driver \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdmysql b/dbd/NWGNUdbdmysql
new file mode 100644
index 0000000..78e865e
--- /dev/null
+++ b/dbd/NWGNUdbdmysql
@@ -0,0 +1,292 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#include $(APR)\build\NWGNUcustom.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+# LINK_STATIC = 1
+
+# for now defined here - should finally go into build/NWGNUenvironment.inc
+MYSQL_INC = $(MYSQLSDK)/include
+MYSQL_IMP = libmysql.imp
+MYSQL_LIB = libmysqlclient_r.lib libz.lib
+MYSQL_NLM = libmysql
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include/arch/netware \
+ $(APR)/include \
+ $(APU)/include \
+ $(APU)/include/private \
+ $(APR) \
+ $(MYSQL_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_DSO_MODULE_BUILD \
+ -DAPU_HAVE_MYSQL=1 \
+ -DHAVE_MYSQL_H \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ -l $(MYSQLSDK)/lib \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = dbdmysql
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache Portability Runtime Library $(VERSION_STR) DBD MySQL Driver Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = dbdmysql
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/apr_dbd_mysql.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifeq ($(LINK_STATIC),1)
+FILES_nlm_libs += \
+ $(MYSQL_LIB) \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_modules += \
+ $(MYSQL_NLM) \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_Ximports += \
+ @$(MYSQL_IMP) \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ apr_dbd_mysql_driver \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdpgsql b/dbd/NWGNUdbdpgsql
new file mode 100644
index 0000000..32ac775
--- /dev/null
+++ b/dbd/NWGNUdbdpgsql
@@ -0,0 +1,301 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#include $(APR)\build\NWGNUcustom.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+# LINK_STATIC = 1
+
+# for now defined here - should finally go into build/NWGNUenvironment.inc
+PGSQL_INC = $(PGSQLSDK)/inc
+PGSQL_IMP = libpq.imp
+PGSQL_LIB = libpq.lib
+PGSQL_NLM = libpq
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include/arch/netware \
+ $(APR)/include \
+ $(APU)/include \
+ $(APU)/include/private \
+ $(APR) \
+ $(PGSQL_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_DSO_MODULE_BUILD \
+ -DAPU_HAVE_PGSQL=1 \
+ -DHAVE_LIBPQ_FE_H \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+ifdef LINK_STATIC
+XLFLAGS += \
+ -l $(PGSQLSDK)/lib \
+ $(EOLIST)
+else
+XLFLAGS += \
+ -l $(PGSQLSDK)/imp \
+ $(EOLIST)
+endif
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = dbdpgsql
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache Portability Runtime Library $(VERSION_STR) DBD PostgreSQL Driver Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = dbdpgsql
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/apr_dbd_pgsql.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifeq ($(LINK_STATIC),1)
+FILES_nlm_libs += \
+ $(PGSQL_LIB) \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_modules += \
+ $(PGSQL_NLM) \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_Ximports += \
+ @$(PGSQL_IMP) \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ apr_dbd_pgsql_driver \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdsqli2 b/dbd/NWGNUdbdsqli2
new file mode 100644
index 0000000..f7288be
--- /dev/null
+++ b/dbd/NWGNUdbdsqli2
@@ -0,0 +1,296 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#include $(APR)\build\NWGNUcustom.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+# LINK_STATIC = 1
+
+# for now defined here - should finally go into build/NWGNUenvironment.inc
+SQLITE2_INC = $(SQLITE2SDK)/src
+SQLITE2_IMP = $(SQLITE2SDK)/lsqlite2.imp
+SQLITE2_LIB = $(SQLITE2SDK)/lsqlite2.lib
+SQLITE2_NLM = lsqlite2
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include/arch/netware \
+ $(APR)/include \
+ $(APU)/include \
+ $(APU)/include/private \
+ $(APR) \
+ $(SQLITE2_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_DSO_MODULE_BUILD \
+ -DAPU_HAVE_SQLITE2=1 \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+ifdef LINK_STATIC
+XLFLAGS += \
+ -l $(SQLITE2SDK) \
+ $(EOLIST)
+endif
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = dbdsqli2
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache Portability Runtime Library $(VERSION_STR) DBD SQLite2 Driver Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = dbdsqli2
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)\$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/apr_dbd_sqlite2.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifeq ($(LINK_STATIC),1)
+FILES_nlm_libs += \
+ $(SQLITE2_LIB) \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_modules += \
+ $(SQLITE2_NLM) \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @$(APR)/aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_Ximports += \
+ @$(SQLITE2_IMP) \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ apr_dbd_sqlite2_driver \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdsqli3 b/dbd/NWGNUdbdsqli3
new file mode 100644
index 0000000..19a5204
--- /dev/null
+++ b/dbd/NWGNUdbdsqli3
@@ -0,0 +1,298 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#include $(APR)\build\NWGNUcustom.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+# LINK_STATIC = 1
+
+# for now defined here - should finally go into build/NWGNUenvironment.inc
+SQLITE3_INC = $(SQLITE3SDK)/src
+SQLITE3_IMP = $(SQLITE3SDK)/lsqlite3.imp
+SQLITE3_LIB = $(SQLITE3SDK)/lsqlite3.lib
+SQLITE3_NLM = lsqlite3
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include/arch/netware \
+ $(APR)/include \
+ $(APU)/include \
+ $(APU)/include/private \
+ $(APR) \
+ $(SQLITE3_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_DSO_MODULE_BUILD \
+ -DAPU_HAVE_SQLITE3=1 \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+ifdef LINK_STATIC
+XLFLAGS += \
+ -l $(SQLITE3SDK) \
+ $(EOLIST)
+endif
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = dbdsqli3
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache Portability Runtime Library $(VERSION_STR) DBD SQLite3 Driver Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = dbdsqli3
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)\$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/apr_dbd_sqlite3.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifeq ($(LINK_STATIC),1)
+FILES_nlm_libs += \
+ $(SQLITE3_LIB) \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_modules += \
+ $(SQLITE3_NLM) \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @$(APR)/aprlib.imp \
+ @libc.imp \
+ apr_dbd_mutex_lock \
+ apr_dbd_mutex_unlock \
+ $(EOLIST)
+
+ifneq ($(LINK_STATIC),1)
+FILES_nlm_Ximports += \
+ @$(SQLITE3_IMP) \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ apr_dbd_sqlite3_driver \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUmakefile b/dbd/NWGNUmakefile
new file mode 100644
index 0000000..b1e3e53
--- /dev/null
+++ b/dbd/NWGNUmakefile
@@ -0,0 +1,262 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(APR_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME =
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION =
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME =
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(EOLIST)
+
+ifeq "$(APU_HAVE_MYSQL)" "1"
+ifeq "$(wildcard apr_dbd_mysql.c)" "apr_dbd_mysql.c"
+TARGET_nlm += $(OBJDIR)/dbdmysql.nlm $(OBJDIR)/dbdmysql.nlm $(EOLIST)
+endif
+endif
+ifeq "$(APU_HAVE_PGSQL)" "1"
+TARGET_nlm += $(OBJDIR)/dbdpgsql.nlm $(OBJDIR)/dbdpgsql.nlm $(EOLIST)
+endif
+ifeq "$(APU_HAVE_SQLITE2)" "1"
+TARGET_nlm += $(OBJDIR)/dbdsqli2.nlm $(OBJDIR)/dbdsqli2.nlm $(EOLIST)
+endif
+ifeq "$(APU_HAVE_SQLITE3)" "1"
+TARGET_nlm += $(OBJDIR)/dbdsqli3.nlm $(OBJDIR)/dbdsqli3.nlm $(EOLIST)
+endif
+ifeq "$(APU_HAVE_FREETDS)" "1"
+TARGET_nlm += $(OBJDIR)/dbdfreetds.nlm $(OBJDIR)/dbdfreetds.nlm $(EOLIST)
+endif
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms $(INSTDIRS) FORCE
+ $(call COPY,$(OBJDIR)/*.nlm,$(INSTALLBASE))
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
+
diff --git a/dbd/apr_dbd.c b/dbd/apr_dbd.c
new file mode 100644
index 0000000..49553f2
--- /dev/null
+++ b/dbd/apr_dbd.c
@@ -0,0 +1,574 @@
+/* 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 <ctype.h>
+#include <stdio.h>
+
+#include "apu_config.h"
+#include "apu.h"
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_thread_mutex.h"
+#include "apr_lib.h"
+#include "apr_atomic.h"
+
+#include "apu_internal.h"
+#include "apr_dbd_internal.h"
+#include "apr_dbd.h"
+#include "apu_version.h"
+
+static apr_hash_t *drivers = NULL;
+static apr_uint32_t initialised = 0, in_init = 1;
+
+#define CLEANUP_CAST (apr_status_t (*)(void*))
+
+#if APR_HAS_THREADS
+/* deprecated, but required for existing providers. Existing and new
+ * providers should be refactored to use a provider-specific mutex so
+ * that different providers do not block one another.
+ * In APR 1.3 this is no longer used for dso module loading, and
+ * apu_dso_mutex_[un]lock is used instead.
+ * In APR 2.0 this should become entirely local to libaprutil-2.so and
+ * no longer be exported.
+ */
+static apr_thread_mutex_t* mutex = NULL;
+APU_DECLARE(apr_status_t) apr_dbd_mutex_lock()
+{
+ return apr_thread_mutex_lock(mutex);
+}
+APU_DECLARE(apr_status_t) apr_dbd_mutex_unlock()
+{
+ return apr_thread_mutex_unlock(mutex);
+}
+#else
+APU_DECLARE(apr_status_t) apr_dbd_mutex_lock() {
+ return APR_SUCCESS;
+}
+APU_DECLARE(apr_status_t) apr_dbd_mutex_unlock() {
+ return APR_SUCCESS;
+}
+#endif
+
+#if !APU_DSO_BUILD
+#define DRIVER_LOAD(name,driver,pool) \
+ { \
+ extern const apr_dbd_driver_t driver; \
+ apr_hash_set(drivers,name,APR_HASH_KEY_STRING,&driver); \
+ if (driver.init) { \
+ driver.init(pool); \
+ } \
+ }
+#endif
+
+static apr_status_t apr_dbd_term(void *ptr)
+{
+ /* set drivers to NULL so init can work again */
+ drivers = NULL;
+
+ /* Everything else we need is handled by cleanups registered
+ * when we created mutexes and loaded DSOs
+ */
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_init(apr_pool_t *pool)
+{
+ apr_status_t ret = APR_SUCCESS;
+ apr_pool_t *parent;
+
+ if (apr_atomic_inc32(&initialised)) {
+ apr_atomic_set32(&initialised, 1); /* prevent wrap-around */
+
+ while (apr_atomic_read32(&in_init)) /* wait until we get fully inited */
+ ;
+
+ return APR_SUCCESS;
+ }
+
+ /* 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
+ ret = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ /* This already registers a pool cleanup */
+#endif
+
+#if !APU_DSO_BUILD
+
+ /* Load statically-linked drivers: */
+#if APU_HAVE_MYSQL
+ DRIVER_LOAD("mysql", apr_dbd_mysql_driver, pool);
+#endif
+#if APU_HAVE_PGSQL
+ DRIVER_LOAD("pgsql", apr_dbd_pgsql_driver, pool);
+#endif
+#if APU_HAVE_SQLITE3
+ DRIVER_LOAD("sqlite3", apr_dbd_sqlite3_driver, pool);
+#endif
+#if APU_HAVE_SQLITE2
+ DRIVER_LOAD("sqlite2", apr_dbd_sqlite2_driver, pool);
+#endif
+#if APU_HAVE_ORACLE
+ DRIVER_LOAD("oracle", apr_dbd_oracle_driver, pool);
+#endif
+#if APU_HAVE_FREETDS
+ DRIVER_LOAD("freetds", apr_dbd_freetds_driver, pool);
+#endif
+#if APU_HAVE_ODBC
+ DRIVER_LOAD("odbc", apr_dbd_odbc_driver, pool);
+#endif
+#if APU_HAVE_SOME_OTHER_BACKEND
+ DRIVER_LOAD("firebird", apr_dbd_other_driver, pool);
+#endif
+#endif /* APU_DSO_BUILD */
+
+ apr_pool_cleanup_register(pool, NULL, apr_dbd_term,
+ apr_pool_cleanup_null);
+
+ apr_atomic_dec32(&in_init);
+
+ return ret;
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_get_driver(apr_pool_t *pool, const char *name,
+ const apr_dbd_driver_t **driver)
+{
+#if APU_DSO_BUILD
+ char modname[32];
+ char symname[34];
+ apr_dso_handle_sym_t symbol;
+#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;
+ }
+
+#if APU_DSO_BUILD
+ /* The driver DSO must have exactly the same lifetime as the
+ * drivers hash table; ignore the passed-in pool */
+ pool = apr_hash_pool_get(drivers);
+
+#if defined(NETWARE)
+ apr_snprintf(modname, sizeof(modname), "dbd%s.nlm", name);
+#elif defined(WIN32)
+ apr_snprintf(modname, sizeof(modname),
+ "apr_dbd_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".dll", name);
+#else
+ apr_snprintf(modname, sizeof(modname),
+ "apr_dbd_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".so", name);
+#endif
+ apr_snprintf(symname, sizeof(symname), "apr_dbd_%s_driver", name);
+ rv = apu_dso_load(NULL, &symbol, modname, symname, pool);
+ if (rv == APR_SUCCESS || rv == APR_EINIT) { /* previously loaded?!? */
+ *driver = symbol;
+ name = apr_pstrdup(pool, name);
+ apr_hash_set(drivers, name, APR_HASH_KEY_STRING, *driver);
+ rv = APR_SUCCESS;
+ if ((*driver)->init) {
+ (*driver)->init(pool);
+ }
+ }
+ apu_dso_mutex_unlock();
+
+#else /* not builtin and !APR_HAS_DSO => not implemented */
+ rv = APR_ENOTIMPL;
+#endif
+
+ return rv;
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_open_ex(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, const char *params,
+ apr_dbd_t **handle,
+ const char **error)
+{
+ apr_status_t rv;
+ *handle = (driver->open)(pool, params, error);
+ if (*handle == NULL) {
+ return APR_EGENERAL;
+ }
+ rv = apr_dbd_check_conn(driver, pool, *handle);
+ if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
+ /* XXX: rv is APR error code, but apr_dbd_error() takes int! */
+ if (error) {
+ *error = apr_dbd_error(driver, *handle, rv);
+ }
+ apr_dbd_close(driver, *handle);
+ return APR_EGENERAL;
+ }
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_open(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, const char *params,
+ apr_dbd_t **handle)
+{
+ return apr_dbd_open_ex(driver,pool,params,handle,NULL);
+}
+
+APU_DECLARE(int) apr_dbd_transaction_start(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ int ret = driver->start_transaction(pool, handle, trans);
+ if (*trans) {
+ apr_pool_cleanup_register(pool, *trans,
+ CLEANUP_CAST driver->end_transaction,
+ apr_pool_cleanup_null);
+ }
+ return ret;
+}
+
+APU_DECLARE(int) apr_dbd_transaction_end(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_transaction_t *trans)
+{
+ apr_pool_cleanup_kill(pool, trans, CLEANUP_CAST driver->end_transaction);
+ return driver->end_transaction(trans);
+}
+
+APU_DECLARE(int) apr_dbd_transaction_mode_get(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans)
+{
+ return driver->transaction_mode_get(trans);
+}
+
+APU_DECLARE(int) apr_dbd_transaction_mode_set(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans,
+ int mode)
+{
+ return driver->transaction_mode_set(trans, mode);
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_close(const apr_dbd_driver_t *driver,
+ apr_dbd_t *handle)
+{
+ return driver->close(handle);
+}
+
+APU_DECLARE(const char*) apr_dbd_name(const apr_dbd_driver_t *driver)
+{
+ return driver->name;
+}
+
+APU_DECLARE(void*) apr_dbd_native_handle(const apr_dbd_driver_t *driver,
+ apr_dbd_t *handle)
+{
+ return driver->native_handle(handle);
+}
+
+APU_DECLARE(int) apr_dbd_check_conn(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ return driver->check_conn(pool, handle);
+}
+
+APU_DECLARE(int) apr_dbd_set_dbname(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, const char *name)
+{
+ return driver->set_dbname(pool,handle,name);
+}
+
+APU_DECLARE(int) apr_dbd_query(const apr_dbd_driver_t *driver,
+ apr_dbd_t *handle,
+ int *nrows, const char *statement)
+{
+ return driver->query(handle,nrows,statement);
+}
+
+APU_DECLARE(int) apr_dbd_select(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, apr_dbd_results_t **res,
+ const char *statement, int random)
+{
+ return driver->select(pool,handle,res,statement,random);
+}
+
+APU_DECLARE(int) apr_dbd_num_cols(const apr_dbd_driver_t *driver,
+ apr_dbd_results_t *res)
+{
+ return driver->num_cols(res);
+}
+
+APU_DECLARE(int) apr_dbd_num_tuples(const apr_dbd_driver_t *driver,
+ apr_dbd_results_t *res)
+{
+ return driver->num_tuples(res);
+}
+
+APU_DECLARE(int) apr_dbd_get_row(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_results_t *res, apr_dbd_row_t **row,
+ int rownum)
+{
+ return driver->get_row(pool,res,row,rownum);
+}
+
+APU_DECLARE(const char*) apr_dbd_get_entry(const apr_dbd_driver_t *driver,
+ apr_dbd_row_t *row, int col)
+{
+ return driver->get_entry(row,col);
+}
+
+APU_DECLARE(const char*) apr_dbd_get_name(const apr_dbd_driver_t *driver,
+ apr_dbd_results_t *res, int col)
+{
+ return driver->get_name(res,col);
+}
+
+APU_DECLARE(const char*) apr_dbd_error(const apr_dbd_driver_t *driver,
+ apr_dbd_t *handle, int errnum)
+{
+ return driver->error(handle,errnum);
+}
+
+APU_DECLARE(const char*) apr_dbd_escape(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, const char *string,
+ apr_dbd_t *handle)
+{
+ return driver->escape(pool,string,handle);
+}
+
+APU_DECLARE(int) apr_dbd_prepare(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, const char *query,
+ const char *label,
+ apr_dbd_prepared_t **statement)
+{
+ size_t qlen;
+ int i, nargs = 0, nvals = 0;
+ char *p, *pq;
+ const char *q;
+ apr_dbd_type_e *t;
+
+ if (!driver->pformat) {
+ return APR_ENOTIMPL;
+ }
+
+ /* find the number of parameters in the query */
+ for (q = query; *q; q++) {
+ if (q[0] == '%') {
+ if (apr_isalpha(q[1])) {
+ nargs++;
+ } else if (q[1] == '%') {
+ q++;
+ }
+ }
+ }
+ nvals = nargs;
+
+ qlen = strlen(query) +
+ nargs * (strlen(driver->pformat) + sizeof(nargs) * 3 + 2) + 1;
+ pq = apr_palloc(pool, qlen);
+ t = apr_pcalloc(pool, sizeof(*t) * nargs);
+
+ for (p = pq, q = query, i = 0; *q; q++) {
+ if (q[0] == '%') {
+ if (apr_isalpha(q[1])) {
+ switch (q[1]) {
+ case 'd': t[i] = APR_DBD_TYPE_INT; break;
+ case 'u': t[i] = APR_DBD_TYPE_UINT; break;
+ case 'f': t[i] = APR_DBD_TYPE_FLOAT; break;
+ case 'h':
+ switch (q[2]) {
+ case 'h':
+ switch (q[3]){
+ case 'd': t[i] = APR_DBD_TYPE_TINY; q += 2; break;
+ case 'u': t[i] = APR_DBD_TYPE_UTINY; q += 2; break;
+ }
+ break;
+ case 'd': t[i] = APR_DBD_TYPE_SHORT; q++; break;
+ case 'u': t[i] = APR_DBD_TYPE_USHORT; q++; break;
+ }
+ break;
+ case 'l':
+ switch (q[2]) {
+ case 'l':
+ switch (q[3]){
+ case 'd': t[i] = APR_DBD_TYPE_LONGLONG; q += 2; break;
+ case 'u': t[i] = APR_DBD_TYPE_ULONGLONG; q += 2; break;
+ }
+ break;
+ case 'd': t[i] = APR_DBD_TYPE_LONG; q++; break;
+ case 'u': t[i] = APR_DBD_TYPE_ULONG; q++; break;
+ case 'f': t[i] = APR_DBD_TYPE_DOUBLE; q++; break;
+ }
+ break;
+ case 'p':
+ if (q[2] == 'D') {
+ switch (q[3]) {
+ case 't': t[i] = APR_DBD_TYPE_TEXT; q += 2; break;
+ case 'i': t[i] = APR_DBD_TYPE_TIME; q += 2; break;
+ case 'd': t[i] = APR_DBD_TYPE_DATE; q += 2; break;
+ case 'a': t[i] = APR_DBD_TYPE_DATETIME; q += 2; break;
+ case 's': t[i] = APR_DBD_TYPE_TIMESTAMP; q += 2; break;
+ case 'z': t[i] = APR_DBD_TYPE_ZTIMESTAMP; q += 2; break;
+ case 'b': t[i] = APR_DBD_TYPE_BLOB; q += 2; break;
+ case 'c': t[i] = APR_DBD_TYPE_CLOB; q += 2; break;
+ case 'n': t[i] = APR_DBD_TYPE_NULL; q += 2; break;
+ }
+ }
+ break;
+ }
+ q++;
+
+ switch (t[i]) {
+ case APR_DBD_TYPE_NONE: /* by default, we expect strings */
+ t[i] = APR_DBD_TYPE_STRING;
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB: /* three (3) more values passed in */
+ nvals += 3;
+ break;
+ default:
+ break;
+ }
+
+ /* insert database specific parameter reference */
+ p += apr_snprintf(p, qlen - (p - pq), driver->pformat, ++i);
+ } else if (q[1] == '%') { /* reduce %% to % */
+ *p++ = *q++;
+ } else {
+ *p++ = *q;
+ }
+ } else {
+ *p++ = *q;
+ }
+ }
+ *p = '\0';
+
+ return driver->prepare(pool,handle,pq,label,nargs,nvals,t,statement);
+}
+
+APU_DECLARE(int) apr_dbd_pquery(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, int *nrows,
+ apr_dbd_prepared_t *statement,
+ int nargs, const char **args)
+{
+ return driver->pquery(pool,handle,nrows,statement,args);
+}
+
+APU_DECLARE(int) apr_dbd_pselect(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ int nargs, const char **args)
+{
+ return driver->pselect(pool,handle,res,statement,random,args);
+}
+
+APU_DECLARE_NONSTD(int) apr_dbd_pvquery(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, int *nrows,
+ apr_dbd_prepared_t *statement, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, statement);
+ ret = driver->pvquery(pool,handle,nrows,statement,args);
+ va_end(args);
+ return ret;
+}
+
+APU_DECLARE_NONSTD(int) apr_dbd_pvselect(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement,
+ int random, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, random);
+ ret = driver->pvselect(pool,handle,res,statement,random,args);
+ va_end(args);
+ return ret;
+}
+
+APU_DECLARE(int) apr_dbd_pbquery(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, int *nrows,
+ apr_dbd_prepared_t *statement,
+ const void **args)
+{
+ return driver->pbquery(pool,handle,nrows,statement,args);
+}
+
+APU_DECLARE(int) apr_dbd_pbselect(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ const void **args)
+{
+ return driver->pbselect(pool,handle,res,statement,random,args);
+}
+
+APU_DECLARE_NONSTD(int) apr_dbd_pvbquery(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool,
+ apr_dbd_t *handle, int *nrows,
+ apr_dbd_prepared_t *statement, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, statement);
+ ret = driver->pvbquery(pool,handle,nrows,statement,args);
+ va_end(args);
+ return ret;
+}
+
+APU_DECLARE_NONSTD(int) apr_dbd_pvbselect(const apr_dbd_driver_t *driver,
+ apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement,
+ int random, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, random);
+ ret = driver->pvbselect(pool,handle,res,statement,random,args);
+ va_end(args);
+ return ret;
+}
+
+APU_DECLARE(apr_status_t) apr_dbd_datum_get(const apr_dbd_driver_t *driver,
+ apr_dbd_row_t *row, int col,
+ apr_dbd_type_e type, void *data)
+{
+ return driver->datum_get(row,col,type,data);
+}
diff --git a/dbd/apr_dbd_freetds.c b/dbd/apr_dbd_freetds.c
new file mode 100644
index 0000000..9a0cae7
--- /dev/null
+++ b/dbd/apr_dbd_freetds.c
@@ -0,0 +1,805 @@
+/* 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"
+#include "apu_config.h"
+
+/* COMPILE_STUBS: compile stubs for unimplemented functions.
+ *
+ * This is required to compile in /trunk/, but can be
+ * undefined to compile a driver for httpd-2.2 and other
+ * APR-1.2 applications
+ */
+#define COMPILE_STUBS
+
+#if APU_HAVE_FREETDS
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "apr_strings.h"
+#include "apr_lib.h"
+
+#include "apr_pools.h"
+#include "apr_dbd_internal.h"
+
+#ifdef HAVE_FREETDS_SYBDB_H
+#include <freetds/sybdb.h>
+#endif
+#ifdef HAVE_SYBDB_H
+#include <sybdb.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+/* This probably needs to change for different applications */
+#define MAX_COL_LEN 256
+
+typedef struct freetds_cell_t {
+ int type;
+ DBINT len;
+ BYTE *data;
+} freetds_cell_t;
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ DBPROCESS *proc;
+ apr_dbd_transaction_t *trans;
+ apr_pool_t *pool;
+ const char *params;
+ RETCODE err;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ size_t ntuples;
+ size_t sz;
+ apr_pool_t *pool;
+ DBPROCESS *proc;
+};
+
+struct apr_dbd_row_t {
+ apr_dbd_results_t *res;
+ BYTE buf[MAX_COL_LEN];
+};
+
+struct apr_dbd_prepared_t {
+ int nargs;
+ regex_t **taint;
+ int *sz;
+ char *fmt;
+};
+
+#define dbd_freetds_is_success(x) (x == SUCCEED)
+
+static int labelnum = 0; /* FIXME */
+static regex_t dbd_freetds_find_arg;
+
+/* execute a query that doesn't return a result set, mop up,
+ * and return and APR-flavoured status
+ */
+static RETCODE freetds_exec(DBPROCESS *proc, const char *query,
+ int want_results, int *nrows)
+{
+ /* TBD */
+ RETCODE rv = dbcmd(proc, query);
+ if (rv != SUCCEED) {
+ return rv;
+ }
+ rv = dbsqlexec(proc);
+ if (rv != SUCCEED) {
+ return rv;
+ }
+ if (!want_results) {
+ while (dbresults(proc) != NO_MORE_RESULTS) {
+ ++*nrows;
+ }
+ }
+ return SUCCEED;
+}
+static apr_status_t clear_result(void *data)
+{
+ /* clear cursor */
+ return (dbcanquery((DBPROCESS*)data) == SUCCEED)
+ ? APR_SUCCESS
+ : APR_EGENERAL;
+}
+
+static int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ const char *query, int seek)
+{
+ apr_dbd_results_t *res;
+ if (sql->trans && (sql->trans->errnum != SUCCEED)) {
+ return 1;
+ }
+ /* the core of this is
+ * dbcmd(proc, query);
+ * dbsqlexec(proc);
+ * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
+ * do things
+ * }
+ *
+ * Ignore seek
+ */
+
+ sql->err = freetds_exec(sql->proc, query, 1, NULL);
+ if (!dbd_freetds_is_success(sql->err)) {
+ if (sql->trans) {
+ sql->trans->errnum = sql->err;
+ }
+ return 1;
+ }
+
+ sql->err = dbresults(sql->proc);
+ if (sql->err != SUCCEED) {
+ if (sql->trans) {
+ sql->trans->errnum = sql->err;
+ }
+ return 1;
+ }
+
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ res = *results;
+ res->proc = sql->proc;
+ res->random = seek;
+ res->pool = pool;
+ res->ntuples = dblastrow(sql->proc);
+ res->sz = dbnumcols(sql->proc);
+ apr_pool_cleanup_register(pool, sql->proc, clear_result,
+ apr_pool_cleanup_null);
+
+#if 0
+ /* Now we have a result set. We need to bind to its vars */
+ res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*));
+ for (i=1; i <= res->sz; ++i) {
+ freetds_cell_t *cell = &res->vars[i-1];
+ cell->type = dbcoltype(sql->proc, i);
+ cell->len = dbcollen(sql->proc, i);
+ cell->data = apr_palloc(pool, cell->len);
+ sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data);
+ if (sql->err != SUCCEED) {
+ fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len);
+ }
+ if ((sql->err != SUCCEED) && (sql->trans != NULL)) {
+ sql->trans->errnum = sql->err;
+ }
+ }
+#endif
+ return (sql->err == SUCCEED) ? 0 : 1;
+}
+static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
+{
+ regmatch_t match[1];
+ if (rx == NULL) {
+ /* no untaint expression */
+ return val;
+ }
+ if (regexec(rx, val, 1, match, 0) == 0) {
+ return apr_pstrndup(pool, val+match[0].rm_so,
+ match[0].rm_eo - match[0].rm_so);
+ }
+ return "";
+}
+static const char *dbd_statement(apr_pool_t *pool,
+ apr_dbd_prepared_t *stmt,
+ int nargs, const char **args)
+{
+ int i;
+ int len;
+ const char *var;
+ char *ret;
+ const char *p_in;
+ char *p_out;
+ char *q;
+
+ /* compute upper bound on length (since untaint shrinks) */
+ len = strlen(stmt->fmt) +1;
+ for (i=0; i<nargs; ++i) {
+ len += strlen(args[i]) - 2;
+ }
+ i = 0;
+ p_in = stmt->fmt;
+ p_out = ret = apr_palloc(pool, len);
+ /* FIXME silly bug - this'll catch %%s */
+ while (q = strstr(p_in, "%s"), q != NULL) {
+ len = q-p_in;
+ strncpy(p_out, p_in, len);
+ p_in += len;
+ p_out += len;
+ var = dbd_untaint(pool, stmt->taint[i], args[i]);
+ len = strlen(var);
+ strncpy(p_out, var, len);
+ p_in += 2;
+ p_out += len;
+ ++i;
+ }
+ strcpy(p_out, p_in);
+ return ret;
+}
+static int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const char **values)
+{
+ const char *query = dbd_statement(pool, statement,
+ statement->nargs, values);
+ return dbd_freetds_select(pool, sql, results, query, seek);
+}
+static int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_freetds_pselect(pool, sql, results, statement, seek, values);
+}
+static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query);
+static int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values)
+{
+ const char *query = dbd_statement(pool, statement,
+ statement->nargs, values);
+ return dbd_freetds_query(sql, nrows, query);
+}
+static int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+ return dbd_freetds_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **rowp, int rownum)
+{
+ RETCODE rv = 0;
+ apr_dbd_row_t *row = *rowp;
+ int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
+
+ if (row == NULL) {
+ row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ *rowp = row;
+ row->res = res;
+ }
+ /*
+ else {
+ if ( sequential ) {
+ ++row->n;
+ }
+ else {
+ row->n = rownum;
+ }
+ }
+ */
+ if (sequential) {
+ rv = dbnextrow(res->proc);
+ }
+ else {
+ rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
+ }
+ switch (rv) {
+ case SUCCEED: return 0;
+ case REG_ROW: return 0;
+ case NO_MORE_ROWS:
+ apr_pool_cleanup_run(pool, res->proc, clear_result);
+ *rowp = NULL;
+ return -1;
+ case FAIL: return 1;
+ case BUF_FULL: return 2; /* FIXME */
+ default: return 3;
+ }
+
+ return 0;
+}
+
+static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
+{
+ /* FIXME: support different data types */
+ /* this fails - bind gets some vars but not others
+ return (const char*)row->res->vars[n].data;
+ */
+ DBPROCESS* proc = row->res->proc;
+ BYTE *ptr = dbdata(proc, n+1);
+ int t = dbcoltype(proc, n+1);
+ int l = dbcollen(proc, n+1);
+ if (dbwillconvert(t, SYBCHAR)) {
+ dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1);
+ return (const char*)row->buf;
+ }
+ return (char*)ptr;
+}
+
+static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
+{
+ /* XXX this doesn't seem to exist in the API ??? */
+ return apr_psprintf(sql->pool, "Error %d", sql->err);
+}
+
+static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+ *nrows = 0;
+ sql->err = freetds_exec(sql->proc, query, 0, nrows);
+
+ if (sql->err != SUCCEED) {
+ if (sql->trans) {
+ sql->trans->errnum = sql->err;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ return arg;
+}
+
+static apr_status_t freetds_regfree(void *rx)
+{
+ regfree((regex_t*)rx);
+ return APR_SUCCESS;
+}
+static int recurse_args(apr_pool_t *pool, int n, const char *query,
+ apr_dbd_prepared_t *stmt, int offs)
+{
+
+ /* we only support %s arguments for now */
+ int ret;
+ char arg[256];
+ regmatch_t matches[3];
+ if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
+ /* No more args */
+ stmt->nargs = n;
+ stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
+ stmt->sz = apr_palloc(pool, n*sizeof(int));
+ ret = 0;
+ }
+ else {
+ int i;
+ int sz = 0;
+ int len = matches[1].rm_eo - matches[1].rm_so - 2;
+ if (len > 255) {
+ return 9999;
+ }
+
+ ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
+ stmt, offs+matches[0].rm_eo);
+
+ memmove(stmt->fmt + offs + matches[1].rm_so,
+ stmt->fmt + offs + matches[0].rm_eo-1,
+ strlen(stmt->fmt+offs+matches[0].rm_eo)+2);
+
+ /* compile untaint to a regex if found */
+ if (matches[1].rm_so == -1) {
+ stmt->taint[n] = NULL;
+ }
+ else {
+ strncpy(arg, query+matches[1].rm_so+1,
+ matches[1].rm_eo - matches[1].rm_so - 2);
+ arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0';
+ stmt->taint[n] = apr_palloc(pool, sizeof(regex_t));
+ if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) {
+ ++ret;
+ }
+ else {
+ apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
+ apr_pool_cleanup_null);
+ }
+ }
+
+ /* record length if specified */
+ for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) {
+ sz = 10*sz + (query[i]-'\0');
+ }
+ }
+ return ret;
+}
+
+static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ apr_dbd_prepared_t *stmt;
+
+ if (label == NULL) {
+ label = apr_psprintf(pool, "%d", labelnum++);
+ }
+
+ if (!*statement) {
+ *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ stmt = *statement;
+
+#if 0
+ /* count args */
+ stmt->fmt = apr_pstrdup(pool, query);
+ stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
+
+ /* overestimate by a byte or two to simplify */
+ len = strlen("CREATE PROC apr.")
+ + strlen(label)
+ + stmt->nargs * strlen(" @arg1 varchar(len1),")
+ + strlen(" AS begin ")
+ + strlen(stmt->fmt)
+ + strlen(" end "); /* extra byte for terminator */
+
+ pquery = apr_pcalloc(pool, len);
+ sprintf(pquery, "CREATE PROC apr.%s", label);
+ for (i=0; i<stmt->nargs; ++i) {
+ sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]);
+ if (i < stmt->nargs-1) {
+ pquery[strlen(pquery)] = ',';
+ }
+ }
+ strcat(pquery, " AS BEGIN ");
+ strcat(pquery, stmt->fmt);
+ strcat(pquery, " END");
+
+ return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
+#else
+ stmt->fmt = apr_pstrdup(pool, query);
+ return recurse_args(pool, 0, query, stmt, 0);
+#endif
+
+}
+
+static int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ int dummy;
+
+ /* XXX handle recursive transactions here */
+
+ handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy);
+
+ if (dbd_freetds_is_success(handle->err)) {
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ }
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int dummy;
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum) {
+ trans->errnum = 0;
+ trans->handle->err = freetds_exec(trans->handle->proc,
+ "ROLLBACK", 0, &dummy);
+ }
+ else {
+ trans->handle->err = freetds_exec(trans->handle->proc,
+ "COMMIT", 0, &dummy);
+ }
+ trans->handle->trans = NULL;
+ }
+ return (trans->handle->err == SUCCEED) ? 0 : 1;
+}
+
+static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ char *server = NULL;
+ DBPROCESS *process;
+ LOGINREC *login;
+ static const char *delims = " \r\n\t;|,";
+ char *ptr;
+ char *key;
+ char *value;
+ int vlen;
+ int klen;
+ char *buf;
+ char *databaseName = NULL;
+
+ /* FIXME - this uses malloc */
+ /* FIXME - pass error message back to the caller in case of failure */
+ login = dblogin();
+ if (login == NULL) {
+ return NULL;
+ }
+ /* now set login properties */
+ for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+ /* don't dereference memory that may not belong to us */
+ if (ptr == params) {
+ ++ptr;
+ continue;
+ }
+ for (key = ptr-1; apr_isspace(*key); --key);
+ klen = 0;
+ while (apr_isalpha(*key)) {
+ --key;
+ ++klen;
+ }
+ ++key;
+ for (value = ptr+1; apr_isspace(*value); ++value);
+
+ vlen = strcspn(value, delims);
+ buf = apr_pstrndup(pool, value, vlen); /* NULL-terminated copy */
+
+ if (!strncasecmp(key, "username", klen)) {
+ DBSETLUSER(login, buf);
+ }
+ else if (!strncasecmp(key, "password", klen)) {
+ DBSETLPWD(login, buf);
+ }
+ else if (!strncasecmp(key, "appname", klen)) {
+ DBSETLAPP(login, buf);
+ }
+ else if (!strncasecmp(key, "dbname", klen)) {
+ databaseName = buf;
+ }
+ else if (!strncasecmp(key, "host", klen)) {
+ DBSETLHOST(login, buf);
+ }
+ else if (!strncasecmp(key, "charset", klen)) {
+ DBSETLCHARSET(login, buf);
+ }
+ else if (!strncasecmp(key, "lang", klen)) {
+ DBSETLNATLANG(login, buf);
+ }
+ else if (!strncasecmp(key, "server", klen)) {
+ server = buf;
+ }
+ else {
+ /* unknown param */
+ }
+ ptr = value+vlen;
+ }
+
+ process = dbopen(login, server);
+
+ if (process != NULL && databaseName != NULL)
+ {
+ dbuse(process, databaseName);
+ }
+
+ dbloginfree(login);
+ if (process == NULL) {
+ return NULL;
+ }
+
+ return process;
+}
+static apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ apr_dbd_t *sql;
+ /* FIXME - pass error message back to the caller in case of failure */
+ DBPROCESS *process = freetds_open(pool, params, error);
+ if (process == NULL) {
+ return NULL;
+ }
+ sql = apr_palloc (pool, sizeof (apr_dbd_t));
+ sql->pool = pool;
+ sql->proc = process;
+ sql->params = params;
+ return sql;
+}
+
+static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
+{
+ dbclose(handle->proc);
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_freetds_check_conn(apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ if (dbdead(handle->proc)) {
+ /* try again */
+ dbclose(handle->proc);
+ handle->proc = freetds_open(handle->pool, handle->params, NULL);
+ if (!handle->proc || dbdead(handle->proc)) {
+ return APR_EGENERAL;
+ }
+ }
+ /* clear it, in case this is called in error handling */
+ dbcancel(handle->proc);
+ return APR_SUCCESS;
+}
+
+static int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+ const char *name)
+{
+ /* ouch, it's declared int. But we can use APR 0/nonzero */
+ return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+static void *dbd_freetds_native(apr_dbd_t *handle)
+{
+ return handle->proc;
+}
+
+static int dbd_freetds_num_cols(apr_dbd_results_t* res)
+{
+ return res->sz;
+}
+
+static int dbd_freetds_num_tuples(apr_dbd_results_t* res)
+{
+ if (res->random) {
+ return res->ntuples;
+ }
+ else {
+ return -1;
+ }
+}
+
+static apr_status_t freetds_term(void *dummy)
+{
+ dbexit();
+ regfree(&dbd_freetds_find_arg);
+ return APR_SUCCESS;
+}
+static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
+ int oserr, char *dberrstr, char *oserrstr)
+{
+ return INT_CANCEL; /* never exit */
+}
+static void dbd_freetds_init(apr_pool_t *pool)
+{
+ int rv = regcomp(&dbd_freetds_find_arg,
+ "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
+ if (rv != 0) {
+ char errmsg[256];
+ regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
+ fprintf(stderr, "regcomp failed: %s\n", errmsg);
+ }
+ dbinit();
+ dberrhandle(freetds_err_handler);
+ apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
+}
+
+#ifdef COMPILE_STUBS
+/* get_name is the only one of these that is implemented */
+static const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n)
+{
+ return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */
+}
+
+/* These are stubs: transaction modes not implemented here */
+#define DBD_NOTIMPL APR_ENOTIMPL;
+static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
+}
+
+static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (trans) {
+ trans->mode = mode & TXN_MODE_BITS;
+ return trans->mode;
+ }
+ return APR_DBD_TRANSACTION_COMMIT;
+}
+static int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ return DBD_NOTIMPL;
+}
+static int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ return DBD_NOTIMPL;
+}
+
+static int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, va_list args)
+{
+ return DBD_NOTIMPL;
+}
+static int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const void **values)
+{
+ return DBD_NOTIMPL;
+}
+static apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ return APR_ENOTIMPL;
+}
+#endif
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
+ "freetds",
+ dbd_freetds_init,
+ dbd_freetds_native,
+ dbd_freetds_open,
+ dbd_freetds_check_conn,
+ dbd_freetds_close,
+ dbd_freetds_select_db,
+ dbd_freetds_start_transaction,
+ dbd_freetds_end_transaction,
+ dbd_freetds_query,
+ dbd_freetds_select,
+ dbd_freetds_num_cols,
+ dbd_freetds_num_tuples,
+ dbd_freetds_get_row,
+ dbd_freetds_get_entry,
+ dbd_freetds_error,
+ dbd_freetds_escape,
+ dbd_freetds_prepare,
+ dbd_freetds_pvquery,
+ dbd_freetds_pvselect,
+ dbd_freetds_pquery,
+ dbd_freetds_pselect,
+ /* this is only implemented to support httpd/2.2 standard usage,
+ * as in the original DBD implementation. Everything else is NOTIMPL.
+ */
+#ifdef COMPILE_STUBS
+ dbd_freetds_get_name,
+ dbd_freetds_transaction_mode_get,
+ dbd_freetds_transaction_mode_set,
+ "",
+ dbd_freetds_pvbquery,
+ dbd_freetds_pvbselect,
+ dbd_freetds_pbquery,
+ dbd_freetds_pbselect,
+ dbd_freetds_datum_get
+#endif
+};
+#endif
diff --git a/dbd/apr_dbd_freetds.dsp b/dbd/apr_dbd_freetds.dsp
new file mode 100644
index 0000000..29f3852
--- /dev/null
+++ b/dbd/apr_dbd_freetds.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_freetds" - 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_freetds - Win32 Release
+!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_freetds.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_freetds.mak" CFG="apr_dbd_freetds - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_freetds - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_freetds - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_freetds - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_freetds - x64 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_freetds - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_FREETDS=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_freetds_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_freetds-1.res" /d DLL_NAME="apr_dbd_freetds" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_freetds-1.dll" /pdb:"Release\apr_dbd_freetds-1.pdb" /implib:"Release\apr_dbd_freetds-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_freetds-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_freetds - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_FREETDS=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_freetds_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_freetds-1.res" /d DLL_NAME="apr_dbd_freetds" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_freetds-1.dll" /pdb:"Debug\apr_dbd_freetds-1.pdb" /implib:"Debug\apr_dbd_freetds-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_freetds-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_freetds - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_FREETDS=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_freetds_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_freetds-1.res" /d DLL_NAME="apr_dbd_freetds" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_freetds-1.dll" /pdb:"x64\Release\apr_dbd_freetds-1.pdb" /implib:"x64\Release\apr_dbd_freetds-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_freetds-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_freetds - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_FREETDS=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_freetds_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_freetds-1.res" /d DLL_NAME="apr_dbd_freetds" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libTDS.lib /nologo /base:"0x6EF60000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_freetds-1.dll" /pdb:"x64\Debug\apr_dbd_freetds-1.pdb" /implib:"x64\Debug\apr_dbd_freetds-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_freetds-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_freetds - Win32 Release"
+# Name "apr_dbd_freetds - Win32 Debug"
+# Name "apr_dbd_freetds - x64 Release"
+# Name "apr_dbd_freetds - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_freetds.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project
diff --git a/dbd/apr_dbd_mysql.c b/dbd/apr_dbd_mysql.c
new file mode 100644
index 0000000..482b82a
--- /dev/null
+++ b/dbd/apr_dbd_mysql.c
@@ -0,0 +1,1305 @@
+/* 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_MYSQL
+
+#include "apu_version.h"
+#include "apu_config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#if defined(HAVE_MYSQL_MYSQL_H)
+#if defined(HAVE_MYSQL_MY_GLOBAL_H)
+#include <mysql/my_global.h>
+#if defined(HAVE_MYSQL_MY_SYS_H)
+#include <mysql/my_sys.h>
+#endif
+#endif
+#include <mysql/mysql.h>
+#include <mysql/errmsg.h>
+#else /* !defined(HAVE_MYSQL_MYSQL_H) */
+#if defined(HAVE_MY_GLOBAL_H)
+#include <my_global.h>
+#if defined(HAVE_MY_SYS_H)
+#include <my_sys.h>
+#endif
+#endif
+#include <mysql.h>
+#include <errmsg.h>
+#endif
+
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+/* default maximum field size 1 MB */
+#define FIELDSIZE 1048575
+
+struct apr_dbd_prepared_t {
+ MYSQL_STMT* stmt;
+ int nargs;
+ int nvals;
+ apr_dbd_type_e *types;
+};
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ MYSQL* conn ;
+ apr_dbd_transaction_t* trans ;
+ unsigned long fldsz;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ MYSQL_RES *res;
+ MYSQL_STMT *statement;
+ MYSQL_BIND *bind;
+ apr_pool_t *pool;
+};
+struct apr_dbd_row_t {
+ MYSQL_ROW row;
+ apr_dbd_results_t *res;
+ unsigned long *len;
+};
+
+/* MySQL specific bucket for BLOB types */
+typedef struct apr_bucket_lob apr_bucket_lob;
+/**
+ * A bucket referring to a MySQL BLOB
+ */
+struct apr_bucket_lob {
+ /** Number of buckets using this memory */
+ apr_bucket_refcount refcount;
+ /** The row this bucket refers to */
+ const apr_dbd_row_t *row;
+ /** The column this bucket refers to */
+ int col;
+ /** The pool into which any needed structures should
+ * be created while reading from this bucket */
+ apr_pool_t *readpool;
+};
+
+static void lob_bucket_destroy(void *data);
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block);
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p);
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list);
+static int dbd_mysql_num_cols(apr_dbd_results_t *res);
+
+static const apr_bucket_type_t apr_bucket_type_lob = {
+ "LOB", 5, APR_BUCKET_DATA,
+ lob_bucket_destroy,
+ lob_bucket_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
+
+static void lob_bucket_destroy(void *data)
+{
+ apr_bucket_lob *f = data;
+
+ if (apr_bucket_shared_destroy(f)) {
+ /* no need to destroy database objects here; it will get
+ * done automatically when the pool gets cleaned up */
+ apr_bucket_free(f);
+ }
+}
+
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_lob *a = e->data;
+ const apr_dbd_row_t *row = a->row;
+ apr_dbd_results_t *res = row->res;
+ int col = a->col;
+ apr_bucket *b = NULL;
+ int rv;
+ apr_size_t blength = e->length; /* bytes remaining in file past offset */
+ apr_off_t boffset = e->start;
+ MYSQL_BIND *bind = &res->bind[col];
+
+ *str = NULL; /* in case we die prematurely */
+
+ /* fetch from offset if not at the beginning */
+ if (boffset > 0) {
+ rv = mysql_stmt_fetch_column(res->statement, bind, col,
+ (unsigned long) boffset);
+ if (rv != 0) {
+ return APR_EGENERAL;
+ }
+ }
+ blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
+ *len = e->length - blength;
+ *str = bind->buffer;
+
+ /* allocate new buffer, since we used this one for the bucket */
+ bind->buffer = apr_palloc(res->pool, bind->buffer_length);
+
+ /*
+ * Change the current bucket to refer to what we read,
+ * even if we read nothing because we hit EOF.
+ */
+ apr_bucket_pool_make(e, *str, *len, res->pool);
+
+ /* If we have more to read from the field, then create another bucket */
+ if (blength > 0) {
+ /* for efficiency, we can just build a new apr_bucket struct
+ * to wrap around the existing LOB bucket */
+ b = apr_bucket_alloc(sizeof(*b), e->list);
+ b->start = boffset + *len;
+ b->length = blength;
+ b->data = a;
+ b->type = &apr_bucket_type_lob;
+ b->free = apr_bucket_free;
+ b->list = e->list;
+ APR_BUCKET_INSERT_AFTER(e, b);
+ }
+ else {
+ lob_bucket_destroy(a);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p)
+{
+ apr_bucket_lob *f;
+
+ f = apr_bucket_alloc(sizeof(*f), b->list);
+ f->row = row;
+ f->col = col;
+ f->readpool = p;
+
+ b = apr_bucket_shared_make(b, f, offset, len);
+ b->type = &apr_bucket_type_lob;
+
+ return b;
+}
+
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_lob_make(b, row, col, offset, len, p);
+}
+
+static apr_status_t free_result(void *data)
+{
+ mysql_free_result(data);
+ return APR_SUCCESS;
+}
+
+static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ const char *query, int seek)
+{
+ int sz;
+ int ret;
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+ ret = mysql_query(sql->conn, query);
+ if (!ret) {
+ if (sz = mysql_field_count(sql->conn), sz > 0) {
+ if (!*results) {
+ *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->random = seek;
+ (*results)->statement = NULL;
+ (*results)->pool = pool;
+ if (seek) {
+ (*results)->res = mysql_store_result(sql->conn);
+ }
+ else {
+ (*results)->res = mysql_use_result(sql->conn);
+ }
+ apr_pool_cleanup_register(pool, (*results)->res,
+ free_result,apr_pool_cleanup_null);
+ }
+ } else {
+ ret = mysql_errno(sql->conn);
+ }
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
+{
+ if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) {
+ return NULL;
+ }
+
+ return mysql_fetch_fields(res->res)[n].name;
+}
+
+static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **row, int rownum)
+{
+ MYSQL_ROW r = NULL;
+ int ret = 0;
+
+ if (res->statement) {
+ if (res->random) {
+ if (rownum > 0) {
+ mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum);
+ }
+ else {
+ return -1; /* invalid row */
+ }
+ }
+ ret = mysql_stmt_fetch(res->statement);
+ switch (ret) {
+ case 1:
+ ret = mysql_stmt_errno(res->statement);
+ break;
+ case MYSQL_NO_DATA:
+ ret = -1;
+ break;
+ default:
+ ret = 0; /* bad luck - get_entry will deal with this */
+ break;
+ }
+ }
+ else {
+ if (res->random) {
+ if (rownum > 0) {
+ mysql_data_seek(res->res, (my_ulonglong) --rownum);
+ }
+ else {
+ return -1; /* invalid row */
+ }
+ }
+ r = mysql_fetch_row(res->res);
+ if (r == NULL) {
+ ret = -1;
+ }
+ }
+ if (ret == 0) {
+ if (!*row) {
+ *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ }
+ (*row)->row = r;
+ (*row)->res = res;
+ (*row)->len = mysql_fetch_lengths(res->res);
+ }
+ else {
+ apr_pool_cleanup_run(pool, res->res, free_result);
+ }
+ return ret;
+}
+#if 0
+/* An improved API that was proposed but not followed up */
+static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
+ apr_dbd_datum_t *val)
+{
+ MYSQL_BIND *bind;
+ if (dbd_mysql_num_cols(row->res) <= n) {
+ return NULL;
+ }
+ if (row->res->statement) {
+ bind = &row->res->bind[n];
+ if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+ val->type = APR_DBD_VALUE_NULL;
+ return -1;
+ }
+ if (*bind->is_null) {
+ val->type = APR_DBD_VALUE_NULL;
+ return -1;
+ }
+ else {
+ val->type = APR_DBD_VALUE_STRING;
+ val->value.stringval = bind->buffer;
+ }
+ }
+ else {
+ val->type = APR_DBD_VALUE_STRING;
+ val->value.stringval = row->row[n];
+ }
+ return 0;
+}
+#else
+
+static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
+{
+ MYSQL_BIND *bind;
+ if (dbd_mysql_num_cols(row->res) <= n) {
+ return NULL;
+ }
+ if (row->res->statement) {
+ bind = &row->res->bind[n];
+ if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+ return NULL;
+ }
+ if (*bind->is_null) {
+ return NULL;
+ }
+ else {
+ return bind->buffer;
+ }
+ }
+ else {
+ return row->row[n];
+ }
+ return NULL;
+}
+#endif
+
+static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ if (row->res->statement) {
+ MYSQL_BIND *bind = &row->res->bind[n];
+ unsigned long len = *bind->length;
+
+ if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+ return APR_EGENERAL;
+ }
+
+ if (*bind->is_null) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(bind->buffer);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(bind->buffer);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(bind->buffer);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = (float) atof(bind->buffer);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(bind->buffer);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *((char*)bind->buffer+bind->buffer_length-1) = '\0';
+ *(char**)data = bind->buffer;
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_lob_create(row, n, 0, len,
+ row->res->pool, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ }
+ else {
+ if (row->row[n] == NULL) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(row->row[n]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(row->row[n]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(row->row[n]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = (float) atof(row->row[n]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(row->row[n]);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = row->row[n];
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_pool_create(row->row[n], row->len[n],
+ row->res->pool, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ }
+ return 0;
+}
+
+static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
+{
+ return mysql_error(sql->conn);
+}
+
+static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ int ret;
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+ ret = mysql_query(sql->conn, query);
+ if (ret != 0) {
+ ret = mysql_errno(sql->conn);
+ }
+ *nrows = (int) mysql_affected_rows(sql->conn);
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ unsigned long len = strlen(arg);
+ char *ret = apr_palloc(pool, 2*len + 1);
+ mysql_real_escape_string(sql->conn, ret, arg, len);
+ return ret;
+}
+
+static apr_status_t stmt_close(void *data)
+{
+ mysql_stmt_close(data);
+ return APR_SUCCESS;
+}
+
+static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ /* Translate from apr_dbd to native query format */
+ int ret;
+
+ if (!*statement) {
+ *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ (*statement)->stmt = mysql_stmt_init(sql->conn);
+
+ if ((*statement)->stmt) {
+ apr_pool_cleanup_register(pool, (*statement)->stmt,
+ stmt_close, apr_pool_cleanup_null);
+ ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
+
+ if (ret != 0) {
+ ret = mysql_stmt_errno((*statement)->stmt);
+ }
+
+ (*statement)->nargs = nargs;
+ (*statement)->nvals = nvals;
+ (*statement)->types = types;
+
+ return ret;
+ }
+
+ return CR_OUT_OF_MEMORY;
+}
+
+static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
+ const char **values, MYSQL_BIND *bind)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_unsigned = 0;
+ bind[i].is_null = NULL;
+
+ if (values[j] == NULL) {
+ bind[i].buffer_type = MYSQL_TYPE_NULL;
+ }
+ else {
+ switch (statement->types[i]) {
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+ bind[i].buffer = (void*)values[j];
+ bind[i].buffer_length = atol(values[++j]);
+
+ /* skip table and column */
+ j += 2;
+ break;
+ default:
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = (void*)values[j];
+ bind[i].buffer_length = strlen(values[j]);
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ MYSQL_BIND *bind)
+{
+ int ret;
+
+ ret = mysql_stmt_bind_param(statement->stmt, bind);
+ if (ret != 0) {
+ *nrows = 0;
+ ret = mysql_stmt_errno(statement->stmt);
+ }
+ else {
+ ret = mysql_stmt_execute(statement->stmt);
+ if (ret != 0) {
+ ret = mysql_stmt_errno(statement->stmt);
+ }
+ *nrows = (int) mysql_stmt_affected_rows(statement->stmt);
+ }
+
+ return ret;
+}
+
+static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values)
+{
+ MYSQL_BIND *bind;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bind(statement, values, bind);
+
+ ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_mysql_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement,
+ int random, MYSQL_BIND *bind)
+{
+ int nfields, i;
+ my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+ my_bool *error;
+#endif
+ int ret;
+ unsigned long *length, maxlen;
+
+ ret = mysql_stmt_bind_param(statement->stmt, bind);
+ if (ret == 0) {
+ ret = mysql_stmt_execute(statement->stmt);
+ if (!ret) {
+ if (!*res) {
+ *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*res)->random = random;
+ (*res)->statement = statement->stmt;
+ (*res)->res = mysql_stmt_result_metadata(statement->stmt);
+ (*res)->pool = pool;
+ apr_pool_cleanup_register(pool, (*res)->res,
+ free_result, apr_pool_cleanup_null);
+ nfields = mysql_num_fields((*res)->res);
+ if (!(*res)->bind) {
+ (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
+ length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
+#if MYSQL_VERSION_ID >= 50000
+ error = apr_palloc(pool, nfields*sizeof(my_bool));
+#endif
+ is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
+ for ( i = 0; i < nfields; ++i ) {
+ maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
+ (*res)->res->fields[i].length : sql->fldsz) + 1;
+ if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
+ (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+ }
+ else {
+ (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ }
+ (*res)->bind[i].buffer_length = maxlen;
+ (*res)->bind[i].length = &length[i];
+ (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
+ (*res)->bind[i].is_null = is_nullr+i;
+#if MYSQL_VERSION_ID >= 50000
+ (*res)->bind[i].error = error+i;
+#endif
+ }
+ }
+ ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
+ if (!ret) {
+ ret = mysql_stmt_store_result(statement->stmt);
+ }
+ }
+ }
+ if (ret != 0) {
+ ret = mysql_stmt_errno(statement->stmt);
+ }
+
+ return ret;
+}
+
+static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ const char **args)
+{
+ int ret;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bind(statement, args, bind);
+
+ ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_mysql_pselect(pool, sql, res, statement, random, values);
+}
+
+static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
+ const void **values, MYSQL_BIND *bind)
+{
+ void *arg;
+ int i, j;
+ apr_dbd_type_e type;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ arg = (void *)values[j];
+
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_null = NULL;
+
+ type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_TINY;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_UTINY:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_TINY;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_SHORT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_SHORT;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_USHORT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_SHORT;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_INT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_UINT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_LONG:
+ if (sizeof(int) == sizeof(long)) {
+ bind[i].buffer = arg;
+ }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(int));
+ *(int*)bind[i].buffer = *(long*)arg;
+ }
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_ULONG:
+ if (sizeof(unsigned int) == sizeof(unsigned long)) {
+ bind[i].buffer = arg;
+ }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
+ *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
+ }
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) {
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+ }
+ else { /* have to downsize, long long is not portable */
+ bind[i].buffer = apr_palloc(pool, sizeof(long));
+ *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ }
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) {
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+ }
+ else { /* have to downsize, long long is not portable */
+ bind[i].buffer = apr_palloc(pool, sizeof(long));
+ *(unsigned long*)bind[i].buffer =
+ (unsigned long) *(apr_uint64_t*)arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ }
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_FLOAT;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].is_unsigned = 0;
+ bind[i].buffer_length = strlen((const char *)arg);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ bind[i].buffer = (void *)arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+ bind[i].is_unsigned = 0;
+ bind[i].buffer_length = *(apr_size_t*)values[++j];
+
+ /* skip table and column */
+ j += 2;
+ break;
+ case APR_DBD_TYPE_NULL:
+ default:
+ bind[i].buffer_type = MYSQL_TYPE_NULL;
+ break;
+ }
+ }
+
+ return;
+}
+
+static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const void **values)
+{
+ MYSQL_BIND *bind;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bbind(pool, statement, values, bind);
+
+ ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ const void **args)
+{
+ int ret;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bbind(pool, statement, args, bind);
+
+ ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
+}
+
+static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int ret = -1;
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+ trans->errnum = 0;
+ ret = mysql_rollback(trans->handle->conn);
+ }
+ else {
+ ret = mysql_commit(trans->handle->conn);
+ }
+ }
+ ret |= mysql_autocommit(trans->handle->conn, 1);
+ trans->handle->trans = NULL;
+ return ret;
+}
+/* Whether or not transactions work depends on whether the
+ * underlying DB supports them within MySQL. Unfortunately
+ * it fails silently with the default InnoDB.
+ */
+
+static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ /* Don't try recursive transactions here */
+ if (handle->trans) {
+ dbd_mysql_end_transaction(handle->trans) ;
+ }
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ }
+ (*trans)->errnum = mysql_autocommit(handle->conn, 0);
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ return (*trans)->errnum;
+}
+
+static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ static const char *const delims = " \r\n\t;|,";
+ const char *ptr;
+ int i;
+ const char *key;
+ size_t klen;
+ const char *value;
+ size_t vlen;
+#if MYSQL_VERSION_ID >= 50013
+ my_bool do_reconnect = 1;
+#endif
+ MYSQL *real_conn;
+ unsigned long flags = 0;
+
+ struct {
+ const char *field;
+ const char *value;
+ } fields[] = {
+ {"host", NULL},
+ {"user", NULL},
+ {"pass", NULL},
+ {"dbname", NULL},
+ {"port", NULL},
+ {"sock", NULL},
+ {"flags", NULL},
+ {"fldsz", NULL},
+ {"group", NULL},
+ {"reconnect", NULL},
+ {NULL, NULL}
+ };
+ unsigned int port = 0;
+ apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
+ sql->fldsz = FIELDSIZE;
+ sql->conn = mysql_init(sql->conn);
+ if ( sql->conn == NULL ) {
+ return NULL;
+ }
+ for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+ /* don't dereference memory that may not belong to us */
+ if (ptr == params) {
+ ++ptr;
+ continue;
+ }
+ for (key = ptr-1; apr_isspace(*key); --key);
+ klen = 0;
+ while (apr_isalpha(*key)) {
+ /* don't parse backwards off the start of the string */
+ if (key == params) {
+ --key;
+ ++klen;
+ break;
+ }
+ --key;
+ ++klen;
+ }
+ ++key;
+ for (value = ptr+1; apr_isspace(*value); ++value);
+ vlen = strcspn(value, delims);
+ for (i = 0; fields[i].field != NULL; i++) {
+ if (!strncasecmp(fields[i].field, key, klen)) {
+ fields[i].value = apr_pstrndup(pool, value, vlen);
+ break;
+ }
+ }
+ ptr = value+vlen;
+ }
+ if (fields[4].value != NULL) {
+ port = atoi(fields[4].value);
+ }
+ if (fields[6].value != NULL &&
+ !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
+ flags |= CLIENT_FOUND_ROWS; /* only option we know */
+ }
+ if (fields[7].value != NULL) {
+ sql->fldsz = atol(fields[7].value);
+ }
+ if (fields[8].value != NULL) {
+ mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value);
+ }
+#if MYSQL_VERSION_ID >= 50013
+ if (fields[9].value != NULL) {
+ do_reconnect = atoi(fields[9].value) ? 1 : 0;
+ }
+#endif
+
+#if MYSQL_VERSION_ID >= 50013
+ /* the MySQL manual says this should be BEFORE mysql_real_connect */
+ mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
+#endif
+
+ real_conn = mysql_real_connect(sql->conn, fields[0].value,
+ fields[1].value, fields[2].value,
+ fields[3].value, port,
+ fields[5].value, flags);
+
+ if(real_conn == NULL) {
+ if (error) {
+ *error = apr_pstrdup(pool, mysql_error(sql->conn));
+ }
+ mysql_close(sql->conn);
+ return NULL;
+ }
+
+#if MYSQL_VERSION_ID >= 50013
+ /* Some say this should be AFTER mysql_real_connect */
+ mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
+#endif
+
+ return sql;
+}
+
+static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
+{
+ mysql_close(handle->conn);
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
+}
+
+static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
+ const char* name)
+{
+ return mysql_select_db(handle->conn, name);
+}
+
+static void *dbd_mysql_native(apr_dbd_t *handle)
+{
+ return handle->conn;
+}
+
+static int dbd_mysql_num_cols(apr_dbd_results_t *res)
+{
+ if (res->statement) {
+ return mysql_stmt_field_count(res->statement);
+ }
+ else {
+ return mysql_num_fields(res->res);
+ }
+}
+
+static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
+{
+ if (res->random) {
+ if (res->statement) {
+ return (int) mysql_stmt_num_rows(res->statement);
+ }
+ else {
+ return (int) mysql_num_rows(res->res);
+ }
+ }
+ else {
+ return -1;
+ }
+}
+
+static apr_status_t thread_end(void *data)
+{
+ mysql_thread_end();
+ return APR_SUCCESS;
+}
+
+static void dbd_mysql_init(apr_pool_t *pool)
+{
+ my_init();
+ mysql_thread_init();
+
+ /* FIXME: this is a guess; find out what it really does */
+ apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
+}
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
+ "mysql",
+ dbd_mysql_init,
+ dbd_mysql_native,
+ dbd_mysql_open,
+ dbd_mysql_check_conn,
+ dbd_mysql_close,
+ dbd_mysql_select_db,
+ dbd_mysql_transaction,
+ dbd_mysql_end_transaction,
+ dbd_mysql_query,
+ dbd_mysql_select,
+ dbd_mysql_num_cols,
+ dbd_mysql_num_tuples,
+ dbd_mysql_get_row,
+ dbd_mysql_get_entry,
+ dbd_mysql_error,
+ dbd_mysql_escape,
+ dbd_mysql_prepare,
+ dbd_mysql_pvquery,
+ dbd_mysql_pvselect,
+ dbd_mysql_pquery,
+ dbd_mysql_pselect,
+ dbd_mysql_get_name,
+ dbd_mysql_transaction_mode_get,
+ dbd_mysql_transaction_mode_set,
+ "?",
+ dbd_mysql_pvbquery,
+ dbd_mysql_pvbselect,
+ dbd_mysql_pbquery,
+ dbd_mysql_pbselect,
+ dbd_mysql_datum_get
+};
+
+#endif
diff --git a/dbd/apr_dbd_mysql.dsp b/dbd/apr_dbd_mysql.dsp
new file mode 100644
index 0000000..6b88d77
--- /dev/null
+++ b/dbd/apr_dbd_mysql.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_mysql" - 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_mysql - Win32 Release
+!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_mysql.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_mysql.mak" CFG="apr_dbd_mysql - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_mysql - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_mysql - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_mysql - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_mysql - x64 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_mysql - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_MYSQL=1 /D "HAVE_MYSQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_mysql_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_mysql-1.res" /d DLL_NAME="apr_dbd_mysql" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_mysql-1.dll" /pdb:"Release\apr_dbd_mysql-1.pdb" /implib:"Release\apr_dbd_mysql-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_mysql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_mysql - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_MYSQL=1 /D "HAVE_MYSQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_mysql_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_mysql-1.res" /d DLL_NAME="apr_dbd_mysql" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_mysql-1.dll" /pdb:"Debug\apr_dbd_mysql-1.pdb" /implib:"Debug\apr_dbd_mysql-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_mysql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_mysql - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_MYSQL=1 /D "HAVE_MYSQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_mysql_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_mysql-1.res" /d DLL_NAME="apr_dbd_mysql" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_mysql-1.dll" /pdb:"x64\Release\apr_dbd_mysql-1.pdb" /implib:"x64\Release\apr_dbd_mysql-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_mysql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_mysql - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_MYSQL=1 /D "HAVE_MYSQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_mysql_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_mysql-1.res" /d DLL_NAME="apr_dbd_mysql" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libmysql.lib /nologo /base:"0x6EF50000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_mysql-1.dll" /pdb:"x64\Debug\apr_dbd_mysql-1.pdb" /implib:"x64\Debug\apr_dbd_mysql-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_mysql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_mysql - Win32 Release"
+# Name "apr_dbd_mysql - Win32 Debug"
+# Name "apr_dbd_mysql - x64 Release"
+# Name "apr_dbd_mysql - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_mysql.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project
diff --git a/dbd/apr_dbd_odbc.c b/dbd/apr_dbd_odbc.c
new file mode 100644
index 0000000..3a97052
--- /dev/null
+++ b/dbd/apr_dbd_odbc.c
@@ -0,0 +1,1734 @@
+/* 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"
+#include "apu_config.h"
+
+#include <stdlib.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 */
+#ifdef HAVE_SQL_H
+#include <sql.h>
+#include <sqlext.h>
+#elif defined(HAVE_ODBC_SQL_H)
+#include <odbc/sql.h>
+#include <odbc/sqlext.h>
+#endif
+
+/* 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.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 */
+};
+#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
+
+/* 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->apr_dbd && dbr->apr_dbd->dbc) {
+ if (!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;
+
+ /* stmt is closed if connection has already been closed */
+ if (statement) {
+ SQLHANDLE hstmt = statement->stmt;
+
+ 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;
+ SQLULEN len;
+ SQLLEN *indicator;
+ static SQLLEN 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 {
+ if (type < 0 || type >= NUM_APR_DBD_TYPES) {
+ return APR_EGENERAL;
+ }
+
+ baseType = sqlBaseType[type];
+ cType = sqlCtype[type];
+ indicator = NULL;
+ /* LOBs */
+ if (IS_LOB(cType)) {
+ ptr = (void *)args[*argp];
+ len = (SQLULEN) * (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 = (SQLULEN)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;
+ SQLLEN 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 >= (SQLLEN)e->start)
+ ? (len_indicator - (SQLLEN)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;
+ SQLLEN 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, *next, *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);
+
+ /* no params is OK here - let connect return a more useful error msg */
+ if (!name[nparams])
+ return SQL_SUCCESS;
+
+ do {
+ if (last[strspn(last, seps)] == CSINGLEQUOTE) {
+ last += strspn(last, seps);
+ seps=SSINGLEQUOTE;
+ }
+ val[nparams] = apr_strtok(NULL, seps, &last);
+ seps = DEFAULTSEPS;
+
+ ++nparams;
+ next = apr_strtok(NULL, seps, &last);
+ if (!next) {
+ break;
+ }
+ if (nparams >= MAX_PARAMS) {
+ /* too many parameters, no place to store */
+ return APR_EGENERAL;
+ }
+ name[nparams] = next;
+ } while (1);
+
+ for (j = i = 0; i < nparams; i++) {
+ if (!apr_strnatcasecmp(name[i], "CONNECT")) {
+ *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
+ *connect = 1;
+ }
+ else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
+ *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
+ *connect = 0;
+ }
+ else if (!apr_strnatcasecmp(name[i], "USER")) {
+ *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
+ }
+ else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
+ *password = (SQLCHAR *)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;
+
+ /* set info about last error in dbc - fast return for SQL_SUCCESS */
+ if (rc == SQL_SUCCESS) {
+ char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
+ apr_size_t successMsgLen = sizeof successMsg - 1;
+
+ dbc->lasterrorcode = SQL_SUCCESS;
+ apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
+ apr_cpystrn(dbc->lastError + successMsgLen, step,
+ sizeof dbc->lastError - successMsgLen);
+ 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 != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
+ dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
+ }
+ 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);
+ }
+ 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);
+ }
+}
+
+static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
+{
+ if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
+ handle->lasterrorcode = SQL_ERROR;
+ apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
+ sizeof handle->lastError);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * 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 = (SQLCHAR *)"", *user = (SQLCHAR *)"",
+ *password = (SQLCHAR *)"";
+ 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], (SQLPOINTER)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((char *)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((char *)datasource),
+ user, (SQLSMALLINT)strlen((char *)user),
+ password, (SQLSMALLINT)strlen((char *)password));
+ }
+ }
+ if (SQL_SUCCEEDED(rc)) {
+ handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
+ handle->dbname = apr_pstrdup(pool, (char *)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;
+ handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
+ 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,
+ (SQLPOINTER)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 = APR_DBD_TRANSACTION_COMMIT;
+ 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 != APR_DBD_TRANSACTION_ROLLBACK)
+ ? SQL_COMMIT : SQL_ROLLBACK;
+
+ rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
+ 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);
+ }
+ trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
+ 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);
+
+ if (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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)) {
+ SQLLEN rowcount;
+
+ rc = SQLRowCount(hstmt, &rowcount);
+ *nrows = (int)rowcount;
+ 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);
+
+ if (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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;
+ SQLLEN 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;
+
+ if (col >= row->res->ncols)
+ return APR_EGENERAL;
+
+ if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
+ data = NULL; /* invalid type */
+ return APR_EGENERAL;
+ }
+
+ len = sqlSizes[dbdtype];
+ 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)
+ *(char**)data = (char *)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);
+
+ /* NULL or invalid (-1) */
+ if (p == NULL || p == (void *)-1)
+ return p;
+ else
+ return apr_pstrdup(row->pool, p);
+}
+
+/** 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);
+
+ if (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ *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;
+
+ if (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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)) {
+ SQLLEN rowcount;
+
+ rc = SQLRowCount(statement->stmt, &rowcount);
+ *nrows = (int)rowcount;
+ 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 **/
+static 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 (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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;
+ SQLULEN 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,
+ (SQLCHAR *)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->can_commit;
+}
+
+/** transaction_mode_set: set the mode of transaction **/
+static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
+{
+ int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
+ | APR_DBD_TRANSACTION_COMMIT
+ | APR_DBD_TRANSACTION_ROLLBACK);
+
+ if ((mode & legal) != mode)
+ return APR_EGENERAL;
+
+ trans->apr_dbd->can_commit = mode;
+ return APR_SUCCESS;
+}
+
+/** 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;
+
+ if (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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)) {
+ SQLLEN rowcount;
+
+ rc = SQLRowCount(statement->stmt, &rowcount);
+ *nrows = (int)rowcount;
+ 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 (odbc_check_rollback(handle))
+ return APR_EGENERAL;
+
+ 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
diff --git a/dbd/apr_dbd_odbc.dsp b/dbd/apr_dbd_odbc.dsp
new file mode 100644
index 0000000..4e6cf4c
--- /dev/null
+++ b/dbd/apr_dbd_odbc.dsp
@@ -0,0 +1,191 @@
+# 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 "apr_dbd_odbc - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_odbc - x64 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "HAVE_SQL_H" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ODBC=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_odbc_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_odbc-1.res" /d DLL_NAME="apr_dbd_odbc" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_odbc-1.dll" /implib:"Release\apr_dbd_odbc-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_odbc-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ODBC=1 /D "HAVE_SQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_odbc_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_odbc-1.res" /d DLL_NAME="apr_dbd_odbc" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_odbc-1.dll" /implib:"Debug\apr_dbd_odbc-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_odbc-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_odbc - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "HAVE_SQL_H" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ODBC=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_odbc_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64\Release/apr_dbd_odbc-1.res" /d DLL_NAME="apr_dbd_odbc" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_odbc-1.dll" /implib:"x64\Release\apr_dbd_odbc-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_odbc-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_odbc - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ODBC=1 /D "HAVE_SQL_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_odbc_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/debug/apr_dbd_odbc-1.res" /d DLL_NAME="apr_dbd_odbc" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib odbc32.lib odbccp32.lib /nologo /base:"0x6EF00000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_odbc-1.dll" /implib:"x64\Debug\apr_dbd_odbc-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_odbc-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_odbc - Win32 Release"
+# Name "apr_dbd_odbc - Win32 Debug"
+# Name "apr_dbd_odbc - x64 Release"
+# Name "apr_dbd_odbc - x64 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
diff --git a/dbd/apr_dbd_oracle.c b/dbd/apr_dbd_oracle.c
new file mode 100644
index 0000000..e2e2e7e
--- /dev/null
+++ b/dbd/apr_dbd_oracle.c
@@ -0,0 +1,2220 @@
+/* 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.
+ */
+
+/* Developed initially by Nick Kew and Chris Darroch.
+ * Contributed to the APR project by kind permission of
+ * Pearson Education Core Technology Group (CTG),
+ * formerly Central Media Group (CMG).
+ */
+
+/* apr_dbd_oracle - a painful attempt
+ *
+ * Based first on the documentation at
+ * http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/toc.htm
+ *
+ * Those docs have a lot of internal inconsistencies, contradictions, etc
+ * So I've snarfed the demo programs (from Oracle 8, not included in
+ * the current downloadable oracle), and used code from them.
+ *
+ * Why do cdemo81.c and cdemo82.c do the same thing in very different ways?
+ * e.g. cdemo82 releases all its handle on shutdown; cdemo81 doesn't
+ *
+ * All the ORA* functions return a "sword". Some of them are documented;
+ * others aren't. So I've adopted a policy of using switch statements
+ * everywhere, even when we're not doing anything with the return values.
+ *
+ * This makes no attempt at performance tuning, such as setting
+ * prefetch cache size. We need some actual performance data
+ * to make that meaningful. Input from someone with experience
+ * as a sysop using oracle would be a good start.
+ */
+
+/* shut compiler up */
+#ifdef DEBUG
+#define int_errorcode int errorcode
+#else
+#define int_errorcode
+#endif
+
+#include "apu.h"
+
+#if APU_HAVE_ORACLE
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <oci.h>
+
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_time.h"
+#include "apr_hash.h"
+#include "apr_buckets.h"
+
+#define TRANS_TIMEOUT 30
+#define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers. We alloc this
+ * lots of times, so a large value gets hungry.
+ * Should really make it configurable
+ */
+#define DEFAULT_LONG_SIZE 4096
+#define DBD_ORACLE_MAX_COLUMNS 256
+#define NUMERIC_FIELD_SIZE 32
+
+#define CHECK_CONN_QUERY "SELECT 1 FROM dual"
+
+#define ERR_BUF_SIZE 200
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include "apr_dbd_internal.h"
+
+/* declarations */
+static const char *dbd_oracle_error(apr_dbd_t *sql, int n);
+static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement);
+static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*);
+static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const char **values);
+static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values);
+static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_transaction_t **trans);
+static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
+
+struct apr_dbd_transaction_t {
+ int mode;
+ enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status;
+ apr_dbd_t *handle;
+ OCITrans *trans;
+ OCISnapshot *snapshot1;
+ OCISnapshot *snapshot2;
+};
+
+struct apr_dbd_results_t {
+ apr_pool_t *pool;
+ apr_dbd_t* handle;
+ unsigned int rownum;
+ int seek;
+ int nrows;
+ apr_dbd_prepared_t *statement;
+};
+
+struct apr_dbd_t {
+ sword status;
+ OCIError *err;
+ OCIServer *svr;
+ OCISvcCtx *svc;
+ OCISession *auth;
+ apr_dbd_transaction_t* trans;
+ apr_pool_t *pool;
+ char buf[ERR_BUF_SIZE]; /* for error messages */
+ apr_size_t long_size;
+ apr_dbd_prepared_t *check_conn_stmt;
+};
+
+struct apr_dbd_row_t {
+ int n;
+ apr_dbd_results_t *res;
+ apr_pool_t *pool;
+};
+
+typedef struct {
+ apr_dbd_type_e type;
+ sb2 ind;
+ sb4 len;
+ OCIBind *bind;
+ union {
+ void *raw;
+ char *sval;
+ int ival;
+ unsigned int uval;
+ double fval;
+ OCILobLocator *lobval;
+ } value;
+} bind_arg;
+
+typedef struct {
+ int type;
+ sb2 ind;
+ ub2 len; /* length of actual output */
+ OCIDefine *defn;
+ apr_size_t sz; /* length of buf for output */
+ union {
+ void *raw;
+ char *sval;
+ OCILobLocator *lobval;
+ } buf;
+ const char *name;
+} define_arg;
+
+struct apr_dbd_prepared_t {
+ OCIStmt *stmt;
+ int nargs;
+ int nvals;
+ bind_arg *args;
+ int nout;
+ define_arg *out;
+ apr_dbd_t *handle;
+ apr_pool_t *pool;
+ ub2 type;
+};
+
+/* AFAICT from the docs, the OCIEnv thingey can be used async
+ * across threads, so lets have a global one.
+ *
+ * We'll need shorter-lived envs to deal with requests and connections
+ *
+ * Hmmm, that doesn't work: we don't have a usermem framework.
+ * OK, forget about using APR pools here, until we figure out
+ * the right way to do it (if such a thing exists).
+ */
+static OCIEnv *dbd_oracle_env = NULL;
+
+/* Oracle specific bucket for BLOB/CLOB types */
+typedef struct apr_bucket_lob apr_bucket_lob;
+/**
+ * A bucket referring to a Oracle BLOB/CLOB
+ */
+struct apr_bucket_lob {
+ /** Number of buckets using this memory */
+ apr_bucket_refcount refcount;
+ /** The row this bucket refers to */
+ const apr_dbd_row_t *row;
+ /** The column this bucket refers to */
+ int col;
+ /** The pool into which any needed structures should
+ * be created while reading from this bucket */
+ apr_pool_t *readpool;
+};
+
+static void lob_bucket_destroy(void *data);
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block);
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p);
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list);
+
+static const apr_bucket_type_t apr_bucket_type_lob = {
+ "LOB", 5, APR_BUCKET_DATA,
+ lob_bucket_destroy,
+ lob_bucket_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
+
+static void lob_bucket_destroy(void *data)
+{
+ apr_bucket_lob *f = data;
+
+ if (apr_bucket_shared_destroy(f)) {
+ /* no need to destroy database objects here; it will get
+ * done automatically when the pool gets cleaned up */
+ apr_bucket_free(f);
+ }
+}
+
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_lob *a = e->data;
+ const apr_dbd_row_t *row = a->row;
+ apr_dbd_results_t *res = row->res;
+ int col = a->col;
+ apr_bucket *b = NULL;
+ apr_size_t blength = e->length; /* bytes remaining in file past offset */
+ apr_off_t boffset = e->start;
+ define_arg *val = &res->statement->out[col];
+ apr_dbd_t *sql = res->handle;
+/* Only with 10g, unfortunately
+ oraub8 length = APR_BUCKET_BUFF_SIZE;
+*/
+ ub4 length = APR_BUCKET_BUFF_SIZE;
+ char *buf = NULL;
+
+ *str = NULL; /* in case we die prematurely */
+
+ /* fetch from offset if not at the beginning */
+ buf = apr_palloc(row->pool, APR_BUCKET_BUFF_SIZE);
+ sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
+ &length, 1 + (size_t)boffset,
+ (dvoid*) buf, APR_BUCKET_BUFF_SIZE,
+ NULL, NULL, 0, SQLCS_IMPLICIT);
+/* Only with 10g, unfortunately
+ sql->status = OCILobRead2(sql->svc, sql->err, val->buf.lobval,
+ &length, NULL, 1 + boffset,
+ (dvoid*) buf, APR_BUCKET_BUFF_SIZE,
+ OCI_ONE_PIECE, NULL, NULL, 0, SQLCS_IMPLICIT);
+*/
+ if (sql->status != OCI_SUCCESS) {
+ return APR_EGENERAL;
+ }
+ blength -= length;
+ *len = length;
+ *str = buf;
+
+ /*
+ * Change the current bucket to refer to what we read,
+ * even if we read nothing because we hit EOF.
+ */
+ apr_bucket_pool_make(e, *str, *len, res->pool);
+
+ /* If we have more to read from the field, then create another bucket */
+ if (blength > 0) {
+ /* for efficiency, we can just build a new apr_bucket struct
+ * to wrap around the existing LOB bucket */
+ b = apr_bucket_alloc(sizeof(*b), e->list);
+ b->start = boffset + *len;
+ b->length = blength;
+ b->data = a;
+ b->type = &apr_bucket_type_lob;
+ b->free = apr_bucket_free;
+ b->list = e->list;
+ APR_BUCKET_INSERT_AFTER(e, b);
+ }
+ else {
+ lob_bucket_destroy(a);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p)
+{
+ apr_bucket_lob *f;
+
+ f = apr_bucket_alloc(sizeof(*f), b->list);
+ f->row = row;
+ f->col = col;
+ f->readpool = p;
+
+ b = apr_bucket_shared_make(b, f, offset, len);
+ b->type = &apr_bucket_type_lob;
+
+ return b;
+}
+
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_lob_make(b, row, col, offset, len, p);
+}
+
+static apr_status_t dbd_free_lobdesc(void *lob)
+{
+ switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) {
+ case OCI_SUCCESS:
+ return APR_SUCCESS;
+ default:
+ return APR_EGENERAL;
+ }
+}
+
+static apr_status_t dbd_free_snapshot(void *snap)
+{
+ switch (OCIDescriptorFree(snap, OCI_DTYPE_SNAP)) {
+ case OCI_SUCCESS:
+ return APR_SUCCESS;
+ default:
+ return APR_EGENERAL;
+ }
+}
+
+static void dbd_oracle_init(apr_pool_t *pool)
+{
+ if (dbd_oracle_env == NULL) {
+ /* Sadly, OCI_SHARED seems to be impossible to use, due to
+ * various Oracle bugs. See, for example, Oracle MetaLink bug 2972890
+ * and PHP bug http://bugs.php.net/bug.php?id=23733
+ */
+#ifdef OCI_NEW_LENGTH_SEMANTICS
+ OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_NEW_LENGTH_SEMANTICS,
+ NULL, NULL, NULL, NULL, 0, NULL);
+#else
+ OCIEnvCreate(&dbd_oracle_env, OCI_THREADED,
+ NULL, NULL, NULL, NULL, 0, NULL);
+#endif
+ }
+}
+
+static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ apr_dbd_t *ret = apr_pcalloc(pool, sizeof(apr_dbd_t));
+ int errorcode;
+
+ char *BLANK = "";
+ struct {
+ const char *field;
+ char *value;
+ } fields[] = {
+ {"user", BLANK},
+ {"pass", BLANK},
+ {"dbname", BLANK},
+ {"server", BLANK},
+ {NULL, NULL}
+ };
+ int i;
+ const char *ptr;
+ const char *key;
+ size_t klen;
+ const char *value;
+ size_t vlen;
+ static const char *const delims = " \r\n\t;|,";
+
+ ret->pool = pool;
+ ret->long_size = DEFAULT_LONG_SIZE;
+
+ /* snitch parsing from the MySQL driver */
+ for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+ /* don't dereference memory that may not belong to us */
+ if (ptr == params) {
+ ++ptr;
+ continue;
+ }
+ for (key = ptr-1; apr_isspace(*key); --key);
+ klen = 0;
+ while (apr_isalpha(*key)) {
+ if (key == params) {
+ /* Don't parse off the front of the params */
+ --key;
+ ++klen;
+ break;
+ }
+ --key;
+ ++klen;
+ }
+ ++key;
+ for (value = ptr+1; apr_isspace(*value); ++value);
+ vlen = strcspn(value, delims);
+ for (i=0; fields[i].field != NULL; ++i) {
+ if (!strncasecmp(fields[i].field, key, klen)) {
+ fields[i].value = apr_pstrndup(pool, value, vlen);
+ break;
+ }
+ }
+ ptr = value+vlen;
+ }
+
+ ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err,
+ OCI_HTYPE_ERROR, 0, NULL);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ printf("ret->status is %d\n", ret->status);
+ break;
+#else
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+
+ ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr,
+ OCI_HTYPE_SERVER, 0, NULL);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (alloc svr): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+
+ ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc,
+ OCI_HTYPE_SVCCTX, 0, NULL);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (alloc svc): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+
+/* All the examples use the #else */
+#if CAN_DO_LOGIN
+ ret->status = OCILogon(dbd_oracle_env, ret->err, &ret->svc, fields[0].value,
+ strlen(fields[0].value), fields[1].value,
+ strlen(fields[1].value), fields[2].value,
+ strlen(fields[2].value));
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR: %s\n", ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+#else
+ ret->status = OCIServerAttach(ret->svr, ret->err, (text*) fields[3].value,
+ strlen(fields[3].value), OCI_DEFAULT);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (server attach): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->svr, 0,
+ OCI_ATTR_SERVER, ret->err);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (attr set): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->auth,
+ OCI_HTYPE_SESSION, 0, NULL);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (alloc auth): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[0].value,
+ strlen(fields[0].value), OCI_ATTR_USERNAME, ret->err);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (attr username): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[1].value,
+ strlen(fields[1].value), OCI_ATTR_PASSWORD, ret->err);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (attr password): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCISessionBegin(ret->svc, ret->err, ret->auth,
+ OCI_CRED_RDBMS, OCI_DEFAULT);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (session begin): %s\n", ret->status, ret->buf);
+ break;
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ case OCI_SUCCESS:
+ break;
+ }
+ ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->auth, 0,
+ OCI_ATTR_SESSION, ret->err);
+ switch (ret->status) {
+ default:
+#ifdef DEBUG
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
+ sizeof(ret->buf), OCI_HTYPE_ERROR);
+ printf("OPEN ERROR %d (attr session): %s\n", ret->status, ret->buf);
+#else
+ if (error) {
+ *error = apr_pcalloc(pool, ERR_BUF_SIZE);
+ OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
+ ERR_BUF_SIZE, OCI_HTYPE_ERROR);
+ }
+ return NULL;
+#endif
+ break;
+ case OCI_SUCCESS:
+ break;
+ }
+#endif
+
+ if(dbd_oracle_prepare(pool, ret, CHECK_CONN_QUERY, NULL, 0, 0, NULL,
+ &ret->check_conn_stmt) != 0) {
+ return NULL;
+ }
+
+ return ret;
+}
+
+#ifdef EXPORT_NATIVE_FUNCS
+static apr_size_t dbd_oracle_long_size_set(apr_dbd_t *sql,
+ apr_size_t long_size)
+{
+ apr_size_t old_size = sql->long_size;
+ sql->long_size = long_size;
+ return old_size;
+}
+#endif
+
+static const char *dbd_oracle_get_name(const apr_dbd_results_t *res, int n)
+{
+ define_arg *val = &res->statement->out[n];
+
+ if ((n < 0) || (n >= res->statement->nout)) {
+ return NULL;
+ }
+ return val->name;
+}
+
+static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **rowp, int rownum)
+{
+ apr_dbd_row_t *row = *rowp;
+ apr_dbd_t *sql = res->handle;
+ int_errorcode;
+
+ if (row == NULL) {
+ row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ *rowp = row;
+ row->res = res;
+ /* Oracle starts counting at 1 according to the docs */
+ row->n = res->seek ? rownum : 1;
+ row->pool = pool;
+ }
+ else {
+ if (res->seek) {
+ row->n = rownum;
+ }
+ else {
+ ++row->n;
+ }
+ }
+
+ if (res->seek) {
+ sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
+ OCI_FETCH_ABSOLUTE, row->n, OCI_DEFAULT);
+ }
+ else {
+ sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
+ OCI_FETCH_NEXT, 0, OCI_DEFAULT);
+ }
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ (*rowp)->res = res;
+ return 0;
+ case OCI_NO_DATA:
+ return -1;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Execute error %d: %s\n", sql->status, sql->buf);
+#endif
+ /* fallthrough */
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static const char *dbd_oracle_error(apr_dbd_t *sql, int n)
+{
+ /* This is ugly. Needs us to pass in a buffer of unknown size.
+ * Either we put it on the handle, or we have to keep allocing/copying
+ */
+ sb4 errorcode;
+
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ return "OCI_SUCCESS";
+ case OCI_SUCCESS_WITH_INFO:
+ return "OCI_SUCCESS_WITH_INFO";
+ case OCI_NEED_DATA:
+ return "OCI_NEED_DATA";
+ case OCI_NO_DATA:
+ return "OCI_NO_DATA";
+ case OCI_INVALID_HANDLE:
+ return "OCI_INVALID_HANDLE";
+ case OCI_STILL_EXECUTING:
+ return "OCI_STILL_EXECUTING";
+ case OCI_CONTINUE:
+ return "OCI_CONTINUE";
+ }
+
+ switch (OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ (text*) sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) {
+ case OCI_SUCCESS:
+ return sql->buf;
+ default:
+ return "internal error: OCIErrorGet failed";
+ }
+}
+
+static apr_status_t freeStatement(void *statement)
+{
+ int rv = APR_SUCCESS;
+ OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt;
+
+#ifdef PREPARE2
+ OCIError *err;
+
+ if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR,
+ 0, NULL) != OCI_SUCCESS) {
+ return APR_EGENERAL;
+ }
+ if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) {
+ rv = APR_EGENERAL;
+ }
+ if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) {
+ rv = APR_EGENERAL;
+ }
+#else
+ if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) {
+ rv = APR_EGENERAL;
+ }
+#endif
+
+ return rv;
+}
+
+static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ const char *query, int seek)
+{
+ int ret = 0;
+ apr_dbd_prepared_t *statement = NULL;
+
+ ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = dbd_oracle_pselect(pool, sql, results, statement, seek, NULL);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return ret;
+}
+
+static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ int ret = 0;
+ apr_pool_t *pool;
+ apr_dbd_prepared_t *statement = NULL;
+
+ if (sql->trans && sql->trans->status == TRANS_ERROR) {
+ return 1;
+ }
+
+ /* make our own pool so that APR allocations don't linger and so that
+ * both Stmt and LOB handles are cleaned up (LOB handles may be
+ * allocated when preparing APR_DBD_TYPE_CLOB/BLOBs)
+ */
+ apr_pool_create(&pool, sql->pool);
+
+ ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
+ if (ret == 0) {
+ ret = dbd_oracle_pquery(pool, sql, nrows, statement, NULL);
+ if (ret == 0) {
+ sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT,
+ nrows, 0, OCI_ATTR_ROW_COUNT,
+ sql->err);
+ }
+ }
+
+ apr_pool_destroy(pool);
+
+ return ret;
+}
+
+static const char *dbd_oracle_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ return arg; /* OCI has no concept of string escape */
+}
+
+static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ int ret = 0;
+ int i;
+ apr_dbd_prepared_t *stmt ;
+
+ if (*statement == NULL) {
+ *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ stmt = *statement;
+ stmt->handle = sql;
+ stmt->pool = pool;
+ stmt->nargs = nargs;
+ stmt->nvals = nvals;
+
+ /* populate our own args, if any */
+ if (nargs > 0) {
+ stmt->args = apr_pcalloc(pool, nargs*sizeof(bind_arg));
+ for (i = 0; i < nargs; i++) {
+ stmt->args[i].type = types[i];
+ }
+ }
+
+ sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt,
+ OCI_HTYPE_STMT, 0, NULL);
+ if (sql->status != OCI_SUCCESS) {
+ return 1;
+ }
+
+ sql->status = OCIStmtPrepare(stmt->stmt, sql->err, (text*) query,
+ strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT);
+ if (sql->status != OCI_SUCCESS) {
+ OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT);
+ return 1;
+ }
+
+ apr_pool_cleanup_register(pool, stmt, freeStatement,
+ apr_pool_cleanup_null);
+
+ /* Perl gets statement type here */
+ sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0,
+ OCI_ATTR_STMT_TYPE, sql->err);
+ if (sql->status != OCI_SUCCESS) {
+ return 1;
+ }
+
+/* Perl sets PREFETCH_MEMORY here, but the docs say there's a working default */
+#if 0
+ sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size,
+ sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY,
+ sql->err);
+ if (sql->status != OCI_SUCCESS) {
+ return 1;
+ }
+#endif
+
+ if (stmt->type == OCI_STMT_SELECT) {
+ ret = outputParams(sql, stmt);
+ }
+ return ret;
+}
+
+static void dbd_oracle_bind(apr_dbd_prepared_t *statement, const char **values)
+{
+ OCIStmt *stmt = statement->stmt;
+ apr_dbd_t *sql = statement->handle;
+ int i, j;
+ sb2 null_ind = -1;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ if (values[j] == NULL) {
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ NULL, 0, SQLT_STR,
+ &null_ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ }
+ else {
+ switch (statement->args[i].type) {
+ case APR_DBD_TYPE_BLOB:
+ {
+ char *data = (char *)values[j];
+ int size = atoi((char*)values[++j]);
+
+ /* skip table and column for now */
+ j += 2;
+
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ data, size, SQLT_LBI,
+ &statement->args[i].ind,
+ NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ }
+ break;
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char *)values[j];
+ int size = atoi((char*)values[++j]);
+
+ /* skip table and column for now */
+ j += 2;
+
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ data, size, SQLT_LNG,
+ &statement->args[i].ind,
+ NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ }
+ break;
+ default:
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ (dvoid*) values[j],
+ strlen(values[j]) + 1,
+ SQLT_STR,
+ &statement->args[i].ind,
+ NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ }
+ }
+
+ if (sql->status != OCI_SUCCESS) {
+ return;
+ }
+ }
+
+ return;
+}
+
+static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt)
+{
+ OCIParam *parms;
+ int i;
+ ub2 paramtype[DBD_ORACLE_MAX_COLUMNS];
+ ub2 paramsize[DBD_ORACLE_MAX_COLUMNS];
+ char *paramname[DBD_ORACLE_MAX_COLUMNS];
+ ub4 paramnamelen[DBD_ORACLE_MAX_COLUMNS];
+ int_errorcode;
+
+ /* Perl uses 0 where we used 1 */
+ sql->status = OCIStmtExecute(sql->svc, stmt->stmt, sql->err, 0, 0,
+ NULL, NULL, OCI_DESCRIBE_ONLY);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ case OCI_SUCCESS_WITH_INFO:
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Describing prepared statement: %s\n", sql->buf);
+#endif
+ default:
+ return 1;
+ }
+ while (sql->status == OCI_SUCCESS) {
+ sql->status = OCIParamGet(stmt->stmt, OCI_HTYPE_STMT,
+ sql->err, (dvoid**)&parms, stmt->nout+1);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
+ &paramtype[stmt->nout],
+ 0, OCI_ATTR_DATA_TYPE, sql->err);
+ sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
+ &paramsize[stmt->nout],
+ 0, OCI_ATTR_DATA_SIZE, sql->err);
+ sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
+ &paramname[stmt->nout],
+ &paramnamelen[stmt->nout],
+ OCI_ATTR_NAME, sql->err);
+ ++stmt->nout;
+ }
+ }
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ case OCI_ERROR:
+ break; /* this is what we expect at end-of-loop */
+ default:
+ return 1;
+ }
+
+ /* OK, the above works. We have the params; now OCIDefine them */
+ stmt->out = apr_palloc(stmt->pool, stmt->nout*sizeof(define_arg));
+ for (i=0; i<stmt->nout; ++i) {
+ stmt->out[i].type = paramtype[i];
+ stmt->out[i].len = stmt->out[i].sz = paramsize[i];
+ stmt->out[i].name = apr_pstrmemdup(stmt->pool,
+ paramname[i], paramnamelen[i]);
+ switch (stmt->out[i].type) {
+ default:
+ switch (stmt->out[i].type) {
+ case SQLT_NUM: /* 2: numeric, Perl worst case=130+38+3 */
+ stmt->out[i].sz = 171;
+ break;
+ case SQLT_CHR: /* 1: char */
+ case SQLT_AFC: /* 96: ANSI fixed char */
+ stmt->out[i].sz *= 4; /* ugh, wasteful UCS-4 handling */
+ break;
+ case SQLT_DAT: /* 12: date, depends on NLS date format */
+ stmt->out[i].sz = 75;
+ break;
+ case SQLT_BIN: /* 23: raw binary, perhaps UTF-16? */
+ stmt->out[i].sz *= 2;
+ break;
+ case SQLT_RID: /* 11: rowid */
+ case SQLT_RDD: /* 104: rowid descriptor */
+ stmt->out[i].sz = 20;
+ break;
+ case SQLT_TIMESTAMP: /* 187: timestamp */
+ case SQLT_TIMESTAMP_TZ: /* 188: timestamp with time zone */
+ case SQLT_INTERVAL_YM: /* 189: interval year-to-month */
+ case SQLT_INTERVAL_DS: /* 190: interval day-to-second */
+ case SQLT_TIMESTAMP_LTZ: /* 232: timestamp with local time zone */
+ stmt->out[i].sz = 75;
+ break;
+ default:
+#ifdef DEBUG
+ printf("Unsupported data type: %d\n", stmt->out[i].type);
+#endif
+ break;
+ }
+ ++stmt->out[i].sz;
+ stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
+ sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
+ sql->err, i+1,
+ stmt->out[i].buf.sval,
+ stmt->out[i].sz, SQLT_STR,
+ &stmt->out[i].ind, &stmt->out[i].len,
+ 0, OCI_DEFAULT);
+ break;
+ case SQLT_LNG: /* 8: long */
+ stmt->out[i].sz = sql->long_size * 4 + 4; /* ugh, UCS-4 handling */
+ stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
+ sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
+ sql->err, i+1,
+ stmt->out[i].buf.raw,
+ stmt->out[i].sz, SQLT_LVC,
+ &stmt->out[i].ind, NULL,
+ 0, OCI_DEFAULT);
+ break;
+ case SQLT_LBI: /* 24: long binary, perhaps UTF-16? */
+ stmt->out[i].sz = sql->long_size * 2 + 4; /* room for int prefix */
+ stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
+ sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
+ sql->err, i+1,
+ stmt->out[i].buf.raw,
+ stmt->out[i].sz, SQLT_LVB,
+ &stmt->out[i].ind, NULL,
+ 0, OCI_DEFAULT);
+ break;
+ case SQLT_BLOB: /* 113 */
+ case SQLT_CLOB: /* 112 */
+/*http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#434937*/
+ sql->status = OCIDescriptorAlloc(dbd_oracle_env,
+ (dvoid**)&stmt->out[i].buf.lobval,
+ OCI_DTYPE_LOB, 0, NULL);
+ apr_pool_cleanup_register(stmt->pool, stmt->out[i].buf.lobval,
+ dbd_free_lobdesc,
+ apr_pool_cleanup_null);
+ sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
+ sql->err, i+1,
+ (dvoid*) &stmt->out[i].buf.lobval,
+ -1, stmt->out[i].type,
+ &stmt->out[i].ind, &stmt->out[i].len,
+ 0, OCI_DEFAULT);
+ break;
+ }
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values)
+{
+ OCISnapshot *oldsnapshot = NULL;
+ OCISnapshot *newsnapshot = NULL;
+ apr_dbd_transaction_t* trans = sql->trans;
+ int exec_mode;
+ int_errorcode;
+
+ if (trans) {
+ switch (trans->status) {
+ case TRANS_ERROR:
+ return -1;
+ case TRANS_NONE:
+ trans = NULL;
+ break;
+ case TRANS_1:
+ oldsnapshot = trans->snapshot1;
+ newsnapshot = trans->snapshot2;
+ trans->status = TRANS_2;
+ break;
+ case TRANS_2:
+ oldsnapshot = trans->snapshot2;
+ newsnapshot = trans->snapshot1;
+ trans->status = TRANS_1;
+ break;
+ }
+ exec_mode = OCI_DEFAULT;
+ }
+ else {
+ exec_mode = OCI_COMMIT_ON_SUCCESS;
+ }
+
+ dbd_oracle_bind(statement, values);
+
+ sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
+ oldsnapshot, newsnapshot, exec_mode);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Execute error %d: %s\n", sql->status, sql->buf);
+#endif
+ /* fallthrough */
+ default:
+ if (TXN_NOTICE_ERRORS(trans)) {
+ trans->status = TRANS_ERROR;
+ }
+ return 1;
+ }
+
+ sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
+ OCI_ATTR_ROW_COUNT, sql->err);
+ return 0;
+}
+
+static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->status == TRANS_ERROR) {
+ return -1;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_oracle_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const char **values)
+{
+ int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
+ OCISnapshot *oldsnapshot = NULL;
+ OCISnapshot *newsnapshot = NULL;
+ apr_dbd_transaction_t* trans = sql->trans;
+ int_errorcode;
+
+ if (trans) {
+ switch (trans->status) {
+ case TRANS_ERROR:
+ return 1;
+ case TRANS_NONE:
+ trans = NULL;
+ break;
+ case TRANS_1:
+ oldsnapshot = trans->snapshot1;
+ newsnapshot = trans->snapshot2;
+ trans->status = TRANS_2;
+ break;
+ case TRANS_2:
+ oldsnapshot = trans->snapshot2;
+ newsnapshot = trans->snapshot1;
+ trans->status = TRANS_1;
+ break;
+ }
+ }
+
+ dbd_oracle_bind(statement, values);
+
+ sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
+ oldsnapshot, newsnapshot, exec_mode);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Executing prepared statement: %s\n", sql->buf);
+#endif
+ /* fallthrough */
+ default:
+ if (TXN_NOTICE_ERRORS(trans)) {
+ trans->status = TRANS_ERROR;
+ }
+ return 1;
+ }
+
+ if (!*results) {
+ *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->handle = sql;
+ (*results)->statement = statement;
+ (*results)->seek = seek;
+ (*results)->rownum = seek ? 0 : -1;
+ (*results)->pool = pool;
+
+ return 0;
+}
+
+static int dbd_oracle_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->status == TRANS_ERROR) {
+ return -1;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_oracle_pselect(pool, sql, results, statement, seek, values);
+}
+
+static void dbd_oracle_bbind(apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ OCIStmt *stmt = statement->stmt;
+ apr_dbd_t *sql = statement->handle;
+ int i, j;
+ sb2 null_ind = -1;
+ apr_dbd_type_e type;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ type = (values[j] == NULL ? APR_DBD_TYPE_NULL
+ : statement->args[i].type);
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ statement->args[i].value.ival = *(char*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.ival,
+ sizeof(statement->args[i].value.ival),
+ SQLT_INT,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ statement->args[i].value.uval = *(unsigned char*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.uval,
+ sizeof(statement->args[i].value.uval),
+ SQLT_UIN,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ statement->args[i].value.ival = *(short*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.ival,
+ sizeof(statement->args[i].value.ival),
+ SQLT_INT,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ statement->args[i].value.uval = *(unsigned short*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.uval,
+ sizeof(statement->args[i].value.uval),
+ SQLT_UIN,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_INT:
+ statement->args[i].value.ival = *(int*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.ival,
+ sizeof(statement->args[i].value.ival),
+ SQLT_INT,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_UINT:
+ statement->args[i].value.uval = *(unsigned int*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.uval,
+ sizeof(statement->args[i].value.uval),
+ SQLT_UIN,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_LONG:
+ statement->args[i].value.sval =
+ apr_psprintf(statement->pool, "%ld", *(long*)values[j]);
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ statement->args[i].value.sval,
+ strlen(statement->args[i].value.sval)+1,
+ SQLT_STR,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ statement->args[i].value.sval =
+ apr_psprintf(statement->pool, "%lu",
+ *(unsigned long*)values[j]);
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ statement->args[i].value.sval,
+ strlen(statement->args[i].value.sval)+1,
+ SQLT_STR,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ statement->args[i].value.sval =
+ apr_psprintf(statement->pool, "%" APR_INT64_T_FMT,
+ *(apr_int64_t*)values[j]);
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ statement->args[i].value.sval,
+ strlen(statement->args[i].value.sval)+1,
+ SQLT_STR,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ statement->args[i].value.sval =
+ apr_psprintf(statement->pool, "%" APR_UINT64_T_FMT,
+ *(apr_uint64_t*)values[j]);
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ statement->args[i].value.sval,
+ strlen(statement->args[i].value.sval)+1,
+ SQLT_UIN,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ statement->args[i].value.fval = *(float*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.fval,
+ sizeof(statement->args[i].value.fval),
+ SQLT_FLT,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ statement->args[i].value.fval = *(double*)values[j];
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ &statement->args[i].value.fval,
+ sizeof(statement->args[i].value.fval),
+ SQLT_FLT,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ (dvoid*) values[j],
+ strlen(values[j]) + 1,
+ SQLT_STR,
+ &statement->args[i].ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ {
+ char *data = (char *)values[j];
+ apr_size_t size = *(apr_size_t*)values[++j];
+
+ /* skip table and column for now */
+ j += 2;
+
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ data, size, SQLT_LBI,
+ &statement->args[i].ind,
+ NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ }
+ break;
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char *)values[j];
+ apr_size_t size = *(apr_size_t*)values[++j];
+
+ /* skip table and column for now */
+ j += 2;
+
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ data, size, SQLT_LNG,
+ &statement->args[i].ind,
+ NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ default:
+ sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
+ sql->err, i + 1,
+ NULL, 0, SQLT_STR,
+ &null_ind, NULL,
+ (ub2) 0, (ub4) 0,
+ (ub4 *) 0, OCI_DEFAULT);
+ break;
+ }
+
+ if (sql->status != OCI_SUCCESS) {
+ return;
+ }
+ }
+
+ return;
+}
+
+static int dbd_oracle_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ OCISnapshot *oldsnapshot = NULL;
+ OCISnapshot *newsnapshot = NULL;
+ apr_dbd_transaction_t* trans = sql->trans;
+ int exec_mode;
+ int_errorcode;
+
+ if (trans) {
+ switch (trans->status) {
+ case TRANS_ERROR:
+ return -1;
+ case TRANS_NONE:
+ trans = NULL;
+ break;
+ case TRANS_1:
+ oldsnapshot = trans->snapshot1;
+ newsnapshot = trans->snapshot2;
+ trans->status = TRANS_2;
+ break;
+ case TRANS_2:
+ oldsnapshot = trans->snapshot2;
+ newsnapshot = trans->snapshot1;
+ trans->status = TRANS_1;
+ break;
+ }
+ exec_mode = OCI_DEFAULT;
+ }
+ else {
+ exec_mode = OCI_COMMIT_ON_SUCCESS;
+ }
+
+ dbd_oracle_bbind(statement, values);
+
+ sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
+ oldsnapshot, newsnapshot, exec_mode);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Execute error %d: %s\n", sql->status, sql->buf);
+#endif
+ /* fallthrough */
+ default:
+ if (TXN_NOTICE_ERRORS(trans)) {
+ trans->status = TRANS_ERROR;
+ }
+ return 1;
+ }
+
+ sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
+ OCI_ATTR_ROW_COUNT, sql->err);
+ return 0;
+}
+
+static int dbd_oracle_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->status == TRANS_ERROR) {
+ return -1;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_oracle_pbquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_oracle_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement,
+ int seek, const void **values)
+{
+ int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
+ OCISnapshot *oldsnapshot = NULL;
+ OCISnapshot *newsnapshot = NULL;
+ apr_dbd_transaction_t* trans = sql->trans;
+ int_errorcode;
+
+ if (trans) {
+ switch (trans->status) {
+ case TRANS_ERROR:
+ return 1;
+ case TRANS_NONE:
+ trans = NULL;
+ break;
+ case TRANS_1:
+ oldsnapshot = trans->snapshot1;
+ newsnapshot = trans->snapshot2;
+ trans->status = TRANS_2;
+ break;
+ case TRANS_2:
+ oldsnapshot = trans->snapshot2;
+ newsnapshot = trans->snapshot1;
+ trans->status = TRANS_1;
+ break;
+ }
+ }
+
+ dbd_oracle_bbind(statement, values);
+
+ sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
+ oldsnapshot, newsnapshot, exec_mode);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Executing prepared statement: %s\n", sql->buf);
+#endif
+ /* fallthrough */
+ default:
+ if (TXN_NOTICE_ERRORS(trans)) {
+ trans->status = TRANS_ERROR;
+ }
+ return 1;
+ }
+
+ if (!*results) {
+ *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->handle = sql;
+ (*results)->statement = statement;
+ (*results)->seek = seek;
+ (*results)->rownum = seek ? 0 : -1;
+ (*results)->pool = pool;
+
+ return 0;
+}
+
+static int dbd_oracle_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement, int seek,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->status == TRANS_ERROR) {
+ return -1;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_oracle_pbselect(pool, sql, results, statement, seek, values);
+}
+
+static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_transaction_t **trans)
+{
+ int ret = 0;
+ int_errorcode;
+ if (*trans) {
+ dbd_oracle_end_transaction(*trans);
+ }
+ else {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ OCIHandleAlloc(dbd_oracle_env, (dvoid**)&(*trans)->trans,
+ OCI_HTYPE_TRANS, 0, 0);
+ OCIAttrSet(sql->svc, OCI_HTYPE_SVCCTX, (*trans)->trans, 0,
+ OCI_ATTR_TRANS, sql->err);
+ }
+
+
+ sql->status = OCITransStart(sql->svc, sql->err, TRANS_TIMEOUT,
+ OCI_TRANS_NEW);
+ switch (sql->status) {
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf,
+ sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Transaction: %s\n", sql->buf);
+#endif
+ ret = 1;
+ break;
+ case OCI_SUCCESS:
+ (*trans)->handle = sql;
+ (*trans)->status = TRANS_1;
+ sql->trans = *trans;
+ switch (OCIDescriptorAlloc(dbd_oracle_env,
+ (dvoid**)&(*trans)->snapshot1,
+ OCI_DTYPE_SNAP, 0, NULL)) {
+ case OCI_SUCCESS:
+ apr_pool_cleanup_register(pool, (*trans)->snapshot1,
+ dbd_free_snapshot, apr_pool_cleanup_null);
+ break;
+ case OCI_INVALID_HANDLE:
+ ret = 1;
+ break;
+ }
+ switch (OCIDescriptorAlloc(dbd_oracle_env,
+ (dvoid**)&(*trans)->snapshot2,
+ OCI_DTYPE_SNAP, 0, NULL)) {
+ case OCI_SUCCESS:
+ apr_pool_cleanup_register(pool, (*trans)->snapshot2,
+ dbd_free_snapshot, apr_pool_cleanup_null);
+ break;
+ case OCI_INVALID_HANDLE:
+ ret = 1;
+ break;
+ }
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int ret = 1; /* no transaction is an error cond */
+ sword status;
+ apr_dbd_t *handle = trans->handle;
+ if (trans) {
+ switch (trans->status) {
+ case TRANS_NONE: /* No trans is an error here */
+ status = OCI_ERROR;
+ break;
+ case TRANS_ERROR:
+ status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
+ break;
+ default:
+ /* rollback on explicit rollback request */
+ if (TXN_DO_ROLLBACK(trans)) {
+ status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
+ } else {
+ status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
+ }
+ break;
+ }
+
+ handle->trans = NULL;
+
+ switch (status) {
+ case OCI_SUCCESS:
+ ret = 0;
+ break;
+ default:
+ ret = 3;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int dbd_oracle_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_oracle_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+/* This doesn't work for BLOB because of NULLs, but it can fake it
+ * if the BLOB is really a string
+ */
+static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n)
+{
+ ub4 len = 0;
+ ub1 csform = 0;
+ ub2 csid = 0;
+ apr_size_t buflen = 0;
+ char *buf = NULL;
+ define_arg *val = &row->res->statement->out[n];
+ apr_dbd_t *sql = row->res->handle;
+ int_errorcode;
+
+ if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) {
+ return NULL;
+ }
+
+ switch (val->type) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval,
+ &len);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ case OCI_SUCCESS_WITH_INFO:
+ if (len == 0) {
+ buf = "";
+ }
+ break;
+ case OCI_ERROR:
+#ifdef DEBUG
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Finding LOB length: %s\n", sql->buf);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (len == 0) {
+ break;
+ }
+
+ if (val->type == APR_DBD_TYPE_CLOB) {
+#if 1
+ /* Is this necessary, or can it be defaulted? */
+ sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err,
+ val->buf.lobval, &csform);
+ if (sql->status == OCI_SUCCESS) {
+ sql->status = OCILobCharSetId(dbd_oracle_env, sql->err,
+ val->buf.lobval, &csid);
+ }
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ case OCI_SUCCESS_WITH_INFO:
+ buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
+ /* zeroise all - where the string ends depends on charset */
+ buf = apr_pcalloc(row->pool, buflen);
+ break;
+#ifdef DEBUG
+ case OCI_ERROR:
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Reading LOB character set: %s\n", sql->buf);
+ break; /*** XXX?? ***/
+#endif
+ default:
+ break; /*** XXX?? ***/
+ }
+#else /* ignore charset */
+ buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
+ /* zeroise all - where the string ends depends on charset */
+ buf = apr_pcalloc(row->pool, buflen);
+#endif
+ } else {
+ /* BUG: this'll only work if the BLOB looks like a string */
+ buflen = len;
+ buf = apr_palloc(row->pool, buflen+1);
+ buf[buflen] = 0;
+ }
+
+ if (!buf) {
+ break;
+ }
+
+ sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
+ &len, 1, (dvoid*) buf, buflen,
+ NULL, NULL, csid, csform);
+ switch (sql->status) {
+ case OCI_SUCCESS:
+ case OCI_SUCCESS_WITH_INFO:
+ break;
+#ifdef DEBUG
+ case OCI_ERROR:
+ OCIErrorGet(sql->err, 1, NULL, &errorcode,
+ sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+ printf("Reading LOB: %s\n", sql->buf);
+ buf = NULL; /*** XXX?? ***/
+ break;
+#endif
+ default:
+ buf = NULL; /*** XXX?? ***/
+ break;
+ }
+
+ break;
+ case SQLT_LNG:
+ case SQLT_LBI:
+ /* raw is struct { ub4 len; char *buf; } */
+ len = *(ub4*) val->buf.raw;
+ buf = apr_pstrndup(row->pool, val->buf.sval + sizeof(ub4), len);
+ break;
+ default:
+ buf = apr_pstrndup(row->pool, val->buf.sval, val->len);
+ break;
+ }
+ return (const char*) buf;
+}
+
+/* XXX Should this use Oracle proper API instead of calling get_entry()? */
+static apr_status_t dbd_oracle_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ define_arg *val = &row->res->statement->out[n];
+ const char *entry;
+
+ if ((n < 0) || (n >= row->res->statement->nout)) {
+ return APR_EGENERAL;
+ }
+
+ if(val->ind == -1) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(char*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(unsigned char*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(short*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(unsigned short*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_INT:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(int*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_UINT:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(unsigned int*)data = atoi(entry);
+ break;
+ case APR_DBD_TYPE_LONG:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(long*)data = atol(entry);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(unsigned long*)data = atol(entry);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(apr_int64_t*)data = apr_atoi64(entry);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(apr_uint64_t*)data = apr_atoi64(entry);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(float*)data = (float)atof(entry);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(double*)data = atof(entry);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ *(char**)data = (char*)entry;
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+ apr_dbd_t *sql = row->res->handle;
+ ub4 len = 0;
+
+ switch (val->type) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ sql->status = OCILobGetLength(sql->svc, sql->err,
+ val->buf.lobval, &len);
+ switch(sql->status) {
+ case OCI_SUCCESS:
+ case OCI_SUCCESS_WITH_INFO:
+ if (len == 0) {
+ e = apr_bucket_eos_create(b->bucket_alloc);
+ }
+ else {
+ e = apr_bucket_lob_create(row, n, 0, len,
+ row->pool, b->bucket_alloc);
+ }
+ break;
+ default:
+ return APR_ENOENT;
+ }
+ break;
+ default:
+ entry = dbd_oracle_get_entry(row, n);
+ if (entry == NULL) {
+ return APR_ENOENT;
+ }
+ e = apr_bucket_pool_create(entry, strlen(entry),
+ row->pool, b->bucket_alloc);
+ break;
+ }
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_oracle_close(apr_dbd_t *handle)
+{
+ /* FIXME: none of the oracle docs/examples say anything about
+ * closing/releasing handles. Which seems unlikely ...
+ */
+
+ /* OK, let's grab from cdemo again.
+ * cdemo81 does nothing; cdemo82 does OCIHandleFree on the handles
+ */
+ switch (OCISessionEnd(handle->svc, handle->err, handle->auth,
+ (ub4)OCI_DEFAULT)) {
+ default:
+ break;
+ }
+ switch (OCIServerDetach(handle->svr, handle->err, (ub4) OCI_DEFAULT )) {
+ default:
+ break;
+ }
+ /* does OCISessionEnd imply this? */
+ switch (OCIHandleFree((dvoid *) handle->auth, (ub4) OCI_HTYPE_SESSION)) {
+ default:
+ break;
+ }
+ switch (OCIHandleFree((dvoid *) handle->svr, (ub4) OCI_HTYPE_SERVER)) {
+ default:
+ break;
+ }
+ switch (OCIHandleFree((dvoid *) handle->svc, (ub4) OCI_HTYPE_SVCCTX)) {
+ default:
+ break;
+ }
+ switch (OCIHandleFree((dvoid *) handle->err, (ub4) OCI_HTYPE_ERROR)) {
+ default:
+ break;
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool, apr_dbd_t *sql)
+{
+ apr_dbd_results_t *res = NULL;
+ apr_dbd_row_t *row = NULL;
+
+ if(dbd_oracle_pselect(pool, sql, &res, sql->check_conn_stmt,
+ 0, NULL) != 0) {
+ return APR_EGENERAL;
+ }
+
+ if(dbd_oracle_get_row(pool, res, &row, -1) != 0) {
+ return APR_EGENERAL;
+ }
+
+ if(dbd_oracle_get_row(pool, res, &row, -1) != -1) {
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static int dbd_oracle_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+ const char *name)
+{
+ /* FIXME: need to find this in the docs */
+ return APR_ENOTIMPL;
+}
+
+static void *dbd_oracle_native(apr_dbd_t *handle)
+{
+ /* FIXME: can we do anything better? Oracle doesn't seem to have
+ * a concept of a handle in the sense we use it.
+ */
+ return dbd_oracle_env;
+}
+
+static int dbd_oracle_num_cols(apr_dbd_results_t* res)
+{
+ return res->statement->nout;
+}
+
+static int dbd_oracle_num_tuples(apr_dbd_results_t* res)
+{
+ if (!res->seek) {
+ return -1;
+ }
+ if (res->nrows >= 0) {
+ return res->nrows;
+ }
+ res->handle->status = OCIAttrGet(res->statement->stmt, OCI_HTYPE_STMT,
+ &res->nrows, 0, OCI_ATTR_ROW_COUNT,
+ res->handle->err);
+ return res->nrows;
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_oracle_driver = {
+ "oracle",
+ dbd_oracle_init,
+ dbd_oracle_native,
+ dbd_oracle_open,
+ dbd_oracle_check_conn,
+ dbd_oracle_close,
+ dbd_oracle_select_db,
+ dbd_oracle_start_transaction,
+ dbd_oracle_end_transaction,
+ dbd_oracle_query,
+ dbd_oracle_select,
+ dbd_oracle_num_cols,
+ dbd_oracle_num_tuples,
+ dbd_oracle_get_row,
+ dbd_oracle_get_entry,
+ dbd_oracle_error,
+ dbd_oracle_escape,
+ dbd_oracle_prepare,
+ dbd_oracle_pvquery,
+ dbd_oracle_pvselect,
+ dbd_oracle_pquery,
+ dbd_oracle_pselect,
+ dbd_oracle_get_name,
+ dbd_oracle_transaction_mode_get,
+ dbd_oracle_transaction_mode_set,
+ ":apr%d",
+ dbd_oracle_pvbquery,
+ dbd_oracle_pvbselect,
+ dbd_oracle_pbquery,
+ dbd_oracle_pbselect,
+ dbd_oracle_datum_get
+};
+#endif
diff --git a/dbd/apr_dbd_oracle.dsp b/dbd/apr_dbd_oracle.dsp
new file mode 100644
index 0000000..e5b97d9
--- /dev/null
+++ b/dbd/apr_dbd_oracle.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_oracle" - 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_oracle - Win32 Release
+!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_oracle.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_oracle.mak" CFG="apr_dbd_oracle - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_oracle - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_oracle - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_oracle - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_oracle - x64 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_oracle - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ORACLE=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_oracle_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_oracle-1.res" /d DLL_NAME="apr_dbd_oracle" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_oracle-1.dll" /pdb:"Release\apr_dbd_oracle-1.pdb" /implib:"Release\apr_dbd_oracle-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_oracle-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_oracle - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ORACLE=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_oracle_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_oracle-1.res" /d DLL_NAME="apr_dbd_oracle" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_oracle-1.dll" /pdb:"Debug\apr_dbd_oracle-1.pdb" /implib:"Debug\apr_dbd_oracle-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_oracle-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_oracle - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ORACLE=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_oracle_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_oracle-1.res" /d DLL_NAME="apr_dbd_oracle" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_oracle-1.dll" /pdb:"x64\Release\apr_dbd_oracle-1.pdb" /implib:"x64\Release\apr_dbd_oracle-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_oracle-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_oracle - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_ORACLE=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_oracle_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_oracle-1.res" /d DLL_NAME="apr_dbd_oracle" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib oci.lib /nologo /base:"0x6EF40000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_oracle-1.dll" /pdb:"x64\Debug\apr_dbd_oracle-1.pdb" /implib:"x64\Debug\apr_dbd_oracle-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_oracle-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_oracle - Win32 Release"
+# Name "apr_dbd_oracle - Win32 Debug"
+# Name "apr_dbd_oracle - x64 Release"
+# Name "apr_dbd_oracle - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_oracle.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project
diff --git a/dbd/apr_dbd_pgsql.c b/dbd/apr_dbd_pgsql.c
new file mode 100644
index 0000000..21f2179
--- /dev/null
+++ b/dbd/apr_dbd_pgsql.c
@@ -0,0 +1,1315 @@
+/* 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_PGSQL
+
+#include "apu_config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#ifdef HAVE_LIBPQ_FE_H
+#include <libpq-fe.h>
+#elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H)
+#include <postgresql/libpq-fe.h>
+#endif
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ PGconn *conn;
+ apr_dbd_transaction_t *trans;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ PGconn *handle;
+ PGresult *res;
+ size_t ntuples;
+ size_t sz;
+ size_t index;
+ apr_pool_t *pool;
+};
+
+struct apr_dbd_row_t {
+ int n;
+ apr_dbd_results_t *res;
+};
+
+struct apr_dbd_prepared_t {
+ const char *name;
+ int prepared;
+ int nargs;
+ int nvals;
+ apr_dbd_type_e *types;
+};
+
+#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
+ || ((x) == PGRES_COMMAND_OK) \
+ || ((x) == PGRES_TUPLES_OK))
+
+static apr_status_t clear_result(void *data)
+{
+ PQclear(data);
+ return APR_SUCCESS;
+}
+
+static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ const char *query, int seek)
+{
+ PGresult *res;
+ int ret;
+ if ( sql->trans && sql->trans->errnum ) {
+ return sql->trans->errnum;
+ }
+ if (seek) { /* synchronous query */
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+ res = PQexec(sql->conn, query);
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ } else {
+ PQclear(res);
+ }
+ } else {
+ ret = PGRES_FATAL_ERROR;
+ }
+ if (ret != 0) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ return ret;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+ }
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->res = res;
+ (*results)->ntuples = PQntuples(res);
+ (*results)->sz = PQnfields(res);
+ (*results)->random = seek;
+ (*results)->pool = pool;
+ apr_pool_cleanup_register(pool, res, clear_result,
+ apr_pool_cleanup_null);
+ }
+ else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+ if (PQsendQuery(sql->conn, query) == 0) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = 1;
+ }
+ return 1;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+ }
+ if (*results == NULL) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->random = seek;
+ (*results)->handle = sql->conn;
+ (*results)->pool = pool;
+ }
+ return 0;
+}
+
+static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
+{
+ if (res->res) {
+ if ((n>=0) && (PQnfields(res->res) > n)) {
+ return PQfname(res->res,n);
+ }
+ }
+ return NULL;
+}
+
+static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **rowp, int rownum)
+{
+ apr_dbd_row_t *row = *rowp;
+ int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
+
+ if (row == NULL) {
+ row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ *rowp = row;
+ row->res = res;
+ if ( sequential ) {
+ row->n = 0;
+ }
+ else {
+ if (rownum > 0) {
+ row->n = --rownum;
+ }
+ else {
+ return -1; /* invalid row */
+ }
+ }
+ }
+ else {
+ if ( sequential ) {
+ ++row->n;
+ }
+ else {
+ if (rownum > 0) {
+ row->n = --rownum;
+ }
+ else {
+ return -1; /* invalid row */
+ }
+ }
+ }
+
+ if (res->random) {
+ if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
+ *rowp = NULL;
+ apr_pool_cleanup_run(pool, res->res, clear_result);
+ res->res = NULL;
+ return -1;
+ }
+ }
+ else {
+ if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
+ /* no data; we have to fetch some */
+ row->n -= res->ntuples;
+ if (res->res != NULL) {
+ PQclear(res->res);
+ }
+ res->res = PQgetResult(res->handle);
+ if (res->res) {
+ res->ntuples = PQntuples(res->res);
+ while (res->ntuples == 0) {
+ /* if we got an empty result, clear it, wait a mo, try
+ * again */
+ PQclear(res->res);
+ apr_sleep(100000); /* 0.1 secs */
+ res->res = PQgetResult(res->handle);
+ if (res->res) {
+ res->ntuples = PQntuples(res->res);
+ }
+ else {
+ return -1;
+ }
+ }
+ if (res->sz == 0) {
+ res->sz = PQnfields(res->res);
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
+{
+ return PQgetvalue(row->res->res, row->n, n);
+}
+
+static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ if (PQgetisnull(row->res->res, row->n, n)) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = PQgetvalue(row->res->res, row->n, n);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
+ PQgetlength(row->res->res, row->n, n),
+ row->res->pool, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
+{
+ return PQerrorMessage(sql->conn);
+}
+
+static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ PGresult *res;
+ int ret;
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+
+ res = PQexec(sql->conn, query);
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ /* ugh, making 0 return-success doesn't fit */
+ ret = 0;
+ }
+ *nrows = atoi(PQcmdTuples(res));
+ PQclear(res);
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+
+ if (ret != 0){
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ size_t len = strlen(arg);
+ char *ret = apr_palloc(pool, 2*len + 2);
+ PQescapeString(ret, arg, len);
+ return ret;
+}
+
+static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ char *sqlcmd;
+ char *sqlptr;
+ size_t length, qlen;
+ int i = 0;
+ const char **args;
+ size_t alen;
+ int ret;
+ PGresult *res;
+
+ if (!*statement) {
+ *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ (*statement)->nargs = nargs;
+ (*statement)->nvals = nvals;
+ (*statement)->types = types;
+
+ args = apr_palloc(pool, nargs * sizeof(*args));
+
+ qlen = strlen(query);
+ length = qlen + 1;
+
+ for (i = 0; i < nargs; i++) {
+ switch (types[i]) {
+ case APR_DBD_TYPE_TINY:
+ case APR_DBD_TYPE_UTINY:
+ case APR_DBD_TYPE_SHORT:
+ case APR_DBD_TYPE_USHORT:
+ args[i] = "smallint";
+ break;
+ case APR_DBD_TYPE_INT:
+ case APR_DBD_TYPE_UINT:
+ args[i] = "integer";
+ break;
+ case APR_DBD_TYPE_LONG:
+ case APR_DBD_TYPE_ULONG:
+ case APR_DBD_TYPE_LONGLONG:
+ case APR_DBD_TYPE_ULONGLONG:
+ args[i] = "bigint";
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ args[i] = "real";
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ args[i] = "double precision";
+ break;
+ case APR_DBD_TYPE_TEXT:
+ args[i] = "text";
+ break;
+ case APR_DBD_TYPE_TIME:
+ args[i] = "time";
+ break;
+ case APR_DBD_TYPE_DATE:
+ args[i] = "date";
+ break;
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ args[i] = "timestamp";
+ break;
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ args[i] = "timestamp with time zone";
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ args[i] = "bytea";
+ break;
+ case APR_DBD_TYPE_NULL:
+ args[i] = "varchar"; /* XXX Eh? */
+ break;
+ default:
+ args[i] = "varchar";
+ break;
+ }
+ length += 1 + strlen(args[i]);
+ }
+
+ if (!label) {
+ /* don't really prepare; use in execParams instead */
+ (*statement)->prepared = 0;
+ (*statement)->name = apr_pstrdup(pool, query);
+ return 0;
+ }
+ (*statement)->name = apr_pstrdup(pool, label);
+
+ /* length of SQL query that prepares this statement */
+ length = 8 + strlen(label) + 2 + 4 + length + 1;
+ sqlcmd = apr_palloc(pool, length);
+ sqlptr = sqlcmd;
+ memcpy(sqlptr, "PREPARE ", 8);
+ sqlptr += 8;
+ length = strlen(label);
+ memcpy(sqlptr, label, length);
+ sqlptr += length;
+ if (nargs > 0) {
+ memcpy(sqlptr, " (",2);
+ sqlptr += 2;
+ for (i=0; i < nargs; ++i) {
+ alen = strlen(args[i]);
+ memcpy(sqlptr, args[i], alen);
+ sqlptr += alen;
+ *sqlptr++ = ',';
+ }
+ sqlptr[-1] = ')';
+ }
+ memcpy(sqlptr, " AS ", 4);
+ sqlptr += 4;
+ memcpy(sqlptr, query, qlen);
+ sqlptr += qlen;
+ *sqlptr = 0;
+
+ res = PQexec(sql->conn, sqlcmd);
+ if ( res ) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ }
+ /* Hmmm, do we do this here or register it on the pool? */
+ PQclear(res);
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+ (*statement)->prepared = 1;
+
+ return ret;
+}
+
+static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values,
+ const int *len, const int *fmt)
+{
+ int ret;
+ PGresult *res;
+
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+
+ if (statement->prepared) {
+ res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
+ values, len, fmt, 0);
+ }
+ else {
+ res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
+ values, len, fmt, 0);
+ }
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ }
+ *nrows = atoi(PQcmdTuples(res));
+ PQclear(res);
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+
+ if (ret != 0){
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
+ const char **values,
+ const char **val, int *len, int *fmt)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ if (values[j] == NULL) {
+ val[i] = NULL;
+ }
+ else {
+ switch (statement->types[i]) {
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ val[i] = (char *)values[j];
+ len[i] = atoi(values[++j]);
+ fmt[i] = 1;
+
+ /* skip table and column */
+ j += 2;
+ break;
+ default:
+ val[i] = values[j];
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values)
+{
+ int *len, *fmt;
+ const char **val;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+ len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+ fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+ dbd_pgsql_bind(statement, values, val, len, fmt);
+
+ return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
+ val, len, fmt);
+}
+
+static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const char **values,
+ const int *len, const int *fmt)
+{
+ PGresult *res;
+ int rv;
+ int ret = 0;
+
+ if (seek) { /* synchronous query */
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ if (statement->prepared) {
+ res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
+ values, len, fmt, 0);
+ }
+ else {
+ res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
+ values, len, fmt, 0);
+ }
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ }
+ else {
+ PQclear(res);
+ }
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+ if (ret != 0) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ return ret;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ }
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->res = res;
+ (*results)->ntuples = PQntuples(res);
+ (*results)->sz = PQnfields(res);
+ (*results)->random = seek;
+ (*results)->pool = pool;
+ apr_pool_cleanup_register(pool, res, clear_result,
+ apr_pool_cleanup_null);
+ }
+ else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ if (statement->prepared) {
+ rv = PQsendQueryPrepared(sql->conn, statement->name,
+ statement->nargs, values, len, fmt, 0);
+ }
+ else {
+ rv = PQsendQueryParams(sql->conn, statement->name,
+ statement->nargs, 0, values, len, fmt, 0);
+ }
+ if (rv == 0) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = 1;
+ }
+ return 1;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ int ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
+ }
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->random = seek;
+ (*results)->handle = sql->conn;
+ (*results)->pool = pool;
+ }
+
+ return ret;
+}
+
+static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, const char **values)
+{
+ int *len, *fmt;
+ const char **val;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+ len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+ fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+ dbd_pgsql_bind(statement, values, val, len, fmt);
+
+ return dbd_pgsql_pselect_internal(pool, sql, results, statement,
+ seek, val, len, fmt);
+}
+
+static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement,
+ int seek, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
+}
+
+static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
+ const void **values,
+ const char **val, int *len, int *fmt)
+{
+ int i, j;
+ apr_dbd_type_e type;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ val[i] = apr_itoa(pool, *(char*)values[j]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ val[i] = apr_itoa(pool, *(short*)values[j]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
+ break;
+ case APR_DBD_TYPE_INT:
+ val[i] = apr_itoa(pool, *(int*)values[j]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ val[i] = apr_ltoa(pool, *(long*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
+ *(apr_int64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
+ *(apr_uint64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ val[i] = values[j];
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ val[i] = (char*)values[j];
+ len[i] = *(apr_size_t*)values[++j];
+ fmt[i] = 1;
+
+ /* skip table and column */
+ j += 2;
+ break;
+ case APR_DBD_TYPE_NULL:
+ default:
+ val[i] = NULL;
+ break;
+ }
+ }
+
+ return;
+}
+
+static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ int *len, *fmt;
+ const char **val;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+ len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+ fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+ dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
+
+ return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
+ val, len, fmt);
+}
+
+static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement,
+ int seek, const void **values)
+{
+ int *len, *fmt;
+ const char **val;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+ len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+ fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+ dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
+
+ return dbd_pgsql_pselect_internal(pool, sql, results, statement,
+ seek, val, len, fmt);
+}
+
+static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement, int seek,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
+}
+
+static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ int ret = 0;
+ PGresult *res;
+
+ /* XXX handle recursive transactions here */
+
+ res = PQexec(handle->conn, "BEGIN TRANSACTION");
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ }
+ }
+ PQclear(res);
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+ return ret;
+}
+
+static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
+{
+ PGresult *res;
+ int ret = -1; /* no transaction is an error cond */
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+ trans->errnum = 0;
+ res = PQexec(trans->handle->conn, "ROLLBACK");
+ }
+ else {
+ res = PQexec(trans->handle->conn, "COMMIT");
+ }
+ if (res) {
+ ret = PQresultStatus(res);
+ if (dbd_pgsql_is_success(ret)) {
+ ret = 0;
+ }
+ PQclear(res);
+ }
+ else {
+ ret = PGRES_FATAL_ERROR;
+ }
+ trans->handle->trans = NULL;
+ }
+ return ret;
+}
+
+static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+static void null_notice_receiver(void *arg, const PGresult *res)
+{
+ /* nothing */
+}
+
+static void null_notice_processor(void *arg, const char *message)
+{
+ /* nothing */
+}
+
+static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ apr_dbd_t *sql;
+
+ PGconn *conn = PQconnectdb(params);
+
+ /* if there's an error in the connect string or something we get
+ * back a * bogus connection object, and things like PQreset are
+ * liable to segfault, so just close it out now. it would be nice
+ * if we could give an indication of why we failed to connect... */
+ if (PQstatus(conn) != CONNECTION_OK) {
+ if (error) {
+ *error = apr_pstrdup(pool, PQerrorMessage(conn));
+ }
+ PQfinish(conn);
+ return NULL;
+ }
+
+ PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
+ PQsetNoticeProcessor(conn, null_notice_processor, NULL);
+
+ sql = apr_pcalloc (pool, sizeof (*sql));
+
+ sql->conn = conn;
+
+ return sql;
+}
+
+static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
+{
+ PQfinish(handle->conn);
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ if (PQstatus(handle->conn) != CONNECTION_OK) {
+ PQreset(handle->conn);
+ if (PQstatus(handle->conn) != CONNECTION_OK) {
+ return APR_EGENERAL;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+ const char *name)
+{
+ return APR_ENOTIMPL;
+}
+
+static void *dbd_pgsql_native(apr_dbd_t *handle)
+{
+ return handle->conn;
+}
+
+static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
+{
+ return res->sz;
+}
+
+static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
+{
+ if (res->random) {
+ return res->ntuples;
+ }
+ else {
+ return -1;
+ }
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
+ "pgsql",
+ NULL,
+ dbd_pgsql_native,
+ dbd_pgsql_open,
+ dbd_pgsql_check_conn,
+ dbd_pgsql_close,
+ dbd_pgsql_select_db,
+ dbd_pgsql_start_transaction,
+ dbd_pgsql_end_transaction,
+ dbd_pgsql_query,
+ dbd_pgsql_select,
+ dbd_pgsql_num_cols,
+ dbd_pgsql_num_tuples,
+ dbd_pgsql_get_row,
+ dbd_pgsql_get_entry,
+ dbd_pgsql_error,
+ dbd_pgsql_escape,
+ dbd_pgsql_prepare,
+ dbd_pgsql_pvquery,
+ dbd_pgsql_pvselect,
+ dbd_pgsql_pquery,
+ dbd_pgsql_pselect,
+ dbd_pgsql_get_name,
+ dbd_pgsql_transaction_mode_get,
+ dbd_pgsql_transaction_mode_set,
+ "$%d",
+ dbd_pgsql_pvbquery,
+ dbd_pgsql_pvbselect,
+ dbd_pgsql_pbquery,
+ dbd_pgsql_pbselect,
+ dbd_pgsql_datum_get
+};
+#endif
diff --git a/dbd/apr_dbd_pgsql.dsp b/dbd/apr_dbd_pgsql.dsp
new file mode 100644
index 0000000..e7f1466
--- /dev/null
+++ b/dbd/apr_dbd_pgsql.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_pgsql" - 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_pgsql - Win32 Release
+!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_pgsql.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_pgsql.mak" CFG="apr_dbd_pgsql - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_pgsql - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_pgsql - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_pgsql - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_pgsql - x64 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_pgsql - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_PGSQL=1 /D "HAVE_LIBPQ_FE_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_pgsql_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_pgsql-1.res" /d DLL_NAME="apr_dbd_pgsql" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_pgsql-1.dll" /pdb:"Release\apr_dbd_pgsql-1.pdb" /implib:"Release\apr_dbd_pgsql-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_pgsql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_pgsql - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_PGSQL=1 /D "HAVE_LIBPQ_FE_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_pgsql_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_pgsql-1.res" /d DLL_NAME="apr_dbd_pgsql" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_pgsql-1.dll" /pdb:"Debug\apr_dbd_pgsql-1.pdb" /implib:"Debug\apr_dbd_pgsql-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_pgsql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_pgsql - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_PGSQL=1 /D "HAVE_LIBPQ_FE_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_pgsql_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_pgsql-1.res" /d DLL_NAME="apr_dbd_pgsql" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_pgsql-1.dll" /pdb:"x64\Release\apr_dbd_pgsql-1.pdb" /implib:"x64\Release\apr_dbd_pgsql-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_pgsql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_pgsql - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_PGSQL=1 /D "HAVE_LIBPQ_FE_H" /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_pgsql_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_pgsql-1.res" /d DLL_NAME="apr_dbd_pgsql" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib libpq.lib /nologo /base:"0x6EF30000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_pgsql-1.dll" /pdb:"x64\Debug\apr_dbd_pgsql-1.pdb" /implib:"x64\Debug\apr_dbd_pgsql-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_pgsql-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_pgsql - Win32 Release"
+# Name "apr_dbd_pgsql - Win32 Debug"
+# Name "apr_dbd_pgsql - x64 Release"
+# Name "apr_dbd_pgsql - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_pgsql.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project
diff --git a/dbd/apr_dbd_sqlite2.c b/dbd/apr_dbd_sqlite2.c
new file mode 100644
index 0000000..132ccc4
--- /dev/null
+++ b/dbd/apr_dbd_sqlite2.c
@@ -0,0 +1,566 @@
+/* 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_SQLITE2
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sqlite.h>
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ sqlite *conn;
+ char *errmsg;
+ apr_dbd_transaction_t *trans;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ sqlite *handle;
+ char **res;
+ size_t ntuples;
+ size_t sz;
+ size_t index;
+ apr_pool_t *pool;
+};
+
+struct apr_dbd_row_t {
+ int n;
+ char **data;
+ apr_dbd_results_t *res;
+};
+
+struct apr_dbd_prepared_t {
+ const char *name;
+ int prepared;
+};
+
+#define FREE_ERROR_MSG(dbd) \
+ do { \
+ if(dbd && dbd->errmsg) { \
+ free(dbd->errmsg); \
+ dbd->errmsg = NULL; \
+ } \
+ } while(0);
+
+static apr_status_t free_table(void *data)
+{
+ sqlite_free_table(data);
+ return APR_SUCCESS;
+}
+
+static int dbd_sqlite_select(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results, const char *query,
+ int seek)
+{
+ char **result;
+ int ret = 0;
+ int tuples = 0;
+ int fields = 0;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ FREE_ERROR_MSG(sql);
+
+ ret = sqlite_get_table(sql->conn, query, &result, &tuples, &fields,
+ &sql->errmsg);
+
+ if (ret == SQLITE_OK) {
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+
+ (*results)->res = result;
+ (*results)->ntuples = tuples;
+ (*results)->sz = fields;
+ (*results)->random = seek;
+ (*results)->pool = pool;
+
+ if (tuples > 0)
+ apr_pool_cleanup_register(pool, result, free_table,
+ apr_pool_cleanup_null);
+
+ ret = 0;
+ }
+ else {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ }
+
+ return ret;
+}
+
+static const char *dbd_sqlite_get_name(const apr_dbd_results_t *res, int n)
+{
+ if ((n < 0) || (n >= res->sz)) {
+ return NULL;
+ }
+
+ return res->res[n];
+}
+
+static int dbd_sqlite_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
+ apr_dbd_row_t ** rowp, int rownum)
+{
+ apr_dbd_row_t *row = *rowp;
+ int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
+
+ if (row == NULL) {
+ row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ *rowp = row;
+ row->res = res;
+ row->n = sequential ? 0 : rownum - 1;
+ }
+ else {
+ if (sequential) {
+ ++row->n;
+ }
+ else {
+ row->n = rownum - 1;
+ }
+ }
+
+ if (row->n >= res->ntuples) {
+ *rowp = NULL;
+ apr_pool_cleanup_run(pool, res->res, free_table);
+ res->res = NULL;
+ return -1;
+ }
+
+ /* Pointer magic explanation:
+ * The sqlite result is an array such that the first res->sz elements are
+ * the column names and each tuple follows afterwards
+ * ex: (from the sqlite2 documentation)
+ SELECT employee_name, login, host FROM users WHERE login LIKE * 'd%';
+
+ nrow = 2
+ ncolumn = 3
+ result[0] = "employee_name"
+ result[1] = "login"
+ result[2] = "host"
+ result[3] = "dummy"
+ result[4] = "No such user"
+ result[5] = 0
+ result[6] = "D. Richard Hipp"
+ result[7] = "drh"
+ result[8] = "zadok"
+ */
+
+ row->data = res->res + res->sz + (res->sz * row->n);
+
+ return 0;
+}
+
+static const char *dbd_sqlite_get_entry(const apr_dbd_row_t * row, int n)
+{
+ if ((n < 0) || (n >= row->res->sz)) {
+ return NULL;
+ }
+
+ return row->data[n];
+}
+
+static apr_status_t dbd_sqlite_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ if ((n < 0) || (n >= row->res->sz)) {
+ return APR_EGENERAL;
+ }
+
+ if (row->data[n] == NULL) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(row->data[n]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(row->data[n]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(row->data[n]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(row->data[n]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(row->data[n]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = atof(row->data[n]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(row->data[n]);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = row->data[n];
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_pool_create(row->data[n],strlen(row->data[n]),
+ row->res->pool, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static const char *dbd_sqlite_error(apr_dbd_t * sql, int n)
+{
+ return sql->errmsg;
+}
+
+static int dbd_sqlite_query(apr_dbd_t * sql, int *nrows, const char *query)
+{
+ char **result;
+ int ret;
+ int tuples = 0;
+ int fields = 0;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ FREE_ERROR_MSG(sql);
+
+ ret =
+ sqlite_get_table(sql->conn, query, &result, &tuples, &fields,
+ &sql->errmsg);
+ if (ret == SQLITE_OK) {
+ *nrows = sqlite_changes(sql->conn);
+
+ if (tuples > 0)
+ free(result);
+
+ ret = 0;
+ }
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+
+ return ret;
+}
+
+static apr_status_t free_mem(void *data)
+{
+ sqlite_freemem(data);
+ return APR_SUCCESS;
+}
+
+static const char *dbd_sqlite_escape(apr_pool_t * pool, const char *arg,
+ apr_dbd_t * sql)
+{
+ char *ret = sqlite_mprintf("%q", arg);
+ apr_pool_cleanup_register(pool, ret, free_mem, apr_pool_cleanup_null);
+ return ret;
+}
+
+static int dbd_sqlite_prepare(apr_pool_t * pool, apr_dbd_t * sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t ** statement)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ const char **values)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ va_list args)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement,
+ int seek, const char **values)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement, int seek,
+ va_list args)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ va_list args)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement,
+ int seek, const void **values)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement, int seek,
+ va_list args)
+{
+ return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_start_transaction(apr_pool_t * pool, apr_dbd_t * handle,
+ apr_dbd_transaction_t ** trans)
+{
+ int ret, rows;
+
+ ret = dbd_sqlite_query(handle, &rows, "BEGIN TRANSACTION");
+ if (ret == 0) {
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ }
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ }
+ else {
+ ret = -1;
+ }
+ return ret;
+}
+
+static int dbd_sqlite_end_transaction(apr_dbd_transaction_t * trans)
+{
+ int rows;
+ int ret = -1; /* no transaction is an error cond */
+
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+ trans->errnum = 0;
+ ret =
+ dbd_sqlite_query(trans->handle, &rows,
+ "ROLLBACK TRANSACTION");
+ }
+ else {
+ ret =
+ dbd_sqlite_query(trans->handle, &rows, "COMMIT TRANSACTION");
+ }
+ trans->handle->trans = NULL;
+ }
+
+ return ret;
+}
+
+static int dbd_sqlite_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_sqlite_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+static apr_status_t error_free(void *data)
+{
+ free(data);
+ return APR_SUCCESS;
+}
+
+static apr_dbd_t *dbd_sqlite_open(apr_pool_t * pool, const char *params_,
+ const char **error)
+{
+ apr_dbd_t *sql;
+ sqlite *conn = NULL;
+ char *perm;
+ int iperms = 600;
+ char* params = apr_pstrdup(pool, params_);
+ /* params = "[filename]:[permissions]"
+ * example: "shopping.db:600"
+ */
+
+ perm = strstr(params, ":");
+ if (perm) {
+ *(perm++) = '\x00'; /* split the filename and permissions */
+
+ if (strlen(perm) > 0)
+ iperms = atoi(perm);
+ }
+
+ if (error) {
+ *error = NULL;
+
+ conn = sqlite_open(params, iperms, (char **)error);
+
+ if (*error) {
+ apr_pool_cleanup_register(pool, *error, error_free,
+ apr_pool_cleanup_null);
+ }
+ }
+ else {
+ conn = sqlite_open(params, iperms, NULL);
+ }
+
+ sql = apr_pcalloc(pool, sizeof(*sql));
+ sql->conn = conn;
+
+ return sql;
+}
+
+static apr_status_t dbd_sqlite_close(apr_dbd_t * handle)
+{
+ if (handle->conn) {
+ sqlite_close(handle->conn);
+ handle->conn = NULL;
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_sqlite_check_conn(apr_pool_t * pool,
+ apr_dbd_t * handle)
+{
+ if (handle->conn == NULL)
+ return -1;
+ return APR_SUCCESS;
+}
+
+static int dbd_sqlite_select_db(apr_pool_t * pool, apr_dbd_t * handle,
+ const char *name)
+{
+ return APR_ENOTIMPL;
+}
+
+static void *dbd_sqlite_native(apr_dbd_t * handle)
+{
+ return handle->conn;
+}
+
+static int dbd_sqlite_num_cols(apr_dbd_results_t * res)
+{
+ return res->sz;
+}
+
+static int dbd_sqlite_num_tuples(apr_dbd_results_t * res)
+{
+ return res->ntuples;
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_sqlite2_driver = {
+ "sqlite2",
+ NULL,
+ dbd_sqlite_native,
+ dbd_sqlite_open,
+ dbd_sqlite_check_conn,
+ dbd_sqlite_close,
+ dbd_sqlite_select_db,
+ dbd_sqlite_start_transaction,
+ dbd_sqlite_end_transaction,
+ dbd_sqlite_query,
+ dbd_sqlite_select,
+ dbd_sqlite_num_cols,
+ dbd_sqlite_num_tuples,
+ dbd_sqlite_get_row,
+ dbd_sqlite_get_entry,
+ dbd_sqlite_error,
+ dbd_sqlite_escape,
+ dbd_sqlite_prepare,
+ dbd_sqlite_pvquery,
+ dbd_sqlite_pvselect,
+ dbd_sqlite_pquery,
+ dbd_sqlite_pselect,
+ dbd_sqlite_get_name,
+ dbd_sqlite_transaction_mode_get,
+ dbd_sqlite_transaction_mode_set,
+ NULL,
+ dbd_sqlite_pvbquery,
+ dbd_sqlite_pvbselect,
+ dbd_sqlite_pbquery,
+ dbd_sqlite_pbselect,
+ dbd_sqlite_datum_get
+};
+#endif
diff --git a/dbd/apr_dbd_sqlite2.dsp b/dbd/apr_dbd_sqlite2.dsp
new file mode 100644
index 0000000..2977606
--- /dev/null
+++ b/dbd/apr_dbd_sqlite2.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_sqlite2" - 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_sqlite2 - Win32 Release
+!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_sqlite2.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_sqlite2.mak" CFG="apr_dbd_sqlite2 - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_sqlite2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite2 - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite2 - x64 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_sqlite2 - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE2=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite2_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_sqlite2-1.res" /d DLL_NAME="apr_dbd_sqlite2" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_sqlite2-1.dll" /pdb:"Release\apr_dbd_sqlite2-1.pdb" /implib:"Release\apr_dbd_sqlite2-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_sqlite2-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite2 - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE2=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite2_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_sqlite2-1.res" /d DLL_NAME="apr_dbd_sqlite2" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_sqlite2-1.dll" /pdb:"Debug\apr_dbd_sqlite2-1.pdb" /implib:"Debug\apr_dbd_sqlite2-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_sqlite2-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite2 - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE2=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite2_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_sqlite2-1.res" /d DLL_NAME="apr_dbd_sqlite2" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_sqlite2-1.dll" /pdb:"x64\Release\apr_dbd_sqlite2-1.pdb" /implib:"x64\Release\apr_dbd_sqlite2-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_sqlite2-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite2 - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE2=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite2_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_sqlite2-1.res" /d DLL_NAME="apr_dbd_sqlite2" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite2.lib /nologo /base:"0x6EF10000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_sqlite2-1.dll" /pdb:"x64\Debug\apr_dbd_sqlite2-1.pdb" /implib:"x64\Debug\apr_dbd_sqlite2-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_sqlite2-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_sqlite2 - Win32 Release"
+# Name "apr_dbd_sqlite2 - Win32 Debug"
+# Name "apr_dbd_sqlite2 - x64 Release"
+# Name "apr_dbd_sqlite2 - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_sqlite2.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project
diff --git a/dbd/apr_dbd_sqlite3.c b/dbd/apr_dbd_sqlite3.c
new file mode 100644
index 0000000..d79dbe1
--- /dev/null
+++ b/dbd/apr_dbd_sqlite3.c
@@ -0,0 +1,914 @@
+/* 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_SQLITE3
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sqlite3.h>
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+#define MAX_RETRY_COUNT 15
+#define MAX_RETRY_SLEEP 100000
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ sqlite3 *conn;
+ apr_dbd_transaction_t *trans;
+ apr_pool_t *pool;
+ apr_dbd_prepared_t *prep;
+};
+
+typedef struct {
+ char *name;
+ char *value;
+ int size;
+ int type;
+} apr_dbd_column_t;
+
+struct apr_dbd_row_t {
+ apr_dbd_results_t *res;
+ apr_dbd_column_t **columns;
+ apr_dbd_row_t *next_row;
+ int columnCount;
+ int rownum;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ sqlite3 *handle;
+ sqlite3_stmt *stmt;
+ apr_dbd_row_t *next_row;
+ size_t sz;
+ int tuples;
+ char **col_names;
+ apr_pool_t *pool;
+};
+
+struct apr_dbd_prepared_t {
+ sqlite3_stmt *stmt;
+ apr_dbd_prepared_t *next;
+ int nargs;
+ int nvals;
+ apr_dbd_type_e *types;
+};
+
+#define dbd_sqlite3_is_success(x) (((x) == SQLITE_DONE) || ((x) == SQLITE_OK))
+
+static int dbd_sqlite3_select_internal(apr_pool_t *pool,
+ apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ sqlite3_stmt *stmt, int seek)
+{
+ int ret, retry_count = 0, column_count;
+ size_t i, num_tuples = 0;
+ int increment = 0;
+ apr_dbd_row_t *row = NULL;
+ apr_dbd_row_t *lastrow = NULL;
+ apr_dbd_column_t *column;
+ char *hold = NULL;
+
+ column_count = sqlite3_column_count(stmt);
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->stmt = stmt;
+ (*results)->sz = column_count;
+ (*results)->random = seek;
+ (*results)->next_row = 0;
+ (*results)->tuples = 0;
+ (*results)->col_names = apr_pcalloc(pool, column_count * sizeof(char *));
+ (*results)->pool = pool;
+ do {
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_BUSY) {
+ if (retry_count++ > MAX_RETRY_COUNT) {
+ ret = SQLITE_ERROR;
+ } else {
+ apr_dbd_mutex_unlock();
+ apr_sleep(MAX_RETRY_SLEEP);
+ apr_dbd_mutex_lock();
+ }
+ } else if (ret == SQLITE_ROW) {
+ int length;
+ row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+ row->res = *results;
+ increment = sizeof(apr_dbd_column_t *);
+ length = increment * (*results)->sz;
+ row->columns = apr_palloc(pool, length);
+ row->columnCount = column_count;
+ for (i = 0; i < (*results)->sz; i++) {
+ column = apr_palloc(pool, sizeof(apr_dbd_column_t));
+ row->columns[i] = column;
+ /* copy column name once only */
+ if ((*results)->col_names[i] == NULL) {
+ (*results)->col_names[i] =
+ apr_pstrdup(pool, sqlite3_column_name(stmt, i));
+ }
+ column->name = (*results)->col_names[i];
+ column->size = sqlite3_column_bytes(stmt, i);
+ column->type = sqlite3_column_type(stmt, i);
+ column->value = NULL;
+ switch (column->type) {
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER:
+ case SQLITE_TEXT:
+ hold = (char *) sqlite3_column_text(stmt, i);
+ if (hold) {
+ column->value = apr_pstrmemdup(pool, hold,
+ column->size);
+ }
+ break;
+ case SQLITE_BLOB:
+ hold = (char *) sqlite3_column_blob(stmt, i);
+ if (hold) {
+ column->value = apr_pstrmemdup(pool, hold,
+ column->size);
+ }
+ break;
+ case SQLITE_NULL:
+ break;
+ }
+ }
+ row->rownum = num_tuples++;
+ row->next_row = 0;
+ (*results)->tuples = num_tuples;
+ if ((*results)->next_row == 0) {
+ (*results)->next_row = row;
+ }
+ if (lastrow != 0) {
+ lastrow->next_row = row;
+ }
+ lastrow = row;
+ }
+ } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_select(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results, const char *query,
+ int seek)
+{
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_prepare(sql->conn, query, strlen(query), &stmt, &tail);
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = dbd_sqlite3_select_internal(pool, sql, results, stmt, seek);
+ }
+ sqlite3_finalize(stmt);
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static const char *dbd_sqlite3_get_name(const apr_dbd_results_t *res, int n)
+{
+ if ((n < 0) || ((size_t)n >= res->sz)) {
+ return NULL;
+ }
+
+ return res->col_names[n];
+}
+
+static int dbd_sqlite3_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **rowp, int rownum)
+{
+ int i = 0;
+
+ if (rownum == -1) {
+ *rowp = res->next_row;
+ if (*rowp == 0)
+ return -1;
+ res->next_row = (*rowp)->next_row;
+ return 0;
+ }
+ if (rownum > res->tuples) {
+ return -1;
+ }
+ rownum--;
+ *rowp = res->next_row;
+ for (; *rowp != 0; i++, *rowp = (*rowp)->next_row) {
+ if (i == rownum) {
+ return 0;
+ }
+ }
+
+ return -1;
+
+}
+
+static const char *dbd_sqlite3_get_entry(const apr_dbd_row_t *row, int n)
+{
+ apr_dbd_column_t *column;
+ const char *value;
+ if ((n < 0) || (n >= row->columnCount)) {
+ return NULL;
+ }
+ column = row->columns[n];
+ value = column->value;
+ return value;
+}
+
+static apr_status_t dbd_sqlite3_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ if ((n < 0) || ((size_t)n >= row->res->sz)) {
+ return APR_EGENERAL;
+ }
+
+ if (row->columns[n]->type == SQLITE_NULL) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = (float)atof(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(row->columns[n]->value);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = row->columns[n]->value;
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_pool_create(row->columns[n]->value,
+ row->columns[n]->size,
+ row->res->pool, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static const char *dbd_sqlite3_error(apr_dbd_t *sql, int n)
+{
+ return sqlite3_errmsg(sql->conn);
+}
+
+static int dbd_sqlite3_query_internal(apr_dbd_t *sql, sqlite3_stmt *stmt,
+ int *nrows)
+{
+ int ret = -1, retry_count = 0;
+
+ while(retry_count++ <= MAX_RETRY_COUNT) {
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_BUSY)
+ break;
+
+ apr_dbd_mutex_unlock();
+ apr_sleep(MAX_RETRY_SLEEP);
+ apr_dbd_mutex_lock();
+ }
+
+ *nrows = sqlite3_changes(sql->conn);
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ int ret = -1, length = 0;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ length = strlen(query);
+ apr_dbd_mutex_lock();
+
+ do {
+ ret = sqlite3_prepare(sql->conn, query, length, &stmt, &tail);
+ if (ret != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ break;
+ }
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_finalize(stmt);
+ length -= (tail - query);
+ query = tail;
+ } while (length > 0);
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static apr_status_t free_mem(void *data)
+{
+ sqlite3_free(data);
+ return APR_SUCCESS;
+}
+
+static const char *dbd_sqlite3_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ char *ret = sqlite3_mprintf("%q", arg);
+ apr_pool_cleanup_register(pool, ret, free_mem,
+ apr_pool_cleanup_null);
+ return ret;
+}
+
+static int dbd_sqlite3_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ sqlite3_stmt *stmt;
+ const char *tail = NULL;
+ int ret;
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_prepare(sql->conn, query, strlen(query), &stmt, &tail);
+ if (ret == SQLITE_OK) {
+ apr_dbd_prepared_t *prep;
+
+ prep = apr_pcalloc(sql->pool, sizeof(*prep));
+ prep->stmt = stmt;
+ prep->next = sql->prep;
+ prep->nargs = nargs;
+ prep->nvals = nvals;
+ prep->types = types;
+
+ /* link new statement to the handle */
+ sql->prep = prep;
+
+ *statement = prep;
+ } else {
+ sqlite3_finalize(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ return ret;
+}
+
+static void dbd_sqlite3_bind(apr_dbd_prepared_t *statement, const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int i, j;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ if (values[j] == NULL) {
+ sqlite3_bind_null(stmt, i + 1);
+ }
+ else {
+ switch (statement->types[i]) {
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char *)values[j];
+ int size = atoi((char*)values[++j]);
+
+ /* skip table and column */
+ j += 2;
+
+ sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC);
+ }
+ break;
+ default:
+ sqlite3_bind_text(stmt, i + 1, values[j],
+ strlen(values[j]), SQLITE_STATIC);
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+static int dbd_sqlite3_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int ret = -1;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ dbd_sqlite3_bind(statement, values);
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_reset(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_sqlite3_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_sqlite3_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement, int seek,
+ const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ dbd_sqlite3_bind(statement, values);
+
+ ret = dbd_sqlite3_select_internal(pool, sql, results, stmt, seek);
+
+ sqlite3_reset(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement, int seek,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_sqlite3_pselect(pool, sql, results, statement, seek, values);
+}
+
+static void dbd_sqlite3_bbind(apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int i, j;
+ apr_dbd_type_e type;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ sqlite3_bind_int(stmt, i + 1, *(char*)values[j]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned char*)values[j]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ sqlite3_bind_int(stmt, i + 1, *(short*)values[j]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned short*)values[j]);
+ break;
+ case APR_DBD_TYPE_INT:
+ sqlite3_bind_int(stmt, i + 1, *(int*)values[j]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned int*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ sqlite3_bind_int64(stmt, i + 1, *(long*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ sqlite3_bind_int64(stmt, i + 1, *(unsigned long*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ sqlite3_bind_int64(stmt, i + 1, *(apr_int64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ sqlite3_bind_int64(stmt, i + 1, *(apr_uint64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ sqlite3_bind_double(stmt, i + 1, *(float*)values[j]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ sqlite3_bind_double(stmt, i + 1, *(double*)values[j]);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ sqlite3_bind_text(stmt, i + 1, values[j], strlen(values[j]),
+ SQLITE_STATIC);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char*)values[j];
+ apr_size_t size = *(apr_size_t*)values[++j];
+
+ sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC);
+
+ /* skip table and column */
+ j += 2;
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ default:
+ sqlite3_bind_null(stmt, i + 1);
+ break;
+ }
+ }
+
+ return;
+}
+
+static int dbd_sqlite3_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int ret = -1;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ dbd_sqlite3_bbind(statement, values);
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_reset(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+ int *nrows, apr_dbd_prepared_t * statement,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_sqlite3_pbquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_sqlite3_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement,
+ int seek, const void **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ dbd_sqlite3_bbind(statement, values);
+
+ ret = dbd_sqlite3_select_internal(pool, sql, results, stmt, seek);
+
+ sqlite3_reset(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+ apr_dbd_results_t ** results,
+ apr_dbd_prepared_t * statement, int seek,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_sqlite3_pbselect(pool, sql, results, statement, seek, values);
+}
+
+static int dbd_sqlite3_start_transaction(apr_pool_t *pool,
+ apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ int ret = 0;
+ int nrows = 0;
+
+ ret = dbd_sqlite3_query(handle, &nrows, "BEGIN IMMEDIATE");
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ }
+
+ return ret;
+}
+
+static int dbd_sqlite3_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int ret = -1; /* ending transaction that was never started is an error */
+ int nrows = 0;
+
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+ trans->errnum = 0;
+ ret = dbd_sqlite3_query(trans->handle, &nrows, "ROLLBACK");
+ } else {
+ ret = dbd_sqlite3_query(trans->handle, &nrows, "COMMIT");
+ }
+ trans->handle->trans = NULL;
+ }
+
+ return ret;
+}
+
+static int dbd_sqlite3_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_sqlite3_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+static apr_dbd_t *dbd_sqlite3_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ apr_dbd_t *sql = NULL;
+ sqlite3 *conn = NULL;
+ int sqlres;
+ if (!params)
+ return NULL;
+ sqlres = sqlite3_open(params, &conn);
+ if (sqlres != SQLITE_OK) {
+ if (error) {
+ *error = apr_pstrdup(pool, sqlite3_errmsg(conn));
+ }
+ sqlite3_close(conn);
+ return NULL;
+ }
+ /* should we register rand or power functions to the sqlite VM? */
+ sql = apr_pcalloc(pool, sizeof(*sql));
+ sql->conn = conn;
+ sql->pool = pool;
+ sql->trans = NULL;
+
+ return sql;
+}
+
+static apr_status_t dbd_sqlite3_close(apr_dbd_t *handle)
+{
+ apr_dbd_prepared_t *prep = handle->prep;
+
+ /* finalize all prepared statements, or we'll get SQLITE_BUSY on close */
+ while (prep) {
+ sqlite3_finalize(prep->stmt);
+ prep = prep->next;
+ }
+
+ sqlite3_close(handle->conn);
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_sqlite3_check_conn(apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ return (handle->conn != NULL) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+static int dbd_sqlite3_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+ const char *name)
+{
+ return APR_ENOTIMPL;
+}
+
+static void *dbd_sqlite3_native(apr_dbd_t *handle)
+{
+ return handle->conn;
+}
+
+static int dbd_sqlite3_num_cols(apr_dbd_results_t *res)
+{
+ return res->sz;
+}
+
+static int dbd_sqlite3_num_tuples(apr_dbd_results_t *res)
+{
+ return res->tuples;
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_sqlite3_driver = {
+ "sqlite3",
+ NULL,
+ dbd_sqlite3_native,
+ dbd_sqlite3_open,
+ dbd_sqlite3_check_conn,
+ dbd_sqlite3_close,
+ dbd_sqlite3_select_db,
+ dbd_sqlite3_start_transaction,
+ dbd_sqlite3_end_transaction,
+ dbd_sqlite3_query,
+ dbd_sqlite3_select,
+ dbd_sqlite3_num_cols,
+ dbd_sqlite3_num_tuples,
+ dbd_sqlite3_get_row,
+ dbd_sqlite3_get_entry,
+ dbd_sqlite3_error,
+ dbd_sqlite3_escape,
+ dbd_sqlite3_prepare,
+ dbd_sqlite3_pvquery,
+ dbd_sqlite3_pvselect,
+ dbd_sqlite3_pquery,
+ dbd_sqlite3_pselect,
+ dbd_sqlite3_get_name,
+ dbd_sqlite3_transaction_mode_get,
+ dbd_sqlite3_transaction_mode_set,
+ "?",
+ dbd_sqlite3_pvbquery,
+ dbd_sqlite3_pvbselect,
+ dbd_sqlite3_pbquery,
+ dbd_sqlite3_pbselect,
+ dbd_sqlite3_datum_get
+};
+#endif
diff --git a/dbd/apr_dbd_sqlite3.dsp b/dbd/apr_dbd_sqlite3.dsp
new file mode 100644
index 0000000..8b33222
--- /dev/null
+++ b/dbd/apr_dbd_sqlite3.dsp
@@ -0,0 +1,207 @@
+# Microsoft Developer Studio Project File - Name="apr_dbd_sqlite3" - 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_sqlite3 - Win32 Release
+!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_sqlite3.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_sqlite3.mak" CFG="apr_dbd_sqlite3 - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "apr_dbd_sqlite3 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite3 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite3 - x64 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apr_dbd_sqlite3 - x64 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_sqlite3 - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE3=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite3_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/apr_dbd_sqlite3-1.res" /d DLL_NAME="apr_dbd_sqlite3" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /out:"Release\apr_dbd_sqlite3-1.dll" /pdb:"Release\apr_dbd_sqlite3-1.pdb" /implib:"Release\apr_dbd_sqlite3-1.lib" /MACHINE:X86 /opt:ref
+# Begin Special Build Tool
+TargetPath=Release\apr_dbd_sqlite3-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite3 - 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 /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE3=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite3_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/apr_dbd_sqlite3-1.res" /d DLL_NAME="apr_dbd_sqlite3" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /out:"Debug\apr_dbd_sqlite3-1.dll" /pdb:"Debug\apr_dbd_sqlite3-1.pdb" /implib:"Debug\apr_dbd_sqlite3-1.lib" /MACHINE:X86
+# Begin Special Build Tool
+TargetPath=Debug\apr_dbd_sqlite3-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite3 - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "x64\Release"
+# PROP BASE Intermediate_Dir "x64\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "x64\Release"
+# PROP Intermediate_Dir "x64\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "../include" /I "../../apr/include" /I "../include/private" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE3=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite3_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"x64/Release/apr_dbd_sqlite3-1.res" /d DLL_NAME="apr_dbd_sqlite3" /d "NDEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Release\apr_dbd_sqlite3-1.dll" /pdb:"x64\Release\apr_dbd_sqlite3-1.pdb" /implib:"x64\Release\apr_dbd_sqlite3-1.lib" /MACHINE:X64 /opt:ref
+# Begin Special Build Tool
+TargetPath=x64\Release\apr_dbd_sqlite3-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "apr_dbd_sqlite3 - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "x64\Debug"
+# PROP BASE Intermediate_Dir "x64\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "x64\Debug"
+# PROP Intermediate_Dir "x64\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "../include" /I "../../apr/include" /I "../include/private" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "APU_DSO_MODULE_BUILD" /D APU_HAVE_SQLITE3=1 /Fo"$(INTDIR)\" /Fd"$(INTDIR)\apr_dbd_sqlite3_src" /FD /EHsc /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"x64/Debug/apr_dbd_sqlite3-1.res" /d DLL_NAME="apr_dbd_sqlite3" /d "_DEBUG" /d "APU_VERSION_ONLY" /I "../include" /I "../../apr/include"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib sqlite3.lib /nologo /base:"0x6EF20000" /subsystem:windows /dll /incremental:no /debug /out:"x64\Debug\apr_dbd_sqlite3-1.dll" /pdb:"x64\Debug\apr_dbd_sqlite3-1.pdb" /implib:"x64\Debug\apr_dbd_sqlite3-1.lib" /MACHINE:X64
+# Begin Special Build Tool
+TargetPath=x64\Debug\apr_dbd_sqlite3-1.dll
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "apr_dbd_sqlite3 - Win32 Release"
+# Name "apr_dbd_sqlite3 - Win32 Debug"
+# Name "apr_dbd_sqlite3 - x64 Release"
+# Name "apr_dbd_sqlite3 - x64 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\apr_dbd_sqlite3.c
+# End Source File
+# End Group
+# Begin Group "Public Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\apr_dbd.h
+# End Source File
+# End Group
+# Begin Group "Internal Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\include\private\apu_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_dbd_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\private\apu_internal.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\libaprutil.rc
+# End Source File
+# End Target
+# End Project