From d0027b1036ee0e1111a3c10bd9f7233892d0582d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 3 Aug 2019 16:34:03 +0200 Subject: [PATCH] db: Implement basic query capabilities This is the first step towards being able to extract information from query rows. Only the most basic types are exposed, the others will be built on top of these primitives. Signed-off-by: Christian Decker --- wallet/db.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ wallet/db.h | 8 ++++++++ wallet/db_common.h | 11 +++++++++++ wallet/db_sqlite3.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/wallet/db.c b/wallet/db.c index d3b3728a2..4e0346b50 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -604,6 +604,41 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, #define db_prepare_v2(db,query) \ db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) +bool db_step(struct db_stmt *stmt) +{ + assert(stmt->executed); + return stmt->db->config->step_fn(stmt); +} + +u64 db_column_u64(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_u64_fn(stmt, col); +} + +int db_column_int(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_int_fn(stmt, col); +} + +size_t db_column_bytes(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_bytes_fn(stmt, col); +} + +int db_column_is_null(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_is_null_fn(stmt, col); +} + +const void *db_column_blob(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_blob_fn(stmt, col); +} + +const unsigned char *db_column_text(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_blob_fn(stmt, col); +} bool db_select_step_(const char *location, struct db *db, struct sqlite3_stmt *stmt) { @@ -1368,3 +1403,14 @@ bool db_exec_prepared_v2(struct db_stmt *stmt TAKES) return ret; } + +bool db_query_prepared(struct db_stmt *stmt) +{ + /* Make sure we don't accidentally execute a modifying query using a + * read-only path. */ + bool ret; + assert(stmt->query->readonly); + ret = stmt->db->config->query_fn(stmt); + stmt->executed = true; + return ret; +} diff --git a/wallet/db.h b/wallet/db.h index bcd29b248..8b388ee59 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -249,6 +249,14 @@ void db_bind_blob(struct db_stmt *stmt, int pos, u8 *val, size_t len); void db_bind_text(struct db_stmt *stmt, int pos, const char *val); bool db_exec_prepared_v2(struct db_stmt *stmt TAKES); +bool db_step(struct db_stmt *stmt); +u64 db_column_u64(struct db_stmt *stmt, int col); +int db_column_int(struct db_stmt *stmt, int col); +size_t db_column_bytes(struct db_stmt *stmt, int col); +int db_column_is_null(struct db_stmt *stmt, int col); +const void* db_column_blob(struct db_stmt *stmt, int col); +const unsigned char *db_column_text(struct db_stmt *stmt, int col); +bool db_query_prepared(struct db_stmt *stmt); struct db_stmt *db_prepare_v2_(const char *location, struct db *db, const char *query_id); diff --git a/wallet/db_common.h b/wallet/db_common.h index e7e176518..5947a7ccc 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -111,6 +111,17 @@ struct db_config { * destructor of `struct db_stmt`, before clearing the db_stmt * itself. */ void (*stmt_free_fn)(struct db_stmt *db_stmt); + + /* Column access in a row. Only covers the primitives, others need to + * use these internally to translate (hence the non-allocating + * column_{text,blob}_fn since most other types want in place + * assignment. */ + bool (*column_is_null_fn)(struct db_stmt *stmt, int col); + u64 (*column_u64_fn)(struct db_stmt *stmt, int col); + size_t (*column_bytes_fn)(struct db_stmt *stmt, int col); + const void *(*column_blob_fn)(struct db_stmt *stmt, int col); + const unsigned char *(*column_text_fn)(struct db_stmt *stmt, int col); + s64 (*column_int_fn)(struct db_stmt *stmt, int col); }; /* Provide a way for DB backends to register themselves */ diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index fe2d68452..189b0e751 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -124,6 +124,42 @@ static bool db_sqlite3_commit_tx(struct db *db) return true; } +static bool db_sqlite3_column_is_null(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_type(s, col) == SQLITE_NULL; +} + +static u64 db_sqlite3_column_u64(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_int64(s, col); +} + +static s64 db_sqlite3_column_int(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_int(s, col); +} + +static size_t db_sqlite3_column_bytes(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_bytes(s, col); +} + +static const void *db_sqlite3_column_blob(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_blob(s, col); +} + +static const unsigned char *db_sqlite3_column_text(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_text(s, col); +} + static void db_sqlite3_stmt_free(struct db_stmt *stmt) { if (stmt->inner_stmt) @@ -142,6 +178,13 @@ struct db_config db_sqlite3_config = { .begin_tx_fn = &db_sqlite3_begin_tx, .commit_tx_fn = &db_sqlite3_commit_tx, .stmt_free_fn = &db_sqlite3_stmt_free, + + .column_is_null_fn = &db_sqlite3_column_is_null, + .column_u64_fn = &db_sqlite3_column_u64, + .column_int_fn = &db_sqlite3_column_int, + .column_bytes_fn = &db_sqlite3_column_bytes, + .column_blob_fn = &db_sqlite3_column_blob, + .column_text_fn = &db_sqlite3_column_text, }; AUTODATA(db_backends, &db_sqlite3_config);