/* 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 #include #include #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; apr_dbd_column_t *col; 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; } col = row->columns[i]; } 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