diff options
Diffstat (limited to 'dbd/apr_dbd_sqlite2.c')
-rw-r--r-- | dbd/apr_dbd_sqlite2.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/dbd/apr_dbd_sqlite2.c b/dbd/apr_dbd_sqlite2.c new file mode 100644 index 0000000..342068c --- /dev/null +++ b/dbd/apr_dbd_sqlite2.c @@ -0,0 +1,566 @@ +/* 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 |