/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Michael Jerris * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Michael Jerris * * * switch_core_db.c -- sqlite wrapper and extensions * */ #include #include "private/switch_core_pvt.h" #include static void db_pick_path(const char *dbname, char *buf, switch_size_t size) { memset(buf, 0, size); if (switch_is_file_path(dbname)) { strncpy(buf, dbname, size-1); } else { switch_snprintf(buf, size, "%s%s%s.db", SWITCH_GLOBAL_dirs.db_dir, SWITCH_PATH_SEPARATOR, dbname); } } SWITCH_DECLARE(int) switch_core_db_open(const char *filename, switch_core_db_t **ppDb) { return sqlite3_open(filename, ppDb); } SWITCH_DECLARE(int) switch_core_db_open_v2(const char *filename, switch_core_db_t **ppDb) { return sqlite3_open_v2(filename, ppDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, NULL); } SWITCH_DECLARE(int) switch_core_db_close(switch_core_db_t *db) { return sqlite3_close(db); } SWITCH_DECLARE(const unsigned char *) switch_core_db_column_text(switch_core_db_stmt_t *stmt, int iCol) { return sqlite3_column_text(stmt, iCol); } SWITCH_DECLARE(const char *) switch_core_db_column_name(switch_core_db_stmt_t *stmt, int N) { return sqlite3_column_name(stmt, N); } SWITCH_DECLARE(int) switch_core_db_column_count(switch_core_db_stmt_t *pStmt) { return sqlite3_column_count(pStmt); } SWITCH_DECLARE(const char *) switch_core_db_errmsg(switch_core_db_t *db) { return sqlite3_errmsg(db); } SWITCH_DECLARE(int) switch_core_db_exec(switch_core_db_t *db, const char *sql, switch_core_db_callback_func_t callback, void *data, char **errmsg) { int ret = 0; int sane = 300; char *err = NULL; while (--sane > 0) { ret = sqlite3_exec(db, sql, callback, data, &err); if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SQLite is %s, sane=%d [%s]\n", (ret == SQLITE_BUSY ? "BUSY" : "LOCKED"), sane, sql); if (sane > 1) { switch_core_db_free(err); switch_yield(100000); continue; } } else { break; } } if (errmsg) { *errmsg = err; } else if (err) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", err); switch_core_db_free(err); err = NULL; } return ret; } SWITCH_DECLARE(int) switch_core_db_finalize(switch_core_db_stmt_t *pStmt) { return sqlite3_finalize(pStmt); } SWITCH_DECLARE(int) switch_core_db_prepare(switch_core_db_t *db, const char *zSql, int nBytes, switch_core_db_stmt_t **ppStmt, const char **pzTail) { return sqlite3_prepare(db, zSql, nBytes, ppStmt, pzTail); } SWITCH_DECLARE(int) switch_core_db_step(switch_core_db_stmt_t *stmt) { return sqlite3_step(stmt); } SWITCH_DECLARE(int) switch_core_db_reset(switch_core_db_stmt_t *pStmt) { return sqlite3_reset(pStmt); } SWITCH_DECLARE(int) switch_core_db_bind_int(switch_core_db_stmt_t *pStmt, int i, int iValue) { return sqlite3_bind_int(pStmt, i, iValue); } SWITCH_DECLARE(int) switch_core_db_bind_int64(switch_core_db_stmt_t *pStmt, int i, int64_t iValue) { return sqlite3_bind_int64(pStmt, i, iValue); } SWITCH_DECLARE(int) switch_core_db_bind_text(switch_core_db_stmt_t *pStmt, int i, const char *zData, int nData, switch_core_db_destructor_type_t xDel) { return sqlite3_bind_text(pStmt, i, zData, nData, xDel); } SWITCH_DECLARE(int) switch_core_db_bind_double(switch_core_db_stmt_t *pStmt, int i, double dValue) { return sqlite3_bind_double(pStmt, i, dValue); } SWITCH_DECLARE(int64_t) switch_core_db_last_insert_rowid(switch_core_db_t *db) { return sqlite3_last_insert_rowid(db); } SWITCH_DECLARE(int) switch_core_db_get_table(switch_core_db_t *db, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg) { return sqlite3_get_table(db, sql, resultp, nrow, ncolumn, errmsg); } SWITCH_DECLARE(void) switch_core_db_free_table(char **result) { sqlite3_free_table(result); } SWITCH_DECLARE(void) switch_core_db_free(char *z) { sqlite3_free(z); } SWITCH_DECLARE(int) switch_core_db_changes(switch_core_db_t *db) { return sqlite3_changes(db); } SWITCH_DECLARE(int) switch_core_db_load_extension(switch_core_db_t *db, const char *extension) { int ret = 0; char *err = NULL; #ifdef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 0); ret = sqlite3_load_extension(db, extension, 0, &err); #else sqlite3_enable_load_extension(db, 1); ret = sqlite3_load_extension(db, extension, 0, &err); sqlite3_enable_load_extension(db, 0); #endif if (err) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "LOAD EXTENSION ERR [%s]\n", err); switch_core_db_free(err); err = NULL; } return ret; } static int switch_core_db_connection_setup(switch_core_db_t *db, switch_bool_t in_memory) { int db_ret; if ((db_ret = switch_core_db_exec(db, "PRAGMA synchronous=OFF;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } if ((db_ret = switch_core_db_exec(db, "PRAGMA count_changes=OFF;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } if ((db_ret = switch_core_db_exec(db, "PRAGMA temp_store=MEMORY;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } if (!in_memory) { if ((db_ret = switch_core_db_exec(db, "PRAGMA cache_size=8000;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } } else { if ((db_ret = switch_core_db_exec(db, "PRAGMA cache_size=-8192;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } if ((db_ret = switch_core_db_exec(db, "PRAGMA journal_mode=OFF;", NULL, NULL, NULL)) != SQLITE_OK) { goto end; } } end: return db_ret; } SWITCH_DECLARE(switch_core_db_t *) switch_core_db_open_file(const char *filename) { switch_core_db_t *db; char path[1024]; int db_ret; db_pick_path(filename, path, sizeof(path)); if ((db_ret = switch_core_db_open(path, &db)) != SQLITE_OK) { goto end; } if ((db_ret = switch_core_db_connection_setup(db, SWITCH_FALSE)) != SQLITE_OK) { goto end; } end: if (db_ret != SQLITE_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", switch_core_db_errmsg(db)); switch_core_db_close(db); db = NULL; } return db; } SWITCH_DECLARE(switch_core_db_t *) switch_core_db_open_in_memory(const char *uri) { switch_core_db_t *db; int db_ret; if ((db_ret = switch_core_db_open_v2(uri, &db)) != SQLITE_OK) { goto end; } if ((db_ret = switch_core_db_connection_setup(db, SWITCH_TRUE)) != SQLITE_OK) { goto end; } end: if (db_ret != SQLITE_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", switch_core_db_errmsg(db)); switch_core_db_close(db); db = NULL; } return db; } SWITCH_DECLARE(void) switch_core_db_test_reactive(switch_core_db_t *db, char *test_sql, char *drop_sql, char *reactive_sql) { char *errmsg; if (!switch_test_flag((&runtime), SCF_CLEAR_SQL)) { return; } if (!switch_test_flag((&runtime), SCF_AUTO_SCHEMAS)) { switch_core_db_exec(db, test_sql, NULL, NULL, NULL); return; } if (db) { if (test_sql) { switch_core_db_exec(db, test_sql, NULL, NULL, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL ERR [%s]\n[%s]\nAuto Generating Table!\n", errmsg, test_sql); switch_core_db_free(errmsg); errmsg = NULL; if (drop_sql) { switch_core_db_exec(db, drop_sql, NULL, NULL, &errmsg); } if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL ERR [%s]\n[%s]\n", errmsg, reactive_sql); switch_core_db_free(errmsg); errmsg = NULL; } switch_core_db_exec(db, reactive_sql, NULL, NULL, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL ERR [%s]\n[%s]\n", errmsg, reactive_sql); switch_core_db_free(errmsg); errmsg = NULL; } } } } } SWITCH_DECLARE(switch_status_t) switch_core_db_persistant_execute_trans(switch_core_db_t *db, char *sql, uint32_t retries) { char *errmsg; switch_status_t status = SWITCH_STATUS_FALSE; uint8_t forever = 0; unsigned begin_retries = 100; uint8_t again = 0; if (!retries) { forever = 1; retries = 1000; } again: while (begin_retries > 0) { again = 0; switch_core_db_exec(db, "BEGIN", NULL, NULL, &errmsg); if (errmsg) { begin_retries--; if (strstr(errmsg, "cannot start a transaction within a transaction")) { again = 1; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL Retry [%s]\n", errmsg); } switch_core_db_free(errmsg); errmsg = NULL; if (again) { switch_core_db_exec(db, "COMMIT", NULL, NULL, NULL); goto again; } switch_yield(100000); if (begin_retries == 0) { goto done; } } else { break; } } while (retries > 0) { switch_core_db_exec(db, sql, NULL, NULL, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); switch_core_db_free(errmsg); errmsg = NULL; switch_yield(100000); retries--; if (retries == 0 && forever) { retries = 1000; continue; } } else { status = SWITCH_STATUS_SUCCESS; break; } } done: switch_core_db_exec(db, "COMMIT", NULL, NULL, NULL); return status; } SWITCH_DECLARE(switch_status_t) switch_core_db_persistant_execute(switch_core_db_t *db, char *sql, uint32_t retries) { char *errmsg; switch_status_t status = SWITCH_STATUS_FALSE; uint8_t forever = 0; if (!retries) { forever = 1; retries = 1000; } while (retries > 0) { switch_core_db_exec(db, sql, NULL, NULL, &errmsg); if (errmsg) { //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); switch_core_db_free(errmsg); switch_yield(100000); retries--; if (retries == 0 && forever) { retries = 1000; continue; } } else { status = SWITCH_STATUS_SUCCESS; break; } } return status; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */