summaryrefslogtreecommitdiff
path: root/dbd
diff options
context:
space:
mode:
Diffstat (limited to 'dbd')
-rw-r--r--dbd/NWGNUdbdmysql296
-rw-r--r--dbd/NWGNUdbdpgsql296
-rw-r--r--dbd/NWGNUdbdsqli2295
-rw-r--r--dbd/NWGNUdbdsqli3297
-rw-r--r--dbd/NWGNUmakefile259
-rw-r--r--dbd/apr_dbd.c305
-rw-r--r--dbd/apr_dbd_mysql.c771
-rw-r--r--dbd/apr_dbd_pgsql.c664
-rw-r--r--dbd/apr_dbd_sqlite2.c396
-rw-r--r--dbd/apr_dbd_sqlite3.c723
10 files changed, 4302 insertions, 0 deletions
diff --git a/dbd/NWGNUdbdmysql b/dbd/NWGNUdbdmysql
new file mode 100644
index 0000000..cc207e2
--- /dev/null
+++ b/dbd/NWGNUdbdmysql
@@ -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
+MYSQL_INC = $(MYSQLSDK)/include
+MYSQL_IMP = $(MYSQLSDK)/lib/libmysql.imp
+MYSQL_LIB = $(MYSQLSDK)/lib/libmysqlclient_r.lib $(MYSQLSDK)/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 \
+ $(APRUTIL)/include \
+ $(APRUTIL)/include/private \
+ $(APR) \
+ $(MYSQL_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DAPU_HAVE_MYSQL=1 \
+ -DHAVE_MYSQL_H \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+ifdef LINK_STATIC
+XLFLAGS += \
+ -l $(MYSQLSDK)/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 = 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 = _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM = _LibCPostlude
+
+#
+# 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 = AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# 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 = \
+ libcpre.o \
+ $(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 = \
+ @$(APR)/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 $(APR_WORK)\build\NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdpgsql b/dbd/NWGNUdbdpgsql
new file mode 100644
index 0000000..e90665c
--- /dev/null
+++ b/dbd/NWGNUdbdpgsql
@@ -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
+PGSQL_INC = $(PGSQLSDK)/inc
+PGSQL_IMP = $(PGSQLSDK)/imp/libpq.imp
+PGSQL_LIB = $(PGSQLSDK)/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 \
+ $(APRUTIL)/include \
+ $(APRUTIL)/include/private \
+ $(APR) \
+ $(PGSQL_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -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)
+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 = _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM = _LibCPostlude
+
+#
+# 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 = AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# 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 = \
+ libcpre.o \
+ $(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 = \
+ @$(APR)/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 $(APR_WORK)\build\NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdsqli2 b/dbd/NWGNUdbdsqli2
new file mode 100644
index 0000000..7aa95c9
--- /dev/null
+++ b/dbd/NWGNUdbdsqli2
@@ -0,0 +1,295 @@
+#
+# 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 \
+ $(APRUTIL)/include \
+ $(APRUTIL)/include/private \
+ $(APR) \
+ $(SQLITE2_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -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 = _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM = _LibCPostlude
+
+#
+# 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 = AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# 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 = \
+ libcpre.o \
+ $(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 $(APR_WORK)\build\NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUdbdsqli3 b/dbd/NWGNUdbdsqli3
new file mode 100644
index 0000000..81bd4db
--- /dev/null
+++ b/dbd/NWGNUdbdsqli3
@@ -0,0 +1,297 @@
+#
+# 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 \
+ $(APRUTIL)/include \
+ $(APRUTIL)/include/private \
+ $(APR) \
+ $(SQLITE3_INC) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -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 = _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM = _LibCPostlude
+
+#
+# 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 = AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# 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 = \
+ libcpre.o \
+ $(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 $(APR_WORK)\build\NWGNUtail.inc
+
+
+
diff --git a/dbd/NWGNUmakefile b/dbd/NWGNUmakefile
new file mode 100644
index 0000000..bf04fc8
--- /dev/null
+++ b/dbd/NWGNUmakefile
@@ -0,0 +1,259 @@
+#
+# 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
+
+#
+# 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
+ copy $(OBJDIR)\*.nlm $(INSTALLBASE)
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APR_WORK)\build\NWGNUtail.inc
+
+
diff --git a/dbd/apr_dbd.c b/dbd/apr_dbd.c
new file mode 100644
index 0000000..4eed391
--- /dev/null
+++ b/dbd/apr_dbd.c
@@ -0,0 +1,305 @@
+/* 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 <stdio.h>
+
+#include "apu.h"
+#include "apr_pools.h"
+#include "apr_dbd_internal.h"
+#include "apr_dbd.h"
+#include "apr_hash.h"
+#include "apr_thread_mutex.h"
+#include "apr_dso.h"
+#include "apr_strings.h"
+
+static apr_hash_t *drivers = NULL;
+
+#define CLEANUP_CAST (apr_status_t (*)(void*))
+
+/* Once the autofoo supports building it for dynamic load, we can use
+ * #define APR_DSO_BUILD APR_HAS_DSO
+ */
+
+#if APR_DSO_BUILD
+#if APR_HAS_THREADS
+static apr_thread_mutex_t* mutex = NULL;
+#endif
+#else
+#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;
+
+ if (drivers != NULL) {
+ return APR_SUCCESS;
+ }
+ drivers = apr_hash_make(pool);
+ apr_pool_cleanup_register(pool, NULL, apr_dbd_term,
+ apr_pool_cleanup_null);
+
+#if APR_DSO_BUILD
+
+#if APR_HAS_THREADS
+ ret = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ /* This already registers a pool cleanup */
+#endif
+
+#else
+
+#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_SOME_OTHER_BACKEND
+ DRIVER_LOAD("firebird", apr_dbd_other_driver, pool);
+#endif
+#endif
+ 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 APR_DSO_BUILD
+ char path[80];
+ apr_dso_handle_t *dlhandle = NULL;
+#endif
+ apr_status_t rv;
+
+ *driver = apr_hash_get(drivers, name, APR_HASH_KEY_STRING);
+ if (*driver) {
+ return APR_SUCCESS;
+ }
+
+#if APR_DSO_BUILD
+
+#if APR_HAS_THREADS
+ rv = apr_thread_mutex_lock(mutex);
+ if (rv != APR_SUCCESS) {
+ goto unlock;
+ }
+ *driver = apr_hash_get(drivers, name, APR_HASH_KEY_STRING);
+ if (*driver) {
+ goto unlock;
+ }
+#endif
+
+#ifdef WIN32
+ apr_snprintf(path, sizeof path, "apr_dbd_%s.dll", name);
+#elif defined(NETWARE)
+ apr_snprintf(path, sizeof path, "dbd%s.nlm", name);
+#else
+ apr_snprintf(path, sizeof path, "apr_dbd_%s.so", name);
+#endif
+ rv = apr_dso_load(&dlhandle, path, pool);
+ if (rv != APR_SUCCESS) { /* APR_EDSOOPEN */
+ goto unlock;
+ }
+ apr_snprintf(path, sizeof path, "apr_dbd_%s_driver", name);
+ rv = apr_dso_sym((void*)driver, dlhandle, path);
+ if (rv != APR_SUCCESS) { /* APR_ESYMNOTFOUND */
+ apr_dso_unload(dlhandle);
+ goto unlock;
+ }
+ if ((*driver)->init) {
+ (*driver)->init(pool);
+ }
+ apr_hash_set(drivers, name, APR_HASH_KEY_STRING, *driver);
+
+unlock:
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(mutex);
+#endif
+
+#else /* APR_DSO_BUILD - so if it wasn't already loaded, it's NOTIMPL */
+ rv = APR_ENOTIMPL;
+#endif
+
+ return rv;
+}
+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)
+{
+ apr_status_t rv;
+ *handle = (driver->open)(pool, params);
+ if (*handle == NULL) {
+ return APR_EGENERAL;
+ }
+ rv = apr_dbd_check_conn(driver, pool, *handle);
+ if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
+ apr_dbd_close(driver, *handle);
+ return APR_EGENERAL;
+ }
+ return APR_SUCCESS;
+}
+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(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_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)
+{
+ return driver->prepare(pool,handle,query,label,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,nargs,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,nargs,args);
+}
+APU_DECLARE(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(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;
+}
diff --git a/dbd/apr_dbd_mysql.c b/dbd/apr_dbd_mysql.c
new file mode 100644
index 0000000..65b862e
--- /dev/null
+++ b/dbd/apr_dbd_mysql.c
@@ -0,0 +1,771 @@
+/* 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"
+#define HAVE_MYSQL_MYSQL_H
+
+#if APU_HAVE_MYSQL
+
+#include "apu_version.h"
+#include "apu_config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#ifdef HAVE_MYSQL_H
+#include <mysql.h>
+#include <errmsg.h>
+#elif defined(HAVE_MYSQL_MYSQL_H)
+#include <mysql/mysql.h>
+#include <mysql/errmsg.h>
+#endif
+
+#include "apr_strings.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;
+};
+
+struct apr_dbd_transaction_t {
+ 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;
+};
+struct apr_dbd_row_t {
+ MYSQL_ROW row;
+ apr_dbd_results_t *res;
+};
+
+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;
+ 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 (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+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);
+ }
+ }
+ 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);
+ }
+ }
+ 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;
+ }
+ 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 (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 (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 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 = mysql_affected_rows(sql->conn);
+ if (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,
+ apr_dbd_prepared_t **statement)
+{
+ /* Translate from apr_dbd to native query format */
+ char *myquery = apr_pstrdup(pool, query);
+ char *p = myquery;
+ const char *q;
+ int ret;
+ for (q = query; *q; ++q) {
+ if (q[0] == '%') {
+ if (isalpha(q[1])) {
+ *p++ = '?';
+ ++q;
+ }
+ else if (q[1] == '%') {
+ /* reduce %% to % */
+ *p++ = *q++;
+ }
+ else {
+ *p++ = *q;
+ }
+ }
+ else {
+ *p++ = *q;
+ }
+ }
+ *p = 0;
+ 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, myquery, strlen(myquery));
+
+ if (ret != 0) {
+ ret = mysql_stmt_errno((*statement)->stmt);
+ }
+
+ return ret;
+ }
+
+ return CR_OUT_OF_MEMORY;
+}
+
+static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ int nargs, const char **values)
+{
+ MYSQL_BIND *bind;
+ char *arg;
+ int ret;
+ int i;
+ my_bool is_null = FALSE;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+ nargs = mysql_stmt_param_count(statement->stmt);
+
+ bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+ for (i=0; i < nargs; ++i) {
+ arg = (char*)values[i];
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = arg;
+ bind[i].buffer_length = strlen(arg);
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_null = &is_null;
+ bind[i].is_unsigned = 0;
+ }
+
+ 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 = mysql_stmt_affected_rows(statement->stmt);
+ }
+ if (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)
+{
+ MYSQL_BIND *bind;
+ char *arg;
+ int ret;
+ int nargs = 0;
+ int i;
+ my_bool is_null = FALSE;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+ nargs = mysql_stmt_param_count(statement->stmt);
+
+ bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+ for (i=0; i < nargs; ++i) {
+ arg = va_arg(args, char*);
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = arg;
+ bind[i].buffer_length = strlen(arg);
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_null = &is_null;
+ bind[i].is_unsigned = 0;
+ }
+
+ 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 = mysql_stmt_affected_rows(statement->stmt);
+ }
+ if (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ 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,
+ int nargs, const char **args)
+{
+ int i;
+ int nfields;
+ char *arg;
+ my_bool is_null = FALSE;
+ my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+ my_bool *error;
+#endif
+ int ret;
+ unsigned long *length, maxlen;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ nargs = mysql_stmt_param_count(statement->stmt);
+ bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+
+ for (i=0; i < nargs; ++i) {
+ arg = (char*)args[i];
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = arg;
+ bind[i].buffer_length = strlen(arg);
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_null = &is_null;
+ bind[i].is_unsigned = 0;
+ }
+
+ 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);
+ 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;
+ (*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);
+ }
+ if (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)
+{
+ int i;
+ int nfields;
+ char *arg;
+ my_bool is_null = FALSE;
+ my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+ my_bool *error;
+#endif
+ int ret;
+ unsigned long *length, maxlen;
+ int nargs;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ nargs = mysql_stmt_param_count(statement->stmt);
+ bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+
+ for (i=0; i < nargs; ++i) {
+ arg = va_arg(args, char*);
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = arg;
+ bind[i].buffer_length = strlen(arg);
+ bind[i].length = &bind[i].buffer_length;
+ bind[i].is_null = &is_null;
+ bind[i].is_unsigned = 0;
+ }
+
+ 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);
+ 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;
+ (*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);
+ }
+ if (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int ret = -1;
+ if (trans) {
+ if (trans->errnum) {
+ 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 apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
+{
+ 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},
+ {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, '=')) {
+ for (key = ptr-1; isspace(*key); --key);
+ klen = 0;
+ while (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; 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
+ /* 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) {
+ 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_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
+};
+
+#endif
diff --git a/dbd/apr_dbd_pgsql.c b/dbd/apr_dbd_pgsql.c
new file mode 100644
index 0000000..2fc28ac
--- /dev/null
+++ b/dbd/apr_dbd_pgsql.c
@@ -0,0 +1,664 @@
+/* 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_dbd_internal.h"
+
+#define QUERY_MAX_ARGS 40
+
+struct apr_dbd_transaction_t {
+ 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;
+};
+
+struct apr_dbd_row_t {
+ int n;
+ apr_dbd_results_t *res;
+};
+
+struct apr_dbd_prepared_t {
+ const char *name;
+ int prepared;
+ int nargs;
+};
+
+#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 */
+ 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 (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+ }
+ 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;
+ apr_pool_cleanup_register(pool, res, clear_result,
+ apr_pool_cleanup_null);
+ }
+ else {
+ if (PQsendQuery(sql->conn, query) == 0) {
+ if (sql->trans) {
+ sql->trans->errnum = 1;
+ }
+ return 1;
+ }
+ if (*results == NULL) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->random = seek;
+ (*results)->handle = sql->conn;
+ }
+ return 0;
+}
+
+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;
+ row->n = sequential ? 0 : rownum;
+ }
+ else {
+ if ( sequential ) {
+ ++row->n;
+ }
+ else {
+ row->n = rownum;
+ }
+ }
+
+ if (res->random) {
+ if (row->n >= res->ntuples) {
+ *rowp = NULL;
+ apr_pool_cleanup_run(pool, res->res, clear_result);
+ res->res = NULL;
+ return -1;
+ }
+ }
+ else {
+ if (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 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;
+ }
+ 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 (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ 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 + 1));
+ 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,
+ apr_dbd_prepared_t **statement)
+{
+ char *sqlcmd;
+ char *sqlptr;
+ size_t length;
+ size_t i = 0;
+ const char *args[QUERY_MAX_ARGS];
+ size_t alen;
+ int ret;
+ PGresult *res;
+ char *pgquery;
+ char *pgptr;
+
+ if (!*statement) {
+ *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ (*statement)->nargs = 0;
+ /* Translate from apr_dbd to native query format */
+ for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+ if (sqlptr[0] == '%') {
+ if (isalpha(sqlptr[1])) {
+ ++(*statement)->nargs;
+ }
+ else if (sqlptr[1] == '%') {
+ ++sqlptr;
+ }
+ }
+ }
+ length = strlen(query) + 1;
+ if ((*statement)->nargs > 8) {
+ length += (*statement)->nargs - 8;
+ }
+ pgptr = pgquery = apr_palloc(pool, length) ;
+
+ for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+ if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
+ *pgptr++ = '$';
+ if (i < 9) {
+ *pgptr++ = '1' + i;
+ }
+ else {
+ *pgptr++ = '0' + ((i+1)/10);
+ *pgptr++ = '0' + ((i+1)%10);
+ }
+ switch (*++sqlptr) {
+ case 'd':
+ args[i] = "integer";
+ break;
+ case 's':
+ args[i] = "varchar";
+ break;
+ default:
+ args[i] = "varchar";
+ break;
+ }
+ length += 1 + strlen(args[i]);
+ ++i;
+ }
+ else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
+ /* reduce %% to % */
+ *pgptr++ = *sqlptr++;
+ }
+ else {
+ *pgptr++ = *sqlptr;
+ }
+ }
+ *pgptr = 0;
+
+ if (!label) {
+ /* don't really prepare; use in execParams instead */
+ (*statement)->prepared = 0;
+ (*statement)->name = apr_pstrdup(pool, pgquery);
+ 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 ((*statement)->nargs > 0) {
+ memcpy(sqlptr, " (",2);
+ sqlptr += 2;
+ for (i=0; i < (*statement)->nargs; ++i) {
+ alen = strlen(args[i]);
+ memcpy(sqlptr, args[i], alen);
+ sqlptr += alen;
+ *sqlptr++ = ',';
+ }
+ sqlptr[-1] = ')';
+ }
+ memcpy(sqlptr, " AS ", 4);
+ sqlptr += 4;
+ memcpy(sqlptr, pgquery, strlen(pgquery));
+ sqlptr += strlen(pgquery);
+ *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(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ int nargs, const char **values)
+{
+ int ret;
+ PGresult *res;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ if (statement->prepared) {
+ res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
+ 0);
+ }
+ else {
+ res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
+ 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 (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+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->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = apr_pstrdup(pool, va_arg(args, const char*));
+ }
+
+ return dbd_pgsql_pquery(pool, sql, nrows, statement,
+ statement->nargs, values);
+}
+
+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, int nargs, const char **values)
+{
+ PGresult *res;
+ int rv;
+ int ret = 0;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ if (seek) { /* synchronous query */
+ if (statement->prepared) {
+ res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
+ 0, 0);
+ }
+ else {
+ res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
+ 0, 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 (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+ }
+ 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;
+ apr_pool_cleanup_register(pool, res, clear_result,
+ apr_pool_cleanup_null);
+ }
+ else {
+ if (statement->prepared) {
+ rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
+ 0, 0, 0);
+ }
+ else {
+ rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
+ values, 0, 0, 0);
+ }
+ if (rv == 0) {
+ if (sql->trans) {
+ sql->trans->errnum = 1;
+ }
+ return 1;
+ }
+ if (!*results) {
+ *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*results)->random = seek;
+ (*results)->handle = sql->conn;
+ }
+
+ if (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+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->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = apr_pstrdup(pool, va_arg(args, const char*));
+ }
+
+ return dbd_pgsql_pselect(pool, sql, results, statement,
+ seek, statement->nargs, 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) {
+ if (trans->errnum) {
+ 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 apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
+{
+ 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) {
+ PQfinish(conn);
+ return 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_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,
+};
+#endif
diff --git a/dbd/apr_dbd_sqlite2.c b/dbd/apr_dbd_sqlite2.c
new file mode 100644
index 0000000..6907be7
--- /dev/null
+++ b/dbd/apr_dbd_sqlite2.c
@@ -0,0 +1,396 @@
+/* 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_dbd_internal.h"
+
+struct apr_dbd_transaction_t {
+ 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;
+};
+
+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;
+
+ if (tuples > 0)
+ apr_pool_cleanup_register(pool, result, free_table,
+ apr_pool_cleanup_null);
+
+ ret = 0;
+ }
+ else {
+ sql->trans->errnum = ret;
+ }
+
+ return ret;
+}
+
+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 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 (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,
+ 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,
+ int nargs, 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, int nargs, 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_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) {
+ if (trans->errnum) {
+ 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 apr_dbd_t *dbd_sqlite_open(apr_pool_t * pool, const char *params_)
+{
+ 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);
+ }
+
+ 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_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,
+};
+#endif
diff --git a/dbd/apr_dbd_sqlite3.c b/dbd/apr_dbd_sqlite3.c
new file mode 100644
index 0000000..32073fc
--- /dev/null
+++ b/dbd/apr_dbd_sqlite3.c
@@ -0,0 +1,723 @@
+/* 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_dbd_internal.h"
+
+#define MAX_RETRY_COUNT 15
+#define MAX_RETRY_SLEEP 100000
+
+struct apr_dbd_transaction_t {
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ sqlite3 *conn;
+ apr_dbd_transaction_t *trans;
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *mutex;
+#endif
+ 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;
+};
+
+struct apr_dbd_prepared_t {
+ sqlite3_stmt *stmt;
+ apr_dbd_prepared_t *next;
+};
+
+#define dbd_sqlite3_is_success(x) (((x) == SQLITE_DONE ) \
+ || ((x) == SQLITE_OK ))
+
+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 i, ret, retry_count = 0;
+ size_t 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;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+
+ ret = sqlite3_prepare(sql->conn, query, strlen(query), &stmt, &tail);
+ if (!dbd_sqlite3_is_success(ret)) {
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ return ret;
+ } else {
+ int column_count;
+ 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 *));
+ do {
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_BUSY) {
+ if (retry_count++ > MAX_RETRY_COUNT) {
+ ret = SQLITE_ERROR;
+ } else {
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+ }
+ } else if (ret == SQLITE_ROW) {
+ int length;
+ apr_dbd_column_t *col;
+ 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 = NULL;
+ hold = (char *) sqlite3_column_text(stmt, i);
+ if (hold) {
+ column->value = apr_palloc(pool, column->size + 1);
+ strncpy(column->value, hold, column->size + 1);
+ }
+ break;
+ case SQLITE_BLOB:
+ break;
+ case SQLITE_NULL:
+ break;
+ }
+ col = row->columns[i];
+ }
+ 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;
+ } else if (ret == SQLITE_DONE) {
+ ret = SQLITE_OK;
+ }
+ } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
+ }
+ ret = sqlite3_finalize(stmt);
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+
+ if (sql->trans) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+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 const char *dbd_sqlite3_error(apr_dbd_t *sql, int n)
+{
+ return sqlite3_errmsg(sql->conn);
+}
+
+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);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+
+ do {
+ int retry_count = 0;
+
+ ret = sqlite3_prepare(sql->conn, query, length, &stmt, &tail);
+ if (ret != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ break;
+ }
+
+ while(retry_count++ <= MAX_RETRY_COUNT) {
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_BUSY)
+ break;
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+ }
+
+ *nrows = sqlite3_changes(sql->conn);
+ sqlite3_finalize(stmt);
+ length -= (tail - query);
+ query = tail;
+ } while (length > 0);
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ if (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,
+ apr_dbd_prepared_t **statement)
+{
+ sqlite3_stmt *stmt;
+ char *p, *slquery = apr_pstrdup(pool, query);
+ const char *tail = NULL, *q;
+ int ret;
+
+ for (p = slquery, q = query; *q; ++q) {
+ if (q[0] == '%') {
+ if (isalpha(q[1])) {
+ *p++ = '?';
+ ++q;
+ }
+ else if (q[1] == '%') {
+ /* reduce %% to % */
+ *p++ = *q++;
+ }
+ else {
+ *p++ = *q;
+ }
+ }
+ else {
+ *p++ = *q;
+ }
+ }
+ *p = 0;
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+
+ ret = sqlite3_prepare(sql->conn, slquery, 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;
+
+ /* link new statement to the handle */
+ sql->prep = prep;
+
+ *statement = prep;
+ } else {
+ sqlite3_finalize(stmt);
+ }
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+
+ return ret;
+}
+
+static int dbd_sqlite3_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ int nargs, const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int ret = -1, retry_count = 0, i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ for (i=0; i < nargs; i++) {
+ sqlite3_bind_text(stmt, i + 1, values[i], strlen(values[i]),
+ SQLITE_STATIC);
+ }
+
+ while(retry_count++ <= MAX_RETRY_COUNT) {
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_BUSY)
+ break;
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+ }
+
+ *nrows = sqlite3_changes(sql->conn);
+
+ sqlite3_reset(stmt);
+ }
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ if (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, nargs;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ nargs = sqlite3_bind_parameter_count(statement->stmt);
+ values = apr_palloc(pool, sizeof(*values) * nargs);
+
+ for (i = 0; i < nargs; i++) {
+ values[i] = apr_pstrdup(pool, va_arg(args, const char*));
+ }
+
+ return dbd_sqlite3_pquery(pool, sql, nrows, statement, nargs, 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,
+ int nargs, const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int i, ret, retry_count = 0;
+ size_t 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;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+
+ ret = sqlite3_reset(stmt);
+ if (ret == SQLITE_OK) {
+ int column_count;
+
+ for (i=0; i < nargs; i++) {
+ sqlite3_bind_text(stmt, i + 1, values[i], strlen(values[i]),
+ SQLITE_STATIC);
+ }
+
+ 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 *));
+ do {
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_BUSY) {
+ if (retry_count++ > MAX_RETRY_COUNT) {
+ ret = SQLITE_ERROR;
+ } else {
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+ apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(sql->mutex);
+#endif
+ }
+ } else if (ret == SQLITE_ROW) {
+ int length;
+ apr_dbd_column_t *col;
+ 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 = NULL;
+ hold = (char *) sqlite3_column_text(stmt, i);
+ if (hold) {
+ column->value = apr_palloc(pool, column->size + 1);
+ strncpy(column->value, hold, column->size + 1);
+ }
+ break;
+ case SQLITE_BLOB:
+ break;
+ case SQLITE_NULL:
+ break;
+ }
+ col = row->columns[i];
+ }
+ 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;
+ } else if (ret == SQLITE_DONE) {
+ ret = SQLITE_OK;
+ }
+ } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
+
+ sqlite3_reset(stmt);
+ }
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(sql->mutex);
+#endif
+
+ if (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, nargs;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ nargs = sqlite3_bind_parameter_count(statement->stmt);
+ values = apr_palloc(pool, sizeof(*values) * nargs);
+
+ for (i = 0; i < nargs; i++) {
+ values[i] = apr_pstrdup(pool, va_arg(args, const char*));
+ }
+
+ return dbd_sqlite3_pselect(pool, sql, results, statement,
+ seek, nargs, 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");
+ 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) {
+ if (trans->errnum) {
+ 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 apr_dbd_t *dbd_sqlite3_open(apr_pool_t *pool, const char *params)
+{
+ apr_dbd_t *sql = NULL;
+ sqlite3 *conn = NULL;
+ apr_status_t res;
+ int sqlres;
+ if (!params)
+ return NULL;
+ sqlres = sqlite3_open(params, &conn);
+ if (sqlres != SQLITE_OK) {
+ 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;
+#if APR_HAS_THREADS
+ /* Create a mutex */
+ res = apr_thread_mutex_create(&sql->mutex, APR_THREAD_MUTEX_DEFAULT,
+ pool);
+ if (res != APR_SUCCESS) {
+ return NULL;
+ }
+#endif
+
+ 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);
+#if APR_HAS_THREADS
+ apr_thread_mutex_destroy(handle->mutex);
+#endif
+ 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_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,
+};
+#endif