From 50a1af890832f21aaffcf1ed080c49f07f38efb3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 25 Jul 2019 22:47:07 +0200 Subject: [PATCH] db: Implement the sqlite3 driver This is the DB-specific counterpart to the previous commit. Signed-off-by: Christian Decker --- wallet/db_sqlite3.c | 136 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index fd5e98090..645c5e9ed 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -1,11 +1,147 @@ #include #include "gen_db_sqlite3.c" +#include +#include +#include +#include + #if HAVE_SQLITE3 +static const char *db_sqlite3_expand(struct db_stmt *stmt) +{ +#if HAVE_SQLITE3_EXPANDED_SQL + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + const char *sql; + char *expanded_sql; + expanded_sql = sqlite3_expanded_sql(s); + sql = tal_strdup(stmt, expanded_sql); + sqlite3_free(expanded_sql); + return sql; +#else + return NULL; +#endif +} + +static const char *db_sqlite3_fmt_error(struct db_stmt *stmt) +{ + return tal_fmt(stmt, "%s: %s: %s", stmt->location, stmt->query->query, + sqlite3_errmsg(stmt->db->conn)); +} + +static bool db_sqlite3_query(struct db_stmt *stmt) +{ + sqlite3_stmt *s; + sqlite3 *conn = (sqlite3*)stmt->db->conn; + int err; + + err = sqlite3_prepare_v2(conn, stmt->query->query, -1, &s, NULL); + + for (size_t i=0; iquery->placeholders; i++) { + struct db_binding *b = &stmt->bindings[i]; + + /* sqlite3 uses printf-like offsets, we don't... */ + int pos = i+1; + switch (b->type) { + case DB_BINDING_UNINITIALIZED: + db_fatal("DB binding not initialized: position=%zu, " + "query=\"%s\n", + i, stmt->query->query); + case DB_BINDING_UINT64: + sqlite3_bind_int64(s, pos, b->v.u64); + break; + case DB_BINDING_INT: + sqlite3_bind_int(s, pos, b->v.i); + break; + case DB_BINDING_BLOB: + sqlite3_bind_blob(s, pos, b->v.blob, b->len, + SQLITE_TRANSIENT); + break; + case DB_BINDING_TEXT: + sqlite3_bind_text(s, pos, b->v.text, b->len, + SQLITE_TRANSIENT); + break; + case DB_BINDING_NULL: + sqlite3_bind_null(s, pos); + break; + } + } + + if (err != SQLITE_OK) { + tal_free(stmt->error); + stmt->error = db_sqlite3_fmt_error(stmt); + return false; + } + + stmt->inner_stmt = s; + return true; +} + +static bool db_sqlite3_exec(struct db_stmt *stmt) +{ + int err; + if (!db_sqlite3_query(stmt)) { + /* If the prepare step caused an error we hand it up. */ + return false; + } + + err = sqlite3_step(stmt->inner_stmt); + if (err != SQLITE_DONE) { + tal_free(stmt->error); + stmt->error = db_sqlite3_fmt_error(stmt); + return false; + } + + return true; +} + +static bool db_sqlite3_step(struct db_stmt *stmt) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_step(s) == SQLITE_ROW; +} + +static bool db_sqlite3_begin_tx(struct db *db) +{ + int err; + char *errmsg; + err = sqlite3_exec(db->conn, "BEGIN TRANSACTION;", NULL, NULL, &errmsg); + if (err != SQLITE_OK) { + db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); + return false; + } + return true; +} + +static bool db_sqlite3_commit_tx(struct db *db) +{ + int err; + char *errmsg; + err = sqlite3_exec(db->conn, "COMMIT;", NULL, NULL, &errmsg); + if (err != SQLITE_OK) { + db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); + return false; + } + return true; +} + +static void db_sqlite3_stmt_free(struct db_stmt *stmt) +{ + if (stmt->inner_stmt) + sqlite3_finalize(stmt->inner_stmt); + tal_free(stmt); +} + struct db_config db_sqlite3_config = { .name = "sqlite3", .queries = db_sqlite3_queries, .num_queries = DB_SQLITE3_QUERY_COUNT, + .expand_fn = &db_sqlite3_expand, + .exec_fn = &db_sqlite3_exec, + .query_fn = &db_sqlite3_query, + .step_fn = &db_sqlite3_step, + .begin_tx_fn = &db_sqlite3_begin_tx, + .commit_tx_fn = &db_sqlite3_commit_tx, + .stmt_free_fn = &db_sqlite3_stmt_free, }; AUTODATA(db_backends, &db_sqlite3_config);