From 048680d0f29ab032bc3cfb3cee4ec4f1467d2e04 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 4 Oct 2017 14:24:12 +0200 Subject: [PATCH] db: Add db_prepare and db_exec_prepared to expose native binding This makes executing a query/command a two step process, but allows us to use the native binding and avoid having to build queries as SQL strings. Two major advantages are that we are no longer vulnerable to SQL injections and that we do not have to hex-encode binary fields like private keys, hashes, and routing onions, halving the storage requirements for those. Signed-off-by: Christian Decker --- wallet/db.c | 32 ++++++++++++++++++++++++++++++++ wallet/db.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/wallet/db.c b/wallet/db.c index 8fb2adef6..efb850704 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -132,6 +132,38 @@ static void db_clear_error(struct db *db) db->err = tal_free(db->err); } +sqlite3_stmt *db_prepare_(const char *caller, struct db *db, const char *query) +{ + int err; + sqlite3_stmt *stmt; + if (db->in_transaction && db->err) + return NULL; + + db_clear_error(db); + err = sqlite3_prepare_v2(db->sql, query, -1, &stmt, NULL); + + if (err != SQLITE_OK) { + db->err = tal_fmt(db, "%s: %s: %s", caller, query, + sqlite3_errmsg(db->sql)); + } + return stmt; +} + +bool db_exec_prepared_(const char *caller, struct db *db, sqlite3_stmt *stmt) +{ + if (db->in_transaction && db->err) + return false; + db_clear_error(db); + + if (sqlite3_step(stmt) != SQLITE_DONE) { + db->err = + tal_fmt(db, "%s: %s", caller, sqlite3_errmsg(db->sql)); + return false; + } else { + return true; + } +} + bool PRINTF_FMT(3, 4) db_exec(const char *caller, struct db *db, const char *fmt, ...) { diff --git a/wallet/db.h b/wallet/db.h index c7e5bd3f7..4217f87bf 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -80,4 +80,34 @@ s64 db_get_intvar(struct db *db, char *varname, s64 defval); bool sqlite3_column_hexval(sqlite3_stmt *s, int col, void *dest, size_t destlen); +/** + * db_prepare -- Prepare a DB query/command + * + * Tiny wrapper around `sqlite3_prepare_v2` that checks and sets + * errors like `db_query` and `db_exec` do. It returns a statement + * `stmt` if the given query/command was successfully compiled into a + * statement, `NULL` otherwise. On failure `db->err` will be set with + * the human readable error. + * + * @db: Database to query/exec + * @query: The SQL statement to compile + */ +#define db_prepare(db,query) db_prepare_(__func__,db,query) +sqlite3_stmt *db_prepare_(const char *caller, struct db *db, const char *query); + +/** + * db_exec_prepared -- Execute a prepared statement + * + * After preparing a statement using `db_prepare`, and after binding + * all non-null variables using the `sqlite3_bind_*` functions, it can + * be executed with this function. It is a small, transaction-aware, + * wrapper around `sqlite3_step`, that also sets `db->err` if the + * execution fails. + * + * @db: The database to execute on + * @stmt: The prepared statement to execute + */ +#define db_exec_prepared(db,stmt) db_exec_prepared_(__func__,db,stmt) +bool db_exec_prepared_(const char *caller, struct db *db, sqlite3_stmt *stmt); + #endif /* WALLET_DB_H */