Index: include/apr_dbd.h
===================================================================
--- include/apr_dbd.h	(revision 431422)
+++ include/apr_dbd.h	(working copy)
@@ -38,6 +38,54 @@
  * @{
  */
 
+typedef struct apr_dbd_blob apr_dbd_blob_t;
+/**
+ * Structure used for passing binary data (BLOB) to apr_dbd_pb[v]query/select
+ */
+struct apr_dbd_blob {
+    const char *data;   /**< binary data payload */
+    apr_size_t size;    /**< data size */
+    const char *table;  /**< table name (used for Oracle) */
+    const char *column; /**< column name (used for Oracle) */
+};
+
+/**
+ * Mapping of C to SQL types, used for prepared statements.
+ * @remarks
+ * For apr_dbd_p[v]query/select functions, in and out parameters are always
+ * const char * (i.e. regular nul terminated strings). BLOB types are encoded
+ * in ASCII as length:table:column:payload, where table and column are used
+ * for Oracle and can be left empty for other databases.
+ * @remarks
+ * For apr_dbd_p[v]bquery/select functions, in and out parameters are
+ * described next to each enumeration constant and are generally native binary
+ * types or some APR data type.
+ */
+typedef enum {
+    APR_DBD_TYPE_NONE,
+    APR_DBD_TYPE_TINY,       /**< %hhd : in, out: char* */
+    APR_DBD_TYPE_UTINY,      /**< %hhu : in, out: unsigned char* */
+    APR_DBD_TYPE_SHORT,      /**< %hd  : in, out: short* */
+    APR_DBD_TYPE_USHORT,     /**< %hu  : in, out: unsigned short* */
+    APR_DBD_TYPE_INT,        /**< %d   : in, out: int* */
+    APR_DBD_TYPE_UINT,       /**< %u   : in, out: unsigned int* */
+    APR_DBD_TYPE_LONG,       /**< %ld  : in, out: long* */
+    APR_DBD_TYPE_ULONG,      /**< %lu  : in, out: unsigned long* */
+    APR_DBD_TYPE_LONGLONG,   /**< %lld : in, out: apr_int64_t* */
+    APR_DBD_TYPE_ULONGLONG,  /**< %llu : in, out: apr_uint64_t* */
+    APR_DBD_TYPE_FLOAT,      /**< %f   : in, out: float* */
+    APR_DBD_TYPE_DOUBLE,     /**< %lf  : in, out: double* */
+    APR_DBD_TYPE_STRING,     /**< %s   : in: char*, out: char** */
+    APR_DBD_TYPE_TEXT,       /**< %pDt : in: char*, out: char** */
+    APR_DBD_TYPE_TIME,       /**< %pDi : in: char*, out: char** */
+    APR_DBD_TYPE_DATE,       /**< %pDd : in: char*, out: char** */
+    APR_DBD_TYPE_DATETIME,   /**< %pDa : in: char*, out: char** */
+    APR_DBD_TYPE_TIMESTAMP,  /**< %pDs : in: char*, out: char** */
+    APR_DBD_TYPE_ZTIMESTAMP, /**< %pDz : in: char*, out: char** */
+    APR_DBD_TYPE_BLOB,       /**< %pDb : in: apr_dbd_blob_t*, out: apr_bucket_brigade* */
+    APR_DBD_TYPE_NULL        /**< %pDn : in: void*, out: void** */
+} apr_dbd_type_e;
+
 /* These are opaque structs.  Instantiation is up to each backend */
 typedef struct apr_dbd_driver_t apr_dbd_driver_t;
 typedef struct apr_dbd_t apr_dbd_t;
@@ -392,6 +440,85 @@
                                   apr_dbd_prepared_t *statement, int random,
                                   ...);
 
+/** apr_dbd_pbquery: query using a prepared statement + binary args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @param nrows - number of rows affected.
+ *  @param statement - the prepared statement to execute
+ *  @param nargs - number of args to prepared statement
+ *  @param args - binary args to prepared statement
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pbquery(const apr_dbd_driver_t *driver,
+                                 apr_pool_t *pool, apr_dbd_t *handle,
+                                 int *nrows, apr_dbd_prepared_t *statement,
+                                 int nargs, const void **args);
+
+/** apr_dbd_pbselect: select using a prepared statement + binary args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @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 - binary args to prepared statement
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pbselect(const apr_dbd_driver_t *driver,
+                                  apr_pool_t *pool,
+                                  apr_dbd_t *handle, apr_dbd_results_t **res,
+                                  apr_dbd_prepared_t *statement, int random,
+                                  int nargs, const void **args);
+
+/** apr_dbd_pvbquery: query using a prepared statement + binary args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @param nrows - number of rows affected.
+ *  @param statement - the prepared statement to execute
+ *  @param ... - varargs list of binary args
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pvbquery(const apr_dbd_driver_t *driver,
+                                  apr_pool_t *pool,
+                                  apr_dbd_t *handle, int *nrows,
+                                  apr_dbd_prepared_t *statement, ...);
+
+/** apr_dbd_pvbselect: select using a prepared statement + binary args
+ *
+ *  @param driver - the driver
+ *  @param pool - working pool
+ *  @param handle - the connection
+ *  @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 ... - varargs list of binary args
+ *  @return 0 for success or error code
+ */
+APU_DECLARE(int) apr_dbd_pvbselect(const apr_dbd_driver_t *driver,
+                                   apr_pool_t *pool,
+                                   apr_dbd_t *handle, apr_dbd_results_t **res,
+                                   apr_dbd_prepared_t *statement, int random,
+                                   ...);
+
+/** apr_dbd_datum_get: get a binary entry from a row
+ *
+ *  @param driver - the driver
+ *  @param row - row pointer
+ *  @param col - entry number
+ *  @param type - type of data to get
+ *  @param data - pointer to data, allocated by the caller
+ *  @return APR_SUCCESS on success, APR_ENOENT if data is NULL or APR_EGENERAL
+ */
+APU_DECLARE(apr_status_t) apr_dbd_datum_get(const apr_dbd_driver_t *driver,
+                                            apr_dbd_row_t *row, int col,
+                                            apr_dbd_type_e type, void *data);
+
 /** @} */
 
 #ifdef __cplusplus
Index: include/private/apr_dbd_internal.h
===================================================================
--- include/private/apr_dbd_internal.h	(revision 431422)
+++ include/private/apr_dbd_internal.h	(working copy)
@@ -196,11 +196,14 @@
      *  @param label - A label for the prepared statement.
      *                 use NULL for temporary prepared statements
      *                 (eg within a Request in httpd)
