From e1e26ca69de2ddba3019ef767e1c07f8dfc4415d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Mar 2019 13:20:18 +1030 Subject: [PATCH] db: create explicit separate API for select statements. I was tempted to create a new db_select_stmt wrapper type, but that means a lot of boilerplate around binding, which expects to work with db_prepare *and* db_select_prepare. This lets us clearly differentiate between db queries (which don't need to go to a plugin) and db changes (which do). Signed-off-by: Rusty Russell --- wallet/db.c | 99 ++++++++++---- wallet/db.h | 39 +++++- wallet/invoices.c | 156 ++++++++++------------ wallet/wallet.c | 331 ++++++++++++++++++++-------------------------- 4 files changed, 320 insertions(+), 305 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index 94a300e72..fa961b4a8 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -440,6 +440,36 @@ void db_stmt_done(sqlite3_stmt *stmt) sqlite3_finalize(stmt); } +sqlite3_stmt *db_select_prepare_(const char *location, struct db *db, const char *query) +{ + int err; + sqlite3_stmt *stmt; + const char *full_query = tal_fmt(db, "SELECT %s", query); + + assert(db->in_transaction); + + err = sqlite3_prepare_v2(db->sql, full_query, -1, &stmt, NULL); + + if (err != SQLITE_OK) + db_fatal("%s: %s: %s", location, full_query, sqlite3_errmsg(db->sql)); + + dev_statement_start(stmt, location); + tal_free(full_query); + return stmt; +} + +bool db_select_step_(const char *location, struct db *db, struct sqlite3_stmt *stmt) +{ + int ret; + + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) + return true; + if (ret != SQLITE_DONE) + db_fatal("%s: %s", location, sqlite3_errmsg(db->sql)); + db_stmt_done(stmt); + return false; +} sqlite3_stmt *db_prepare_(const char *location, struct db *db, const char *query) { int err; @@ -496,24 +526,39 @@ static void PRINTF_FMT(3, 4) tal_free(cmd); } +/* This one can fail: returns NULL if so */ +static sqlite3_stmt *db_query(const char *location, + struct db *db, const char *query) +{ + sqlite3_stmt *stmt; + + assert(db->in_transaction); + + /* Sets stmt to NULL if not SQLITE_OK */ + sqlite3_prepare_v2(db->sql, query, -1, &stmt, NULL); + if (stmt) + dev_statement_start(stmt, location); + return stmt; +} + sqlite3_stmt *PRINTF_FMT(3, 4) - db_query_(const char *location, struct db *db, const char *fmt, ...) + db_select_(const char *location, struct db *db, const char *fmt, ...) { va_list ap; - char *query; + char *query = tal_strdup(db, "SELECT "); sqlite3_stmt *stmt; assert(db->in_transaction); va_start(ap, fmt); - query = tal_vfmt(db, fmt, ap); + tal_append_vfmt(&query, fmt, ap); va_end(ap); - /* Sets stmt to NULL if not SQLITE_OK */ - sqlite3_prepare_v2(db->sql, query, -1, &stmt, NULL); + stmt = db_query(location, db, query); + if (!stmt) + db_fatal("%s:%s:%s", location, query, sqlite3_errmsg(db->sql)); tal_free(query); - if (stmt) - dev_statement_start(stmt, location); + return stmt; } @@ -577,22 +622,19 @@ static struct db *db_open(const tal_t *ctx, char *filename) */ static int db_get_version(struct db *db) { - int err; - u64 res = -1; - sqlite3_stmt *stmt = db_query(db, "SELECT version FROM version LIMIT 1"); + int res; + sqlite3_stmt *stmt = db_query(__func__, + db, "SELECT version FROM version LIMIT 1"); if (!stmt) return -1; - err = sqlite3_step(stmt); - if (err != SQLITE_ROW) { - db_stmt_done(stmt); + if (!db_select_step(db, stmt)) return -1; - } else { - res = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return res; - } + + res = sqlite3_column_int64(stmt, 0); + db_stmt_done(stmt); + return res; } /** @@ -670,21 +712,24 @@ void db_reopen_after_fork(struct db *db) s64 db_get_intvar(struct db *db, char *varname, s64 defval) { - int err; - s64 res = defval; - sqlite3_stmt *stmt = - db_query(db, - "SELECT val FROM vars WHERE name='%s' LIMIT 1", varname); + s64 res; + sqlite3_stmt *stmt; + const char *query; + + query = tal_fmt(db, "SELECT val FROM vars WHERE name='%s' LIMIT 1", varname); + stmt = db_query(__func__, db, query); + tal_free(query); if (!stmt) return defval; - err = sqlite3_step(stmt); - if (err == SQLITE_ROW) { + if (db_select_step(db, stmt)) { const unsigned char *stringvar = sqlite3_column_text(stmt, 0); res = atol((const char *)stringvar); - } - db_stmt_done(stmt); + db_stmt_done(stmt); + } else + res = defval; + return res; } diff --git a/wallet/db.h b/wallet/db.h index 2286fefb7..348707a84 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -37,12 +37,14 @@ struct db { struct db *db_setup(const tal_t *ctx, struct lightningd *ld, struct log *log); /** - * db_query - Prepare and execute a query, and return the result (or NULL) + * db_select - Prepare and execute a SELECT, and return the result + * + * A simpler version of db_select_prepare. */ sqlite3_stmt *PRINTF_FMT(3, 4) - db_query_(const char *location, struct db *db, const char *fmt, ...); -#define db_query(db, ...) \ - db_query_(__FILE__ ":" stringify(__LINE__), db, __VA_ARGS__) + db_select_(const char *location, struct db *db, const char *fmt, ...); +#define db_select(db, ...) \ + db_select_(__FILE__ ":" stringify(__LINE__), db, __VA_ARGS__) /** * db_begin_transaction - Begin a transaction @@ -77,6 +79,35 @@ void db_set_intvar(struct db *db, char *varname, s64 val); */ s64 db_get_intvar(struct db *db, char *varname, s64 defval); +/** + * db_select_prepare -- Prepare a DB select statement (read-only!) + * + * Tiny wrapper around `sqlite3_prepare_v2` that checks and sets + * errors like `db_query` and `db_exec` do. It calls fatal if + * the stmt is not valid. + * + * Call db_select_step() until it returns false (which will also consume + * the stmt). + * + * @db: Database to query/exec + * @query: The SELECT SQL statement to compile + */ +#define db_select_prepare(db, query) \ + db_select_prepare_(__FILE__ ":" stringify(__LINE__), db, query) +sqlite3_stmt *db_select_prepare_(const char *location, + struct db *db, const char *query); + +/** + * db_select_step -- iterate through db results. + * + * Returns false and frees stmt if we've we've reached end, otherwise + * it means sqlite3_step has returned SQLITE_ROW. + */ +#define db_select_step(db, stmt) \ + db_select_step_(__FILE__ ":" stringify(__LINE__), db, stmt) +bool db_select_step_(const char *location, + struct db *db, struct sqlite3_stmt *stmt); + /** * db_prepare -- Prepare a DB query/command * diff --git a/wallet/invoices.c b/wallet/invoices.c index c318c8141..2466e9745 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -185,19 +185,18 @@ static void trigger_expiration(struct invoices *invoices) /* Acquire all expired invoices and save them in a list */ list_head_init(&idlist); - stmt = db_prepare(invoices->db, - "SELECT id" - " FROM invoices" - " WHERE state = ?" - " AND expiry_time <= ?;"); + stmt = db_select_prepare(invoices->db, + "id" + " FROM invoices" + " WHERE state = ?" + " AND expiry_time <= ?;"); sqlite3_bind_int(stmt, 1, UNPAID); sqlite3_bind_int64(stmt, 2, now); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(invoices->db, stmt)) { idn = tal(tmpctx, struct invoice_id_node); list_add_tail(&idlist, &idn->list); idn->id = sqlite3_column_int64(stmt, 0); } - db_stmt_done(stmt); /* Expire all those invoices */ update_db_expirations(invoices, now); @@ -216,7 +215,7 @@ static void trigger_expiration(struct invoices *invoices) static void install_expiration_timer(struct invoices *invoices) { - int res; + bool res; sqlite3_stmt *stmt; struct timerel rel; struct timeabs expiry; @@ -225,13 +224,13 @@ static void install_expiration_timer(struct invoices *invoices) assert(!invoices->expiration_timer); /* Find unpaid invoice with nearest expiry time */ - stmt = db_prepare(invoices->db, - "SELECT MIN(expiry_time)" - " FROM invoices" - " WHERE state = ?;"); + stmt = db_select_prepare(invoices->db, + "MIN(expiry_time)" + " FROM invoices" + " WHERE state = ?;"); sqlite3_bind_int(stmt, 1, UNPAID); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + res = db_select_step(invoices->db, stmt); + assert(res); if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) { /* Nothing to install */ db_stmt_done(stmt); @@ -337,19 +336,17 @@ bool invoices_find_by_label(struct invoices *invoices, { sqlite3_stmt *stmt; - stmt = db_prepare(invoices->db, - "SELECT id" - " FROM invoices" - " WHERE label = ?;"); + stmt = db_select_prepare(invoices->db, + "id" + " FROM invoices" + " WHERE label = ?;"); sqlite3_bind_json_escaped(stmt, 1, label); - if (sqlite3_step(stmt) == SQLITE_ROW) { - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return true; - } else { - db_stmt_done(stmt); + if (!db_select_step(invoices->db, stmt)) return false; - } + + pinvoice->id = sqlite3_column_int64(stmt, 0); + db_stmt_done(stmt); + return true; } bool invoices_find_by_rhash(struct invoices *invoices, @@ -358,19 +355,17 @@ bool invoices_find_by_rhash(struct invoices *invoices, { sqlite3_stmt *stmt; - stmt = db_prepare(invoices->db, - "SELECT id" - " FROM invoices" - " WHERE payment_hash = ?;"); + stmt = db_select_prepare(invoices->db, + "id" + " FROM invoices" + " WHERE payment_hash = ?;"); sqlite3_bind_blob(stmt, 1, rhash, sizeof(*rhash), SQLITE_TRANSIENT); - if (sqlite3_step(stmt) == SQLITE_ROW) { - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return true; - } else { - db_stmt_done(stmt); + if (!db_select_step(invoices->db, stmt)) return false; - } + + pinvoice->id = sqlite3_column_int64(stmt, 0); + db_stmt_done(stmt); + return true; } bool invoices_find_unpaid(struct invoices *invoices, @@ -379,21 +374,19 @@ bool invoices_find_unpaid(struct invoices *invoices, { sqlite3_stmt *stmt; - stmt = db_prepare(invoices->db, - "SELECT id" - " FROM invoices" - " WHERE payment_hash = ?" - " AND state = ?;"); + stmt = db_select_prepare(invoices->db, + " id" + " FROM invoices" + " WHERE payment_hash = ?" + " AND state = ?;"); sqlite3_bind_blob(stmt, 1, rhash, sizeof(*rhash), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 2, UNPAID); - if (sqlite3_step(stmt) == SQLITE_ROW) { - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return true; - } else { - db_stmt_done(stmt); + if (!db_select_step(invoices->db, stmt)) return false; - } + + pinvoice->id = sqlite3_column_int64(stmt, 0); + db_stmt_done(stmt); + return true; } bool invoices_delete(struct invoices *invoices, @@ -468,24 +461,22 @@ bool invoices_iterate(struct invoices *invoices, struct invoice_iterator *it) { sqlite3_stmt *stmt; - int res; + if (!it->p) { - stmt = db_prepare(invoices->db, "SELECT " INVOICE_TBL_FIELDS - " FROM invoices;"); + stmt = db_select_prepare(invoices->db, + INVOICE_TBL_FIELDS + " FROM invoices;"); it->p = stmt; } else stmt = it->p; - res = sqlite3_step(stmt); - if (res == SQLITE_DONE) { - db_stmt_done(stmt); - it->p = NULL; - return false; - } else { - assert(res == SQLITE_ROW); + if (db_select_step(invoices->db, stmt)) return true; - } + + it->p = NULL; + return false; } + const struct invoice_details * invoices_iterator_deref(const tal_t *ctx, struct invoices *invoices UNUSED, const struct invoice_iterator *it) @@ -573,19 +564,17 @@ void invoices_waitany(const tal_t *ctx, { sqlite3_stmt *stmt; struct invoice invoice; - int res; /* Look for an already-paid invoice. */ - stmt = db_prepare(invoices->db, - "SELECT id" - " FROM invoices" - " WHERE pay_index NOT NULL" - " AND pay_index > ?" - " ORDER BY pay_index ASC LIMIT 1;"); + stmt = db_select_prepare(invoices->db, + "id" + " FROM invoices" + " WHERE pay_index NOT NULL" + " AND pay_index > ?" + " ORDER BY pay_index ASC LIMIT 1;"); sqlite3_bind_int64(stmt, 1, lastpay_index); - res = sqlite3_step(stmt); - if (res == SQLITE_ROW) { + if (db_select_step(invoices->db, stmt)) { invoice.id = sqlite3_column_int64(stmt, 0); db_stmt_done(stmt); @@ -593,8 +582,6 @@ void invoices_waitany(const tal_t *ctx, return; } - db_stmt_done(stmt); - /* None found. */ add_invoice_waiter(ctx, &invoices->waiters, true, 0, cb, cbarg); @@ -608,16 +595,17 @@ void invoices_waitone(const tal_t *ctx, void *cbarg) { sqlite3_stmt *stmt; - int res; + bool res; enum invoice_status state; - stmt = db_prepare(invoices->db, - "SELECT state" - " FROM invoices" - " WHERE id = ?;"); + stmt = db_select_prepare(invoices->db, + "state" + " FROM invoices" + " WHERE id = ?;"); sqlite3_bind_int64(stmt, 1, invoice.id); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + + res = db_select_step(invoices->db, stmt); + assert(res); state = sqlite3_column_int(stmt, 0); db_stmt_done(stmt); @@ -636,16 +624,16 @@ const struct invoice_details *invoices_get_details(const tal_t *ctx, struct invoice invoice) { sqlite3_stmt *stmt; - int result; + bool res; struct invoice_details *details; - stmt = db_prepare(invoices->db, - "SELECT " INVOICE_TBL_FIELDS - " FROM invoices" - " WHERE id = ?;"); + stmt = db_select_prepare(invoices->db, + INVOICE_TBL_FIELDS + " FROM invoices" + " WHERE id = ?;"); sqlite3_bind_int64(stmt, 1, invoice.id); - result = sqlite3_step(stmt); - assert(result == SQLITE_ROW); + res = db_select_step(invoices->db, stmt); + assert(res); details = wallet_stmt2invoice_details(ctx, stmt); db_stmt_done(stmt); diff --git a/wallet/wallet.c b/wallet/wallet.c index e6af28b2a..72452258e 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -34,15 +34,13 @@ static void outpointfilters_init(struct wallet *w) tal_free(utxos); w->utxoset_outpoints = outpointfilter_new(w); - stmt = db_prepare(w->db, "SELECT txid, outnum FROM utxoset WHERE spendheight is NULL"); + stmt = db_select_prepare(w->db, "txid, outnum FROM utxoset WHERE spendheight is NULL"); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { sqlite3_column_sha256_double(stmt, 0, &txid.shad); outnum = sqlite3_column_int(stmt, 1); outpointfilter_add(w->utxoset_outpoints, &txid, outnum); } - - db_stmt_done(stmt); } struct wallet *wallet_new(struct lightningd *ld, @@ -73,17 +71,16 @@ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, { sqlite3_stmt *stmt; - stmt = db_prepare(w->db, - "SELECT * from outputs WHERE prev_out_tx=? AND prev_out_index=?"); + stmt = db_select_prepare(w->db, + "* from outputs WHERE prev_out_tx=? AND prev_out_index=?"); sqlite3_bind_blob(stmt, 1, &utxo->txid, sizeof(utxo->txid), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 2, utxo->outnum); /* If we get a result, that means a clash. */ - if (sqlite3_step(stmt) == SQLITE_ROW) { + if (db_select_step(w->db, stmt)) { db_stmt_done(stmt); return false; } - db_stmt_done(stmt); stmt = db_prepare(w->db, "INSERT INTO outputs (" UTXO_FIELDS @@ -203,22 +200,19 @@ struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum ou sqlite3_stmt *stmt; if (state == output_state_any) - stmt = db_prepare(w->db, "SELECT "UTXO_FIELDS - " FROM outputs"); + stmt = db_select_prepare(w->db, UTXO_FIELDS " FROM outputs"); else { - stmt = db_prepare(w->db, "SELECT "UTXO_FIELDS - " FROM outputs WHERE status=?1"); + stmt = db_select_prepare(w->db, UTXO_FIELDS + " FROM outputs WHERE status=?1"); sqlite3_bind_int(stmt, 1, output_status_in_db(state)); } results = tal_arr(ctx, struct utxo*, 0); - for (i=0; sqlite3_step(stmt) == SQLITE_ROW; i++) { + for (i=0; db_select_step(w->db, stmt); i++) { struct utxo *u = wallet_stmt2output(results, stmt); tal_arr_expand(&results, u); } - db_stmt_done(stmt); - return results; } @@ -227,16 +221,15 @@ struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, struct wa struct utxo **results; int i; - sqlite3_stmt *stmt = db_prepare( - w->db, "SELECT " UTXO_FIELDS + sqlite3_stmt *stmt = db_select_prepare( + w->db, UTXO_FIELDS " FROM outputs WHERE channel_id IS NOT NULL and confirmation_height IS NULL"); results = tal_arr(ctx, struct utxo*, 0); - for (i=0; sqlite3_step(stmt) == SQLITE_ROW; i++) { + for (i=0; db_select_step(w->db, stmt); i++) { struct utxo *u = wallet_stmt2output(results, stmt); tal_arr_expand(&results, u); } - db_stmt_done(stmt); return results; } @@ -526,36 +519,30 @@ bool wallet_shachain_add_hash(struct wallet *wallet, bool wallet_shachain_load(struct wallet *wallet, u64 id, struct wallet_shachain *chain) { - int err; sqlite3_stmt *stmt; chain->id = id; shachain_init(&chain->chain); /* Load shachain metadata */ - stmt = db_prepare(wallet->db, "SELECT min_index, num_valid FROM shachains WHERE id=?"); + stmt = db_select_prepare(wallet->db, "min_index, num_valid FROM shachains WHERE id=?"); sqlite3_bind_int64(stmt, 1, id); - err = sqlite3_step(stmt); - if (err != SQLITE_ROW) { - db_stmt_done(stmt); + if (!db_select_step(wallet->db, stmt)) return false; - } chain->chain.min_index = sqlite3_column_int64(stmt, 0); chain->chain.num_valid = sqlite3_column_int64(stmt, 1); db_stmt_done(stmt); /* Load shachain known entries */ - stmt = db_prepare(wallet->db, "SELECT idx, hash, pos FROM shachain_known WHERE shachain_id=?"); + stmt = db_select_prepare(wallet->db, "idx, hash, pos FROM shachain_known WHERE shachain_id=?"); sqlite3_bind_int64(stmt, 1, id); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(wallet->db, stmt)) { int pos = sqlite3_column_int(stmt, 2); chain->chain.known[pos].index = sqlite3_column_int64(stmt, 0); memcpy(&chain->chain.known[pos].hash, sqlite3_column_blob(stmt, 1), sqlite3_column_bytes(stmt, 1)); } - - db_stmt_done(stmt); return true; } @@ -567,13 +554,12 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) struct wireaddr_internal addr; sqlite3_stmt *stmt = - db_query(w->db, - "SELECT id, node_id, address FROM peers WHERE id=%"PRIu64";", dbid); + db_select(w->db, + "id, node_id, address FROM peers WHERE id=%"PRIu64";", dbid); - if (!stmt || sqlite3_step(stmt) != SQLITE_ROW) { - db_stmt_done(stmt); + if (!db_select_step(w->db, stmt)) return NULL; - } + if (!sqlite3_column_pubkey(stmt, 1, &id)) { db_stmt_done(stmt); return NULL; @@ -594,17 +580,16 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) static secp256k1_ecdsa_signature * wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid) { - sqlite3_stmt *stmt = db_prepare(w->db, "SELECT signature FROM htlc_sigs WHERE channelid = ?"); + sqlite3_stmt *stmt = db_select_prepare(w->db, "signature FROM htlc_sigs WHERE channelid = ?"); secp256k1_ecdsa_signature *htlc_sigs = tal_arr(ctx, secp256k1_ecdsa_signature, 0); sqlite3_bind_int64(stmt, 1, channelid); - while (stmt && sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { secp256k1_ecdsa_signature sig; sqlite3_column_signature(stmt, 0, &sig); tal_arr_expand(&htlc_sigs, sig); } - db_stmt_done(stmt); log_debug(w->log, "Loaded %zu HTLC signatures from DB", tal_count(htlc_sigs)); return htlc_sigs; @@ -787,16 +772,16 @@ bool wallet_channels_load_active(const tal_t *ctx, struct wallet *w) sqlite3_stmt *stmt; /* We load all channels */ - stmt = db_query(w->db, "SELECT %s FROM channels;", - channel_fields); + stmt = db_select(w->db, "%s FROM channels;", channel_fields); w->max_channel_dbid = 0; int count = 0; - while (ok && stmt && sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { struct channel *c = wallet_stmt2channel(ctx, w, stmt); if (!c) { ok = false; + db_stmt_done(stmt); break; } if (c->dbid > w->max_channel_dbid) @@ -804,7 +789,6 @@ bool wallet_channels_load_active(const tal_t *ctx, struct wallet *w) count++; } log_debug(w->log, "Loaded %d channels from DB", count); - db_stmt_done(stmt); return ok; } @@ -857,11 +841,11 @@ void wallet_channel_stats_load(struct wallet *w, { sqlite3_stmt *stmt; int res; - stmt = db_prepare(w->db, - "SELECT in_payments_offered, in_payments_fulfilled" - " , in_msatoshi_offered, in_msatoshi_fulfilled" - " , out_payments_offered, out_payments_fulfilled" - " , out_msatoshi_offered, out_msatoshi_fulfilled" + stmt = db_select_prepare(w->db, + " in_payments_offered, in_payments_fulfilled" + ", in_msatoshi_offered, in_msatoshi_fulfilled" + ", out_payments_offered, out_payments_fulfilled" + ", out_msatoshi_offered, out_msatoshi_fulfilled" " FROM channels" " WHERE id = ?"); sqlite3_bind_int64(stmt, 1, id); @@ -885,17 +869,19 @@ void wallet_channel_stats_load(struct wallet *w, void wallet_blocks_heights(struct wallet *w, u32 def, u32 *min, u32 *max) { assert(min != NULL && max != NULL); - sqlite3_stmt *stmt = db_prepare(w->db, "SELECT MIN(height), MAX(height) FROM blocks;"); + sqlite3_stmt *stmt = db_select_prepare(w->db, "MIN(height), MAX(height) FROM blocks;"); + + *min = def; + *max = def; /* If we ever processed a block we'll get the latest block in the chain */ - if (sqlite3_step(stmt) == SQLITE_ROW && sqlite3_column_type(stmt, 0) != SQLITE_NULL) { - *min = sqlite3_column_int(stmt, 0); - *max = sqlite3_column_int(stmt, 1); - } else { - *min = def; - *max = def; + if (db_select_step(w->db, stmt)) { + if (sqlite3_column_type(stmt, 0) != SQLITE_NULL) { + *min = sqlite3_column_int(stmt, 0); + *max = sqlite3_column_int(stmt, 1); + } + db_stmt_done(stmt); } - db_stmt_done(stmt); } static void wallet_channel_config_insert(struct wallet *w, @@ -940,14 +926,13 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, bool ok = true; int col = 1; const char *query = - "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " + "id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " "max_accepted_htlcs FROM channel_configs WHERE id=%" PRIu64 ";"; - sqlite3_stmt *stmt = db_query(w->db, query, id); - if (!stmt || sqlite3_step(stmt) != SQLITE_ROW) { - db_stmt_done(stmt); + sqlite3_stmt *stmt = db_select(w->db, query, id); + if (!db_select_step(w->db, stmt)) return false; - } + cc->id = id; cc->dust_limit = sqlite3_column_amount_sat(stmt, col++); cc->max_htlc_value_in_flight = sqlite3_column_amount_msat(stmt, col++); @@ -1140,11 +1125,11 @@ void wallet_peer_delete(struct wallet *w, u64 peer_dbid) sqlite3_stmt *stmt; /* Must not have any channels still using this peer */ - stmt = db_query(w->db, - "SELECT * FROM channels WHERE peer_id = %"PRIu64, - peer_dbid); - assert(sqlite3_step(stmt) == SQLITE_DONE); - db_stmt_done(stmt); + stmt = db_select(w->db, + "* FROM channels WHERE peer_id = %"PRIu64, + peer_dbid); + if (db_select_step(w->db, stmt)) + fatal("We have channels using peer %"PRIu64, peer_dbid); stmt = db_prepare(w->db, "DELETE FROM peers WHERE id=?"); sqlite3_bind_int64(stmt, 1, peer_dbid); @@ -1498,18 +1483,13 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, int incount = 0, outcount = 0; log_debug(wallet->log, "Loading HTLCs for channel %"PRIu64, chan->dbid); - sqlite3_stmt *stmt = db_query( + sqlite3_stmt *stmt = db_select( wallet->db, - "SELECT " HTLC_FIELDS " FROM channel_htlcs WHERE " + HTLC_FIELDS " FROM channel_htlcs WHERE " "direction=%d AND channel_id=%" PRIu64 " AND hstate != %d", DIRECTION_INCOMING, chan->dbid, SENT_REMOVE_ACK_REVOCATION); - if (!stmt) { - log_broken(wallet->log, "Could not select htlc_ins"); - return false; - } - - while (ok && sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(wallet->db, stmt)) { struct htlc_in *in = tal(chan, struct htlc_in); ok &= wallet_stmt2htlc_in(chan, stmt, in); connect_htlc_in(htlcs_in, in); @@ -1517,20 +1497,14 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, ok &= htlc_in_check(in, NULL) != NULL; incount++; } - db_stmt_done(stmt); - stmt = db_query( + stmt = db_select( wallet->db, - "SELECT " HTLC_FIELDS " FROM channel_htlcs WHERE " + HTLC_FIELDS " FROM channel_htlcs WHERE " "direction=%d AND channel_id=%" PRIu64 " AND hstate != %d", DIRECTION_OUTGOING, chan->dbid, RCVD_REMOVE_ACK_REVOCATION); - if (!stmt) { - log_broken(wallet->log, "Could not select htlc_outs"); - return false; - } - - while (ok && sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(wallet->db, stmt)) { struct htlc_out *out = tal(chan, struct htlc_out); ok &= wallet_stmt2htlc_out(chan, stmt, out); connect_htlc_out(htlcs_out, out); @@ -1538,7 +1512,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, * dependencies in yet */ outcount++; } - db_stmt_done(stmt); + log_debug(wallet->log, "Restored %d incoming and %d outgoing HTLCS", incount, outcount); return ok; @@ -1633,15 +1607,15 @@ struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet, { struct htlc_stub *stubs; struct sha256 payment_hash; - sqlite3_stmt *stmt = db_prepare(wallet->db, - "SELECT channel_id, direction, cltv_expiry, channel_htlc_id, payment_hash " + sqlite3_stmt *stmt = db_select_prepare(wallet->db, + "channel_id, direction, cltv_expiry, channel_htlc_id, payment_hash " "FROM channel_htlcs WHERE channel_id = ?;"); sqlite3_bind_int64(stmt, 1, chan->dbid); stubs = tal_arr(ctx, struct htlc_stub, 0); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(wallet->db, stmt)) { struct htlc_stub stub; assert(sqlite3_column_int64(stmt, 0) == chan->dbid); @@ -1655,7 +1629,6 @@ struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet, ripemd160(&stub.ripemd, payment_hash.u.u8, sizeof(payment_hash.u)); tal_arr_expand(&stubs, stub); } - db_stmt_done(stmt); return stubs; } @@ -1715,13 +1688,13 @@ void wallet_payment_store(struct wallet *wallet, /* Double-check that it is indeed stored to disk * (catch bug, where we call this on a payment_hash * we never paid to) */ - int res; - stmt = db_prepare(wallet->db, - "SELECT status FROM payments" - " WHERE payment_hash=?;"); + bool res; + stmt = db_select_prepare(wallet->db, + "status FROM payments" + " WHERE payment_hash=?;"); sqlite3_bind_sha256(stmt, 1, payment_hash); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + res = db_select_step(wallet->db, stmt); + assert(res); db_stmt_done(stmt); #endif return; @@ -1859,14 +1832,14 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, if (payment) return payment; - stmt = db_prepare(wallet->db, "SELECT " PAYMENT_FIELDS " FROM payments " + stmt = db_select_prepare(wallet->db, PAYMENT_FIELDS " FROM payments " "WHERE payment_hash = ?"); sqlite3_bind_sha256(stmt, 1, payment_hash); - if (sqlite3_step(stmt) == SQLITE_ROW) { + if (db_select_step(wallet->db, stmt)) { payment = wallet_stmt2payment(ctx, stmt); + db_stmt_done(stmt); } - db_stmt_done(stmt); return payment; } @@ -1930,20 +1903,19 @@ void wallet_payment_get_failinfo(const tal_t *ctx, int *faildirection) { sqlite3_stmt *stmt; - int res; bool resb; size_t len; - stmt = db_prepare(wallet->db, - "SELECT failonionreply, faildestperm" - " , failindex, failcode" - " , failnode, failchannel" - " , failupdate, faildetail, faildirection" - " FROM payments" - " WHERE payment_hash=?;"); + stmt = db_select_prepare(wallet->db, + "failonionreply, faildestperm" + ", failindex, failcode" + ", failnode, failchannel" + ", failupdate, faildetail, faildirection" + " FROM payments" + " WHERE payment_hash=?;"); sqlite3_bind_sha256(stmt, 1, payment_hash); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + resb = db_select_step(wallet->db, stmt); + assert(resb); if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) *failonionreply = NULL; else { @@ -2061,22 +2033,20 @@ wallet_payment_list(const tal_t *ctx, payments = tal_arr(ctx, const struct wallet_payment *, 0); if (payment_hash) { - stmt = db_prepare(wallet->db, - "SELECT " PAYMENT_FIELDS " FROM payments " - "WHERE payment_hash = ?;"); + stmt = db_select_prepare(wallet->db, + PAYMENT_FIELDS " FROM payments " + "WHERE payment_hash = ?;"); sqlite3_bind_sha256(stmt, 1, payment_hash); } else { - stmt = db_prepare(wallet->db, - "SELECT " PAYMENT_FIELDS " FROM payments;"); + stmt = db_select_prepare(wallet->db, + PAYMENT_FIELDS " FROM payments;"); } - for (i = 0; sqlite3_step(stmt) == SQLITE_ROW; i++) { + for (i = 0; db_select_step(wallet->db, stmt); i++) { tal_resize(&payments, i+1); payments[i] = wallet_stmt2payment(payments, stmt); } - db_stmt_done(stmt); - /* Now attach payments not yet in db. */ list_for_each(&wallet->unstored_payments, p, list) { if (payment_hash && !sha256_eq(&p->payment_hash, payment_hash)) @@ -2109,11 +2079,11 @@ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, bool wallet_network_check(struct wallet *w, const struct chainparams *chainparams) { - sqlite3_stmt *stmt = db_query(w->db, - "SELECT val FROM vars WHERE name='genesis_hash'"); + sqlite3_stmt *stmt = db_select(w->db, + "val FROM vars WHERE name='genesis_hash'"); struct bitcoin_blkid chainhash; - if (stmt && sqlite3_step(stmt) == SQLITE_ROW) { + if (db_select_step(w->db, stmt)) { sqlite3_column_sha256_double(stmt, 0, &chainhash.shad); db_stmt_done(stmt); if (!bitcoin_blkid_eq(&chainhash, @@ -2133,7 +2103,6 @@ bool wallet_network_check(struct wallet *w, } else { /* Still a pristine wallet, claim it for the chain * that we are running */ - db_stmt_done(stmt); stmt = db_prepare(w->db, "INSERT INTO vars (name, val) VALUES ('genesis_hash', ?);"); sqlite3_bind_sha256_double(stmt, 1, &chainparams->genesis_blockhash.shad); db_exec_prepared(w->db, stmt); @@ -2149,14 +2118,13 @@ static void wallet_utxoset_prune(struct wallet *w, const u32 blockheight) sqlite3_stmt *stmt; struct bitcoin_txid txid; - stmt = db_prepare(w->db, "SELECT txid, outnum FROM utxoset WHERE spendheight < ?"); + stmt = db_select_prepare(w->db, "txid, outnum FROM utxoset WHERE spendheight < ?"); sqlite3_bind_int(stmt, 1, blockheight - UTXO_PRUNE_DEPTH); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { sqlite3_column_sha256_double(stmt, 0, &txid.shad); outpointfilter_remove(w->utxoset_outpoints, &txid, sqlite3_column_int(stmt, 1)); } - db_stmt_done(stmt); stmt = db_prepare(w->db, "DELETE FROM utxoset WHERE spendheight < ?"); sqlite3_bind_int(stmt, 1, blockheight - UTXO_PRUNE_DEPTH); @@ -2189,10 +2157,9 @@ void wallet_block_remove(struct wallet *w, struct block *b) sqlite3_bind_sha256_double(stmt, 1, &b->blkid.shad); db_exec_prepared(w->db, stmt); - stmt = db_prepare(w->db, "SELECT * FROM blocks WHERE height >= ?;"); + stmt = db_select_prepare(w->db, "* FROM blocks WHERE height >= ?;"); sqlite3_bind_int(stmt, 1, b->height); - assert(sqlite3_step(stmt) == SQLITE_DONE); - db_stmt_done(stmt); + assert(!db_select_step(w->db, stmt)); } void wallet_blocks_rollback(struct wallet *w, u32 height) @@ -2209,7 +2176,7 @@ wallet_outpoint_spend(struct wallet *w, const tal_t *ctx, const u32 blockheight, { struct short_channel_id *scid; sqlite3_stmt *stmt; - int res; + bool res; if (outpointfilter_matches(w->owned_outpoints, txid, outnum)) { stmt = db_prepare(w->db, "UPDATE outputs " @@ -2242,15 +2209,15 @@ wallet_outpoint_spend(struct wallet *w, const tal_t *ctx, const u32 blockheight, } /* Now look for the outpoint's short_channel_id */ - stmt = db_prepare(w->db, - "SELECT blockheight, txindex " - "FROM utxoset " - "WHERE txid = ? AND outnum = ?"); + stmt = db_select_prepare(w->db, + "blockheight, txindex " + "FROM utxoset " + "WHERE txid = ? AND outnum = ?"); sqlite3_bind_sha256_double(stmt, 1, &txid->shad); sqlite3_bind_int(stmt, 2, outnum); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + res = db_select_step(w->db, stmt); + assert(res); scid = tal(ctx, struct short_channel_id); if (!mk_short_channel_id(scid, sqlite3_column_int(stmt, 0), @@ -2299,7 +2266,7 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, { sqlite3_stmt *stmt; struct outpoint *op; - stmt = db_prepare(w->db, "SELECT" + stmt = db_select_prepare(w->db, " txid," " spendheight," " scriptpubkey," @@ -2314,10 +2281,8 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, sqlite3_bind_int(stmt, 3, short_channel_id_outnum(scid)); - if (sqlite3_step(stmt) != SQLITE_ROW) { - db_stmt_done(stmt); + if (!db_select_step(w->db, stmt)) return NULL; - } op = tal(ctx, struct outpoint); op->blockheight = short_channel_id_blocknum(scid); @@ -2337,15 +2302,11 @@ void wallet_transaction_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 blockheight, const u32 txindex) { struct bitcoin_txid txid; - sqlite3_stmt *stmt = db_prepare(w->db, "SELECT blockheight FROM transactions WHERE id=?"); - bool known; + sqlite3_stmt *stmt = db_select_prepare(w->db, "blockheight FROM transactions WHERE id=?"); bitcoin_txid(tx, &txid); sqlite3_bind_sha256(stmt, 1, &txid.shad.sha); - known = sqlite3_step(stmt) == SQLITE_ROW; - db_stmt_done(stmt); - - if (!known) { + if (!db_select_step(w->db, stmt)) { /* This transaction is still unknown, insert */ stmt = db_prepare(w->db, "INSERT INTO transactions (" @@ -2363,30 +2324,32 @@ void wallet_transaction_add(struct wallet *w, const struct bitcoin_tx *tx, } sqlite3_bind_tx(stmt, 4, tx); db_exec_prepared(w->db, stmt); - } else if (blockheight){ - /* We know about the transaction, update */ - stmt = db_prepare(w->db, - "UPDATE transactions " - "SET blockheight = ?, txindex = ? " - "WHERE id = ?"); - sqlite3_bind_int(stmt, 1, blockheight); - sqlite3_bind_int(stmt, 2, txindex); - sqlite3_bind_sha256(stmt, 3, &txid.shad.sha); - db_exec_prepared(w->db, stmt); + } else { + db_stmt_done(stmt); + + if (blockheight) { + /* We know about the transaction, update */ + stmt = db_prepare(w->db, + "UPDATE transactions " + "SET blockheight = ?, txindex = ? " + "WHERE id = ?"); + sqlite3_bind_int(stmt, 1, blockheight); + sqlite3_bind_int(stmt, 2, txindex); + sqlite3_bind_sha256(stmt, 3, &txid.shad.sha); + db_exec_prepared(w->db, stmt); + } } } u32 wallet_transaction_height(struct wallet *w, const struct bitcoin_txid *txid) { u32 blockheight; - sqlite3_stmt *stmt = db_prepare( - w->db, "SELECT blockheight FROM transactions WHERE id=?"); + sqlite3_stmt *stmt = db_select_prepare( + w->db, "blockheight FROM transactions WHERE id=?"); sqlite3_bind_sha256(stmt, 1, &txid->shad.sha); - if (sqlite3_step(stmt) != SQLITE_ROW) { - db_stmt_done(stmt); + if (!db_select_step(w->db, stmt)) return 0; - } blockheight = sqlite3_column_int(stmt, 0); db_stmt_done(stmt); @@ -2399,26 +2362,22 @@ struct txlocator *wallet_transaction_locate(const tal_t *ctx, struct wallet *w, struct txlocator *loc; sqlite3_stmt *stmt; - stmt = db_prepare( - w->db, "SELECT blockheight, txindex FROM transactions WHERE id=?"); + stmt = db_select_prepare( + w->db, "blockheight, txindex FROM transactions WHERE id=?"); sqlite3_bind_sha256(stmt, 1, &txid->shad.sha); - if (sqlite3_step(stmt) != SQLITE_ROW) { - goto fail; + if (!db_select_step(w->db, stmt)) + return NULL; - } if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) - goto fail; - - loc = tal(ctx, struct txlocator); - loc->blkheight = sqlite3_column_int(stmt, 0); - loc->index = sqlite3_column_int(stmt, 1); + loc = NULL; + else { + loc = tal(ctx, struct txlocator); + loc->blkheight = sqlite3_column_int(stmt, 0); + loc->index = sqlite3_column_int(stmt, 1); + } db_stmt_done(stmt); return loc; - -fail: - db_stmt_done(stmt); - return NULL; } struct bitcoin_txid *wallet_transactions_by_height(const tal_t *ctx, @@ -2428,16 +2387,15 @@ struct bitcoin_txid *wallet_transactions_by_height(const tal_t *ctx, sqlite3_stmt *stmt; struct bitcoin_txid *txids = tal_arr(ctx, struct bitcoin_txid, 0); int count = 0; - stmt = db_prepare( - w->db, "SELECT id FROM transactions WHERE blockheight=?"); + stmt = db_select_prepare( + w->db, "id FROM transactions WHERE blockheight=?"); sqlite3_bind_int(stmt, 1, blockheight); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { count++; tal_resize(&txids, count); sqlite3_column_sha256(stmt, 0, &txids[count-1].shad.sha); } - db_stmt_done(stmt); return txids; } @@ -2469,15 +2427,14 @@ u32 *wallet_onchaind_channels(struct wallet *w, sqlite3_stmt *stmt; size_t count = 0; u32 *channel_ids = tal_arr(ctx, u32, 0); - stmt = db_prepare(w->db, "SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;"); + stmt = db_select_prepare(w->db, "DISTINCT(channel_id) FROM channeltxs WHERE type = ?;"); sqlite3_bind_int(stmt, 1, WIRE_ONCHAIN_INIT); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { count++; tal_resize(&channel_ids, count); channel_ids[count-1] = sqlite3_column_int64(stmt, 0); } - db_stmt_done(stmt); return channel_ids; } @@ -2488,8 +2445,7 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, sqlite3_stmt *stmt; size_t count = 0; struct channeltx *res = tal_arr(ctx, struct channeltx, 0); - stmt = db_prepare(w->db, - "SELECT" + stmt = db_select_prepare(w->db, " c.type" ", c.blockheight" ", t.rawtx" @@ -2502,7 +2458,7 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, "ORDER BY c.id ASC;"); sqlite3_bind_int(stmt, 1, channel_id); - while (sqlite3_step(stmt) == SQLITE_ROW) { + while (db_select_step(w->db, stmt)) { count++; tal_resize(&res, count); @@ -2514,7 +2470,6 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, res[count-1].depth = sqlite3_column_int(stmt, 4); sqlite3_column_sha256(stmt, 5, &res[count-1].txid.shad.sha); } - db_stmt_done(stmt); return res; } @@ -2547,18 +2502,17 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) { sqlite3_stmt *stmt; struct amount_msat total; - int res; + bool res; - stmt = db_prepare(w->db, - "SELECT" + stmt = db_select_prepare(w->db, " SUM(in_msatoshi - out_msatoshi) " "FROM forwarded_payments " "WHERE state = ?;"); sqlite3_bind_int(stmt, 1, wallet_forward_status_in_db(FORWARD_SETTLED)); - res = sqlite3_step(stmt); - assert(res == SQLITE_ROW); + res = db_select_step(w->db, stmt); + assert(res); total = sqlite3_column_amount_msat(stmt, 0); db_stmt_done(stmt); @@ -2572,8 +2526,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, struct forwarding *results = tal_arr(ctx, struct forwarding, 0); size_t count = 0; sqlite3_stmt *stmt; - stmt = db_prepare(w->db, - "SELECT" + stmt = db_select_prepare(w->db, " f.state" ", in_msatoshi" ", out_msatoshi" @@ -2583,7 +2536,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, "FROM forwarded_payments f " "LEFT JOIN channel_htlcs hin ON (f.in_htlc_id == hin.id)"); - for (count=0; sqlite3_step(stmt) == SQLITE_ROW; count++) { + for (count=0; db_select_step(w->db, stmt); count++) { tal_resize(&results, count+1); struct forwarding *cur = &results[count]; cur->status = sqlite3_column_int(stmt, 0); @@ -2609,7 +2562,5 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, cur->channel_out.u64 = sqlite3_column_int64(stmt, 5); } - db_stmt_done(stmt); - return results; }