937a200089
This is actually a fully functional build except: * All internal shared libraries are static linked to make sure there is no interference with ports (and to reduce build time). * It does not have the python/perl/etc plugin or API support. * By default, it installs as "svnlite" rather than "svn". * If WITH_SVN added in make.conf, you get "svn". * If WITHOUT_SVNLITE is in make.conf, this is completely disabled. To be absolutely clear, this is not intended for any use other than checking out freebsd source and committing, like we once did with cvs. It should be usable for small scale local repositories that don't need the python/perl plugin architecture.
915 lines
24 KiB
C
915 lines
24 KiB
C
/* 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
|