+     *  @param nargs - number of parameters in the query
+     *  @param types - pointer to an array with types of parameters
      *  @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_t **statement);
+                   const char *label, int nargs, apr_dbd_type_e *types,
+                   apr_dbd_prepared_t **statement);
 
     /** pvquery: query using a prepared statement + args
      *
@@ -280,6 +283,75 @@
      *  @return the mode of transaction in force after the call
      */
     int (*transaction_mode_set)(apr_dbd_transaction_t *trans, int mode);
+
+    /** format of prepared statement parameters */
+    const char *pformat;
+
+    /** pvbquery: query using a prepared statement + binary args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param nrows - number of rows affected.
+     *  @param statement - the prepared statement to execute
+     *  @param args - binary args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pvbquery)(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
+                    apr_dbd_prepared_t *statement, va_list args);
+
+    /** pvbselect: select using a prepared statement + binary args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @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 - binary args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pvbselect)(apr_pool_t *pool, apr_dbd_t *handle,
+                     apr_dbd_results_t **res,
+                     apr_dbd_prepared_t *statement, int random, va_list args);
+
+    /** pbquery: query using a prepared statement + binary args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @param nrows - number of rows affected.
+     *  @param statement - the prepared statement to execute
+     *  @param nargs - number of args to prepared statement
+     *  @param args - binary args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pbquery)(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
+                   apr_dbd_prepared_t *statement, int nargs,
+                   const void **args);
+
+    /** pbselect: select using a prepared statement + binary args
+     *
+     *  @param pool - working pool
+     *  @param handle - the connection
+     *  @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 - binary args to prepared statement
+     *  @return 0 for success or error code
+     */
+    int (*pbselect)(apr_pool_t *pool, apr_dbd_t *handle,
+                    apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
+                    int random, int nargs, const void **args);
+  
+    /** datum_get: get a binary entry from a row
+     *
+     *  @param row - row pointer
+     *  @param col - entry number
+     *  @param type - type of data to get
+     *  @param data - pointer to data, allocated by the caller
+     *  @return APR_SUCCESS, an error code on error or if col is out of bounds
+     */
+    apr_status_t (*datum_get)(const apr_dbd_row_t *row, int col,
+                              apr_dbd_type_e type, void *data);
 };
 
 /* Export mutex lock/unlock for drivers that need it */
Index: dbd/apr_dbd_sqlite2.c
===================================================================
--- dbd/apr_dbd_sqlite2.c	(revision 431422)
+++ dbd/apr_dbd_sqlite2.c	(working copy)
@@ -25,6 +25,7 @@
 
 #include "apr_strings.h"
 #include "apr_time.h"
+#include "apr_buckets.h"
 
 #include "apr_dbd_internal.h"
 
@@ -187,6 +188,79 @@
     return row->data[n];
 }
 
