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,