From fcf3d0ce6ce51baa641bdd691354a4fcf1cf3939 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:33:46 +1030 Subject: [PATCH] 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 --- devtools/sql-rewrite.py | 24 ++++++++++++++++++++---- wallet/db.c | 33 +++++++++++++++++---------------- wallet/db_common.h | 6 +++--- wallet/db_postgres.c | 4 ++-- wallet/db_sqlite3.c | 4 ++-- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 6cfb44ce6..e6f70689c 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -27,6 +27,8 @@ class Rewriter(object): def rewrite(self, queries): for i, q in enumerate(queries): + if q['name'] is None: + continue org = q['query'] queries[i]['query'] = self.rewrite_single(org) eprint("Rewritten statement\n\tfrom {}\n\t to {}".format(org, q['query'])) @@ -136,10 +138,11 @@ static const struct sqlname_map ${colname}[] = { % endfor -struct db_query db_${f}_queries[] = { +const struct db_query db_${f}_queries[] = { % for elem in queries: { +% if elem['name'] is not None: .name = "${elem['name']}", .query = "${elem['query']}", .placeholders = ${elem['placeholders']}, @@ -147,19 +150,32 @@ struct db_query db_${f}_queries[] = { % if elem['colnames'] is not None: .colnames = ${elem['colnames']}, .num_colnames = ARRAY_SIZE(${elem['colnames']}), +% endif % endif }, % endfor }; -#define DB_${f.upper()}_QUERY_COUNT ${len(queries)} - #endif /* HAVE_${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): # Given a po-file, extract all queries and their associated names, and # return them as a list. @@ -204,7 +220,7 @@ def extract_queries(pofile): 'readonly': "true" if is_select else "false", 'colnames': colnames, }) - return colhtables, queries + return colhtables, queries_htable(queries) if __name__ == "__main__": diff --git a/wallet/db.c b/wallet/db.c index d33747ba4..4e9eb042b 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -903,12 +903,20 @@ static void db_stmt_free(struct db_stmt *stmt) 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, const char *query_id) { struct db_stmt *stmt = tal(db, struct db_stmt); - size_t num_slots; - stmt->query = NULL; + size_t num_slots, pos; /* Normalize query_id paths, because unit tests are compiled with this * prefix. */ @@ -920,14 +928,16 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, "transaction: %s", location); /* Look up the query by its ID */ - for (size_t i = 0; i < db->config->num_queries; i++) { - if (streq(query_id, db->config->queries[i].name)) { - stmt->query = &db->config->queries[i]; + pos = hash_djb2(query_id) % db->config->query_table_size; + for (;;) { + 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; } + 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; /* Allocate the slots for placeholders/bindings, zeroed next since @@ -2335,15 +2345,6 @@ const char **db_changes(struct db *db) 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, const char *colname) { diff --git a/wallet/db_common.h b/wallet/db_common.h index 470ccb2e6..c89240284 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -84,7 +84,7 @@ struct db_stmt { struct db *db; /* 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? */ struct db_binding *bindings; @@ -109,8 +109,8 @@ struct db_stmt { struct db_config { const char *name; - struct db_query *queries; - size_t num_queries; + const struct db_query *query_table; + size_t query_table_size; /* Function used to execute a statement that doesn't result in a * response. */ diff --git a/wallet/db_postgres.c b/wallet/db_postgres.c index 3a190a870..a8a239e93 100644 --- a/wallet/db_postgres.c +++ b/wallet/db_postgres.c @@ -275,8 +275,8 @@ static bool db_postgres_vacuum(struct db *db) struct db_config db_postgres_config = { .name = "postgres", - .queries = db_postgres_queries, - .num_queries = DB_POSTGRES_QUERY_COUNT, + .query_table = db_postgres_queries, + .query_table_size = ARRAY_SIZE(db_postgres_queries), .exec_fn = db_postgres_exec, .query_fn = db_postgres_query, .step_fn = db_postgres_step, diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 6ff339ffe..00d6af7da 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -253,8 +253,8 @@ static bool db_sqlite3_vacuum(struct db *db) struct db_config db_sqlite3_config = { .name = "sqlite3", - .queries = db_sqlite3_queries, - .num_queries = DB_SQLITE3_QUERY_COUNT, + .query_table = db_sqlite3_queries, + .query_table_size = ARRAY_SIZE(db_sqlite3_queries), .exec_fn = &db_sqlite3_exec, .query_fn = &db_sqlite3_query, .step_fn = &db_sqlite3_step,