+static apr_status_t dbd_sqlite_datum_get(const apr_dbd_row_t *row, int n,
+                                         apr_dbd_type_e type, void *data)
+{
+    if ((n < 0) || (n >= row->res->sz)) {
+      return APR_EGENERAL;
+    }
+
+    switch (type) {
+    case APR_DBD_TYPE_TINY:
+        *(char*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_UTINY:
+        *(unsigned char*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_SHORT:
+        *(short*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_USHORT:
+        *(unsigned short*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_INT:
+        *(int*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_UINT:
+        *(unsigned int*)data = atoi(row->data[n]);
+        break;
+    case APR_DBD_TYPE_LONG:
+        *(long*)data = atol(row->data[n]);
+        break;
+    case APR_DBD_TYPE_ULONG:
+        *(unsigned long*)data = atol(row->data[n]);
+        break;
+    case APR_DBD_TYPE_LONGLONG:
+        *(apr_int64_t*)data = apr_atoi64(row->data[n]);
+        break;
+    case APR_DBD_TYPE_ULONGLONG:
+        *(apr_uint64_t*)data = apr_atoi64(row->data[n]);
+        break;
+    case APR_DBD_TYPE_FLOAT:
+        *(float*)data = atof(row->data[n]);
+        break;
+    case APR_DBD_TYPE_DOUBLE:
+        *(double*)data = atof(row->data[n]);
+        break;
+    case APR_DBD_TYPE_STRING:
+    case APR_DBD_TYPE_TEXT:
+    case APR_DBD_TYPE_TIME:
+    case APR_DBD_TYPE_DATE:
+    case APR_DBD_TYPE_DATETIME:
+    case APR_DBD_TYPE_TIMESTAMP:
+    case APR_DBD_TYPE_ZTIMESTAMP:
+        *(char**)data = row->data[n];
+        break;
+    case APR_DBD_TYPE_BLOB:
+        {
+        apr_bucket *e;
+        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+        e = apr_bucket_immortal_create(row->data[n],strlen(row->data[n]),
+                                       b->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(b, e);
+        }
+        break;
+    case APR_DBD_TYPE_NULL:
+        *(void**)data = NULL;
+        break;
+    default:
+        return APR_EGENERAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 static const char *dbd_sqlite_error(apr_dbd_t * sql, int n)
 {
     return sql->errmsg;
@@ -240,6 +314,7 @@
 
 static int dbd_sqlite_prepare(apr_pool_t * pool, apr_dbd_t * sql,
                               const char *query, const char *label,
+                              int nargs, apr_dbd_type_e *types,
                               apr_dbd_prepared_t ** statement)
 {
     return APR_ENOTIMPL;
@@ -275,6 +350,36 @@
     return APR_ENOTIMPL;
 }
 
+static int dbd_sqlite_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                              int *nrows, apr_dbd_prepared_t * statement,
+                              int nargs, const void **values)
+{
+    return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                               int *nrows, apr_dbd_prepared_t * statement,
+                               va_list args)
+{
+    return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                               apr_dbd_results_t ** results,
+                               apr_dbd_prepared_t * statement,
+                               int seek, int nargs, const void **values)
+{
+    return APR_ENOTIMPL;
+}
+
+static int dbd_sqlite_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                                apr_dbd_results_t ** results,
+                                apr_dbd_prepared_t * statement, int seek,
+                                va_list args)
+{
+    return APR_ENOTIMPL;
+}
+
 static int dbd_sqlite_start_transaction(apr_pool_t * pool, apr_dbd_t * handle,
                                         apr_dbd_transaction_t ** trans)
 {
@@ -424,6 +529,12 @@
     dbd_sqlite_pselect,
     dbd_sqlite_get_name,
     dbd_sqlite_transaction_mode_get,
-    dbd_sqlite_transaction_mode_set
+    dbd_sqlite_transaction_mode_set,
+    NULL,
+    dbd_sqlite_pvbquery,
+    dbd_sqlite_pvbselect,
+    dbd_sqlite_pbquery,
+    dbd_sqlite_pbselect,
+    dbd_sqlite_datum_get
 };
 #endif
Index: dbd/apr_dbd_sqlite3.c
===================================================================
--- dbd/apr_dbd_sqlite3.c	(revision 431422)
+++ dbd/apr_dbd_sqlite3.c	(working copy)
@@ -25,6 +25,7 @@
 
 #include "apr_strings.h"
 #include "apr_time.h"
+#include "apr_buckets.h"
 
 #include "apr_dbd_internal.h"
 
@@ -72,16 +73,18 @@
 struct apr_dbd_prepared_t {
     sqlite3_stmt *stmt;
     apr_dbd_prepared_t *next;
+    int nargs;
+    apr_dbd_type_e *types;
 };
 
 #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)
+static int dbd_sqlite3_select_internal(apr_pool_t *pool,
+                                       apr_dbd_results_t **results,
+                                       sqlite3_stmt *stmt, int seek)
 {
-    sqlite3_stmt *stmt = NULL;
-    const char *tail = NULL;
-    int i, ret, retry_count = 0;
+    int i, ret, retry_count = 0, column_count;
     size_t num_tuples = 0;
     int increment = 0;
     apr_dbd_row_t *row = NULL;
@@ -89,6 +92,90 @@
     apr_dbd_column_t *column;
     char *hold = NULL;
 
+     column_count = sqlite3_column_count(stmt);
+     if (!*results) {
+         *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+     }
+     (*results)->stmt = stmt;
+     (*results)->sz = column_count;
+     (*results)->random = seek;
+     (*results)->next_row = 0;
+     (*results)->tuples = 0;
+     (*results)->col_names = apr_pcalloc(pool,
+                                         column_count * sizeof(char *));
+     do {
+         ret = sqlite3_step(stmt);
+         if (ret == SQLITE_BUSY) {
+             if (retry_count++ > MAX_RETRY_COUNT) {
+                 ret = SQLITE_ERROR;
+             } else {
+                 apr_dbd_mutex_unlock();
+                 apr_sleep(MAX_RETRY_SLEEP);
+                 apr_dbd_mutex_lock();
+             }
+         } else if (ret == SQLITE_ROW) {
+             int length;
+             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);
+
+    return ret;
+}
+
+static int dbd_sqlite3_select(apr_pool_t * pool, apr_dbd_t * sql, apr_dbd_results_t ** results, const char *query, int seek)
+{
+    sqlite3_stmt *stmt = NULL;
+    const char *tail = NULL;
+    int ret;
+
     if (sql->trans && sql->trans->errnum) {
         return sql->trans->errnum;
     }
@@ -100,81 +187,7 @@
         apr_dbd_mutex_unlock();
         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 {
-                    apr_dbd_mutex_unlock();
-                    apr_sleep(MAX_RETRY_SLEEP);
-                    apr_dbd_mutex_lock();
-                }
-            } 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 = dbd_sqlite3_select_internal(pool, results, stmt, seek);
     }
     ret = sqlite3_finalize(stmt);
     apr_dbd_mutex_unlock();
@@ -233,11 +246,108 @@
     return value;
 }
 
+static apr_status_t dbd_sqlite3_datum_get(const apr_dbd_row_t *row, int n,
+                                          apr_dbd_type_e type, void *data)
+{
+    if ((n < 0) || (n >= row->res->sz)) {
+      return APR_EGENERAL;
+    }
+
+    if (row->columns[n]->type == SQLITE_NULL) {
+        return APR_ENOENT;
+    }
+
+    switch (type) {
+    case APR_DBD_TYPE_TINY:
+        *(char*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_UTINY:
+        *(unsigned char*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_SHORT:
+        *(short*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_USHORT:
+        *(unsigned short*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_INT:
+        *(int*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_UINT:
+        *(unsigned int*)data = atoi(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_LONG:
+        *(long*)data = atol(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_ULONG:
+        *(unsigned long*)data = atol(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_LONGLONG:
+        *(apr_int64_t*)data = apr_atoi64(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_ULONGLONG:
+        *(apr_uint64_t*)data = apr_atoi64(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_FLOAT:
+        *(float*)data = atof(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_DOUBLE:
+        *(double*)data = atof(row->columns[n]->value);
+        break;
+    case APR_DBD_TYPE_STRING:
+    case APR_DBD_TYPE_TEXT:
+    case APR_DBD_TYPE_TIME:
+    case APR_DBD_TYPE_DATE:
+    case APR_DBD_TYPE_DATETIME:
+    case APR_DBD_TYPE_TIMESTAMP:
+    case APR_DBD_TYPE_ZTIMESTAMP:
+        *(char**)data = row->columns[n]->value;
+        break;
+    case APR_DBD_TYPE_BLOB:
+        {
+        apr_bucket *e;
+        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+        e = apr_bucket_immortal_create(row->columns[n]->value,
+                                       row->columns[n]->size,b->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(b, e);
+        }
+        break;
+    case APR_DBD_TYPE_NULL:
+        *(void**)data = NULL;
+        break;
+    default:
+        return APR_EGENERAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 static const char *dbd_sqlite3_error(apr_dbd_t *sql, int n)
 {
     return sqlite3_errmsg(sql->conn);
 }
 
+static int dbd_sqlite3_query_internal(sqlite3 *conn, sqlite3_stmt *stmt,
+                                      int *nrows)
+{
+    int ret = -1, retry_count = 0;
+
+    while(retry_count++ <= MAX_RETRY_COUNT) {
+        ret = sqlite3_step(stmt);
+        if (ret != SQLITE_BUSY)
+            break;
+
+        apr_dbd_mutex_unlock();
+        apr_sleep(MAX_RETRY_SLEEP);
+        apr_dbd_mutex_lock();
+    }
+
+    *nrows = sqlite3_changes(conn);
+
+    return ret;
+}
+
 static int dbd_sqlite3_query(apr_dbd_t *sql, int *nrows, const char *query)
 {
     sqlite3_stmt *stmt = NULL;
@@ -252,25 +362,14 @@
     apr_dbd_mutex_lock();
 
     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;
+        ret = dbd_sqlite3_query_internal(sql->conn, stmt, nrows);
 
-            apr_dbd_mutex_unlock();
-            apr_sleep(MAX_RETRY_SLEEP);
-            apr_dbd_mutex_lock();
-        }
-
-        *nrows = sqlite3_changes(sql->conn);
         sqlite3_finalize(stmt);
         length -= (tail - query);
         query = tail;
@@ -303,42 +402,24 @@
 
 static int dbd_sqlite3_prepare(apr_pool_t *pool, apr_dbd_t *sql,
                                const char *query, const char *label,
+                               int nargs, apr_dbd_type_e *types,
                                apr_dbd_prepared_t **statement)
 {
     sqlite3_stmt *stmt;
-    char *p, *slquery = apr_pstrdup(pool, query);
-    const char *tail = NULL, *q;
+    const char *tail = NULL;
     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;
-
     apr_dbd_mutex_lock();
 
-    ret = sqlite3_prepare(sql->conn, slquery, strlen(query), &stmt, &tail);
+    ret = sqlite3_prepare(sql->conn, query, strlen(query), &stmt, &tail);
     if (ret == SQLITE_OK) {
         apr_dbd_prepared_t *prep; 
 
         prep = apr_pcalloc(sql->pool, sizeof(*prep));
         prep->stmt = stmt;
         prep->next = sql->prep;
+        prep->nargs = nargs;
+        prep->types = types;
 
         /* link new statement to the handle */
         sql->prep = prep;
@@ -353,12 +434,45 @@
     return ret;
 }
 
+static void dbd_sqlite3_bind(apr_dbd_prepared_t *statement,
+                             int nargs, const char **values)
+{
+    sqlite3_stmt *stmt = statement->stmt;
+    int i, j;
+
+    for (i = 0; i < nargs; i++) {
+        if (values[i] == NULL) {
+            sqlite3_bind_null(stmt, i + 1);
+        }
+        else {
+            if (statement->types[i] == APR_DBD_TYPE_BLOB) {
+                char *data = (char *)values[i];
+                int size = atoi(data);
+
+                /* encoding: length:table:column:payload */
+                for (j = 0; j < 3; j++) {
+                    data = strchr(data, ':');
+                }
+                data++;
+
+                sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC);
+            }
+            else {
+                sqlite3_bind_text(stmt, i + 1, values[i],
+                                  strlen(values[i]), SQLITE_STATIC);
+            }
+        }
+    }
+
+    return;
+}
+
 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;
+    int ret = -1;
 
     if (sql->trans && sql->trans->errnum) {
         return sql->trans->errnum;
@@ -368,23 +482,10 @@
 
     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);
-        }
+        dbd_sqlite3_bind(statement, nargs, values);
 
-        while(retry_count++ <= MAX_RETRY_COUNT) {
-            ret = sqlite3_step(stmt);
-            if (ret != SQLITE_BUSY)
-                break;
+        ret = dbd_sqlite3_query_internal(sql->conn, stmt, nrows);
 
-            apr_dbd_mutex_unlock();
-            apr_sleep(MAX_RETRY_SLEEP);
-            apr_dbd_mutex_lock();
-        }
-
-        *nrows = sqlite3_changes(sql->conn);
-
         sqlite3_reset(stmt);
     }
 
@@ -403,20 +504,20 @@
                                apr_dbd_prepared_t *statement, va_list args)
 {
     const char **values;
-    int i, nargs;
+    int i;
 
     if (sql->trans && sql->trans->errnum) {
         return sql->trans->errnum;
     }
 
-    nargs = sqlite3_bind_parameter_count(statement->stmt);
-    values = apr_palloc(pool, sizeof(*values) * nargs);
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
 
-    for (i = 0; i < nargs; i++) {
-        values[i] = apr_pstrdup(pool, va_arg(args, const char*));
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, const char*);
     }
 
-    return dbd_sqlite3_pquery(pool, sql, nrows, statement, nargs, values);
+    return dbd_sqlite3_pquery(pool, sql, nrows, statement,
+                              statement->nargs, values);
 }
 
 static int dbd_sqlite3_pselect(apr_pool_t *pool, apr_dbd_t *sql,
@@ -425,13 +526,7 @@
                                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;
+    int ret;
 
     if (sql->trans && sql->trans->errnum) {
         return sql->trans->errnum;
@@ -441,88 +536,10 @@
 
     ret = sqlite3_reset(stmt);
     if (ret == SQLITE_OK) {
-        int column_count;
+        dbd_sqlite3_bind(statement, nargs, values);
 
-        for (i=0; i < nargs; i++) {
-            sqlite3_bind_text(stmt, i + 1, values[i], strlen(values[i]),
-                              SQLITE_STATIC);
-        }
+        ret = dbd_sqlite3_select_internal(pool, results, stmt, seek);
 
-        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 {
-                    apr_dbd_mutex_unlock();
-                    apr_sleep(MAX_RETRY_SLEEP);
-                    apr_dbd_mutex_lock();
-                }
-            } 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);
     }
     apr_dbd_mutex_unlock();
@@ -539,23 +556,203 @@
                                 va_list args)
 {
     const char **values;
-    int i, nargs;
+    int i;
 
     if (sql->trans && sql->trans->errnum) {
         return sql->trans->errnum;
     }
 
-    nargs = sqlite3_bind_parameter_count(statement->stmt);
-    values = apr_palloc(pool, sizeof(*values) * nargs);
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
 
-    for (i = 0; i < nargs; i++) {
+    for (i = 0; i < statement->nargs; i++) {
         values[i] = apr_pstrdup(pool, va_arg(args, const char*));
     }
 
     return dbd_sqlite3_pselect(pool, sql, results, statement,
-                               seek, nargs, values);
+                               seek, statement->nargs, values);
 }
 
+static void dbd_sqlite3_bbind(apr_dbd_prepared_t * statement,
+                              int nargs, const void **values)
+{
+    sqlite3_stmt *stmt = statement->stmt;
+    int i;
+    apr_dbd_type_e type;
+
+    for (i = 0; i < nargs; i++) {
+        type = (values[i] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+
+        switch (type) {
+        case APR_DBD_TYPE_TINY:
+            sqlite3_bind_int(stmt, i + 1, *(char*)values[i]);
+            break;
+        case APR_DBD_TYPE_UTINY:
+            sqlite3_bind_int(stmt, i + 1, *(unsigned char*)values[i]);
+            break;
+        case APR_DBD_TYPE_SHORT:
+            sqlite3_bind_int(stmt, i + 1, *(short*)values[i]);
+            break;
+        case APR_DBD_TYPE_USHORT:
+            sqlite3_bind_int(stmt, i + 1, *(unsigned short*)values[i]);
+            break;
+        case APR_DBD_TYPE_INT:
+            sqlite3_bind_int(stmt, i + 1, *(int*)values[i]);
+            break;
+        case APR_DBD_TYPE_UINT:
+            sqlite3_bind_int(stmt, i + 1, *(unsigned int*)values[i]);
+            break;
+        case APR_DBD_TYPE_LONG:
+            sqlite3_bind_int64(stmt, i + 1, *(long*)values[i]);
+            break;
+        case APR_DBD_TYPE_ULONG:
+            sqlite3_bind_int64(stmt, i + 1, *(unsigned long*)values[i]);
+            break;
+        case APR_DBD_TYPE_LONGLONG:
+            sqlite3_bind_int64(stmt, i + 1, *(apr_int64_t*)values[i]);
+            break;
+        case APR_DBD_TYPE_ULONGLONG:
+            sqlite3_bind_int64(stmt, i + 1, *(apr_uint64_t*)values[i]);
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            sqlite3_bind_double(stmt, i + 1, *(float*)values[i]);
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            sqlite3_bind_double(stmt, i + 1, *(double*)values[i]);
+            break;
+        case APR_DBD_TYPE_STRING:
+        case APR_DBD_TYPE_TEXT:
+        case APR_DBD_TYPE_TIME:
+        case APR_DBD_TYPE_DATE:
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            sqlite3_bind_text(stmt, i + 1, values[i], strlen(values[i]),
+                              SQLITE_STATIC);
+            break;
+        case APR_DBD_TYPE_BLOB:
+            {
+            apr_dbd_blob_t *blob = (void *)values[i];
+
+            sqlite3_bind_blob(stmt, i + 1, blob->data, blob->size,
+                              SQLITE_STATIC);
+            }
+            break;
+        case APR_DBD_TYPE_NULL:
+        default:
+            sqlite3_bind_null(stmt, i + 1);
+            break;
+        }
+    }
+
+    return;
+}
+
+static int dbd_sqlite3_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                               int *nrows, apr_dbd_prepared_t * statement,
+                               int nargs, const void **values)
+{
+    sqlite3_stmt *stmt = statement->stmt;
+    int ret = -1;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    apr_dbd_mutex_lock();
+
+    ret = sqlite3_reset(stmt);
+    if (ret == SQLITE_OK) {
+        dbd_sqlite3_bbind(statement, nargs, values);
+
+        ret = dbd_sqlite3_query_internal(sql->conn, stmt, nrows);
+
+        sqlite3_reset(stmt);
+    }
+
+    if (dbd_sqlite3_is_success(ret)) {
+        ret = 0;
+    }
+    apr_dbd_mutex_unlock();
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+
+    return ret;
+}
+
+static int dbd_sqlite3_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                                int *nrows, apr_dbd_prepared_t * statement,
+                                va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, void*);
+    }
+
+    return dbd_sqlite3_pbquery(pool, sql, nrows, statement,
+                               statement->nargs, values);
+}
+
+static int dbd_sqlite3_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                                apr_dbd_results_t ** results,
+                                apr_dbd_prepared_t * statement,
+                                int seek, int nargs, const void **values)
+{
+    sqlite3_stmt *stmt = statement->stmt;
+    int ret;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    apr_dbd_mutex_lock();
+
+    ret = sqlite3_reset(stmt);
+    if (ret == SQLITE_OK) {
+        dbd_sqlite3_bbind(statement, nargs, values);
+
+        ret = dbd_sqlite3_select_internal(pool, results, stmt, seek);
+
+        sqlite3_reset(stmt);
+    }
+    apr_dbd_mutex_unlock();
+
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_sqlite3_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                                 apr_dbd_results_t ** results,
+                                 apr_dbd_prepared_t * statement, int seek,
+                                 va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, void*);
+    }
+
+    return dbd_sqlite3_pbselect(pool, sql, results, statement,
+                                seek, statement->nargs, values);
+}
+
 static int dbd_sqlite3_start_transaction(apr_pool_t *pool,
                                          apr_dbd_t *handle,
                                          apr_dbd_transaction_t **trans)
@@ -696,6 +893,12 @@
     dbd_sqlite3_pselect,
     dbd_sqlite3_get_name,
     dbd_sqlite3_transaction_mode_get,
-    dbd_sqlite3_transaction_mode_set
+    dbd_sqlite3_transaction_mode_set,
+    "?",
+    dbd_sqlite3_pvbquery,
+    dbd_sqlite3_pvbselect,
+    dbd_sqlite3_pbquery,
+    dbd_sqlite3_pbselect,
+    dbd_sqlite3_datum_get
 };
 #endif
Index: dbd/apr_dbd_pgsql.c
===================================================================
--- dbd/apr_dbd_pgsql.c	(revision 431422)
+++ dbd/apr_dbd_pgsql.c	(working copy)
@@ -31,11 +31,10 @@
 
 #include "apr_strings.h"
 #include "apr_time.h"
+#include "apr_buckets.h"
 
 #include "apr_dbd_internal.h"
 
-#define QUERY_MAX_ARGS 40
-
 struct apr_dbd_transaction_t {
     int mode;
     int errnum;
@@ -65,6 +64,7 @@
     const char *name;
     int prepared;
     int nargs;
+    apr_dbd_type_e *types;
 };
 
 #define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
@@ -286,6 +286,80 @@
     return PQgetvalue(row->res->res, row->n, n);
 }
 
+static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
+                                        apr_dbd_type_e type, void *data)
+{
+    if (PQgetisnull(row->res->res, row->n, n)) {
+        return APR_ENOENT;
+    }
+
+    switch (type) {
+    case APR_DBD_TYPE_TINY:
+        *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_UTINY:
+        *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_SHORT:
+        *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_USHORT:
+        *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_INT:
+        *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_UINT:
+        *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_LONG:
+        *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_ULONG:
+        *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_LONGLONG:
+        *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_ULONGLONG:
+        *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_FLOAT:
+        *(float*)data = atof(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_DOUBLE:
+        *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
+        break;
+    case APR_DBD_TYPE_STRING:
+    case APR_DBD_TYPE_TEXT:
+    case APR_DBD_TYPE_TIME:
+    case APR_DBD_TYPE_DATE:
+    case APR_DBD_TYPE_DATETIME:
+    case APR_DBD_TYPE_TIMESTAMP:
+    case APR_DBD_TYPE_ZTIMESTAMP:
+        *(char**)data = PQgetvalue(row->res->res, row->n, n);
+        break;
+    case APR_DBD_TYPE_BLOB:
+        {
+        apr_bucket *e;
+        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+        e = apr_bucket_immortal_create(PQgetvalue(row->res->res, row->n, n),
+                                       PQgetlength(row->res->res, row->n, n),
+                                       b->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(b, e);
+        }
+        break;
+    case APR_DBD_TYPE_NULL:
+        *(void**)data = NULL;
+        break;
+    default:
+        return APR_EGENERAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
 {
     return PQerrorMessage(sql->conn);
@@ -377,78 +451,86 @@
 
 static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
                              const char *query, const char *label,
+                             int nargs, apr_dbd_type_e *types,
                              apr_dbd_prepared_t **statement)
 {
     char *sqlcmd;
     char *sqlptr;
-    size_t length;
+    size_t length, qlen;
     size_t i = 0;
-    const char *args[QUERY_MAX_ARGS];
+    const char **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) ;
+    (*statement)->nargs = nargs;
+    (*statement)->types = types;
 
-    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;
+    args = apr_palloc(pool, nargs * sizeof(*args));
+
+    qlen = strlen(query);
+    length = qlen + 1;
+
+    for (i = 0; i < nargs; i++) {
+        switch (types[i]) {
+        case APR_DBD_TYPE_TINY: 
+        case APR_DBD_TYPE_UTINY: 
+        case APR_DBD_TYPE_SHORT: 
+        case APR_DBD_TYPE_USHORT:
+            args[i] = "smallint";
+            break;
+        case APR_DBD_TYPE_INT: 
+        case APR_DBD_TYPE_UINT:
+            args[i] = "integer";
+            break;
+        case APR_DBD_TYPE_LONG: 
+        case APR_DBD_TYPE_ULONG:   
+        case APR_DBD_TYPE_LONGLONG: 
+        case APR_DBD_TYPE_ULONGLONG:
+            args[i] = "bigint";
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            args[i] = "real";
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            args[i] = "double precision";
+            break;
+        case APR_DBD_TYPE_TEXT:
+            args[i] = "text";
+            break;
+        case APR_DBD_TYPE_TIME:
+            args[i] = "time";
+            break;
+        case APR_DBD_TYPE_DATE:
+            args[i] = "date";
+            break;
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+            args[i] = "timestamp";
+            break;
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            args[i] = "timestamp with time zone";
+            break;
+        case APR_DBD_TYPE_BLOB:
+            args[i] = "bytea";
+            break;
+        case APR_DBD_TYPE_NULL:
+            args[i] = "varchar"; /* XXX Eh? */
+            break;
+        default:
+            args[i] = "varchar";
+            break;
         }
-        else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
-            /* reduce %% to % */
-            *pgptr++ = *sqlptr++;
-        }
-        else {
-            *pgptr++ = *sqlptr;
-        }
+        length += 1 + strlen(args[i]);
     }
-    *pgptr = 0;
 
     if (!label) {
         /* don't really prepare; use in execParams instead */
         (*statement)->prepared = 0;
-        (*statement)->name = apr_pstrdup(pool, pgquery);
+        (*statement)->name = apr_pstrdup(pool, query);
         return 0;
     }
     (*statement)->name = apr_pstrdup(pool, label);
@@ -462,10 +544,10 @@
     length = strlen(label);
     memcpy(sqlptr, label, length);
     sqlptr += length;
-    if ((*statement)->nargs > 0) {
+    if (nargs > 0) {
         memcpy(sqlptr, " (",2);
         sqlptr += 2;
-        for (i=0; i < (*statement)->nargs; ++i) {
+        for (i=0; i < nargs; ++i) {
             alen = strlen(args[i]);
             memcpy(sqlptr, args[i], alen);
             sqlptr += alen;
@@ -475,8 +557,8 @@
     }
     memcpy(sqlptr, " AS ", 4);
     sqlptr += 4;
-    memcpy(sqlptr, pgquery, strlen(pgquery));
-    sqlptr += strlen(pgquery);
+    memcpy(sqlptr, query, qlen);
+    sqlptr += qlen;
     *sqlptr = 0;
 
     res = PQexec(sql->conn, sqlcmd);
@@ -496,17 +578,14 @@
     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)
+static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
+                                     int *nrows, apr_dbd_prepared_t *statement,
+                                     int nargs, const char **values,
+                                     const int *len, const int *fmt)
 {
     int ret;
     PGresult *res;
 
-    if (sql->trans && sql->trans->errnum) {
-        return sql->trans->errnum;
-    }
-
     if (TXN_IGNORE_ERRORS(sql->trans)) {
         PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
         if (res) {
@@ -522,12 +601,12 @@
     }
 
     if (statement->prepared) {
-        res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
-                             0);
+        res = PQexecPrepared(sql->conn, statement->name, nargs, values,
+                             len, fmt, 0);
     }
     else {
-        res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
-                           0);
+        res = PQexecParams(sql->conn, statement->name, nargs, 0, values,
+                           len, fmt, 0);
     }
     if (res) {
         ret = PQresultStatus(res);
@@ -580,6 +659,59 @@
     return ret;
 }
 
+static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
+                           int nargs, const char **values,
+                           const char **val, int *len)
+{
+    int i, j;
+
+    for (i = 0; i < nargs; i++) {
+        if (values[i] == NULL) {
+            val[i] = NULL;
+        }
+        else {
+            if (statement->types[i] == APR_DBD_TYPE_BLOB) {
+                char *data = (char *)values[i];
+
+                len[i] = atoi(data);
+
+                /* encoding: length:table:column:payload */
+                for (j = 0; j < 3; j++) {
+                    data = strchr(data, ':');
+                }
+                data++;
+
+                val[i] = data;
+            }
+            else {
+                val[i] = values[i];
+            }
+        }
+    }
+
+    return;
+}
+
+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 *len;
+    const char **val;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    val = apr_palloc(pool, sizeof(*val) * nargs);
+    len = apr_pcalloc(pool, sizeof(*len) * nargs);
+
+    dbd_pgsql_bind(statement, nargs, values, val, len);
+
+    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
+                                     nargs, val, len, NULL);
+}
+
 static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
                              int *nrows, apr_dbd_prepared_t *statement,
                              va_list args)
@@ -594,26 +726,23 @@
     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*));
+        values[i] = 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)
+static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
+                                      apr_dbd_results_t **results,
+                                      apr_dbd_prepared_t *statement,
+                                      int seek, int nargs, const char **values,
+                                      const int *len, const int *fmt)
 {
     PGresult *res;
     int rv;
     int ret = 0;
 
-    if (sql->trans && sql->trans->errnum) {
-        return sql->trans->errnum;
-    }
-
     if (seek) { /* synchronous query */
         if (TXN_IGNORE_ERRORS(sql->trans)) {
             PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
@@ -630,12 +759,12 @@
             }
         }
         if (statement->prepared) {
-            res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
-                                 0, 0);
+            res = PQexecPrepared(sql->conn, statement->name, nargs, values,
+                                 len, fmt, 0);
         }
         else {
-            res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
-                               0, 0);
+            res = PQexecParams(sql->conn, statement->name, nargs, 0, values,
+                               len, fmt, 0);
         }
         if (res) {
             ret = PQresultStatus(res);
@@ -712,11 +841,11 @@
         }
         if (statement->prepared) {
             rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
-                                     0, 0, 0);
+                                     len, fmt, 0);
         }
         else {
             rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
-                                   values, 0, 0, 0);
+                                   values, len, fmt, 0);
         }
         if (rv == 0) {
             if (TXN_IGNORE_ERRORS(sql->trans)) {
@@ -764,6 +893,27 @@
     return ret;
 }
 
+static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+                             apr_dbd_results_t **results,
+                             apr_dbd_prepared_t *statement,
+                             int seek, int nargs, const char **values)
+{
+    int *len;
+    const char **val;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    val = apr_palloc(pool, sizeof(*val) * nargs);
+    len = apr_pcalloc(pool, sizeof(*len) * nargs);
+
+    dbd_pgsql_bind(statement, nargs, values, val, len);
+
+    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
+                                      seek, nargs, val, len, NULL);
+}
+
 static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
                               apr_dbd_results_t **results,
                               apr_dbd_prepared_t *statement,
