Here is a "backport" of the _prepare() unified parsing support to 1.2.x and with it a more explicit support for SQL types when using _p[v]select/query.

Please review, test, flame etc.

--
Bojan
Index: include/apr_dbd.h
===================================================================
--- include/apr_dbd.h	(revision 443458)
+++ include/apr_dbd.h	(working copy)
@@ -272,12 +272,21 @@
  *                 (eg within a Request in httpd)
  *  @param statement - statement to prepare.  May point to null on entry.
  *  @return 0 for success or error code
- *  @remarks To specify parameters of the prepared query, use %s in place of
- *  database specific parameter syntax (e.g. for PostgreSQL, this would be $1,
- *  $2, for SQLite3 this would be ? etc.). For instance: "SELECT name FROM
- *  customers WHERE name=%s" would be a query that this function understands.
- *  Some drivers may support different data types using printf-like format:
- *  for example %d (e.g. PostgreSQL) or %f for numeric data.
+ *  @remarks To specify parameters of the prepared query, use %s, %d etc. (see
+ *  below for full list) in place of database specific parameter syntax (e.g.
+ *  for PostgreSQL, this would be $1, $2, for SQLite3 this would be ? etc.).
+ *  For instance: "SELECT name FROM customers WHERE name=%s" would be a query
+ *  that this function understands.
+ *  @remarks Here is a full list of format specifiers that this function
+ *  understands and what they map to in SQL: %hhd (TINY INT), %hhu (UNSIGNED
+ *  TINY INT), %hd (SHORT), %hu (UNSIGNED SHORT), %d (INT), %u (UNSIGNED INT),
+ *  %ld (LONG), %lu (UNSIGNED LONG), %lld (LONG LONG), %llu (UNSIGNED LONG
+ *  LONG), %f (FLOAT, REAL), %lf (DOUBLE PRECISION), %s (VARCHAR), %pDt (TEXT),
+ *  %pDi (TIME), %pDd (DATE), %pDa (SQL: DATETIME), %pDs (TIMESTAMP), %pDz
+ *  (TIMESTAMP WITH TIME ZONE), %pDb (BLOB), %pDn (NULL). Not all databases
+ *  have support for all these types, so the underlying driver will attempt the
+ *  "best match" where possible.  A % followed by any letter not in the above
+ *  list will be interpreted as VARCHAR (i.e. %s).
  */
 APU_DECLARE(int) apr_dbd_prepare(const apr_dbd_driver_t *driver, apr_pool_t *pool,
                                  apr_dbd_t *handle, const char *query,
Index: include/private/apr_dbd_internal.h
===================================================================
--- include/private/apr_dbd_internal.h	(revision 443458)
+++ include/private/apr_dbd_internal.h	(working copy)
@@ -29,6 +29,39 @@
 extern "C" {
 #endif
 
+/**
+ * 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.
+ */
+typedef enum {
+    APR_DBD_TYPE_NONE,
+    APR_DBD_TYPE_TINY,       /**< %hhd */
+    APR_DBD_TYPE_UTINY,      /**< %hhu */
+    APR_DBD_TYPE_SHORT,      /**< %hd */
+    APR_DBD_TYPE_USHORT,     /**< %hu */
+    APR_DBD_TYPE_INT,        /**< %d */
+    APR_DBD_TYPE_UINT,       /**< %u */
+    APR_DBD_TYPE_LONG,       /**< %ld */
+    APR_DBD_TYPE_ULONG,      /**< %lu */
+    APR_DBD_TYPE_LONGLONG,   /**< %lld */
+    APR_DBD_TYPE_ULONGLONG,  /**< %llu */
+    APR_DBD_TYPE_FLOAT,      /**< %f */
+    APR_DBD_TYPE_DOUBLE,     /**< %lf */
+    APR_DBD_TYPE_STRING,     /**< %s */
+    APR_DBD_TYPE_TEXT,       /**< %pDt */
+    APR_DBD_TYPE_TIME,       /**< %pDi */
+    APR_DBD_TYPE_DATE,       /**< %pDd */
+    APR_DBD_TYPE_DATETIME,   /**< %pDa */
+    APR_DBD_TYPE_TIMESTAMP,  /**< %pDs */
+    APR_DBD_TYPE_ZTIMESTAMP, /**< %pDz */
+    APR_DBD_TYPE_BLOB,       /**< %pDb */
+    APR_DBD_TYPE_NULL        /**< %pDn */
+} apr_dbd_type_e;
+
 struct apr_dbd_driver_t {
     /** name */
     const char *name;
@@ -185,11 +218,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
      *
@@ -246,7 +282,8 @@
                    apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
                    int random, int nargs, const char **args);
 
-
+    /** format of prepared statement parameters */
+    const char *pformat;
 };
 
 
Index: dbd/apr_dbd_sqlite2.c
===================================================================
--- dbd/apr_dbd_sqlite2.c	(revision 443458)
+++ 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"
 
@@ -46,6 +47,7 @@
     size_t ntuples;
     size_t sz;
     size_t index;
+    apr_pool_t *pool;
 };
 
 struct apr_dbd_row_t {
@@ -100,6 +102,7 @@
         (*results)->ntuples = tuples;
         (*results)->sz = fields;
         (*results)->random = seek;
+        (*results)->pool = pool;
 
         if (tuples > 0)
             apr_pool_cleanup_register(pool, result, free_table,
@@ -228,6 +231,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;
@@ -392,5 +396,6 @@
     dbd_sqlite_pvselect,
     dbd_sqlite_pquery,
     dbd_sqlite_pselect,
+    NULL
 };
 #endif
Index: dbd/apr_dbd_sqlite3.c
===================================================================
--- dbd/apr_dbd_sqlite3.c	(revision 443458)
+++ 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"
 
@@ -69,21 +70,24 @@
     size_t sz;
     int tuples;
     char **col_names;
+    apr_pool_t *pool;
 };
 
 struct apr_dbd_prepared_t {
     sqlite3_stmt *stmt;
     apr_dbd_prepared_t *next;
+    int nargs;
+    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_t *sql,
+                                       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;
@@ -91,6 +95,98 @@
     apr_dbd_column_t *column;
     char *hold = NULL;
 
+    column_count = sqlite3_column_count(stmt);
+    if (!*results) {
+        *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+    }
+    (*results)->stmt = stmt;
+    (*results)->sz = column_count;
+    (*results)->random = seek;
+    (*results)->next_row = 0;
+    (*results)->tuples = 0;
+    (*results)->col_names = apr_pcalloc(pool, column_count * sizeof(char *));
+    (*results)->pool = pool;
+    do {
+        ret = sqlite3_step(stmt);
+        if (ret == SQLITE_BUSY) {
+            if (retry_count++ > MAX_RETRY_COUNT) {
+                ret = SQLITE_ERROR;
+            } else {
+#if APR_HAS_THREADS
+                apr_thread_mutex_unlock(sql->mutex);
+#endif
+                apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+                apr_thread_mutex_lock(sql->mutex);
+#endif
+            }
+        } else if (ret == SQLITE_ROW) {
+            int length;
+            apr_dbd_column_t *col;
+            row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+            row->res = *results;
+            increment = sizeof(apr_dbd_column_t *);
+            length = increment * (*results)->sz;
+            row->columns = apr_palloc(pool, length);
+            row->columnCount = column_count;
+            for (i = 0; i < (*results)->sz; i++) {
+                column = apr_palloc(pool, sizeof(apr_dbd_column_t));
+                row->columns[i] = column;
+                /* copy column name once only */
+                if ((*results)->col_names[i] == NULL) {
+                    (*results)->col_names[i] =
+                        apr_pstrdup(pool, sqlite3_column_name(stmt, i));
+                }
+                column->name = (*results)->col_names[i];
+                column->size = sqlite3_column_bytes(stmt, i);
+                column->type = sqlite3_column_type(stmt, i);
+                column->value = NULL;
+                switch (column->type) {
+                case SQLITE_FLOAT:
+                case SQLITE_INTEGER:
+                case SQLITE_TEXT:
+                    hold = (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:
+                     hold = (char *) sqlite3_column_blob(stmt, i);
+                     if (hold) {
+                         column->value = apr_pstrmemdup(pool, hold,
+                                                        column->size);
+                     }
+                    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;
     }
@@ -106,85 +202,7 @@
 #endif
         return ret;
     } else {
-        int column_count;
-        column_count = sqlite3_column_count(stmt);
-        if (!*results) {
-            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
-        }
-        (*results)->stmt = stmt;
-        (*results)->sz = column_count;
-        (*results)->random = seek;
-        (*results)->next_row = 0;
-        (*results)->tuples = 0;
-        (*results)->col_names = apr_pcalloc(pool,
-                                            column_count * sizeof(char *));
-        do {
-            ret = sqlite3_step(stmt);
-            if (ret == SQLITE_BUSY) {
-                if (retry_count++ > MAX_RETRY_COUNT) {
-                    ret = SQLITE_ERROR;
-                } else {
-#if APR_HAS_THREADS
-                    apr_thread_mutex_unlock(sql->mutex);
-#endif
-                    apr_sleep(MAX_RETRY_SLEEP);
-#if APR_HAS_THREADS
-                    apr_thread_mutex_lock(sql->mutex);
-#endif
-                }
-            } else if (ret == SQLITE_ROW) {
-                int length;
-                apr_dbd_column_t *col;
-                row = apr_palloc(pool, sizeof(apr_dbd_row_t));
-                row->res = *results;
-                increment = sizeof(apr_dbd_column_t *);
-                length = increment * (*results)->sz;
-                row->columns = apr_palloc(pool, length);
-                row->columnCount = column_count;
-                for (i = 0; i < (*results)->sz; i++) {
-                    column = apr_palloc(pool, sizeof(apr_dbd_column_t));
-                    row->columns[i] = column;
-                    /* copy column name once only */
-                    if ((*results)->col_names[i] == NULL) {
-                      (*results)->col_names[i] =
-                          apr_pstrdup(pool, sqlite3_column_name(stmt, i));
-                    }
-                    column->name = (*results)->col_names[i];
-                    column->size = sqlite3_column_bytes(stmt, i);
-                    column->type = sqlite3_column_type(stmt, i);
-                    column->value = NULL;
-                    switch (column->type) {
-                    case SQLITE_FLOAT:
-                    case SQLITE_INTEGER:
-                    case SQLITE_TEXT:
-                        hold = NULL;
-                        hold = (char *) sqlite3_column_text(stmt, i);
-                        if (hold) {
-                            column->value = apr_palloc(pool, column->size + 1);
-                            strncpy(column->value, hold, column->size + 1);
-                        }
-                        break;
-                    case SQLITE_BLOB:
-                        break;
-                    case SQLITE_NULL:
-                        break;
-                    }
-                    col = row->columns[i];
-                }
-                row->rownum = num_tuples++;
-                row->next_row = 0;
-                (*results)->tuples = num_tuples;
-                if ((*results)->next_row == 0) {
-                    (*results)->next_row = row;
-                }
-                if (lastrow != 0) {
-                    lastrow->next_row = row;
-                }
-                lastrow = row;
-            } else if (ret == SQLITE_DONE) {
-                ret = SQLITE_OK;
-            }
-        } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
+        ret = dbd_sqlite3_select_internal(pool, sql, results, stmt, seek);
     }
     ret = sqlite3_finalize(stmt);
 #if APR_HAS_THREADS
@@ -241,6 +259,30 @@
     return sqlite3_errmsg(sql->conn);
 }
 
+static int dbd_sqlite3_query_internal(apr_dbd_t *sql, sqlite3_stmt *stmt,
+                                      int *nrows)
+{
+    int ret = -1, retry_count = 0;
+
+    while(retry_count++ <= MAX_RETRY_COUNT) {
+        ret = sqlite3_step(stmt);
+        if (ret != SQLITE_BUSY)
+            break;
+
+#if APR_HAS_THREADS
+        apr_thread_mutex_unlock(sql->mutex);
+#endif
+        apr_sleep(MAX_RETRY_SLEEP);
+#if APR_HAS_THREADS
+        apr_thread_mutex_lock(sql->mutex);
+#endif
+    }
+
+    *nrows = sqlite3_changes(sql->conn);
+
+    return ret;
+}
+
 static int dbd_sqlite3_query(apr_dbd_t *sql, int *nrows, const char *query)
 {
     sqlite3_stmt *stmt = NULL;
@@ -257,29 +299,14 @@
 #endif
 
     do {
-        int retry_count = 0;
-
         ret = sqlite3_prepare(sql->conn, query, length, &stmt, &tail);
         if (ret != SQLITE_OK) {
             sqlite3_finalize(stmt);
             break;
         }
 
-        while(retry_count++ <= MAX_RETRY_COUNT) {
-            ret = sqlite3_step(stmt);
-            if (ret != SQLITE_BUSY)
-                break;
+        ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
 
-#if APR_HAS_THREADS
-            apr_thread_mutex_unlock(sql->mutex);
-#endif
-            apr_sleep(MAX_RETRY_SLEEP);
-#if APR_HAS_THREADS
-            apr_thread_mutex_lock(sql->mutex);
-#endif
-        }
-
-        *nrows = sqlite3_changes(sql->conn);
         sqlite3_finalize(stmt);
         length -= (tail - query);
         query = tail;
@@ -314,44 +341,26 @@
 
 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;
-
 #if APR_HAS_THREADS
     apr_thread_mutex_lock(sql->mutex);
 #endif
 
-    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;
@@ -368,12 +377,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;
@@ -385,27 +427,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, stmt, nrows);
 
-#if APR_HAS_THREADS
-            apr_thread_mutex_unlock(sql->mutex);
-#endif
-            apr_sleep(MAX_RETRY_SLEEP);
-#if APR_HAS_THREADS
-            apr_thread_mutex_lock(sql->mutex);
-#endif
-        }
-
-        *nrows = sqlite3_changes(sql->conn);
-
         sqlite3_reset(stmt);
     }
 
@@ -426,20 +451,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,
@@ -448,13 +473,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;
@@ -466,92 +485,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, sql, 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 {
-#if APR_HAS_THREADS
-                    apr_thread_mutex_unlock(sql->mutex);
-#endif
-                    apr_sleep(MAX_RETRY_SLEEP);
-#if APR_HAS_THREADS
-                    apr_thread_mutex_lock(sql->mutex);
-#endif
-                }
-            } else if (ret == SQLITE_ROW) {
-                int length;
-                apr_dbd_column_t *col;
-                row = apr_palloc(pool, sizeof(apr_dbd_row_t));
-                row->res = *results;
-                increment = sizeof(apr_dbd_column_t *);
-                length = increment * (*results)->sz;
-                row->columns = apr_palloc(pool, length);
-                row->columnCount = column_count;
-                for (i = 0; i < (*results)->sz; i++) {
-                    column = apr_palloc(pool, sizeof(apr_dbd_column_t));
-                    row->columns[i] = column;
-                    /* copy column name once only */
-                    if ((*results)->col_names[i] == NULL) {
-                      (*results)->col_names[i] =
-                          apr_pstrdup(pool, sqlite3_column_name(stmt, i));
-                    }
-                    column->name = (*results)->col_names[i];
-                    column->size = sqlite3_column_bytes(stmt, i);
-                    column->type = sqlite3_column_type(stmt, i);
-                    column->value = NULL;
-                    switch (column->type) {
-                    case SQLITE_FLOAT:
-                    case SQLITE_INTEGER:
-                    case SQLITE_TEXT:
-                        hold = NULL;
-                        hold = (char *) sqlite3_column_text(stmt, i);
-                        if (hold) {
-                            column->value = apr_palloc(pool, column->size + 1);
-                            strncpy(column->value, hold, column->size + 1);
-                        }
-                        break;
-                    case SQLITE_BLOB:
-                        break;
-                    case SQLITE_NULL:
-                        break;
-                    }
-                    col = row->columns[i];
-                }
-                row->rownum = num_tuples++;
-                row->next_row = 0;
-                (*results)->tuples = num_tuples;
-                if ((*results)->next_row == 0) {
-                    (*results)->next_row = row;
-                }
-                if (lastrow != 0) {
-                    lastrow->next_row = row;
-                }
-                lastrow = row;
-            } else if (ret == SQLITE_DONE) {
-                ret = SQLITE_OK;
-            }
-        } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
-
         sqlite3_reset(stmt);
     }
 #if APR_HAS_THREADS
