4705e3668d
components: apr-1.4.6 -> 1.4.8 and apr-util-1.4.1 -> 1.5.2. This is a post point-zero bug-fix / fix-sharp-edges release, including some workarounds for UTF-8 for people who haven't yet turned on WITH_ICONV.
567 lines
14 KiB
C
567 lines
14 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_SQLITE2
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sqlite.h>
|
|
|
|
#include "apr_strings.h"
|
|
#include "apr_time.h"
|
|
#include "apr_buckets.h"
|
|
|
|
#include "apr_dbd_internal.h"
|
|
|
|
struct apr_dbd_transaction_t {
|
|
int mode;
|
|
int errnum;
|
|
apr_dbd_t *handle;
|
|
};
|
|
|
|
struct apr_dbd_t {
|
|
sqlite *conn;
|
|
char *errmsg;
|
|
apr_dbd_transaction_t *trans;
|
|
};
|
|
|
|
struct apr_dbd_results_t {
|
|
int random;
|
|
sqlite *handle;
|
|
char **res;
|
|
size_t ntuples;
|
|
size_t sz;
|
|
size_t index;
|
|
apr_pool_t *pool;
|
|
};
|
|
|
|
struct apr_dbd_row_t {
|
|
int n;
|
|
char **data;
|
|
apr_dbd_results_t *res;
|
|
};
|
|
|
|
struct apr_dbd_prepared_t {
|
|
const char *name;
|
|
int prepared;
|
|
};
|
|
|
|
#define FREE_ERROR_MSG(dbd) \
|
|
do { \
|
|
if(dbd && dbd->errmsg) { \
|
|
free(dbd->errmsg); \
|
|
dbd->errmsg = NULL; \
|
|
} \
|
|
} while(0);
|
|
|
|
static apr_status_t free_table(void *data)
|
|
{
|
|
sqlite_free_table(data);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static int dbd_sqlite_select(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results, const char *query,
|
|
int seek)
|
|
{
|
|
char **result;
|
|
int ret = 0;
|
|
int tuples = 0;
|
|
int fields = 0;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
FREE_ERROR_MSG(sql);
|
|
|
|
ret = sqlite_get_table(sql->conn, query, &result, &tuples, &fields,
|
|
&sql->errmsg);
|
|
|
|
if (ret == SQLITE_OK) {
|
|
if (!*results) {
|
|
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
|
|
}
|
|
|
|
(*results)->res = result;
|
|
(*results)->ntuples = tuples;
|
|
(*results)->sz = fields;
|
|
(*results)->random = seek;
|
|
(*results)->pool = pool;
|
|
|
|
if (tuples > 0)
|
|
apr_pool_cleanup_register(pool, result, free_table,
|
|
apr_pool_cleanup_null);
|
|
|
|
ret = 0;
|
|
}
|
|
else {
|
|
if (TXN_NOTICE_ERRORS(sql->trans)) {
|
|
sql->trans->errnum = ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const char *dbd_sqlite_get_name(const apr_dbd_results_t *res, int n)
|
|
{
|
|
if ((n < 0) || (n >= res->sz)) {
|
|
return NULL;
|
|
}
|
|
|
|
return res->res[n];
|
|
}
|
|
|
|
static int dbd_sqlite_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
|
|
apr_dbd_row_t ** rowp, int rownum)
|
|
{
|
|
apr_dbd_row_t *row = *rowp;
|
|
int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
|
|
|
|
if (row == NULL) {
|
|
row = apr_palloc(pool, sizeof(apr_dbd_row_t));
|
|
*rowp = row;
|
|
row->res = res;
|
|
row->n = sequential ? 0 : rownum - 1;
|
|
}
|
|
else {
|
|
if (sequential) {
|
|
++row->n;
|
|
}
|
|
else {
|
|
row->n = rownum - 1;
|
|
}
|
|
}
|
|
|
|
if (row->n >= res->ntuples) {
|
|
*rowp = NULL;
|
|
apr_pool_cleanup_run(res->pool, res->res, free_table);
|
|
res->res = NULL;
|
|
return -1;
|
|
}
|
|
|
|
/* Pointer magic explanation:
|
|
* The sqlite result is an array such that the first res->sz elements are
|
|
* the column names and each tuple follows afterwards
|
|
* ex: (from the sqlite2 documentation)
|
|
SELECT employee_name, login, host FROM users WHERE login LIKE * 'd%';
|
|
|
|
nrow = 2
|
|
ncolumn = 3
|
|
result[0] = "employee_name"
|
|
result[1] = "login"
|
|
result[2] = "host"
|
|
result[3] = "dummy"
|
|
result[4] = "No such user"
|
|
result[5] = 0
|
|
result[6] = "D. Richard Hipp"
|
|
result[7] = "drh"
|
|
result[8] = "zadok"
|
|
*/
|
|
|
|
row->data = res->res + res->sz + (res->sz * row->n);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *dbd_sqlite_get_entry(const apr_dbd_row_t * row, int n)
|
|
{
|
|
if ((n < 0) || (n >= row->res->sz)) {
|
|
return NULL;
|
|
}
|
|
|
|
return row->data[n];
|
|
}
|
|
|
|
static apr_status_t dbd_sqlite_datum_get(const apr_dbd_row_t *row, int n,
|
|
apr_dbd_type_e type, void *data)
|
|
{
|
|
if ((n < 0) || (n >= row->res->sz)) {
|
|
return APR_EGENERAL;
|
|
}
|
|
|
|
if (row->data[n] == NULL) {
|
|
return APR_ENOENT;
|
|
}
|
|
|
|
switch (type) {
|
|
case APR_DBD_TYPE_TINY:
|
|
*(char*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_UTINY:
|
|
*(unsigned char*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_SHORT:
|
|
*(short*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_USHORT:
|
|
*(unsigned short*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_INT:
|
|
*(int*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_UINT:
|
|
*(unsigned int*)data = atoi(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_LONG:
|
|
*(long*)data = atol(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_ULONG:
|
|
*(unsigned long*)data = atol(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_LONGLONG:
|
|
*(apr_int64_t*)data = apr_atoi64(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_ULONGLONG:
|
|
*(apr_uint64_t*)data = apr_atoi64(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_FLOAT:
|
|
*(float*)data = atof(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_DOUBLE:
|
|
*(double*)data = atof(row->data[n]);
|
|
break;
|
|
case APR_DBD_TYPE_STRING:
|
|
case APR_DBD_TYPE_TEXT:
|
|
case APR_DBD_TYPE_TIME:
|
|
case APR_DBD_TYPE_DATE:
|
|
case APR_DBD_TYPE_DATETIME:
|
|
case APR_DBD_TYPE_TIMESTAMP:
|
|
case APR_DBD_TYPE_ZTIMESTAMP:
|
|
*(char**)data = row->data[n];
|
|
break;
|
|
case APR_DBD_TYPE_BLOB:
|
|
case APR_DBD_TYPE_CLOB:
|
|
{
|
|
apr_bucket *e;
|
|
apr_bucket_brigade *b = (apr_bucket_brigade*)data;
|
|
|
|
e = apr_bucket_pool_create(row->data[n],strlen(row->data[n]),
|
|
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_sqlite_error(apr_dbd_t * sql, int n)
|
|
{
|
|
return sql->errmsg;
|
|
}
|
|
|
|
static int dbd_sqlite_query(apr_dbd_t * sql, int *nrows, const char *query)
|
|
{
|
|
char **result;
|
|
int ret;
|
|
int tuples = 0;
|
|
int fields = 0;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
FREE_ERROR_MSG(sql);
|
|
|
|
ret =
|
|
sqlite_get_table(sql->conn, query, &result, &tuples, &fields,
|
|
&sql->errmsg);
|
|
if (ret == SQLITE_OK) {
|
|
*nrows = sqlite_changes(sql->conn);
|
|
|
|
if (tuples > 0)
|
|
free(result);
|
|
|
|
ret = 0;
|
|
}
|
|
|
|
if (TXN_NOTICE_ERRORS(sql->trans)) {
|
|
sql->trans->errnum = ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static apr_status_t free_mem(void *data)
|
|
{
|
|
sqlite_freemem(data);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static const char *dbd_sqlite_escape(apr_pool_t * pool, const char *arg,
|
|
apr_dbd_t * sql)
|
|
{
|
|
char *ret = sqlite_mprintf("%q", arg);
|
|
apr_pool_cleanup_register(pool, ret, free_mem, apr_pool_cleanup_null);
|
|
return ret;
|
|
}
|
|
|
|
static int dbd_sqlite_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)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
const char **values)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pvquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
va_list args)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement,
|
|
int seek, const char **values)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pvselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement, int seek,
|
|
va_list args)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
const void **values)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
va_list args)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement,
|
|
int seek, const void **values)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement, int seek,
|
|
va_list args)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static int dbd_sqlite_start_transaction(apr_pool_t * pool, apr_dbd_t * handle,
|
|
apr_dbd_transaction_t ** trans)
|
|
{
|
|
int ret, rows;
|
|
|
|
ret = dbd_sqlite_query(handle, &rows, "BEGIN TRANSACTION");
|
|
if (ret == 0) {
|
|
if (!*trans) {
|
|
*trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
|
|
}
|
|
(*trans)->handle = handle;
|
|
handle->trans = *trans;
|
|
}
|
|
else {
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int dbd_sqlite_end_transaction(apr_dbd_transaction_t * trans)
|
|
{
|
|
int rows;
|
|
int ret = -1; /* no transaction is an error cond */
|
|
|
|
if (trans) {
|
|
/* rollback on error or explicit rollback request */
|
|
if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
|
|
trans->errnum = 0;
|
|
ret =
|
|
dbd_sqlite_query(trans->handle, &rows,
|
|
"ROLLBACK TRANSACTION");
|
|
}
|
|
else {
|
|
ret =
|
|
dbd_sqlite_query(trans->handle, &rows, "COMMIT TRANSACTION");
|
|
}
|
|
trans->handle->trans = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dbd_sqlite_transaction_mode_get(apr_dbd_transaction_t *trans)
|
|
{
|
|
if (!trans)
|
|
return APR_DBD_TRANSACTION_COMMIT;
|
|
|
|
return trans->mode;
|
|
}
|
|
|
|
static int dbd_sqlite_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_status_t error_free(void *data)
|
|
{
|
|
free(data);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static apr_dbd_t *dbd_sqlite_open(apr_pool_t * pool, const char *params_,
|
|
const char **error)
|
|
{
|
|
apr_dbd_t *sql;
|
|
sqlite *conn = NULL;
|
|
char *perm;
|
|
int iperms = 600;
|
|
char* params = apr_pstrdup(pool, params_);
|
|
/* params = "[filename]:[permissions]"
|
|
* example: "shopping.db:600"
|
|
*/
|
|
|
|
perm = strstr(params, ":");
|
|
if (perm) {
|
|
*(perm++) = '\x00'; /* split the filename and permissions */
|
|
|
|
if (strlen(perm) > 0)
|
|
iperms = atoi(perm);
|
|
}
|
|
|
|
if (error) {
|
|
*error = NULL;
|
|
|
|
conn = sqlite_open(params, iperms, (char **)error);
|
|
|
|
if (*error) {
|
|
apr_pool_cleanup_register(pool, *error, error_free,
|
|
apr_pool_cleanup_null);
|
|
}
|
|
}
|
|
else {
|
|
conn = sqlite_open(params, iperms, NULL);
|
|
}
|
|
|
|
sql = apr_pcalloc(pool, sizeof(*sql));
|
|
sql->conn = conn;
|
|
|
|
return sql;
|
|
}
|
|
|
|
static apr_status_t dbd_sqlite_close(apr_dbd_t * handle)
|
|
{
|
|
if (handle->conn) {
|
|
sqlite_close(handle->conn);
|
|
handle->conn = NULL;
|
|
}
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static apr_status_t dbd_sqlite_check_conn(apr_pool_t * pool,
|
|
apr_dbd_t * handle)
|
|
{
|
|
if (handle->conn == NULL)
|
|
return -1;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static int dbd_sqlite_select_db(apr_pool_t * pool, apr_dbd_t * handle,
|
|
const char *name)
|
|
{
|
|
return APR_ENOTIMPL;
|
|
}
|
|
|
|
static void *dbd_sqlite_native(apr_dbd_t * handle)
|
|
{
|
|
return handle->conn;
|
|
}
|
|
|
|
static int dbd_sqlite_num_cols(apr_dbd_results_t * res)
|
|
{
|
|
return res->sz;
|
|
}
|
|
|
|
static int dbd_sqlite_num_tuples(apr_dbd_results_t * res)
|
|
{
|
|
return res->ntuples;
|
|
}
|
|
|
|
APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_sqlite2_driver = {
|
|
"sqlite2",
|
|
NULL,
|
|
dbd_sqlite_native,
|
|
dbd_sqlite_open,
|
|
dbd_sqlite_check_conn,
|
|
dbd_sqlite_close,
|
|
dbd_sqlite_select_db,
|
|
dbd_sqlite_start_transaction,
|
|
dbd_sqlite_end_transaction,
|
|
dbd_sqlite_query,
|
|
dbd_sqlite_select,
|
|
dbd_sqlite_num_cols,
|
|
dbd_sqlite_num_tuples,
|
|
dbd_sqlite_get_row,
|
|
dbd_sqlite_get_entry,
|
|
dbd_sqlite_error,
|
|
dbd_sqlite_escape,
|
|
dbd_sqlite_prepare,
|
|
dbd_sqlite_pvquery,
|
|
dbd_sqlite_pvselect,
|
|
dbd_sqlite_pquery,
|
|
dbd_sqlite_pselect,
|
|
dbd_sqlite_get_name,
|
|
dbd_sqlite_transaction_mode_get,
|
|
dbd_sqlite_transaction_mode_set,
|
|
NULL,
|
|
dbd_sqlite_pvbquery,
|
|
dbd_sqlite_pvbselect,
|
|
dbd_sqlite_pbquery,
|
|
dbd_sqlite_pbselect,
|
|
dbd_sqlite_datum_get
|
|
};
|
|
#endif
|