@@ -779,13 +929,176 @@
     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*));
+        values[i] = va_arg(args, const char*);
     }
 
     return dbd_pgsql_pselect(pool, sql, results, statement,
-                             seek, statement->nargs, values) ;
+                             seek, statement->nargs, values);
 }
 
+static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
+                            int nargs, const void **values,
+                            const char **val, int *len, int *fmt)
+{
+    int i;
+    apr_dbd_type_e type;
+
+    for (i = 0; i < nargs; i++) {
+        type = (values[i] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+
+        switch (type) {
+        case APR_DBD_TYPE_TINY:
+            val[i] = apr_itoa(pool, *(char*)values[i]);
+            break;
+        case APR_DBD_TYPE_UTINY:
+            val[i] = apr_itoa(pool, *(unsigned char*)values[i]);
+            break;
+        case APR_DBD_TYPE_SHORT:
+            val[i] = apr_itoa(pool, *(short*)values[i]);
+            break;
+        case APR_DBD_TYPE_USHORT:
+            val[i] = apr_itoa(pool, *(unsigned short*)values[i]);
+            break;
+        case APR_DBD_TYPE_INT:
+            val[i] = apr_itoa(pool, *(int*)values[i]);
+            break;
+        case APR_DBD_TYPE_UINT:
+            val[i] = apr_itoa(pool, *(unsigned int*)values[i]);
+            break;
+        case APR_DBD_TYPE_LONG:
+            val[i] = apr_ltoa(pool, *(long*)values[i]);
+            break;
+        case APR_DBD_TYPE_ULONG:
+            val[i] = apr_ltoa(pool, *(unsigned long*)values[i]);
+            break;
+        case APR_DBD_TYPE_LONGLONG:
+            val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
+                                  *(apr_int64_t*)values[i]);
+            break;
+        case APR_DBD_TYPE_ULONGLONG:
+            val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
+                                  *(apr_uint64_t*)values[i]);
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            val[i] = apr_psprintf(pool, "%f", *(float*)values[i]);
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            val[i] = apr_psprintf(pool, "%lf", *(double*)values[i]);
+            break;
+        case APR_DBD_TYPE_STRING:
+        case APR_DBD_TYPE_TEXT:
+        case APR_DBD_TYPE_TIME:
+        case APR_DBD_TYPE_DATE:
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            val[i] = values[i];
+            break;
+        case APR_DBD_TYPE_BLOB:
+            {
+            apr_dbd_blob_t *blob = (void *)values[i];
+
+            val[i] = blob->data;
+            len[i] = blob->size;
+            fmt[i] = 1;
+            }
+            break;
+        case APR_DBD_TYPE_NULL:
+        default:
+            val[i] = NULL;
+            break;
+        }
+    }
+
+    return;
+}
+
+static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                             int *nrows, apr_dbd_prepared_t * statement,
+                             int nargs, const void **values)
+{
+    int *len, *fmt;
+    const char **val;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+    dbd_pgsql_bbind(pool, statement, nargs, values, val, len, fmt);
+
+    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
+                                     nargs, val, len, fmt);
+}
+
+static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
+                              int *nrows, apr_dbd_prepared_t * statement,
+                              va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, void*);
+    }
+
+    return dbd_pgsql_pbquery(pool, sql, nrows, statement,
+                             statement->nargs, values);
+}
+
+static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                              apr_dbd_results_t ** results,
+                              apr_dbd_prepared_t * statement,
+                              int seek, int nargs, const void **values)
+{
+    int *len, *fmt;
+    const char **val;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
+    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
+    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
+
+    dbd_pgsql_bbind(pool, statement, nargs, values, val, len, fmt);
+
+    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
+                                      seek, nargs, val, len, fmt);
+}
+
+static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
+                               apr_dbd_results_t ** results,
+                               apr_dbd_prepared_t * statement, int seek,
+                               va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, void*);
+    }
+
+    return dbd_pgsql_pbselect(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)
 {
@@ -949,6 +1262,12 @@
     dbd_pgsql_pselect,
     dbd_pgsql_get_name,
     dbd_pgsql_transaction_mode_get,
-    dbd_pgsql_transaction_mode_set
+    dbd_pgsql_transaction_mode_set,
+    "$%d",
+    dbd_pgsql_pvbquery,
+    dbd_pgsql_pvbselect,
+    dbd_pgsql_pbquery,
+    dbd_pgsql_pbselect,
+    dbd_pgsql_datum_get
 };
 #endif