@@ -570,21 +507,20 @@
                                 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_pselect(pool, sql, results, statement,
-                               seek, nargs, values);
+                               seek, statement->nargs, values);
 }
 
 static int dbd_sqlite3_start_transaction(apr_pool_t *pool,
@@ -719,5 +655,6 @@
     dbd_sqlite3_pvselect,
     dbd_sqlite3_pquery,
     dbd_sqlite3_pselect,
+    "?"
 };
 #endif
Index: dbd/apr_dbd_pgsql.c
===================================================================
--- dbd/apr_dbd_pgsql.c	(revision 443458)
+++ 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 errnum;
     apr_dbd_t *handle;
@@ -53,6 +52,7 @@
     size_t ntuples;
     size_t sz;
     size_t index;
+    apr_pool_t *pool;
 };
 
 struct apr_dbd_row_t {
@@ -64,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) \
@@ -110,6 +111,7 @@
         (*results)->ntuples = PQntuples(res);
         (*results)->sz = PQnfields(res);
         (*results)->random = seek;
+        (*results)->pool = pool;
         apr_pool_cleanup_register(pool, res, clear_result,
                                   apr_pool_cleanup_null);
     }
@@ -125,6 +127,7 @@
         }
         (*results)->random = seek;
         (*results)->handle = sql->conn;
