@ -129,6 +129,20 @@ DEFINE_SQL_STATEMENT(gettree_all_stmt, "SELECT key, value FROM astdb ORDER BY ke
DEFINE_SQL_STATEMENT ( showkey_stmt , " SELECT key, value FROM astdb WHERE key LIKE '%' || '/' || ? ORDER BY key " )
DEFINE_SQL_STATEMENT ( create_astdb_stmt , " CREATE TABLE IF NOT EXISTS astdb(key VARCHAR(256), value VARCHAR(256), PRIMARY KEY(key)) " )
/* This query begs an explanation:
*
* First , the parameter binding syntax used here is slightly different then the other
* queries in that we use a numbered parameter so that we can bind once and get the same
* value substituted multiple times within the executed query .
*
* Second , the key comparison is being used to find all keys that are lexicographically
* greater than the provided key , but less than the provided key with a high ( but
* invalid ) Unicode codepoint appended to it . This will give us all keys in the database
* that have ' key ' as a prefix and performs much better than the equivalent " LIKE key ||
* ' % ' " operation.
*/
DEFINE_SQL_STATEMENT ( gettree_prefix_stmt , " SELECT key, value FROM astdb WHERE key > ?1 AND key <= ?1 || X'ffff' " )
static int init_stmt ( sqlite3_stmt * * stmt , const char * sql , size_t len )
{
ast_mutex_lock ( & dblock ) ;
@ -169,6 +183,7 @@ static void clean_statements(void)
clean_stmt ( & deltree_all_stmt , deltree_all_stmt_sql ) ;
clean_stmt ( & gettree_stmt , gettree_stmt_sql ) ;
clean_stmt ( & gettree_all_stmt , gettree_all_stmt_sql ) ;
clean_stmt ( & gettree_prefix_stmt , gettree_prefix_stmt_sql ) ;
clean_stmt ( & showkey_stmt , showkey_stmt_sql ) ;
clean_stmt ( & put_stmt , put_stmt_sql ) ;
clean_stmt ( & create_astdb_stmt , create_astdb_stmt_sql ) ;
@ -184,6 +199,7 @@ static int init_statements(void)
| | init_stmt ( & deltree_all_stmt , deltree_all_stmt_sql , sizeof ( deltree_all_stmt_sql ) )
| | init_stmt ( & gettree_stmt , gettree_stmt_sql , sizeof ( gettree_stmt_sql ) )
| | init_stmt ( & gettree_all_stmt , gettree_all_stmt_sql , sizeof ( gettree_all_stmt_sql ) )
| | init_stmt ( & gettree_prefix_stmt , gettree_prefix_stmt_sql , sizeof ( gettree_prefix_stmt_sql ) )
| | init_stmt ( & showkey_stmt , showkey_stmt_sql , sizeof ( showkey_stmt_sql ) )
| | init_stmt ( & put_stmt , put_stmt_sql , sizeof ( put_stmt_sql ) ) ;
}
@ -475,19 +491,64 @@ int ast_db_deltree(const char *family, const char *keytree)
return res ;
}
static struct ast_db_entry * db_gettree_common ( sqlite3_stmt * stmt )
{
struct ast_db_entry * head = NULL , * prev = NULL , * cur ;
while ( sqlite3_step ( stmt ) = = SQLITE_ROW ) {
const char * key , * value ;
size_t key_len , value_len ;
key = ( const char * ) sqlite3_column_text ( stmt , 0 ) ;
value = ( const char * ) sqlite3_column_text ( stmt , 1 ) ;
if ( ! key | | ! value ) {
break ;
}
key_len = strlen ( key ) ;
value_len = strlen ( value ) ;
cur = ast_malloc ( sizeof ( * cur ) + key_len + value_len + 2 ) ;
if ( ! cur ) {
break ;
}
cur - > next = NULL ;
cur - > key = cur - > data + value_len + 1 ;
memcpy ( cur - > data , value , value_len + 1 ) ;
memcpy ( cur - > key , key , key_len + 1 ) ;
if ( prev ) {
prev - > next = cur ;
} else {
head = cur ;
}
prev = cur ;
}
return head ;
}
struct ast_db_entry * ast_db_gettree ( const char * family , const char * keytree )
{
char prefix [ MAX_DB_FIELD ] ;
sqlite3_stmt * stmt = gettree_stmt ;
struct ast_db_entry * cur , * last = NULL , * ret = NULL ;
size_t res = 0 ;
struct ast_db_entry * ret ;
if ( ! ast_strlen_zero ( family ) ) {
if ( ! ast_strlen_zero ( keytree ) ) {
/* Family and key tree */
snprintf ( prefix , sizeof ( prefix ) , " /%s/%s " , family , keytree ) ;
res = snprintf ( prefix , sizeof ( prefix ) , " /%s/%s " , family , keytree ) ;
} else {
/* Family only */
snprintf ( prefix , sizeof ( prefix ) , " /%s " , family ) ;
res = snprintf ( prefix , sizeof ( prefix ) , " /%s " , family ) ;
}
if ( res > = sizeof ( prefix ) ) {
ast_log ( LOG_WARNING , " Requested prefix is too long: %s \n " , keytree ) ;
return NULL ;
}
} else {
prefix [ 0 ] = ' \0 ' ;
@ -495,41 +556,47 @@ struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
}
ast_mutex_lock ( & dblock ) ;
if ( ! ast_strlen_zero ( prefix ) & & ( sqlite3_bind_text ( stmt , 1 , prefix , - 1 , SQLITE_STATIC ) ! = SQLITE_OK ) ) {
ast_log ( LOG_WARNING , " Could bind %s to stmt: %s\n " , prefix , sqlite3_errmsg ( astdb ) ) ;
if ( res & & ( sqlite3_bind_text ( stmt , 1 , prefix , res , SQLITE_STATIC ) ! = SQLITE_OK ) ) {
ast_log ( LOG_WARNING , " Could not bind %s to stmt: %s\n " , prefix , sqlite3_errmsg ( astdb ) ) ;
sqlite3_reset ( stmt ) ;
ast_mutex_unlock ( & dblock ) ;
return NULL ;
}
while ( sqlite3_step ( stmt ) = = SQLITE_ROW ) {
const char * key_s , * value_s ;
if ( ! ( key_s = ( const char * ) sqlite3_column_text ( stmt , 0 ) ) ) {
break ;
}
if ( ! ( value_s = ( const char * ) sqlite3_column_text ( stmt , 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 ) {
last - > next = cur ;
} else {
ret = cur ;
}
last = cur ;
}
ret = db_gettree_common ( stmt ) ;
sqlite3_reset ( stmt ) ;
ast_mutex_unlock ( & dblock ) ;
return ret ;
}
struct ast_db_entry * ast_db_gettree_by_prefix ( const char * family , const char * key_prefix )
{
char prefix [ MAX_DB_FIELD ] ;
size_t res ;
struct ast_db_entry * ret ;
res = snprintf ( prefix , sizeof ( prefix ) , " /%s/%s " , family , key_prefix ) ;
if ( res > = sizeof ( prefix ) ) {
ast_log ( LOG_WARNING , " Requested key prefix is too long: %s \n " , key_prefix ) ;
return NULL ;
}
ast_mutex_lock ( & dblock ) ;
if ( sqlite3_bind_text ( gettree_prefix_stmt , 1 , prefix , res , SQLITE_STATIC ) ! = SQLITE_OK ) {
ast_log ( LOG_WARNING , " Could not bind %s to stmt: %s \n " , prefix , sqlite3_errmsg ( astdb ) ) ;
sqlite3_reset ( gettree_prefix_stmt ) ;
ast_mutex_unlock ( & dblock ) ;
return NULL ;
}
ret = db_gettree_common ( gettree_prefix_stmt ) ;
sqlite3_reset ( gettree_prefix_stmt ) ;
ast_mutex_unlock ( & dblock ) ;
return ret ;
}
void ast_db_freetree ( struct ast_db_entry * dbe )
{
struct ast_db_entry * last ;