db: turn generated queries array into a simple hash table.

Since we have that functionality, let's use it.  Also, make table
const.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-11-15 04:33:46 +10:30
parent 53c9d9853d
commit fcf3d0ce6c
5 changed files with 44 additions and 27 deletions

View File

@ -27,6 +27,8 @@ class Rewriter(object):
def rewrite(self, queries): def rewrite(self, queries):
for i, q in enumerate(queries): for i, q in enumerate(queries):
if q['name'] is None:
continue
org = q['query'] org = q['query']
queries[i]['query'] = self.rewrite_single(org) queries[i]['query'] = self.rewrite_single(org)
eprint("Rewritten statement\n\tfrom {}\n\t to {}".format(org, q['query'])) eprint("Rewritten statement\n\tfrom {}\n\t to {}".format(org, q['query']))
@ -136,10 +138,11 @@ static const struct sqlname_map ${colname}[] = {
% endfor % endfor
struct db_query db_${f}_queries[] = { const struct db_query db_${f}_queries[] = {
% for elem in queries: % for elem in queries:
{ {
% if elem['name'] is not None:
.name = "${elem['name']}", .name = "${elem['name']}",
.query = "${elem['query']}", .query = "${elem['query']}",
.placeholders = ${elem['placeholders']}, .placeholders = ${elem['placeholders']},
@ -147,19 +150,32 @@ struct db_query db_${f}_queries[] = {
% if elem['colnames'] is not None: % if elem['colnames'] is not None:
.colnames = ${elem['colnames']}, .colnames = ${elem['colnames']},
.num_colnames = ARRAY_SIZE(${elem['colnames']}), .num_colnames = ARRAY_SIZE(${elem['colnames']}),
% endif
% endif % endif
}, },
% endfor % endfor
}; };
#define DB_${f.upper()}_QUERY_COUNT ${len(queries)}
#endif /* HAVE_${f.upper()} */ #endif /* HAVE_${f.upper()} */
#endif /* LIGHTNINGD_WALLET_GEN_DB_${f.upper()} */ #endif /* LIGHTNINGD_WALLET_GEN_DB_${f.upper()} */
""") """)
def queries_htable(queries):
# Converts a list of queries into a hash table.
tablesize = len(queries) * 2 - 1
htable = [{'name': None}] * tablesize
for q in queries:
pos = hash_djb2(q['name']) % tablesize
while htable[pos]['name'] is not None:
pos = (pos + 1) % tablesize
htable[pos] = q
return htable
def extract_queries(pofile): def extract_queries(pofile):
# Given a po-file, extract all queries and their associated names, and # Given a po-file, extract all queries and their associated names, and
# return them as a list. # return them as a list.
@ -204,7 +220,7 @@ def extract_queries(pofile):
'readonly': "true" if is_select else "false", 'readonly': "true" if is_select else "false",
'colnames': colnames, 'colnames': colnames,
}) })
return colhtables, queries return colhtables, queries_htable(queries)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -903,12 +903,20 @@ static void db_stmt_free(struct db_stmt *stmt)
assert(stmt->inner_stmt == NULL); assert(stmt->inner_stmt == NULL);
} }
/* Matches the hash function used in devtools/sql-rewrite.py */
static u32 hash_djb2(const char *str)
{
u32 hash = 5381;
for (size_t i = 0; str[i]; i++)
hash = ((hash << 5) + hash) ^ str[i];
return hash;
}
struct db_stmt *db_prepare_v2_(const char *location, struct db *db, struct db_stmt *db_prepare_v2_(const char *location, struct db *db,
const char *query_id) const char *query_id)
{ {
struct db_stmt *stmt = tal(db, struct db_stmt); struct db_stmt *stmt = tal(db, struct db_stmt);
size_t num_slots; size_t num_slots, pos;
stmt->query = NULL;
/* Normalize query_id paths, because unit tests are compiled with this /* Normalize query_id paths, because unit tests are compiled with this
* prefix. */ * prefix. */
@ -920,14 +928,16 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db,
"transaction: %s", location); "transaction: %s", location);
/* Look up the query by its ID */ /* Look up the query by its ID */
for (size_t i = 0; i < db->config->num_queries; i++) { pos = hash_djb2(query_id) % db->config->query_table_size;
if (streq(query_id, db->config->queries[i].name)) { for (;;) {
stmt->query = &db->config->queries[i]; if (!db->config->query_table[pos].name)
fatal("Could not resolve query %s", query_id);
if (streq(query_id, db->config->query_table[pos].name)) {
stmt->query = &db->config->query_table[pos];
break; break;
} }
pos = (pos + 1) % db->config->query_table_size;
} }
if (stmt->query == NULL)
fatal("Could not resolve query %s", query_id);
num_slots = stmt->query->placeholders; num_slots = stmt->query->placeholders;
/* Allocate the slots for placeholders/bindings, zeroed next since /* Allocate the slots for placeholders/bindings, zeroed next since
@ -2335,15 +2345,6 @@ const char **db_changes(struct db *db)
return db->changes; return db->changes;
} }
/* Matches the hash function used in devtools/sql-rewrite.py */
static u32 hash_djb2(const char *str)
{
u32 hash = 5381;
for (size_t i = 0; str[i]; i++)
hash = ((hash << 5) + hash) ^ str[i];
return hash;
}
size_t db_query_colnum(const struct db_stmt *stmt, size_t db_query_colnum(const struct db_stmt *stmt,
const char *colname) const char *colname)
{ {

View File

@ -84,7 +84,7 @@ struct db_stmt {
struct db *db; struct db *db;
/* Which SQL statement are we trying to execute? */ /* Which SQL statement are we trying to execute? */
struct db_query *query; const struct db_query *query;
/* Which parameters are we binding to the statement? */ /* Which parameters are we binding to the statement? */
struct db_binding *bindings; struct db_binding *bindings;
@ -109,8 +109,8 @@ struct db_stmt {
struct db_config { struct db_config {
const char *name; const char *name;
struct db_query *queries; const struct db_query *query_table;
size_t num_queries; size_t query_table_size;
/* Function used to execute a statement that doesn't result in a /* Function used to execute a statement that doesn't result in a
* response. */ * response. */

View File

@ -275,8 +275,8 @@ static bool db_postgres_vacuum(struct db *db)
struct db_config db_postgres_config = { struct db_config db_postgres_config = {
.name = "postgres", .name = "postgres",
.queries = db_postgres_queries, .query_table = db_postgres_queries,
.num_queries = DB_POSTGRES_QUERY_COUNT, .query_table_size = ARRAY_SIZE(db_postgres_queries),
.exec_fn = db_postgres_exec, .exec_fn = db_postgres_exec,
.query_fn = db_postgres_query, .query_fn = db_postgres_query,
.step_fn = db_postgres_step, .step_fn = db_postgres_step,

View File

@ -253,8 +253,8 @@ static bool db_sqlite3_vacuum(struct db *db)
struct db_config db_sqlite3_config = { struct db_config db_sqlite3_config = {
.name = "sqlite3", .name = "sqlite3",
.queries = db_sqlite3_queries, .query_table = db_sqlite3_queries,
.num_queries = DB_SQLITE3_QUERY_COUNT, .query_table_size = ARRAY_SIZE(db_sqlite3_queries),
.exec_fn = &db_sqlite3_exec, .exec_fn = &db_sqlite3_exec,
.query_fn = &db_sqlite3_query, .query_fn = &db_sqlite3_query,
.step_fn = &db_sqlite3_step, .step_fn = &db_sqlite3_step,