[svn-commits] twilson: branch twilson/sqlite_astdb r323103 - in /team/twilson/sqlite_astdb:...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sun Jun 12 17:25:26 CDT 2011


Author: twilson
Date: Sun Jun 12 17:25:23 2011
New Revision: 323103

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=323103
Log:
Initial import

Functionally equivalent, passes tests.

TODO: cleanup, autoconf/makefile stuff, work on performance

Performance note: Currently every write goes straight to disk.
This makes things really slow. Wrapping a transaction around 100000
inserts makes things about 500 times faster. So I should probably
implement something similar to the sync loop in the current db code.

Even with transactions, it is still about 5x slower than the Berkeley DB
code. Of course, that means that it is still doing 50k inserts/second on
a VM on my laptop. It certainly shouldn't be a bottleneck. I'm not sure
if I can speed that up or not.

Modified:
    team/twilson/sqlite_astdb/   (props changed)
    team/twilson/sqlite_astdb/include/asterisk/astdb.h
    team/twilson/sqlite_astdb/main/Makefile
    team/twilson/sqlite_astdb/main/db.c
    team/twilson/sqlite_astdb/tests/test_db.c

Propchange: team/twilson/sqlite_astdb/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/twilson/sqlite_astdb/
------------------------------------------------------------------------------
    automerge-email = twilson at digium.com

Propchange: team/twilson/sqlite_astdb/
------------------------------------------------------------------------------
    svnmerge-integrated = /trunk:1-323101

Modified: team/twilson/sqlite_astdb/include/asterisk/astdb.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/include/asterisk/astdb.h?view=diff&rev=323103&r1=323102&r2=323103
==============================================================================
--- team/twilson/sqlite_astdb/include/asterisk/astdb.h (original)
+++ team/twilson/sqlite_astdb/include/asterisk/astdb.h Sun Jun 12 17:25:23 2011
@@ -66,6 +66,8 @@
 /*!\brief Free structure created by ast_db_gettree() */
 void ast_db_freetree(struct ast_db_entry *entry);
 
+int ast_db_begin_transaction(void);
+int ast_db_commit_transaction(void);
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/twilson/sqlite_astdb/main/Makefile
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/main/Makefile?view=diff&rev=323103&r1=323102&r2=323103
==============================================================================
--- team/twilson/sqlite_astdb/main/Makefile (original)
+++ team/twilson/sqlite_astdb/main/Makefile Sun Jun 12 17:25:23 2011
@@ -169,6 +169,7 @@
 cygload: asterisk.dll
 else
 MAIN_TGT:=asterisk
+ASTLDFLAGS+=-lsqlite3
 endif
 
 ifneq ($(findstring ENABLE_UPLOADS,$(MENUSELECT_CFLAGS)),)

Modified: team/twilson/sqlite_astdb/main/db.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/main/db.c?view=diff&rev=323103&r1=323102&r2=323103
==============================================================================
--- team/twilson/sqlite_astdb/main/db.c (original)
+++ team/twilson/sqlite_astdb/main/db.c Sun Jun 12 17:25:23 2011
@@ -44,9 +44,8 @@
 #include "asterisk/astdb.h"
 #include "asterisk/cli.h"
 #include "asterisk/utils.h"
-#include "asterisk/lock.h"
 #include "asterisk/manager.h"
