summaryrefslogtreecommitdiffstats
path: root/dbd/apr_dbd_sqlite3.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dbd/apr_dbd_sqlite3.c914
1 files changed, 914 insertions, 0 deletions
diff --git a/dbd/apr_dbd_sqlite3.c b/dbd/apr_dbd_sqlite3.c
new file mode 100644
index 0000000..d79dbe1
--- /dev/null
+++ b/dbd/apr_dbd_sqlite3.c
@@ -0,0 +1,914 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+
+#if APU_HAVE_SQLITE3
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sqlite3.h>
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+#define MAX_RETRY_COUNT 15
+#define MAX_RETRY_SLEEP 100000
+
+struct apr_dbd_transaction_t {
+ int mode;
+ int errnum;
+ apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+ sqlite3 *conn;
+ apr_dbd_transaction_t *trans;
+ apr_pool_t *pool;
+ apr_dbd_prepared_t *prep;
+};
+
+typedef struct {
+ char *name;
+ char *value;
+ int size;
+ int type;
+} apr_dbd_column_t;
+
+struct apr_dbd_row_t {
+ apr_dbd_results_t *res;
+ apr_dbd_column_t **columns;
+ apr_dbd_row_t *next_row;
+ int columnCount;
+ int rownum;
+};
+
+struct apr_dbd_results_t {
+ int random;
+ sqlite3 *handle;
+ sqlite3_stmt *stmt;
+ apr_dbd_row_t *next_row;
+ size_t sz;
+ int tuples;
+ char **col_names;
+ apr_pool_t *pool;
+};
+
+struct apr_dbd_prepared_t {
+ sqlite3_stmt *stmt;
+ apr_dbd_prepared_t *next;
+ int nargs;
+ int nvals;
+ apr_dbd_type_e *types;
+};
+
+#define dbd_sqlite3_is_success(x) (((x) == SQLITE_DONE) || ((x) == SQLITE_OK))
+
+static int dbd_sqlite3_select_internal(apr_pool_t *pool,
+ apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ sqlite3_stmt *stmt, int seek)
+{
+ int ret, retry_count = 0, column_count;
+ size_t i, 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;
+
+ 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 {
+ apr_dbd_mutex_unlock();
+ apr_sleep(MAX_RETRY_SLEEP);
+ apr_dbd_mutex_lock();
+ }
+ } else if (ret == SQLITE_ROW) {
+ int length;
+ 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_pstrmemdup(pool, hold,
+ column->size);
+ }
+ 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;
+ }
+ }
+ 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;
+ }
+ } while (ret == SQLITE_ROW || ret == SQLITE_BUSY);
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+ 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;
+ }
+
+ apr_dbd_mutex_lock();
+
+ ret = sqlite3_prepare(sql->conn, query, strlen(query), &stmt, &tail);
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = dbd_sqlite3_select_internal(pool, sql, results, stmt, seek);
+ }
+ sqlite3_finalize(stmt);
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static const char *dbd_sqlite3_get_name(const apr_dbd_results_t *res, int n)
+{
+ if ((n < 0) || ((size_t)n >= res->sz)) {
+ return NULL;
+ }
+
+ return res->col_names[n];
+}
+
+static int dbd_sqlite3_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+ apr_dbd_row_t **rowp, int rownum)
+{
+ int i = 0;
+
+ if (rownum == -1) {
+ *rowp = res->next_row;
+ if (*rowp == 0)
+ return -1;
+ res->next_row = (*rowp)->next_row;
+ return 0;
+ }
+ if (rownum > res->tuples) {
+ return -1;
+ }
+ rownum--;
+ *rowp = res->next_row;
+ for (; *rowp != 0; i++, *rowp = (*rowp)->next_row) {
+ if (i == rownum) {
+ return 0;
+ }
+ }
+
+ return -1;
+
+}
+
+static const char *dbd_sqlite3_get_entry(const apr_dbd_row_t *row, int n)
+{
+ apr_dbd_column_t *column;
+ const char *value;
+ if ((n < 0) || (n >= row->columnCount)) {
+ return NULL;
+ }
+ column = row->columns[n];
+ value = column->value;
+ return value;
+}
+
+static 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) || ((size_t)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 = (float)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:
+ case APR_DBD_TYPE_CLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_pool_create(row->columns[n]->value,
+ row->columns[n]->size,
+ row->res->pool, 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(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;
+
+ apr_dbd_mutex_unlock();
+ apr_sleep(MAX_RETRY_SLEEP);
+ apr_dbd_mutex_lock();
+ }
+
+ *nrows = sqlite3_changes(sql->conn);
+
+ if (dbd_sqlite3_is_success(ret)) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ int ret = -1, length = 0;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ length = strlen(query);
+ apr_dbd_mutex_lock();
+
+ do {
+ ret = sqlite3_prepare(sql->conn, query, length, &stmt, &tail);
+ if (ret != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ break;
+ }
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_finalize(stmt);
+ length -= (tail - query);
+ query = tail;
+ } while (length > 0);
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static apr_status_t free_mem(void *data)
+{
+ sqlite3_free(data);
+ return APR_SUCCESS;
+}
+
+static const char *dbd_sqlite3_escape(apr_pool_t *pool, const char *arg,
+ apr_dbd_t *sql)
+{
+ char *ret = sqlite3_mprintf("%q", arg);
+ apr_pool_cleanup_register(pool, ret, free_mem,
+ apr_pool_cleanup_null);
+ return ret;
+}
+
+static int dbd_sqlite3_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ int nargs, int nvals, apr_dbd_type_e *types,
+ apr_dbd_prepared_t **statement)
+{
+ sqlite3_stmt *stmt;
+ const char *tail = NULL;
+ int ret;
+
+ apr_dbd_mutex_lock();
+
+ 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->nvals = nvals;
+ prep->types = types;
+
+ /* link new statement to the handle */
+ sql->prep = prep;
+
+ *statement = prep;
+ } else {
+ sqlite3_finalize(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ return ret;
+}
+
+static void dbd_sqlite3_bind(apr_dbd_prepared_t *statement, const char **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int i, j;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ if (values[j] == NULL) {
+ sqlite3_bind_null(stmt, i + 1);
+ }
+ else {
+ switch (statement->types[i]) {
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char *)values[j];
+ int size = atoi((char*)values[++j]);
+
+ /* skip table and column */
+ j += 2;
+
+ sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC);
+ }
+ break;
+ default:
+ sqlite3_bind_text(stmt, i + 1, values[j],
+ strlen(values[j]), SQLITE_STATIC);
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+static int dbd_sqlite3_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ const char **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_bind(statement, values);
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_reset(stmt);
+ }
+
+ apr_dbd_mutex_unlock();
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_sqlite3_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_sqlite3_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_sqlite3_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement, int seek,
+ const char **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_bind(statement, values);
+
+ ret = dbd_sqlite3_select_internal(pool, sql, 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_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **results,
+ apr_dbd_prepared_t *statement, int seek,
+ va_list args)
+{
+ const char **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_sqlite3_pselect(pool, sql, results, statement, seek, values);
+}
+
+static void dbd_sqlite3_bbind(apr_dbd_prepared_t * statement,
+ const void **values)
+{
+ sqlite3_stmt *stmt = statement->stmt;
+ int i, j;
+ apr_dbd_type_e type;
+
+ for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+ type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ sqlite3_bind_int(stmt, i + 1, *(char*)values[j]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned char*)values[j]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ sqlite3_bind_int(stmt, i + 1, *(short*)values[j]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned short*)values[j]);
+ break;
+ case APR_DBD_TYPE_INT:
+ sqlite3_bind_int(stmt, i + 1, *(int*)values[j]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ sqlite3_bind_int(stmt, i + 1, *(unsigned int*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ sqlite3_bind_int64(stmt, i + 1, *(long*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ sqlite3_bind_int64(stmt, i + 1, *(unsigned long*)values[j]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ sqlite3_bind_int64(stmt, i + 1, *(apr_int64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ sqlite3_bind_int64(stmt, i + 1, *(apr_uint64_t*)values[j]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ sqlite3_bind_double(stmt, i + 1, *(float*)values[j]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ sqlite3_bind_double(stmt, i + 1, *(double*)values[j]);
+ 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[j], strlen(values[j]),
+ SQLITE_STATIC);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ case APR_DBD_TYPE_CLOB:
+ {
+ char *data = (char*)values[j];
+ apr_size_t size = *(apr_size_t*)values[++j];
+
+ sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC);
+
+ /* skip table and column */
+ j += 2;
+ }
+ 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,
+ 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, values);
+
+ ret = dbd_sqlite3_query_internal(sql, stmt, nrows);
+
+ sqlite3_reset(stmt);
+ }
+
+ 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->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_sqlite3_pbquery(pool, sql, nrows, statement, 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, 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, values);
+
+ ret = dbd_sqlite3_select_internal(pool, sql, 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->nvals);
+
+ for (i = 0; i < statement->nvals; i++) {
+ values[i] = va_arg(args, const void*);
+ }
+
+ return dbd_sqlite3_pbselect(pool, sql, results, statement, seek, values);
+}
+
+static int dbd_sqlite3_start_transaction(apr_pool_t *pool,
+ apr_dbd_t *handle,
+ apr_dbd_transaction_t **trans)
+{
+ int ret = 0;
+ int nrows = 0;
+
+ ret = dbd_sqlite3_query(handle, &nrows, "BEGIN IMMEDIATE");
+ if (!*trans) {
+ *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+ (*trans)->handle = handle;
+ handle->trans = *trans;
+ }
+
+ return ret;
+}
+
+static int dbd_sqlite3_end_transaction(apr_dbd_transaction_t *trans)
+{
+ int ret = -1; /* ending transaction that was never started is an error */
+ int nrows = 0;
+
+ if (trans) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+ trans->errnum = 0;
+ ret = dbd_sqlite3_query(trans->handle, &nrows, "ROLLBACK");
+ } else {
+ ret = dbd_sqlite3_query(trans->handle, &nrows, "COMMIT");
+ }
+ trans->handle->trans = NULL;
+ }
+
+ return ret;
+}
+
+static int dbd_sqlite3_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_sqlite3_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
+static apr_dbd_t *dbd_sqlite3_open(apr_pool_t *pool, const char *params,
+ const char **error)
+{
+ apr_dbd_t *sql = NULL;
+ sqlite3 *conn = NULL;
+ int sqlres;
+ if (!params)
+ return NULL;
+ sqlres = sqlite3_open(params, &conn);
+ if (sqlres != SQLITE_OK) {
+ if (error) {
+ *error = apr_pstrdup(pool, sqlite3_errmsg(conn));
+ }
+ sqlite3_close(conn);
+ return NULL;
+ }
+ /* should we register rand or power functions to the sqlite VM? */
+ sql = apr_pcalloc(pool, sizeof(*sql));
+ sql->conn = conn;
+ sql->pool = pool;
+ sql->trans = NULL;
+
+ return sql;
+}
+
+static apr_status_t dbd_sqlite3_close(apr_dbd_t *handle)
+{
+ apr_dbd_prepared_t *prep = handle->prep;
+
+ /* finalize all prepared statements, or we'll get SQLITE_BUSY on close */
+ while (prep) {
+ sqlite3_finalize(prep->stmt);
+ prep = prep->next;
+ }
+
+ sqlite3_close(handle->conn);
+ return APR_SUCCESS;
+}
+
+static apr_status_t dbd_sqlite3_check_conn(apr_pool_t *pool,
+ apr_dbd_t *handle)
+{
+ return (handle->conn != NULL) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+static int dbd_sqlite3_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+ const char *name)
+{
+ return APR_ENOTIMPL;
+}
+
+static void *dbd_sqlite3_native(apr_dbd_t *handle)
+{
+ return handle->conn;
+}
+
+static int dbd_sqlite3_num_cols(apr_dbd_results_t *res)
+{
+ return res->sz;
+}
+
+static int dbd_sqlite3_num_tuples(apr_dbd_results_t *res)
+{
+ return res->tuples;
+}
+
+APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_sqlite3_driver = {
+ "sqlite3",
+ NULL,
+ dbd_sqlite3_native,
+ dbd_sqlite3_open,
+ dbd_sqlite3_check_conn,
+ dbd_sqlite3_close,
+ dbd_sqlite3_select_db,
+ dbd_sqlite3_start_transaction,
+ dbd_sqlite3_end_transaction,
+ dbd_sqlite3_query,
+ dbd_sqlite3_select,
+ dbd_sqlite3_num_cols,
+ dbd_sqlite3_num_tuples,
+ dbd_sqlite3_get_row,
+ dbd_sqlite3_get_entry,
+ dbd_sqlite3_error,
+ dbd_sqlite3_escape,
+ dbd_sqlite3_prepare,
+ dbd_sqlite3_pvquery,
+ dbd_sqlite3_pvselect,
+ dbd_sqlite3_pquery,
+ dbd_sqlite3_pselect,
+ dbd_sqlite3_get_name,
+ dbd_sqlite3_transaction_mode_get,
+ dbd_sqlite3_transaction_mode_set,
+ "?",
+ dbd_sqlite3_pvbquery,
+ dbd_sqlite3_pvbselect,
+ dbd_sqlite3_pbquery,
+ dbd_sqlite3_pbselect,
+ dbd_sqlite3_datum_get
+};
+#endif