Index: dbd/apr_dbd.c
===================================================================
--- dbd/apr_dbd.c	(revision 431422)
+++ dbd/apr_dbd.c	(working copy)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <stdio.h>
 
 #include "apu.h"
@@ -298,7 +299,100 @@
                                  const char *label,
                                  apr_dbd_prepared_t **statement)
 {
-    return driver->prepare(pool,handle,query,label,statement);
+    size_t qlen;
+    int nargs = 0, i;
+    char *p, *pq;
+    const char *q;
+    apr_dbd_type_e *t;
+
+    if (!driver->pformat) {
+        return APR_ENOTIMPL;
+    }
+
+    /* find the number of parameters in the query */
+    for (q = query; *q; q++) {
+        if (q[0] == '%') {
+            if (isalpha(q[1])) {
+                nargs++;
+            } else if (q[1] == '%') {
+                q++;
+            }
+        }
+    }
+
+    qlen = strlen(query) +
+           nargs * (strlen(driver->pformat) + sizeof(nargs) * 3 + 2) + 1;
+    pq = apr_palloc(pool, qlen);
+    t = apr_pcalloc(pool, sizeof(*t) * nargs);
+
+    for (p = pq, q = query, i = 0; *q; q++) {
+        if (q[0] == '%') {
+            if (isalpha(q[1])) {
+                switch (q[1]) {
+                case 'd': t[i] = APR_DBD_TYPE_INT;   break;
+                case 'u': t[i] = APR_DBD_TYPE_UINT;  break;
+                case 'f': t[i] = APR_DBD_TYPE_FLOAT; break;
+                case 'h':
+                    switch (q[2]) {
+                    case 'h':
+                        switch (q[3]){
+                        case 'd': t[i] = APR_DBD_TYPE_TINY;  q += 2; break;
+                        case 'u': t[i] = APR_DBD_TYPE_UTINY; q += 2; break;
+                        }
+                        break;
+                    case 'd': t[i] = APR_DBD_TYPE_SHORT;  q++; break;
+                    case 'u': t[i] = APR_DBD_TYPE_USHORT; q++; break;
+                    }
+                    break;
+                case 'l':
+                    switch (q[2]) {
+                    case 'l':
+                        switch (q[3]){
+                        case 'd': t[i] = APR_DBD_TYPE_LONGLONG;  q += 2; break;
+                        case 'u': t[i] = APR_DBD_TYPE_ULONGLONG; q += 2; break;
+                        }
+                        break;
+                    case 'd': t[i] = APR_DBD_TYPE_LONG;   q++; break;
+                    case 'u': t[i] = APR_DBD_TYPE_ULONG;  q++; break;
+                    case 'f': t[i] = APR_DBD_TYPE_DOUBLE; q++; break;
+                    }
+                    break;
+                case 'p':
+                    if (q[2] == 'D') {
+                        switch (q[3]) {
+                        case 't': t[i] = APR_DBD_TYPE_TEXT;       q += 2; break;
+                        case 'i': t[i] = APR_DBD_TYPE_TIME;       q += 2; break;
+                        case 'd': t[i] = APR_DBD_TYPE_DATE;       q += 2; break;
+                        case 'a': t[i] = APR_DBD_TYPE_DATETIME;   q += 2; break;
+                        case 's': t[i] = APR_DBD_TYPE_TIMESTAMP;  q += 2; break;
+                        case 'z': t[i] = APR_DBD_TYPE_ZTIMESTAMP; q += 2; break;
+                        case 'b': t[i] = APR_DBD_TYPE_BLOB;       q += 2; break;
+                        case 'n': t[i] = APR_DBD_TYPE_NULL;       q += 2; break;
+                        }
+                    } 
+                    break;
+                }
+                q++;
+
+                /* by default, we expect strings */
+                if (t[i] == APR_DBD_TYPE_NONE) {
+                    t[i] = APR_DBD_TYPE_STRING;
+                }
+
+                /* insert database specific parameter reference */
+                p += apr_snprintf(p, qlen - (p - pq), driver->pformat, ++i);
+            } else if (q[1] == '%') { /* reduce %% to % */
+                *p++ = *q++;
+            } else {
+                *p++ = *q;
+            }
+        } else {
+            *p++ = *q;
+        }
+    }
+    *p = '\0';
+
+    return driver->prepare(pool,handle,pq,label,nargs,t,statement);
 }
 APU_DECLARE(int) apr_dbd_pquery(const apr_dbd_driver_t *driver, apr_pool_t *pool,
                                 apr_dbd_t *handle, int *nrows,
@@ -336,3 +430,44 @@
     va_end(args);
     return ret;
 }
