apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From n..@apache.org
Subject svn commit: r149483 - in apr/apr-util/trunk/dbd: ./ apr_dbd.c apr_dbd.h apr_dbd_pgsql.c apr_dbd_test.c
Date Wed, 02 Feb 2005 05:14:53 GMT
Author: niq
Date: Tue Feb  1 21:14:50 2005
New Revision: 149483

URL: http://svn.apache.org/viewcvs?view=rev&rev=149483
Log:
Introducing apr_dbd stuff

Added:
    apr/apr-util/trunk/dbd/
    apr/apr-util/trunk/dbd/apr_dbd.c
    apr/apr-util/trunk/dbd/apr_dbd.h
    apr/apr-util/trunk/dbd/apr_dbd_pgsql.c
    apr/apr-util/trunk/dbd/apr_dbd_test.c

Added: apr/apr-util/trunk/dbd/apr_dbd.c
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/dbd/apr_dbd.c?view=auto&rev=149483
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd.c Tue Feb  1 21:14:50 2005
@@ -0,0 +1,164 @@
+/*        Copyright (c) 2004, Nick Kew.  All rights reserved.
+
+        If this is accepted by ASF for inclusion in APR-UTIL,
+        I will assign copyright to ASF and license under ASF terms.
+
+        Otherwise I will retain copyright and license under
+        terms of my choice.
+*/
+
+#include <stdio.h>
+
+#include "apu.h"
+#include "apr_pools.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;
+
+
+#if APR_HAS_DSO
+#if APR_HAS_THREADS
+static apr_thread_mutex_t* mutex = NULL;
+#endif
+#else
+#define DRIVER_LOAD(name,driver,pool) \
+    {   \
+        extern apr_dbd_driver_t *driver; \
+        apr_hash_set(drivers,name,APR_HASH_KEY_STRING,driver); \
+        if (driver->init) {     \
+            driver->init(pool); \
+        }  \
+    }
+#endif
+
+APU_DECLARE(apr_status_t) apr_dbd_init(apr_pool_t *pool)
+{
+    apr_status_t ret;
+    drivers = apr_hash_make(pool);
+
+#if APR_HAS_DSO
+
+#if APR_HAS_THREADS
+    ret = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+    apr_pool_cleanup_register(pool, mutex, (void*)apr_thread_mutex_destroy,
+                              apr_pool_cleanup_null);
+#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_FIREBIRD
+    DRIVER_LOAD("firebird", apr_dbd_firebird_driver, pool);
+#endif
+#if APU_HAVE_MSQL
+    DRIVER_LOAD("msql", apr_dbd_msql_driver, pool);
+#endif
+#if APU_HAVE_DB2
+    DRIVER_LOAD("db2", apr_dbd_db2_driver, pool);
+#endif
+#if APU_HAVE_ODBC
+    DRIVER_LOAD("odbc", apr_dbd_odbc_driver, pool);
+#endif
+#if APU_HAVE_ORACLE
+    DRIVER_LOAD("oracle", apr_dbd_oracle_driver, pool);
+#endif
+#if APU_HAVE_MSSQL
+    DRIVER_LOAD("mssql", apr_dbd_mssql_driver, pool);
+#endif
+#endif
+    return ret;
+}
+APU_DECLARE(apr_status_t) apr_dbd_get_driver(apr_pool_t *pool, const char *name,
+                                             apr_dbd_driver_t **driver)
+{
+#if APR_HAS_DSO
+    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_HAS_DSO
+
+#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
+
+    sprintf(path, "apr_dbd_%s.so", name);
+    rv = apr_dso_load(&dlhandle, path, pool);
+    if (rv != APR_SUCCESS) { /* APR_EDSOOPEN */
+        goto unlock;
+    }
+    sprintf(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_HAS_DSO - so if it wasn't already loaded, it's NOTIMPL */
+    rv = APR_ENOTIMPL;
+#endif
+
+    return rv;
+}
+APU_DECLARE(apr_status_t) apr_dbd_open(apr_dbd_driver_t *driver,
+                                       apr_pool_t *pool, const char *params,
+                                       apr_dbd_t **handle)
+{
+    *handle = driver->open(pool, params);
+    if (*handle == NULL) {
+        return APR_EGENERAL;
+    }
+    if (apr_dbd_check_conn(driver, pool, *handle) != APR_SUCCESS) {
+        apr_dbd_close(driver, *handle);
+        return APR_EGENERAL;
+    }
+    return APR_SUCCESS;
+}
+APU_DECLARE(int) apr_dbd_transaction_start(apr_dbd_driver_t *driver,
+                                           apr_pool_t *pool, apr_dbd_t *handle,
+                                           apr_dbd_transaction **trans)
+{
+    int ret = driver->transaction(pool, handle, trans);
+    if (*trans) {
+        apr_pool_cleanup_register(pool, *trans, (void*)driver->end_transaction,
+                                  apr_pool_cleanup_null);
+    }
+    return ret;
+}
+APU_DECLARE(int) apr_dbd_transaction_end(apr_dbd_driver_t *driver,
+                                         apr_pool_t *pool,
+                                         apr_dbd_transaction *trans)
+{
+    apr_pool_cleanup_kill(pool, trans, (void*)driver->end_transaction);
+    return driver->end_transaction(trans);
+}

Added: apr/apr-util/trunk/dbd/apr_dbd.h
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/dbd/apr_dbd.h?view=auto&rev=149483
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd.h (added)
+++ apr/apr-util/trunk/dbd/apr_dbd.h Tue Feb  1 21:14:50 2005
@@ -0,0 +1,612 @@
+/*        Copyright (c) 2004, Nick Kew.  All rights reserved.
+
+        If this is accepted by ASF for inclusion in APR-UTIL,
+        I will assign copyright to ASF and license under ASF terms.
+
+        Otherwise I will retain copyright and license under
+        terms of my choice.
+*/
+
+#ifndef APR_DBD_H
+#define APR_DBD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Patch required to build against APR/0.9 and HTTPD/2.0
+ *  Invalidate a resource in the pool - e.g. a database connection
+ *  that returns a "lost connection" error and can't be restored.
+ *  Use this instead of apr_reslist_release if the resource is bad.
+ */
+#if APR_HAS_THREADS
+#include "apr_reslist.h"
+APU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist,
+                                                 void *resource);
+#endif
+
+/* These are opaque structs.  Instantiation is up to each backend */
+#ifndef APR_DBD_INTERNAL
+typedef struct apr_dbd_t apr_dbd_t;
+typedef struct apr_dbd_transaction apr_dbd_transaction;
+typedef struct apr_dbd_results apr_dbd_results;
+typedef struct apr_dbd_row apr_dbd_row;
+typedef struct apr_dbd_prepared apr_dbd_prepared;
+#endif
+
+typedef struct apr_dbd_driver_t {
+    /** name */
+    const char *name;
+
+    /** init: allow driver to perform once-only initialisation.
+     *  Called once only.  May be NULL
+     */
+    void (*init)(apr_pool_t *pool);
+
+    /** native_handle: return the native database handle of the underlying db
+     *
+     * @param handle - apr_dbd handle
+     * @return - native handle
+     */
+    void *(*native_handle)(apr_dbd_t *handle);
+
+    /** open: obtain a database connection from the server rec.
+     *  Must be explicitly closed when you're finished with it.
+     *  WARNING: only use this when you need a connection with
+     *  a lifetime other than a request
+     *
+     *  @param pool - a pool to use for error messages (if any).
+     *  @param s - server rec managing the underlying connection/pool.
+     *  @return database handle, or NULL on error.
+     */
+    apr_dbd_t *(*open)(apr_pool_t *pool, const char *params);
+
+    /** check_conn: check status of a database connection
+     *
+     *  @param pool - a pool to use for error messages (if any).
+     *  @param handle - the connection to check
+     *  @return APR_SUCCESS or error
+     */
+    apr_status_t (*check_conn)(apr_pool_t *pool, apr_dbd_t *handle);
+
+    /** close: close/release a connection obtained from open()
+     *
+     *  @param handle - the connection to release
+     *  @return APR_SUCCESS or error
+     */
+    apr_status_t (*close)(apr_dbd_t *handle);
+
+    /** set_dbname: select database name.  May be a no-op if not supported.
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param name - the database to select
+     *  @return 0 for success or error code
+     */
+    int (*set_dbname)(apr_pool_t* pool, apr_dbd_t *handle, const char *name);
+
+    /** transaction: start a transaction.  May be a no-op.
+     *
+     *  @param pool - a pool to use for error messages (if any).
+     *  @param handle - the connection
+     *  @param transaction - ptr to a transaction.  May be null on entry
+     *  @return 0 for success or error code
+     */
+    int (*transaction)(apr_pool_t *pool, apr_dbd_t *handle,
+                       apr_dbd_transaction **trans);
+
+    /** end_transaction: end a transaction
+     *  (commit on success, rollback on error).
+     *  May be a no-op.
+     *
+     *  @param transaction - the transaction.
+     *  @return 0 for success or error code
+     */
+    int (*end_transaction)(apr_dbd_transaction *trans);
+
+    /** query: execute an SQL query that doesn't return a result set
+     *
+     *  @param handle - the connection
+     *  @param transaction - current transaction.  May be null.
+     *  @param nrows - number of rows affected.
+     *  @param statement - the SQL statement to execute
+     *  @return 0 for success or error code
+     */
+    int (*query)(apr_dbd_t *handle, apr_dbd_transaction *trans,
+                 int *nrows, const char *statement);
+
+    /** select: execute an SQL query that returns a result set
+     *
+     *  @param pool - pool to allocate the result set
+     *  @param handle - the connection
+     *  @param transaction - current transaction.  May be null.
+     *  @param res - pointer to result set pointer.  May point to NULL on entry
+     *  @param statement - the SQL statement to execute
+     *  @param random - 1 to support random access to results (seek any row);
+     *                  0 to support only looping through results in order
+     *                    (async access - faster)
+     *  @return 0 for success or error code
+     */
+    int (*select)(apr_pool_t *pool, apr_dbd_t *handle,
+                  apr_dbd_transaction *trans, apr_dbd_results **res,
+                  const char *statement, int random);
+
+    /** num_cols: get the number of columns in a results set
+     *
+     *  @param res - result set.
+     *  @return number of columns
+     */
+    int (*num_cols)(apr_dbd_results *res);
+
+    /** num_tuples: get the number of rows in a results set
+     *  of a synchronous select
+     *
+     *  @param res - result set.
+     *  @return number of rows, or -1 if the results are asynchronous
+     */
+    int (*num_tuples)(apr_dbd_results *res);
+
+    /** get_row: get a row from a result set
+     *
+     *  @param pool - pool to allocate the row
+     *  @param res - result set pointer
+     *  @param row - pointer to row pointer.  May point to NULL on entry
+     *  @param rownum - row number, or -1 for "next row".  Ignored if random
+     *                  access is not supported.
+     *  @return 0 for success, -1 for rownum out of range or data finished
+     */
+    int (*get_row)(apr_pool_t *pool, apr_dbd_results *res,
+                   apr_dbd_row **row, int rownum);
+  
+    /** get_entry: get an entry from a row
+     *
+     *  @param row - row pointer
+     *  @param col - entry number
+     *  @return value from the row, or NULL if col is out of bounds.
+     */
+    const char *(*get_entry)(const apr_dbd_row *row, int col);
+  
+    /** error: get current error message (if any)
+     *
+     *  @param handle - the connection
+     *  @param errnum - error code from operation that returned an error
+     *  @return the database current error message, or message for errnum
+     *          (implementation-dependent whether errnum is ignored)
+     */
+    const char *(*error)(apr_dbd_t *handle, int errnum);
+  
+    /** escape: escape a string so it is safe for use in query/select
+     *
+     *  @param pool - pool to alloc the result from
+     *  @param string - the string to escape
+     *  @param handle - the connection
+     *  @return the escaped, safe string
+     */
+    const char *(*escape)(apr_pool_t *pool, const char *string,
+                          apr_dbd_t *handle);
+  
+    /** prepare: prepare a statement
+     *
+     *  @param pool - pool to alloc the result from
+     *  @param handle - the connection
+     *  @param query - the SQL query
+     *  @param label - A label for the prepared statement.
+     *                 use NULL for temporary prepared statements
+     *                 (eg within a Request in httpd)
+     *  @param statement - statement to prepare.  May point to null on entry.
+     *  @return 0 for success or error code
+     */
+    int (*prepare)(apr_pool_t *pool, apr_dbd_t *handle, const char *query,
+                   const char *label, apr_dbd_prepared **statement);
+
+    /** pvquery: query using a prepared statement + args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param trans - current transaction.  May be null.
+     *  @param nrows - number of rows affected.
+     *  @param statement - the prepared statement to execute
+     *  @param ... - args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pvquery)(apr_pool_t *pool, apr_dbd_t *handle,
+                  apr_dbd_transaction *trans, int *nrows,
+                  apr_dbd_prepared *statement, ...);
+
+    /** pvselect: select using a prepared statement + args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param trans - current transaction.  May be null.
+     *  @param res - pointer to query results.  May point to NULL on entry
+     *  @param statement - the prepared statement to execute
+     *  @param random - Whether to support random-access to results
+     *  @param ... - args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pvselect)(apr_pool_t *pool, apr_dbd_t *handle,
+                    apr_dbd_transaction *trans, apr_dbd_results **res,
+                    apr_dbd_prepared *statement, int random, ...);
+
+    /** pquery: query using a prepared statement + args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param trans - current transaction.  May be null.
+     *  @param nrows - number of rows affected.
+     *  @param statement - the prepared statement to execute
+     *  @param nargs - number of args to prepared statement
+     *  @param args - args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pquery)(apr_pool_t *pool, apr_dbd_t *handle,
+                  apr_dbd_transaction *trans, int *nrows,
+                  apr_dbd_prepared *statement, int nargs, const char **args);
+
+    /** pselect: select using a prepared statement + args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param trans - current transaction.  May be null.
+     *  @param res - pointer to query results.  May point to NULL on entry
+     *  @param statement - the prepared statement to execute
+     *  @param random - Whether to support random-access to results
+     *  @param nargs - number of args to prepared statement
+     *  @param args - args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pselect)(apr_pool_t *pool, apr_dbd_t *handle,
+                   apr_dbd_transaction *trans, apr_dbd_results **res,
+                   apr_dbd_prepared *statement, int random, int nargs,
+                   const char **args);
+
+
+} apr_dbd_driver_t;
+
+/** apr_dbd_init: perform once-only initialisation.  Call once only.
+ *
+ *  @param pool - pool to register any shutdown cleanups, etc
+ */
+APU_DECLARE(apr_status_t) apr_dbd_init(apr_pool_t *pool);
+
+/** apr_dbd_get_driver: get the driver struct for a name
+ *
+ *  @param pool - (process) pool to register cleanup
+ *  @param name - driver name
+ *  @param driver - pointer to driver struct.
+ *  @return APR_SUCCESS for success
+ *  @return APR_ENOTIMPL for no driver (when DSO not enabled)
+ *  @return APR_EDSOOPEN if DSO driver file can't be opened
+ *  @return APR_ESYMNOTFOUND if the driver file doesn't contain a driver
+ */
+APU_DECLARE(apr_status_t) apr_dbd_get_driver(apr_pool_t *pool, const char *name,
+                                             apr_dbd_driver_t **driver);
+
+/** apr_dbd_open: open a connection to a backend
+ *
+ *  @param ptmp - working pool
+ *  @param params - arguments to driver (implementation-dependent)
+ *  @param handle - pointer to handle to return
+ *  @param driver - driver struct.
+ *  @return APR_SUCCESS for success
+ *  @return APR_EGENERAL if driver exists but connection failed
+ */
+APU_DECLARE(apr_status_t) apr_dbd_open(apr_dbd_driver_t *driver,
+                                       apr_pool_t *ptmp, const char *params,
+                                       apr_dbd_t **handle);
+
+#ifdef DOXYGEN
+/** apr_dbd_close: close a connection to a backend.
+ *  Only required for explicit close or
+ *
+ *  @param handle - handle to close
+ *  @param driver - driver struct.
+ *  @return APR_SUCCESS for success or error status
+ */
+APU_DECLARE(apr_status_t) apr_dbd_close(apr_dbd_driver_t *driver,
+                                        apr_dbd_t *handle);
+#else
+#define apr_dbd_close(driver,handle) (driver)->close((handle))
+#endif
+
+/* apr-function-shaped versions of things */
+
+#ifdef DOXYGEN
+/** apr_dbd_name: get the name of the driver
+ *
+ *  @param driver - the driver
+ *  @return - name
+ */
+APU_DECLARE(const char*) apr_dbd_name(apr_dbd_driver_t *driver);
+#else
+#define apr_dbd_name(driver) \
+        (driver)->name
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_native_handle: get native database handle of the underlying db
+ *
+ *  @param driver - the driver
+ *  @param handle - apr_dbd handle
+ *  @return - native handle
+ */
+APU_DECLARE(void*) apr_dbd_native_handle(apr_dbd_driver_t *driver,
+                                         apr_dbd_t *handle);
+#else
+#define apr_dbd_native_handle(driver,handler) \
+        (driver)->native_handle(handler)
+#endif
+
+#ifdef DOXYGEN
+/** check_conn: check status of a database connection
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection to check
+ *  @return APR_SUCCESS or error
+ */
+APU_DECLARE(int) apr_dbd_check_conn(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                    apr_dbd_t *handle);
+#else
+#define apr_dbd_check_conn(driver,pool,handle) \
+        (driver)->check_conn((pool),(handle))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_set_dbname: select database name.  May be a no-op if not supported.
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @param name - the database to select
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_set_dbname(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                    apr_dbd_t *handle, const char *name);
+#else
+#define apr_dbd_set_dbname(driver,pool,handle,name) \
+        (driver)->set_dbname((pool),(handle),(name))
+#endif
+
+/** apr_dbd_transaction_start: start a transaction.  May be a no-op.
+ *
+ *  @param driver - the driver
+ *  @param pool - a pool to use for error messages (if any).
+ *  @param handle - the db connection
+ *  @param transaction - ptr to a transaction.  May be null on entry
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_transaction_start(apr_dbd_driver_t *driver,
+                                           apr_pool_t *pool,
+                                           apr_dbd_t *handle,
+                                           apr_dbd_transaction **trans);
+
+/** apr_dbd_transaction_end: end a transaction
+ *  (commit on success, rollback on error).
+ *  May be a no-op.
+ *
+ *  @param driver - the driver
+ *  @param handle - the db connection
+ *  @param transaction - the transaction.
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_transaction_end(apr_dbd_driver_t *driver,
+                                         apr_pool_t *pool,
+                                         apr_dbd_transaction *trans);
+
+#ifdef DOXYGEN
+/** apr_dbd_query: execute an SQL query that doesn't return a result set
+ *
+ *  @param driver - the driver
+ *  @param handle - the connection
+ *  @param transaction - current transaction.  May be null.
+ *  @param nrows - number of rows affected.
+ *  @param statement - the SQL statement to execute
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_query(apr_dbd_driver_t *driver, apr_dbd_t *handle,
+                               apr_dbd_transaction *trans, int *nrows,
+                               const char *statement);
+#else
+#define apr_dbd_query(driver,handle,trans,nrows,statement) \
+        (driver)->query((handle),(trans),(nrows),(statement))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_select: execute an SQL query that returns a result set
+ *
+ *  @param driver - the driver
+ *  @param pool - pool to allocate the result set
+ *  @param handle - the connection
+ *  @param transaction - current transaction.  May be null.
+ *  @param res - pointer to result set pointer.  May point to NULL on entry
+ *  @param statement - the SQL statement to execute
+ *  @param random - 1 to support random access to results (seek any row);
+ *                  0 to support only looping through results in order
+ *                    (async access - faster)
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_select(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                apr_dbd_t *handle, apr_dbd_transaction *trans,
+                                apr_dbd_results *res, const char *statement,
+                                int random);
+#else
+#define apr_dbd_select(driver,pool,handle,trans,res,statement,random) \
+        (driver)->select((pool),(handle),(trans),(res),(statement),(random))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_num_cols: get the number of columns in a results set
+ *
+ *  @param driver - the driver
+ *  @param res - result set.
+ *  @return number of columns
+ */
+APU_DECLARE(int) apr_dbd_num_cols(apr_dbd_driver_t *driver,
+                                  apr_dbd_results *res);
+#else
+#define apr_dbd_num_cols(driver,res) \
+        (driver)->num_cols((res))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_num_tuples: get the number of rows in a results set
+ *  of a synchronous select
+ *
+ *  @param driver - the driver
+ *  @param res - result set.
+ *  @return number of rows, or -1 if the results are asynchronous
+ */
+APU_DECLARE(int) apr_dbd_num_tuples(apr_dbd_driver_t *driver,
+                                    apr_dbd_results *res);
+#else
+#define apr_dbd_num_tuples(driver,res) \
+        (driver)->num_tuples((res))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_get_row: get a row from a result set
+ *
+ *  @param driver - the driver
+ *  @param pool - pool to allocate the row
+ *  @param res - result set pointer
+ *  @param row - pointer to row pointer.  May point to NULL on entry
+ *  @param rownum - row number, or -1 for "next row".  Ignored if random
+ *                  access is not supported.
+ *  @return 0 for success, -1 for rownum out of range or data finished
+ */
+APU_DECLARE(int) apr_dbd_get_row(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                 apr_dbd_results *res, apr_dbd_row **row,
+                                 int rownum);
+#else
+#define apr_dbd_get_row(driver,pool,res,row,rownum) \
+        (driver)->get_row((pool),(res),(row),(rownum))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_get_entry: get an entry from a row
+ *
+ *  @param driver - the driver
+ *  @param row - row pointer
+ *  @param col - entry number
+ *  @return value from the row, or NULL if col is out of bounds.
+ */
+APU_DECLARE(const char*) apr_dbd_get_entry(apr_dbd_driver_t *driver,
+                                           apr_dbd_row *row, int col);
+#else
+#define apr_dbd_get_entry(driver,row,col) \
+        (driver)->get_entry((row),(col))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_error: get current error message (if any)
+ *
+ *  @param driver - the driver
+ *  @param handle - the connection
+ *  @param errnum - error code from operation that returned an error
+ *  @return the database current error message, or message for errnum
+ *          (implementation-dependent whether errnum is ignored)
+ */
+APU_DECLARE(const char*) apr_dbd_error(apr_dbd_driver_t *driver,
+                                       apr_dbd_t *handle, int errnum);
+#else
+#define apr_dbd_error(driver,handle,errnum) \
+        (driver)->error((handle),(errnum))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_escape: escape a string so it is safe for use in query/select
+ *
+ *  @param driver - the driver
+ *  @param pool - pool to alloc the result from
+ *  @param string - the string to escape
+ *  @param handle - the connection
+ *  @return the escaped, safe string
+ */
+APU_DECLARE(const char*) apr_dbd_escape(apr_dbd_driver_t *driver,
+                                        apr_pool_t *pool, const char *string,
+                                        apr_dbd_t *handle);
+#else
+#define apr_dbd_escape(driver,pool,string,handle) \
+        (driver)->escape((pool),(string),(handle))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_prepare: prepare a statement
+ *
+ *  @param driver - the driver
+ *  @param pool - pool to alloc the result from
+ *  @param handle - the connection
+ *  @param query - the SQL query
+ *  @param label - A label for the prepared statement.
+ *                 use NULL for temporary prepared statements
+ *                 (eg within a Request in httpd)
+ *  @param statement - statement to prepare.  May point to null on entry.
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_prepare(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                 apr_dbd_t *handle, const char *query,
+                                 const char *label,
+                                 apr_dbd_prepared **statement);
+#else
+#define apr_dbd_prepare(driver,pool,handle,query,label,statement) \
+        (driver)->prepare((pool),(handle),(query),(label),(statement))
+#endif
+
+
+
+/* need macros that do varargs to deal with pvquery and pvselect :-) */
+
+#ifdef DOXYGEN
+/** apr_dbd_pquery: query using a prepared statement + args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @param trans - current transaction.  May be null.
+ *  @param nrows - number of rows affected.
+ *  @param statement - the prepared statement to execute
+ *  @param nargs - number of args to prepared statement
+ *  @param args - args to prepared statement
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pquery(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                apr_dbd_t *handle, apr_dbd_transaction *trans,
+                                int *nrows, apr_dbd_prepared *statement,
+                                int nargs, const char **args);
+#else
+#define apr_dbd_pquery(driver,pool,handle,trans,nrows,statement,nargs,args) \
+        (driver)->pquery((pool),(handle),(trans),(nrows),(statement), \
+                         (nargs),(args))
+#endif
+
+#ifdef DOXYGEN
+/** apr_dbd_pselect: select using a prepared statement + args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @param trans - current transaction.  May be null.
+ *  @param res - pointer to query results.  May point to NULL on entry
+ *  @param statement - the prepared statement to execute
+ *  @param random - Whether to support random-access to results
+ *  @param nargs - number of args to prepared statement
+ *  @param args - args to prepared statement
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pselect(apr_dbd_driver_t *driver, apr_pool_t *pool,
+                                 apr_dbd_t *handle, apr_dbd_transaction *trans,
+                                 apr_dbd_results **res,
+                                 apr_dbd_prepared *statement, int random,
+                                 int nargs, const char **args);
+#else
+#define apr_dbd_pselect(driver,pool,handle,trans,res,statement,random,nargs,args) \
+        (driver)->pselect((pool),(handle),(trans),(res),(statement), \
+                          (random),(nargs),(args))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

Added: apr/apr-util/trunk/dbd/apr_dbd_pgsql.c
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/dbd/apr_dbd_pgsql.c?view=auto&rev=149483
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_pgsql.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_pgsql.c Tue Feb  1 21:14:50 2005
@@ -0,0 +1,583 @@
+/*      Copyright (c) 2004, Nick Kew.  All rights reserved.
+
+        If this is accepted by ASF for inclusion in APR-UTIL,
+        I will assign copyright to ASF and license under ASF terms.
+
+        Otherwise I will retain copyright and license under
+        terms of my choice.
+*/
+
+#if APU_HAVE_PGSQL
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <libpq-fe.h>
+
+#include "apr_strings.h"
+
+#define QUERY_MAX_ARGS 40
+
+typedef PGconn apr_dbd_t;
+typedef struct {
+    int errnum;
+    apr_dbd_t *handle;
+} apr_dbd_transaction;
+
+typedef struct {
+    int random;
+    PGconn *handle;
+    PGresult *res;
+    size_t ntuples;
+    size_t sz;
+    size_t index;
+} apr_dbd_results;
+typedef struct {
+    int n;
+    apr_dbd_results *res;
+} apr_dbd_row;
+typedef struct apr_dbd_prepared {
+    const char *name;
+    int prepared;
+} apr_dbd_prepared;
+
+#define dbd_pgsql_is_success(x) (((x)==PGRES_EMPTY_QUERY)||((x)==PGRES_COMMAND_OK)||((x)==PGRES_TUPLES_OK))
+
+#define APR_DBD_INTERNAL
+#include "apr_dbd.h"
+
+static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
+                            apr_dbd_transaction *trans,
+                            apr_dbd_results **results,
+                            const char *query, int seek)
+{
+    PGresult *res;
+    int ret;
+    if ( trans && trans->errnum ) {
+        return trans->errnum;
+    }
+    if (seek) { /* synchronous query */
+        res = PQexec(sql, 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 (trans) {
+                trans->errnum = ret;
+            }
+            return ret;
+        }
+        if (!*results) {
+            *results = apr_pcalloc(pool, sizeof(apr_dbd_results));
+        }
+        (*results)->res = res;
+        (*results)->ntuples = PQntuples(res);
+        (*results)->sz = PQnfields(res);
+        (*results)->random = seek;
+        apr_pool_cleanup_register(pool, res, (void*)PQclear,
+                                  apr_pool_cleanup_null);
+    }
+    else {
+        if (PQsendQuery(sql, query) == 0) {
+            if (trans) {
+                trans->errnum = 1;
+            }
+            return 1;
+        }
+        if (*results == NULL) {
+            *results = apr_pcalloc(pool, sizeof(apr_dbd_results));
+        }
+        (*results)->random = seek;
+        (*results)->handle = sql;
+    }
+    return 0;
+}
+static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results *res,
+                             apr_dbd_row **rowp, int rownum)
+{
+    apr_dbd_row *row = *rowp;
+    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
+
+    if (row == NULL) {
+        row = apr_palloc(pool, sizeof(apr_dbd_row));
+        *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_kill(pool, res->res, (void*)PQclear);
+            PQclear(res->res);
+            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 *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);
+}
+static int dbd_pgsql_query(apr_dbd_t *sql, apr_dbd_transaction *trans,
+                           int *nrows, const char *query)
+{
+    PGresult *res;
+    int ret;
+    if (trans && trans->errnum) {
+        return trans->errnum;
+    }
+    res = PQexec(sql, 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 (trans) {
+        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, 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 **statement)
+{
+    char *sqlcmd;
+    char *sqlptr;
+    size_t length;
+    size_t i = 0;
+    const char *args[QUERY_MAX_ARGS];
+    size_t alen;
+    int nargs = 0;
+    int ret;
+    PGresult *res;
+    char *pgquery;
+    char *pgptr;
+
+    if (!*statement) {
+        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared));
+    }
+    /* Translate from apr_dbd to native query format */
+    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+        if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
+            ++nargs;
+        }
+    }
+    length = strlen(query) + 1;
+    if (nargs > 8) {
+        length += 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 (nargs > 0) {
+        memcpy(sqlptr, " (",2);
+        sqlptr += 2;
+        for (i=0; i<nargs; ++i) {
+            alen = strlen(args[i]);
+            memcpy(sqlptr, args[i], alen);
+            sqlptr += alen;
+            *sqlptr++ = ',';
+        }
+        sqlptr[-1] =  ')';
+    }
+    memcpy(sqlptr, " AS ", 4);
+    sqlptr += 4;
+    memcpy(sqlptr, pgquery, strlen(pgquery));
+    sqlptr += strlen(pgquery);
+    *sqlptr = 0;
+
+    res = PQexec(sql, 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,
+                            apr_dbd_transaction *trans, int *nrows,
+                            apr_dbd_prepared *statement, int nargs,
+                            const char **values)
+{
+    int ret;
+    PGresult *res;
+    if (statement->prepared) {
+        res = PQexecPrepared(sql, statement->name, nargs, values, 0, 0, 0);
+    }
+    else {
+        res = PQexecParams(sql, statement->name, nargs, 0, values, 0, 0, 0);
+    }
+    if (res) {
+        ret = PQresultStatus(res);
+        if (dbd_pgsql_is_success(ret)) {
+            ret = 0;
+        }
+        PQclear(res);
+    }
+    else {
+        ret = PGRES_FATAL_ERROR;
+    }
+
+    if (trans) {
+        trans->errnum = ret;
+    }
+    return ret;
+}
+static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
+                             apr_dbd_transaction *trans, int *nrows,
+                             apr_dbd_prepared *statement, ...)
+{
+    const char *arg;
+    int nargs = 0;
+    va_list args;
+    const char *values[QUERY_MAX_ARGS];
+
+    if (trans && trans->errnum) {
+        return trans->errnum;
+    }
+    va_start(args, statement);
+    while ( arg = va_arg(args, const char*), arg ) {
+        if ( nargs >= QUERY_MAX_ARGS) {
+            va_end(args);
+            return -1;
+        }
+        values[nargs++] = apr_pstrdup(pool, arg);
+    }
+    va_end(args);
+    values[nargs] = NULL;
+    return dbd_pgsql_pquery(pool, sql, trans, nrows, statement, nargs, values);
+}
+static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+                             apr_dbd_transaction *trans,
+                             apr_dbd_results **results,
+                             apr_dbd_prepared *statement,
+                             int seek, int nargs, const char **values)
+{
+    PGresult *res;
+    int rv;
+    int ret = 0;
+    if (seek) { /* synchronous query */
+        if (statement->prepared) {
+            res = PQexecPrepared(sql, statement->name, nargs, values, 0, 0, 0);
+        }
+        else {
+            res = PQexecParams(sql, 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 (trans) {
+                trans->errnum = ret;
+            }
+            return ret;
+        }
+        if (!*results) {
+            *results = apr_pcalloc(pool, sizeof(apr_dbd_results));
+        }
+        (*results)->res = res;
+        (*results)->ntuples = PQntuples(res);
+        (*results)->sz = PQnfields(res);
+        (*results)->random = seek;
+        apr_pool_cleanup_register(pool, res, (void*)PQclear,
+                                  apr_pool_cleanup_null);
+    }
+    else {
+        if (statement->prepared) {
+            rv = PQsendQueryPrepared(sql, statement->name, nargs, values,0,0,0);
+        }
+        else {
+            rv = PQsendQueryParams(sql, statement->name, nargs, 0, values,0,0,0);
+        }
+        if (rv == 0) {
+            if (trans) {
+                trans->errnum = 1;
+            }
+            return 1;
+        }
+        if (!*results) {
+            *results = apr_pcalloc(pool, sizeof(apr_dbd_results));
+        }
+        (*results)->random = seek;
+        (*results)->handle = sql;
+    }
+
+    if (trans) {
+        trans->errnum = ret;
+    }
+    return ret;
+}
+static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+                              apr_dbd_transaction *trans,
+                              apr_dbd_results **results,
+                              apr_dbd_prepared *statement,
+                              int seek, ...)
+{
+    const char *arg;
+    int nargs = 0;
+    va_list args;
+    const char *values[QUERY_MAX_ARGS];
+
+    if (trans && trans->errnum) {
+        return trans->errnum;
+    }
+
+    va_start(args, seek);
+    while (arg = va_arg(args, const char*), arg) {
+        if ( nargs >= QUERY_MAX_ARGS) {
+            va_end(args);
+            return -1;
+        }
+        values[nargs++] = apr_pstrdup(pool, arg);
+    }
+    va_end(args);
+    return dbd_pgsql_pselect(pool, sql, trans, results, statement,
+                             seek, nargs, values) ;
+}
+static int dbd_pgsql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+                                 apr_dbd_transaction **trans)
+{
+    int ret = 0;
+    PGresult *res = PQexec(handle, "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));
+            }
+        }
+        PQclear(res);
+        (*trans)->handle = handle;
+    }
+    else {
+        ret = PGRES_FATAL_ERROR;
+    }
+    return ret;
+}
+static int dbd_pgsql_end_transaction(apr_dbd_transaction *trans)
+{
+    PGresult *res;
+    int ret = -1;                /* no transaction is an error cond */
+    if (trans) {
+        if (trans->errnum) {
+            trans->errnum = 0;
+            res = PQexec(trans->handle, "ROLLBACK");
+        }
+        else {
+            res = PQexec(trans->handle, "COMMIT");
+        }
+        if (res) {
+            ret = PQresultStatus(res);
+            if (dbd_pgsql_is_success(ret)) {
+                ret = 0;
+            }
+            PQclear(res);
+        }
+        else {
+            ret = PGRES_FATAL_ERROR;
+        }
+    }
+    return ret;
+}
+static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
+{
+    return PQconnectdb(params);
+}
+static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
+{
+    PQfinish(handle);
+    return APR_SUCCESS;
+}
+static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
+                                         apr_dbd_t *handle)
+{
+    if (PQstatus(handle) != CONNECTION_OK) {
+        PQreset(handle);
+        if (PQstatus(handle) != 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;
+}
+static int dbd_pgsql_num_cols(apr_dbd_results* res)
+{
+    return res->sz;
+}
+static int dbd_pgsql_num_tuples(apr_dbd_results* 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_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

Added: apr/apr-util/trunk/dbd/apr_dbd_test.c
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/dbd/apr_dbd_test.c?view=auto&rev=149483
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_test.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_test.c Tue Feb  1 21:14:50 2005
@@ -0,0 +1,399 @@
+/*      Copyright (c) 2004, Nick Kew.  All rights reserved.
+
+        If this is accepted by ASF for inclusion in APR-UTIL,
+        I will assign copyright to ASF and license under ASF terms.
+
+        Otherwise I will retain copyright and license under
+        terms of my choice.
+*/
+#include "apu.h"
+#include "apr_pools.h"
+#include "apr_dbd.h"
+
+#include <stdio.h>
+
+#define TEST(msg,func)					\
+    printf("======== %s ========\n", msg);		\
+    rv = func(pool, sql, driver);			\
+    if (rv != 0) {					\
+        printf("Error in %s: rc=%d\n\n", msg, rv);	\
+    }							\
+    else {						\
+        printf("%s test successful\n\n", msg);		\
+    }							\
+    fflush(stdout);
+
+static int create_table(apr_pool_t* pool, apr_dbd_t* handle,
+                        apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int nrows;
+    const char *statement = "CREATE TABLE apr_dbd_test ("
+        "col1 varchar(40) not null,"
+        "col2 varchar(40),"
+        "col3 integer)" ;
+    rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+    return rv;
+}
+static int drop_table(apr_pool_t* pool, apr_dbd_t* handle,
+                      apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int nrows;
+    const char *statement = "DROP TABLE apr_dbd_test" ;
+    rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+    return rv;
+}
+static int insert_rows(apr_pool_t* pool, apr_dbd_t* handle,
+                       apr_dbd_driver_t* driver)
+{
+    int i;
+    int rv = 0;
+    int nrows;
+    int nerrors = 0;
+    const char *statement =
+        "INSERT into apr_dbd_test (col1) values ('foo');"
+        "INSERT into apr_dbd_test values ('wibble', 'other', 5);"
+        "INSERT into apr_dbd_test values ('wibble', 'nothing', 5);"
+        "INSERT into apr_dbd_test values ('qwerty', 'foo', 0);"
+        "INSERT into apr_dbd_test values ('asdfgh', 'bar', 1);"
+    ;
+    rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+    if (rv) {
+        const char* stmt[] = {
+            "INSERT into apr_dbd_test (col1) values ('foo');",
+            "INSERT into apr_dbd_test values ('wibble', 'other', 5);",
+            "INSERT into apr_dbd_test values ('wibble', 'nothing', 5);",
+            "INSERT into apr_dbd_test values ('qwerty', 'foo', 0);",
+            "INSERT into apr_dbd_test values ('asdfgh', 'bar', 1);",
+            NULL
+        };
+        printf("Compound insert failed; trying statements one-by-one\n") ;
+        for (i=0; stmt[i] != NULL; ++i) {
+            statement = stmt[i];
+            rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+            if (rv) {
+                nerrors++;
+            }
+        }
+        if (nerrors) {
+            printf("%d single inserts failed too.\n", nerrors) ;
+        }
+    }
+    return rv;
+}
+static int invalid_op(apr_pool_t* pool, apr_dbd_t* handle,
+                      apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int nrows;
+    const char *statement = "INSERT into apr_dbd_test1 (col2) values ('foo')" ;
+    rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+    printf("invalid op returned %d (should be nonzero).  Error msg follows\n", rv);
+    printf("'%s'\n", apr_dbd_error(driver, handle, rv));
+    statement = "INSERT into apr_dbd_test (col1, col2) values ('bar', 'foo')" ;
+    rv = apr_dbd_query(driver, handle, NULL, &nrows, statement);
+    printf("valid op returned %d (should be zero; error shouldn't affect subsequent ops)\n", rv);
+    return rv;
+}
+static int select_sequential(apr_pool_t* pool, apr_dbd_t* handle,
+                             apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int i = 0;
+    int n;
+    const char* entry;
+    const char* statement = "SELECT * FROM apr_dbd_test ORDER BY col1, col2";
+    apr_dbd_results *res = NULL;
+    apr_dbd_row *row = NULL;
+    rv = apr_dbd_select(driver,pool,handle,NULL,&res,statement,0);
+    if (rv) {
+        printf("Select failed: %s", apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    for (rv = apr_dbd_get_row(driver, pool, res, &row, -1);
+         rv == 0;
+         rv = apr_dbd_get_row(driver, pool, res, &row, -1)) {
+        printf("ROW %d:	", i++) ;
+        for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
+            entry = apr_dbd_get_entry(driver, row, n);
+            if (entry == NULL) {
+                printf("(null)	") ;
+            }
+            else {
+                printf("%s	", entry);
+            }
+        }
+	fputs("\n", stdout);
+    }
+    return (rv == -1) ? 0 : 1;
+}
+static int select_random(apr_pool_t* pool, apr_dbd_t* handle,
+                         apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int n;
+    const char* entry;
+    const char* statement = "SELECT * FROM apr_dbd_test ORDER BY col1, col2";
+    apr_dbd_results *res = NULL;
+    apr_dbd_row *row = NULL;
+    rv = apr_dbd_select(driver,pool,handle,NULL,&res,statement,1);
+    if (rv) {
+        printf("Select failed: %s", apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    rv = apr_dbd_get_row(driver, pool, res, &row, 5) ;
+    if (rv) {
+        printf("get_row failed: %s", apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    printf("ROW 5:	");
+    for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
+        entry = apr_dbd_get_entry(driver, row, n);
+        if (entry == NULL) {
+            printf("(null)	") ;
+        }
+        else {
+            printf("%s	", entry);
+        }
+    }
+    fputs("\n", stdout);
+    rv = apr_dbd_get_row(driver, pool, res, &row, 1) ;
+    if (rv) {
+        printf("get_row failed: %s", apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    printf("ROW 1:	");
+    for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
+        entry = apr_dbd_get_entry(driver, row, n);
+        if (entry == NULL) {
+            printf("(null)	") ;
+        }
+        else {
+            printf("%s	", entry);
+        }
+    }
+    fputs("\n", stdout);
+    rv = apr_dbd_get_row(driver, pool, res, &row, 11) ;
+    if (rv != -1) {
+        printf("Oops!  get_row out of range but thinks it succeeded!\n%s\n",
+                apr_dbd_error(driver, handle, rv));
+        return -1;
+    }
+    rv = 0;
+
+    return rv;
+}
+static int test_transactions(apr_pool_t* pool, apr_dbd_t* handle,
+                             apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int nrows;
+    apr_dbd_transaction* trans = NULL;
+    const char* statement;
+
+    /* trans 1 - error out early */
+    printf("Transaction 1\n");
+    rv = apr_dbd_transaction_start(driver, pool, handle, &trans);
+    if (rv) {
+        printf("Start transaction failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    statement = "UPDATE apr_dbd_test SET col2 = 'failed'";
+    rv = apr_dbd_query(driver, handle, trans, &nrows, statement);
+    if (rv) {
+        printf("Update failed: '%s'\n", apr_dbd_error(driver, handle, rv));
+        apr_dbd_transaction_end(driver, pool, trans);
+        return rv;
+    }
+    printf("%d rows updated\n", nrows);
+
+    statement = "INSERT INTO apr_dbd_test1 (col3) values (3)";
+    rv = apr_dbd_query(driver, handle, trans, &nrows, statement);
+    if (!rv) {
+        printf("Oops, invalid op succeeded but shouldn't!\n");
+    }
+    statement = "INSERT INTO apr_dbd_test values ('zzz', 'aaa', 3)";
+    rv = apr_dbd_query(driver, handle, trans, &nrows, statement);
+    printf("Valid insert returned %d.  Should be nonzero (fail) because transaction is bad\n", rv) ;
+
+    rv = apr_dbd_transaction_end(driver, pool, trans);
+    if (rv) {
+        printf("End transaction failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    printf("Transaction ended (should be rollback) - viewing table\n"
+           "A column of \"failed\" indicates transaction failed (no rollback)\n");
+    select_sequential(pool, handle, driver);
+
+    /* trans 2 - complete successfully */
+    printf("Transaction 2\n");
+    rv = apr_dbd_transaction_start(driver, pool, handle, &trans);
+    if (rv) {
+        printf("Start transaction failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    statement = "UPDATE apr_dbd_test SET col2 = 'success'";
+    rv = apr_dbd_query(driver, handle, trans, &nrows, statement);
+    if (rv) {
+        printf("Update failed: '%s'\n", apr_dbd_error(driver, handle, rv));
+        apr_dbd_transaction_end(driver, pool, trans);
+        return rv;
+    }
+    printf("%d rows updated\n", nrows);
+    statement = "INSERT INTO apr_dbd_test values ('aaa', 'zzz', 3)";
+    rv = apr_dbd_query(driver, handle, trans, &nrows, statement);
+    printf("Valid insert returned %d.  Should be zero (OK)\n", rv) ;
+    rv = apr_dbd_transaction_end(driver, pool, trans);
+    if (rv) {
+        printf("End transaction failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    printf("Transaction ended (should be commit) - viewing table\n");
+    select_sequential(pool, handle, driver);
+    return rv;
+}
+static int test_pselect(apr_pool_t* pool, apr_dbd_t* handle,
+                        apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    int i, n;
+    const char *query =
+        "SELECT * FROM apr_dbd_test WHERE col3 <= %s or col1 = 'bar'" ;
+    const char *label = "lowvalues";
+    apr_dbd_prepared* statement = NULL;
+    apr_dbd_results* res = NULL;
+    apr_dbd_row* row = NULL;
+    const char *entry = NULL;
+
+    rv = apr_dbd_prepare(driver, pool, handle, query, label, &statement);
+    if (rv) {
+        printf("Prepare statement failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    rv = driver->pvselect(pool, handle, NULL, &res, statement, 0, "3", NULL);
+    if (rv) {
+        printf("Exec of prepared statement failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    i = 0;
+    printf("Selecting rows where col3 <= 3 and bar row where it's unset.\nShould show four rows.\n");
+    for (rv = apr_dbd_get_row(driver, pool, res, &row, -1);
+         rv == 0;
+         rv = apr_dbd_get_row(driver, pool, res, &row, -1)) {
+        printf("ROW %d:	", i++) ;
+        for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
+            entry = apr_dbd_get_entry(driver, row, n);
+            if (entry == NULL) {
+                printf("(null)	") ;
+            }
+            else {
+                printf("%s	", entry);
+            }
+        }
+	fputs("\n", stdout);
+    }
+    return (rv == -1) ? 0 : 1;
+}
+static int test_pquery(apr_pool_t* pool, apr_dbd_t* handle,
+                       apr_dbd_driver_t* driver)
+{
+    int rv = 0;
+    const char *query = "INSERT INTO apr_dbd_test VALUES (%s, %s, %d)";
+    apr_dbd_prepared *statement = NULL;
+    const char *label = "testpquery";
+    int nrows;
+    apr_dbd_transaction* trans =0;
+
+    rv = apr_dbd_prepare(driver, pool, handle, query, label, &statement);
+    //rv = apr_dbd_prepare(driver, pool, handle, query, NULL, &statement);
+    if (rv) {
+        printf("Prepare statement failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    apr_dbd_transaction_start(driver, pool, handle, &trans);
+    rv = driver->pvquery(pool, handle, trans, &nrows, statement,
+                         "prepared", "insert", "2", NULL);
+    apr_dbd_transaction_end(driver, pool, trans);
+    if (rv) {
+        printf("Exec of prepared statement failed!\n%s\n",
+               apr_dbd_error(driver, handle, rv));
+        return rv;
+    }
+    printf("Showing table (should now contain row \"prepared insert 2\")\n");
+    select_sequential(pool, handle, driver);
+    return rv;
+}
+int main(int argc, char** argv)
+{
+    const char *name;
+    const char *params;
+    apr_pool_t *pool = NULL;
+    apr_dbd_t *sql = NULL;
+    apr_dbd_driver_t *driver = NULL;
+    int rv;
+
+    apr_initialize();
+    apr_pool_create(&pool, NULL);
+
+    if (argc >= 2 && argc <= 3) {
+        name = argv[1];
+        params = ( argc == 3 ) ? argv[2] : "";
+        apr_dbd_init(pool);
+        setbuf(stdout,NULL);
+        rv = apr_dbd_get_driver(pool, name, &driver);
+        switch (rv) {
+        case APR_SUCCESS:
+           printf("Loaded %s driver OK.\n", name);
+           break;
+        case APR_EDSOOPEN:
+           printf("Failed to load driver file apr_dbd_%s.so\n", name);
+           goto finish;
+        case APR_ESYMNOTFOUND:
+           printf("Failed to load driver apr_dbd_%s_driver.\n", name);
+           goto finish;
+        case APR_ENOTIMPL:
+           printf("No driver available for %s.\n", name);
+           goto finish;
+        default:        /* it's a bug if none of the above happen */
+           printf("Internal error loading %s.\n", name);
+           goto finish;
+        }
+        rv = apr_dbd_open(driver, pool, params, &sql);
+        switch (rv) {
+        case APR_SUCCESS:
+           printf("Opened %s[%s] OK\n", name, params);
+           break;
+        case APR_EGENERAL:
+           printf("Failed to open %s[%s]\n", name, params);
+           goto finish;
+        default:        /* it's a bug if none of the above happen */
+           printf("Internal error opening %s[%s]\n", name, params);
+           goto finish;
+        }
+        TEST("create table", create_table);
+        TEST("insert rows", insert_rows);
+        TEST("invalid op", invalid_op);
+        TEST("select random", select_random);
+        TEST("select sequential", select_sequential);
+        TEST("transactions", test_transactions);
+        TEST("prepared select", test_pselect);
+        TEST("prepared query", test_pquery);
+        TEST("drop table", drop_table);
+        apr_dbd_close(driver, sql);
+    }
+    else {
+        fprintf(stderr, "Usage: %s driver-name [params]\n", argv[0]);
+    }
+finish:
+    apr_pool_destroy(pool);
+    apr_terminate();
+    return 0;
+}



Mime
View raw message