summaryrefslogtreecommitdiff
path: root/dbd/apr_dbd_sqlite2.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbd/apr_dbd_sqlite2.c')
-rw-r--r--dbd/apr_dbd_sqlite2.c566
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..132ccc4
--- /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(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