#include #include "gen_db_sqlite3.c" #include #include #include #include #if HAVE_SQLITE3 #if !HAVE_SQLITE3_EXPANDED_SQL /* Prior to sqlite3 v3.14, we have to use tracing to dump statements */ static void trace_sqlite3(void *stmtv, const char *stmt) { struct db_stmt = (struct db_stmt*)stmtv; db_changes_add(stmt, stmt); } #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_setup(struct db *db) { sqlite3_stmt *stmt; int err; sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); err = sqlite3_step(stmt); sqlite3_finalize(stmt); return err == SQLITE_DONE; } 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 !HAVE_SQLITE3_EXPANDED_SQL /* Register the tracing function if we don't have an explicit way of * expanding the statement. */ sqlite3_trace(db->sql, trace_sqlite3, stmt); #endif 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; } #if HAVE_SQLITE3_EXPANDED_SQL /* Manually expand and call the callback */ char *expanded_sql; expanded_sql = sqlite3_expanded_sql(stmt->inner_stmt); db_changes_add(stmt, expanded_sql); sqlite3_free(expanded_sql); #else /* Unregister the trace callback to avoid it accessing the potentially * stale pointer to stmt */ sqlite3_trace(db->sql, NULL, NULL); #endif 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 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) sqlite3_finalize(stmt->inner_stmt); stmt->inner_stmt = NULL; } static size_t db_sqlite3_count_changes(struct db_stmt *stmt) { sqlite3 *s = stmt->db->conn; return sqlite3_changes(s); } static void db_sqlite3_close(struct db *db) { sqlite3_close(db->sql); } static u64 db_sqlite3_last_insert_id(struct db_stmt *stmt) { sqlite3 *s = stmt->db->conn; return sqlite3_last_insert_rowid(s); } struct db_config db_sqlite3_config = { .name = "sqlite3", .queries = db_sqlite3_queries, .num_queries = DB_SQLITE3_QUERY_COUNT, .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, .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, .last_insert_id_fn = &db_sqlite3_last_insert_id, .count_changes_fn = &db_sqlite3_count_changes, .setup_fn = &db_sqlite3_setup, .teardown_fn = &db_sqlite3_close, }; AUTODATA(db_backends, &db_sqlite3_config); #endif