mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 14:42:40 +01:00
db: create simple hashtable of fields in SELECT.
This simplistically maps names to numbers, eg: SELECT foo, bar FROM tbl; 'foo' -> 0 'bar' -> 1 If a statement is too complex for our simple parsing, we treat it as a single field (which currently it always is). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
57328fe59e
commit
787fbb1228
3 changed files with 72 additions and 5 deletions
|
@ -83,13 +83,58 @@ rewriters = {
|
|||
"postgres": PostgresRewriter(),
|
||||
}
|
||||
|
||||
|
||||
# djb2 is simple and effective: see http://www.cse.yorku.ca/~oz/hash.html
|
||||
def hash_djb2(string):
|
||||
val = 5381
|
||||
for s in string:
|
||||
val = ((val * 33) & 0xFFFFFFFF) ^ ord(s)
|
||||
return val
|
||||
|
||||
|
||||
def colname_htable(query):
|
||||
assert query.upper().startswith("SELECT")
|
||||
colquery = query[6:query.upper().index(" FROM ")]
|
||||
colnames = colquery.split(',')
|
||||
|
||||
# If split caused unbalanced brackets, it's complex: assume
|
||||
# a single field!
|
||||
if any([colname.count('(') != colname.count(')') for colname in colnames]):
|
||||
return [('"' + colquery.strip() + '"', 0)]
|
||||
|
||||
# 50% density htable
|
||||
tablesize = len(colnames) * 2 - 1
|
||||
table = [("NULL", -1)] * tablesize
|
||||
for colnum, colname in enumerate(colnames):
|
||||
colname = colname.strip()
|
||||
# SELECT xxx AS yyy -> Y
|
||||
as_clause = colname.upper().find(" AS ")
|
||||
if as_clause != -1:
|
||||
colname = colname[as_clause + 4:].strip()
|
||||
|
||||
pos = hash_djb2(colname) % tablesize
|
||||
while table[pos][0] != "NULL":
|
||||
pos = (pos + 1) % tablesize
|
||||
table[pos] = ('"' + colname + '"', colnum)
|
||||
return table
|
||||
|
||||
|
||||
template = Template("""#ifndef LIGHTNINGD_WALLET_GEN_DB_${f.upper()}
|
||||
#define LIGHTNINGD_WALLET_GEN_DB_${f.upper()}
|
||||
|
||||
#include <config.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <wallet/db_common.h>
|
||||
|
||||
#if HAVE_${f.upper()}
|
||||
% for colname, table in colhtables.items():
|
||||
static const struct sqlname_map ${colname}[] = {
|
||||
% for t in table:
|
||||
{ ${t[0]}, ${t[1]} },
|
||||
% endfor
|
||||
};
|
||||
|
||||
% endfor
|
||||
|
||||
struct db_query db_${f}_queries[] = {
|
||||
|
||||
|
@ -99,6 +144,10 @@ struct db_query db_${f}_queries[] = {
|
|||
.query = "${elem['query']}",
|
||||
.placeholders = ${elem['placeholders']},
|
||||
.readonly = ${elem['readonly']},
|
||||
% if elem['colnames'] is not None:
|
||||
.colnames = ${elem['colnames']},
|
||||
.num_colnames = ARRAY_SIZE(${elem['colnames']}),
|
||||
% endif
|
||||
},
|
||||
% endfor
|
||||
};
|
||||
|
@ -129,6 +178,7 @@ def extract_queries(pofile):
|
|||
if chunk != []:
|
||||
yield chunk
|
||||
|
||||
colhtables = {}
|
||||
queries = []
|
||||
for c in chunk(pofile):
|
||||
|
||||
|
@ -140,13 +190,21 @@ def extract_queries(pofile):
|
|||
# Strip header and surrounding quotes
|
||||
query = c[i][7:][:-1]
|
||||
|
||||
is_select = query.upper().startswith("SELECT")
|
||||
if is_select:
|
||||
colnames = 'col_table{}'.format(len(queries))
|
||||
colhtables[colnames] = colname_htable(query)
|
||||
else:
|
||||
colnames = None
|
||||
|
||||
queries.append({
|
||||
'name': query,
|
||||
'query': query,
|
||||
'placeholders': query.count('?'),
|
||||
'readonly': "true" if query.upper().startswith("SELECT") else "false",
|
||||
'readonly': "true" if is_select else "false",
|
||||
'colnames': colnames,
|
||||
})
|
||||
return queries
|
||||
return colhtables, queries
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -165,7 +223,7 @@ if __name__ == "__main__":
|
|||
|
||||
rewriter = rewriters[dialect]
|
||||
|
||||
queries = extract_queries(sys.argv[1])
|
||||
colhtables, queries = extract_queries(sys.argv[1])
|
||||
queries = rewriter.rewrite(queries)
|
||||
|
||||
print(template.render(f=dialect, queries=queries))
|
||||
print(template.render(f=dialect, queries=queries, colhtables=colhtables))
|
||||
|
|
|
@ -49,6 +49,10 @@ struct db_query {
|
|||
/* Is this a read-only query? If it is there's no need to tell plugins
|
||||
* about it. */
|
||||
bool readonly;
|
||||
|
||||
/* If this is a select statement, what column names */
|
||||
const struct sqlname_map *colnames;
|
||||
size_t num_colnames;
|
||||
};
|
||||
|
||||
enum db_binding_type {
|
||||
|
@ -155,5 +159,10 @@ AUTODATA_TYPE(db_backends, struct db_config);
|
|||
*/
|
||||
void db_changes_add(struct db_stmt *db_stmt, const char * expanded);
|
||||
|
||||
/* devtools/sql-rewrite.py generates this simple htable */
|
||||
struct sqlname_map {
|
||||
const char *sqlname;
|
||||
int val;
|
||||
};
|
||||
|
||||
#endif /* LIGHTNING_WALLET_DB_COMMON_H */
|
||||
|
|
|
@ -4321,7 +4321,7 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w)
|
|||
|
||||
stmt = db_prepare_v2(w->db, SQL("SELECT"
|
||||
" CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)"
|
||||
"FROM forwarded_payments "
|
||||
" FROM forwarded_payments "
|
||||
"WHERE state = ?;"));
|
||||
db_bind_int(stmt, 0, wallet_forward_status_in_db(FORWARD_SETTLED));
|
||||
db_query_prepared(stmt);
|
||||
|
|
Loading…
Add table
Reference in a new issue