-#include "db1-ast/include/db.h"
+#include <sqlite3.h>
 
 /*** DOCUMENTATION
 	<manager name="DBGet" language="en_US">
@@ -102,241 +101,419 @@
 
 #define MAX_DB_FIELD 256
 
-static DB *astdb;
-AST_MUTEX_DEFINE_STATIC(dblock);
-static ast_cond_t dbcond;
-typedef int (*process_keys_cb)(DBT *key, DBT *value, const char *filter, void *data);
-
-static void db_sync(void);
-
-static int dbinit(void) 
-{
-	if (!astdb && !(astdb = dbopen(ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) {
-		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", ast_config_AST_DB, strerror(errno));
-		return -1;
-	}
-	return 0;
-}
-
-
-static inline int keymatch(const char *key, const char *prefix)
-{
-	int preflen = strlen(prefix);
-	if (!preflen)
-		return 1;
-	if (!strcasecmp(key, prefix))
-		return 1;
-	if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) {
-		if (key[preflen] == '/')
-			return 1;
-	}
-	return 0;
-}
-
-static inline int subkeymatch(const char *key, const char *suffix)
-{
-	int suffixlen = strlen(suffix);
-	if (suffixlen) {
-		const char *subkey = key + strlen(key) - suffixlen;
-		if (subkey < key)
-			return 0;
-		if (!strcasecmp(subkey, suffix))
-			return 1;
-	}
-	return 0;
-}
-
-static const char *dbt_data2str(DBT *dbt)
-{
-	char *data = "";
-
-	if (dbt->size) {
-		data = dbt->data;
-		data[dbt->size - 1] = '\0';
-	}
-
-	return data;
-}
-
-static inline const char *dbt_data2str_full(DBT *dbt, const char *def)
-{
-	return S_OR(dbt_data2str(dbt), def);
-}
-
-static int process_db_keys(process_keys_cb cb, void *data, const char *filter, int sync)
-{
-	DBT key = { 0, }, value = { 0, }, last_key = { 0, };
-	int counter = 0;
-	int res, last = 0;
-	char last_key_s[MAX_DB_FIELD];
-
-	ast_mutex_lock(&dblock);
-	if (dbinit()) {
-		ast_mutex_unlock(&dblock);
-		return -1;
-	}
-
-	/* Somehow, the database can become corrupted such that astdb->seq will continue looping through
-	 * the database indefinitely. The pointer to last_key.data ends up getting re-used by the BDB lib
-	 * so this specifically queries for the last entry, makes a copy of the key, and then uses it as
-	 * a sentinel to avoid repeatedly looping over the list. */
-
-	if (astdb->seq(astdb, &last_key, &value, R_LAST)) {
-		/* Empty database */
-		ast_mutex_unlock(&dblock);
+/* Give each thread their own DB connection
+ * Some functions like sqlite3_errmsg and sqlite_changes can be stepped on
+ * by other threads. SQLite can handle the threading on its own if we have
+ * a connection per thread, so we just create thread-local DB connections.
+ */
+static int db_init(void *astdb);
+static void db_close(void *astdb);
+AST_THREADSTORAGE_CUSTOM(astdb_ts, db_init, db_close);
+
+static int db_open(sqlite3 **astdb)
+{
+	char *dbname;
+	if (!(dbname = alloca(strlen(ast_config_AST_DB) + sizeof(".sqlite3")))) {
+		return -1;
+	}
+	strcpy(dbname, ast_config_AST_DB);
+	strcat(dbname, ".sqlite3");
+	if (sqlite3_open_v2(dbname, astdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", "/var/lib/asterisk/astdb.sqlite3", sqlite3_errmsg(*astdb));
+		sqlite3_close(*astdb);
+		abort();
+		return -1;
+	}
+	return 0;
+}
+
+static int db_create_astdb(sqlite3 *astdb)
+{
+	static sqlite3_stmt *createStmt = NULL;
+	static const char createSQL[] =
+		"CREATE TABLE IF NOT EXISTS astdb("
+		"	key VARCHAR(256),"
+		"	value VARCHAR(256),"
+		"	PRIMARY KEY(key))";
+	static int created = 0;
+
+	if (created) {
 		return 0;
 	}
-
-	memcpy(last_key_s, last_key.data, MIN(last_key.size - 1, sizeof(last_key_s)));
-	last_key_s[last_key.size - 1] = '\0';
-	for (res = astdb->seq(astdb, &key, &value, R_FIRST);
-			!res;
-			res = astdb->seq(astdb, &key, &value, R_NEXT)) {
-		/* The callback might delete the key, so we have to check it before calling */
-		last = !strcmp(dbt_data2str_full(&key, "<bad key>"), last_key_s);
-		counter += cb(&key, &value, filter, data);
+	if (!createStmt) {
+		if (sqlite3_prepare_v2(astdb, createSQL, sizeof(createSQL), &createStmt, NULL) != SQLITE_OK) {
+			ast_log(LOG_WARNING, "Couldn't prepare statement to create astdb table: %s\n", sqlite3_errmsg(astdb));
+			abort();
+			return -1;
+		}
+	}
+
+	if (sqlite3_step(createStmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't create astdb table: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(createStmt);
+		abort();
+		return -1;
+	}
+
+	sqlite3_reset(createStmt);
+	created = 1;
+	return 0;
+}
+
+static int db_init(void *data) 
+{
+	struct sqlite3 **db = data;
+	if (db_open(db)) {
+		return -1;
+	}
+	if (db_create_astdb(*db)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static void db_close(void *data)
+{
+	sqlite3 **astdb = data;
+
+	sqlite3_close(*astdb);
+}
+
+static sqlite3 *get_db_ts(void)
+{
+	sqlite3 **astdb;
+	if (!(astdb = ast_threadstorage_get(&astdb_ts, sizeof(astdb)))) {
+		return NULL;
+	}
+	return *astdb;
+}
+
+int ast_db_begin_transaction(void)
+{
+	sqlite3 *astdb;
+	if (!(astdb = get_db_ts())) {
+		return -1;
+	}
+	sqlite3_exec(astdb, "BEGIN TRANSACTION", NULL, NULL, NULL);
+	return 0;
+}
+
+int ast_db_commit_transaction(void)
+{
+	sqlite3 *astdb;
+	if (!(astdb = get_db_ts())) {
+		return -1;
+	}
+	sqlite3_exec(astdb, "COMMIT", NULL, NULL, NULL);
+	return 0;
+}
+
+static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len)
+{
+	sqlite3 *astdb;
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return -1;
+	}
+
+	if (sqlite3_prepare_v2(astdb, sql, len, stmt, NULL) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't prepare statement to create put data: %s\n", sqlite3_errmsg(astdb));
+		return -1;
+	}
+	return 0;
+}
+
+static void finalize_stmt(void *data)
+{
+	sqlite3_stmt **putStmt = data;
+	sqlite3_finalize(*putStmt);
+}
+
+#define STMT_THREADSTORAGE(name,sql) static int __xinit_##name(void *__data) \
+{ \
+	sqlite3_stmt **__stmt = __data; \
+	static const char __sql[] = sql; \
+	return init_stmt(__stmt, __sql, sizeof(__sql)); \
+} \
+AST_THREADSTORAGE_CUSTOM(name, __xinit_##name, finalize_stmt)
+
+STMT_THREADSTORAGE(put_stmt,"INSERT OR REPLACE INTO astdb (key, value) VALUES (?, ?)");
+STMT_THREADSTORAGE(get_stmt, "SELECT value FROM astdb WHERE key=?");
+STMT_THREADSTORAGE(del_stmt, "DELETE FROM astdb WHERE key=?");
+STMT_THREADSTORAGE(deltree_stmt, "DELETE FROM astdb WHERE key LIKE ? || '/' || '%'");
+STMT_THREADSTORAGE(deltree_all_stmt, "DELETE FROM astdb");
+STMT_THREADSTORAGE(gettree_stmt, "SELECT key, value FROM astdb WHERE key LIKE ? || '/' || '%'");
+STMT_THREADSTORAGE(gettree_all_stmt, "SELECT key, value FROM astdb");
+STMT_THREADSTORAGE(showkey_stmt, "SELECT key, value FROM astdb WHERE key LIKE '%' || ?");
+
+static sqlite3_stmt *get_stmt_ts(struct ast_threadstorage *stmt_ts)
+{
+	sqlite3_stmt **stmt;
+	if (!(stmt = ast_threadstorage_get(stmt_ts, sizeof(stmt)))) {
+		return NULL;
+	}
+	return *stmt;
+}
+
+int ast_db_put(const char *family, const char *key, const char *value)
+{
+	static sqlite3_stmt *putStmt;
+	char fullkey[MAX_DB_FIELD];
+	sqlite3 *astdb;
+
+	if (!(putStmt = get_stmt_ts(&put_stmt))) {
+		return -1;
+	}
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
+		return -1;
+	}
+
+	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return -1;
+	}
+
+	if (sqlite3_bind_text(putStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(putStmt);
+		return -1;
+	}
+
+	if (sqlite3_bind_text(putStmt, 2, value, -1, SQLITE_STATIC) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't bind value to stmt: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(putStmt);
+		return -1;
+	}
+
+	if (sqlite3_step(putStmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't create execute statment: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(putStmt);
+		return -1;
+	}
+
+	sqlite3_reset(putStmt);
+
+	return 0;
+}
+
+int ast_db_get(const char *family, const char *key, char *value, int valuelen)
+{
+	static sqlite3_stmt *getStmt;
+	const unsigned char *result;
+	char fullkey[MAX_DB_FIELD];
+	sqlite3 *astdb;
+
+	if (!(getStmt = get_stmt_ts(&get_stmt))) {
+		return -1;
+	}
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return -1;
+	}
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
+		return -1;
+	}
+
+	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	if (sqlite3_bind_text(getStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(getStmt);
+		return -1;
+	}
+
+	if (sqlite3_step(getStmt) != SQLITE_ROW) {
+		ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
+		sqlite3_reset(getStmt);
+		return -1;
+	}
+
+	if ((result = sqlite3_column_text(getStmt, 0))) {
+		strncpy(value, (const char *) result, valuelen);
+	} else {
+		ast_log(LOG_WARNING, "Couldn't get value\n");
+		sqlite3_reset(getStmt);
+		return -1;
+	}
+
+	sqlite3_reset(getStmt);
+
+	return 0;
+}
+
+int ast_db_del(const char *family, const char *key)
+{
+	static sqlite3_stmt *delStmt;
+	char fullkey[MAX_DB_FIELD];
+	sqlite3 *astdb;
+
+	if (!(delStmt = get_stmt_ts(&del_stmt))) {
+		return -1;
+	}
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return -1;
+	}
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
+		return -1;
+	}
+
+	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	if (sqlite3_bind_text(delStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(delStmt);
+		return -1;
+	}
+
+	if (sqlite3_step(delStmt) != SQLITE_DONE) {
+		ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
+		sqlite3_reset(delStmt);
+		return -1;
+	}
+
+	sqlite3_reset(delStmt);
+
+	return 0;
+}
+
+int ast_db_deltree(const char *family, const char *subfamily)
+{
+	sqlite3_stmt *deltreeStmt;
+	char prefix[MAX_DB_FIELD];
+	sqlite3 *astdb;
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return -1;
+	}
+
+	if (!(ast_strlen_zero(family) || ast_strlen_zero(subfamily))) {
+		snprintf(prefix, sizeof(prefix), "/%s/%s", family, subfamily);
+		if (!(deltreeStmt = get_stmt_ts(&deltree_stmt))) {
+			return -1;
+		}
+	} else if (!ast_strlen_zero(family)) {
+		snprintf(prefix, sizeof(prefix), "/%s", family);
+		if (!(deltreeStmt = get_stmt_ts(&deltree_stmt))) {
+			return -1;
+		}
+	} else if (!ast_strlen_zero(subfamily)) {
+		ast_log(LOG_WARNING, "Can't have just a subfamily\n");
+		return -1;
+	} else {
+		prefix[0] = '\0';
+		if (!(deltreeStmt = get_stmt_ts(&deltree_all_stmt))) {
+			return -1;
+		}
+	}
+
+	if (!ast_strlen_zero(prefix)) {
+		if (sqlite3_bind_text(deltreeStmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK) {
+			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
+			sqlite3_reset(deltreeStmt);
+			return -1;
+		}
+	}
+
+	if (sqlite3_step(deltreeStmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't execute stmt: %s\n", sqlite3_errmsg(astdb));
+		sqlite3_reset(deltreeStmt);
+		return -1;
+	}
+
+	sqlite3_reset(deltreeStmt);
+	return sqlite3_changes(astdb);;
+}
+
+struct ast_db_entry *ast_db_gettree(const char *family, const char *subfamily)
+{
+	char prefix[MAX_DB_FIELD];
+	sqlite3_stmt *gettreeStmt;
+	struct ast_db_entry *cur, *last = NULL, *ret = NULL;
+	sqlite3 *astdb;
+
+	if (!(astdb = get_db_ts())) {
+		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
+		return NULL;
+	}
+
+	if (!ast_strlen_zero(family)) {
+		if (!ast_strlen_zero(subfamily)) {
+			/* Family and key tree */
+			snprintf(prefix, sizeof(prefix), "/%s/%s", family, subfamily);
+			if (!(gettreeStmt = get_stmt_ts(&gettree_stmt))) {
+				return NULL;
+			}
+		} else {
+			/* Family only */
+			snprintf(prefix, sizeof(prefix), "/%s", family);
+			if (!(gettreeStmt = get_stmt_ts(&gettree_stmt))) {
+				return NULL;
+			}
+		}
+	} else {
+		prefix[0] = '\0';
+		if (!(gettreeStmt = get_stmt_ts(&gettree_all_stmt))) {
+			return NULL;
+		}
+	}
+
+	if (!ast_strlen_zero(prefix)) {
+		if (sqlite3_bind_text(gettreeStmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK) {
+			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
+			sqlite3_reset(gettreeStmt);
+			return NULL;
+		}
+	}
+
+	while (sqlite3_step(gettreeStmt) == SQLITE_ROW) {
+		const char *key_s, *value_s;
+		if (!(key_s = (const char *) sqlite3_column_text(gettreeStmt, 0))) {
+			break;
+		}
+		if (!(value_s = (const char *) sqlite3_column_text(gettreeStmt, 1))) {
+			break;
+		}
+		if (!(cur = ast_malloc(sizeof(*cur) + strlen(key_s) + strlen(value_s) + 2))) {
+			break;
+		}
+		cur->next = NULL;
+		cur->key = cur->data + strlen(value_s) + 1;
+		strcpy(cur->data, value_s);
+		strcpy(cur->key, key_s);
 		if (last) {
-			break;
-		}
-	}
-
-	if (sync) {
-		db_sync();
-	}
-
-	ast_mutex_unlock(&dblock);
-
-	return counter;
-}
-
-static int db_deltree_cb(DBT *key, DBT *value, const char *filter, void *data)
-{
-	int res = 0;
-
-	if (keymatch(dbt_data2str_full(key, "<bad key>"), filter)) {
-		astdb->del(astdb, key, 0);
-		res = 1;
-	}
-	return res;
-}
-
-int ast_db_deltree(const char *family, const char *keytree)
-{
-	char prefix[MAX_DB_FIELD];
-
-	if (family) {
-		if (keytree) {
-			snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
+			last->next = cur;
 		} else {
-			snprintf(prefix, sizeof(prefix), "/%s", family);
-		}
-	} else if (keytree) {
-		return -1;
-	} else {
-		prefix[0] = '\0';
-	}
-
-	return process_db_keys(db_deltree_cb, NULL, prefix, 1);
-}
-
-int ast_db_put(const char *family, const char *keys, const char *value)
-{
-	char fullkey[MAX_DB_FIELD];
-	DBT key, data;
-	int res, fullkeylen;
-
-	ast_mutex_lock(&dblock);
-	if (dbinit()) {
-		ast_mutex_unlock(&dblock);
-		return -1;
-	}
-
-	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
-	memset(&key, 0, sizeof(key));
-	memset(&data, 0, sizeof(data));
-	key.data = fullkey;
-	key.size = fullkeylen + 1;
-	data.data = (char *) value;
-	data.size = strlen(value) + 1;
-	res = astdb->put(astdb, &key, &data, 0);
-	db_sync();
-	ast_mutex_unlock(&dblock);
-	if (res)
-		ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
-
-	return res;
-}
-
-int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
-{
-	char fullkey[MAX_DB_FIELD] = "";
-	DBT key, data;
-	int res, fullkeylen;
-
-	ast_mutex_lock(&dblock);
-	if (dbinit()) {
-		ast_mutex_unlock(&dblock);
-		return -1;
-	}
-
-	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
-	memset(&key, 0, sizeof(key));
-	memset(&data, 0, sizeof(data));
-	memset(value, 0, valuelen);
-	key.data = fullkey;
-	key.size = fullkeylen + 1;
-
-	res = astdb->get(astdb, &key, &data, 0);
-
-	/* Be sure to NULL terminate our data either way */
-	if (res) {
-		ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
-	} else {
-#if 0
-		printf("Got value of size %d\n", data.size);
-#endif
-		if (data.size) {
-			((char *)data.data)[data.size - 1] = '\0';
-			/* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
-			ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen);
-		} else {
-			ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
-		}
-	}
-
-	/* Data is not fully isolated for concurrency, so the lock must be extended
-	 * to after the copy to the output buffer. */
-	ast_mutex_unlock(&dblock);
-
-	return res;
-}
-
-int ast_db_del(const char *family, const char *keys)
-{
-	char fullkey[MAX_DB_FIELD];
-	DBT key;
-	int res, fullkeylen;
-
-	ast_mutex_lock(&dblock);
-	if (dbinit()) {
-		ast_mutex_unlock(&dblock);
-		return -1;
-	}
-	
-	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
-	memset(&key, 0, sizeof(key));
-	key.data = fullkey;
-	key.size = fullkeylen + 1;
-	
-	res = astdb->del(astdb, &key, 0);
-	db_sync();
-	
-	ast_mutex_unlock(&dblock);
-
-	if (res) {
-		ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
-	}
-	return res;
+			ret = cur;
+		}
+		last = cur;
+	}
+
+	sqlite3_reset(gettreeStmt);
+	return ret;
+}
+
+void ast_db_freetree(struct ast_db_entry *dbe)
+{
+	struct ast_db_entry *last;
+	while (dbe) {
+		last = dbe;
+		dbe = dbe->next;
+		ast_free(last);
+	}
 }
 
 static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -429,8 +606,8 @@
 	case CLI_INIT:
 		e->command = "database deltree";
 		e->usage =
-			"Usage: database deltree <family> [keytree]\n"
-			"       Deletes a family or specific keytree within a family\n"
+			"Usage: database deltree <family> [subfamily]\n"
+			"       Deletes a family or specific subfamily within a family\n"
 			"       in the Asterisk database.\n";
 		return NULL;
 	case CLI_GENERATE:
@@ -452,32 +629,19 @@
 	return CLI_SUCCESS;
 }
 
-static int db_show_cb(DBT *key, DBT *value, const char *filter, void *data)
-{
-	struct ast_cli_args *a = data;
-	const char *key_s = dbt_data2str_full(key, "<bad key>");
-	const char *value_s = dbt_data2str_full(value, "<bad value>");
-
-	if (keymatch(key_s, filter)) {
-		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
-		return 1;
-	}
-
-	return 0;
-}
-
 static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char prefix[MAX_DB_FIELD];
 	int counter = 0;
+	sqlite3_stmt *gettreeStmt;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "database show";
 		e->usage =
-			"Usage: database show [family [keytree]]\n"
+			"Usage: database show [family [subfamily]]\n"
 			"       Shows Asterisk database contents, optionally restricted\n"
-			"       to a given family, or family and keytree.\n";
+			"       to a given family, or family and subfamily.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -496,39 +660,39 @@
 		return CLI_SHOWUSAGE;
 	}
 
-	if((counter = process_db_keys(db_show_cb, a, prefix, 0)) < 0) {
-		ast_cli(a->fd, "Database unavailable\n");
+	if (!(gettreeStmt = get_stmt_ts(&gettree_all_stmt))) {
 		return CLI_SUCCESS;
 	}
+
+	while (sqlite3_step(gettreeStmt) == SQLITE_ROW) {
+		const char *key_s, *value_s;
+		if (!(key_s = (const char *) sqlite3_column_text(gettreeStmt, 0))) {
+			break;
+		}
+		if (!(value_s = (const char *) sqlite3_column_text(gettreeStmt, 1))) {
+			break;
+		}
+		++counter;
+		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
+	}
+
+	sqlite3_reset(gettreeStmt);
 
 	ast_cli(a->fd, "%d results found.\n", counter);
 	return CLI_SUCCESS;
 }
 
-static int db_showkey_cb(DBT *key, DBT *value, const char *filter, void *data)
-{
-	struct ast_cli_args *a = data;
-	const char *key_s = dbt_data2str_full(key, "<bad key>");
-	const char *value_s = dbt_data2str_full(value, "<bad value>");
-
-	if (subkeymatch(key_s, filter)) {
-		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
-		return 1;
-	}
-
-	return 0;
-}
-
 static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char suffix[MAX_DB_FIELD];
 	int counter = 0;
+	sqlite3_stmt *showkeyStmt;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "database showkey";
 		e->usage =
-			"Usage: database showkey <keytree>\n"
+			"Usage: database showkey <subfamily>\n"
 			"       Shows Asterisk database contents, restricted to a given key.\n";
 		return NULL;
 	case CLI_GENERATE:
@@ -542,68 +706,34 @@
 		return CLI_SHOWUSAGE;
 	}
 
-	if ((counter = process_db_keys(db_showkey_cb, a, suffix, 0)) < 0) {
-		ast_cli(a->fd, "Database unavailable\n");
+	if (!(showkeyStmt = get_stmt_ts(&showkey_stmt))) {
 		return CLI_SUCCESS;
 	}
+
+	if (!ast_strlen_zero(suffix)) {
+		if (sqlite3_bind_text(showkeyStmt, 1, suffix, -1, SQLITE_STATIC) != SQLITE_OK) {
+			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", suffix, sqlite3_errmsg(sqlite3_db_handle(showkeyStmt)));
+			sqlite3_reset(showkeyStmt);
+			return NULL;
+		}
+	}
+
+	while (sqlite3_step(showkeyStmt) == SQLITE_ROW) {
+		const char *key_s, *value_s;
+		if (!(key_s = (const char *) sqlite3_column_text(showkeyStmt, 0))) {
+			break;
+		}
+		if (!(value_s = (const char *) sqlite3_column_text(showkeyStmt, 1))) {
+			break;
+		}
+		++counter;
+		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
+	}
+
+	sqlite3_reset(showkeyStmt);
 
 	ast_cli(a->fd, "%d results found.\n", counter);
 	return CLI_SUCCESS;
-}
-
-static int db_gettree_cb(DBT *key, DBT *value, const char *filter, void *data)
-{
-	struct ast_db_entry **ret = data;
-	struct ast_db_entry *cur;
-	const char *key_s = dbt_data2str_full(key, "<bad key>");
-	const char *value_s = dbt_data2str_full(value, "<bad value>");
-	size_t key_slen = strlen(key_s) + 1, value_slen = strlen(value_s) + 1;
-
-	if (keymatch(key_s, filter) && (cur = ast_malloc(sizeof(*cur) + key_slen + value_slen))) {
-		cur->next = *ret;
-		cur->key = cur->data + value_slen;
-		strcpy(cur->data, value_s);
-		strcpy(cur->key, key_s);
-		*ret = cur;
-		return 1;
-	}
-
-	return 0;
-}
-
-struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
-{
-	char prefix[MAX_DB_FIELD];
-	struct ast_db_entry *ret = NULL;
-
-	if (!ast_strlen_zero(family)) {
-		if (!ast_strlen_zero(keytree)) {
-			/* Family and key tree */
-			snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
-		} else {
-			/* Family only */
-			snprintf(prefix, sizeof(prefix), "/%s", family);
-		}
-	} else {
-		prefix[0] = '\0';
-	}
-
-	if (process_db_keys(db_gettree_cb, &ret, prefix, 0) < 0) {
-		ast_log(LOG_WARNING, "Database unavailable\n");
-		return NULL;
-	}
-
-	return ret;
-}
-
-void ast_db_freetree(struct ast_db_entry *dbe)
-{
-	struct ast_db_entry *last;
-	while (dbe) {
-		last = dbe;
-		dbe = dbe->next;
-		ast_free(last);
-	}
 }
 
 static struct ast_cli_entry cli_database[] = {
@@ -612,7 +742,7 @@
 	AST_CLI_DEFINE(handle_cli_database_get,     "Gets database value"),
 	AST_CLI_DEFINE(handle_cli_database_put,     "Adds/updates database value"),
 	AST_CLI_DEFINE(handle_cli_database_del,     "Removes database key/value"),
-	AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values")
+	AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database subfamily/values")
 };
 
 static int manager_dbput(struct mansession *s, const struct message *m)
@@ -730,51 +860,9 @@
 	return 0;
 }
 
-/*!
- * \internal
- * \brief Signal the astdb sync thread to do its thing.
- *
- * \note dblock is assumed to be held when calling this function.
- */
-static void db_sync(void)
-{
-	ast_cond_signal(&dbcond);
-}
-
-/*!
- * \internal
- * \brief astdb sync thread
- *
- * This thread is in charge of syncing astdb to disk after a change.
- * By pushing it off to this thread to take care of, this I/O bound operation
- * will not block other threads from performing other critical processing.
- * If changes happen rapidly, this thread will also ensure that the sync
- * operations are rate limited.
- */
-static void *db_sync_thread(void *data)
-{
-	ast_mutex_lock(&dblock);
-	for (;;) {
-		ast_cond_wait(&dbcond, &dblock);
-		ast_mutex_unlock(&dblock);
-		sleep(1);
-		ast_mutex_lock(&dblock);
-		astdb->sync(astdb, 0);
-	}
-
-	return NULL;
-}
-
 int astdb_init(void)
 {
-	pthread_t dont_care;
-
-	ast_cond_init(&dbcond, NULL);
-	if (ast_pthread_create_background(&dont_care, NULL, db_sync_thread, NULL)) {
-		return -1;
-	}
-
-	dbinit();
+	sqlite3_config(SQLITE_CONFIG_SERIALIZED);
 	ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database));
 	ast_manager_register_xml("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
 	ast_manager_register_xml("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);

Modified: team/twilson/sqlite_astdb/tests/test_db.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/tests/test_db.c?view=diff&rev=323103&r1=323102&r2=323103
==============================================================================
--- team/twilson/sqlite_astdb/tests/test_db.c (original)
+++ team/twilson/sqlite_astdb/tests/test_db.c Sun Jun 12 17:25:23 2011
@@ -205,10 +205,39 @@
 	return res;
 }
 
+AST_TEST_DEFINE(perftest)
+{
+	int res = AST_TEST_PASS;
+	size_t x;
+	char buf[10];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "perftest";
+		info->category = "/main/astdb/";
+		info->summary = "astdb performance unit test";
+		info->description =
+			"Measure astdb performance";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_db_begin_transaction();
+	for (x = 0; x < 100000; x++) {
+		sprintf(buf, "%zu", x);
+		ast_db_put("astdbtest", buf, buf);
+	}
+	ast_db_commit_transaction();
+	ast_db_deltree("astdbtest", NULL);
+
+	return res;
+}
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(put_get_del);
 	AST_TEST_UNREGISTER(gettree_deltree);
+	AST_TEST_UNREGISTER(perftest);
 	return 0;
 }
 
@@ -216,6 +245,7 @@
 {
 	AST_TEST_REGISTER(put_get_del);
 	AST_TEST_REGISTER(gettree_deltree);
+	AST_TEST_REGISTER(perftest);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 




More information about the svn-commits mailing list