+APU_DECLARE(int) apr_dbd_pbquery(const apr_dbd_driver_t *driver,
+                                 apr_pool_t *pool,
+                                 apr_dbd_t *handle, int *nrows,
+                                 apr_dbd_prepared_t *statement, int nargs,
+                                 const void **args)
+{
+    return driver->pbquery(pool,handle,nrows,statement,nargs,args);
+}
+APU_DECLARE(int) apr_dbd_pbselect(const apr_dbd_driver_t *driver,
+                                  apr_pool_t *pool,
+                                  apr_dbd_t *handle, apr_dbd_results_t **res,
+                                  apr_dbd_prepared_t *statement, int random,
+                                  int nargs, const void **args)
+{
+    return driver->pbselect(pool,handle,res,statement,random,nargs,args);
+}
+APU_DECLARE(int) apr_dbd_pvbquery(const apr_dbd_driver_t *driver,
+                                  apr_pool_t *pool,
+                                  apr_dbd_t *handle, int *nrows,
+                                  apr_dbd_prepared_t *statement,...)
+{
+    int ret;
+    va_list args;
+    va_start(args, statement);
+    ret = driver->pvbquery(pool,handle,nrows,statement,args);
+    va_end(args);
+    return ret;
+}
+APU_DECLARE(int) apr_dbd_pvbselect(const apr_dbd_driver_t *driver,
+                                   apr_pool_t *pool,
+                                   apr_dbd_t *handle, apr_dbd_results_t **res,
+                                   apr_dbd_prepared_t *statement,
+                                   int random,...)
+{
+    int ret;
+    va_list args;
+    va_start(args, random);
+    ret = driver->pvbselect(pool,handle,res,statement,random,args);
+    va_end(args);
+    return ret;
+}