+        (*results)->pool = pool;
     }
     return 0;
 }
@@ -240,78 +243,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);
@@ -325,10 +336,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;
@@ -338,8 +349,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);
@@ -359,24 +370,21 @@
     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 (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);
@@ -396,6 +404,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)
@@ -410,34 +471,31 @@
     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 (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);
@@ -464,17 +522,18 @@
         (*results)->ntuples = PQntuples(res);
         (*results)->sz = PQnfields(res);
         (*results)->random = seek;
+        (*results)->pool = pool;
         apr_pool_cleanup_register(pool, res, clear_result,
                                   apr_pool_cleanup_null);
     }
     else {
         if (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 (sql->trans) {
@@ -487,6 +546,7 @@
         }
         (*results)->random = seek;
         (*results)->handle = sql->conn;
+        (*results)->pool = pool;
     }
 
     if (sql->trans) {
@@ -495,6 +555,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,
@@ -510,11 +591,11 @@
     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 int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
@@ -660,5 +741,6 @@
     dbd_pgsql_pvselect,
     dbd_pgsql_pquery,
     dbd_pgsql_pselect,
+    "$%d"
 };
 #endif
Index: dbd/apr_dbd.c
===================================================================
--- dbd/apr_dbd.c	(revision 443458)
+++ dbd/apr_dbd.c	(working copy)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <stdio.h>
 
 #include "apu.h"
@@ -263,7 +264,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,

Reply via email to