2021-12-04 12:23:56 +01:00
|
|
|
#include "config.h"
|
2020-08-31 03:21:16 +02:00
|
|
|
#include "db_sqlite3_sqlgen.c"
|
2019-07-25 22:47:07 +02:00
|
|
|
#include <ccan/ccan/tal/str/str.h>
|
2021-11-14 19:04:46 +01:00
|
|
|
#include <common/utils.h>
|
2019-07-25 22:47:07 +02:00
|
|
|
#include <lightningd/log.h>
|
|
|
|
|
2019-07-24 18:29:03 +02:00
|
|
|
#if HAVE_SQLITE3
|
2020-10-22 02:57:07 +02:00
|
|
|
#include <sqlite3.h>
|
2019-07-24 18:29:03 +02:00
|
|
|
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3 {
|
|
|
|
/* The actual db connection. */
|
|
|
|
sqlite3 *conn;
|
|
|
|
/* A replica db connection, if requested, or NULL otherwise. */
|
|
|
|
sqlite3 *backup_conn;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param conn: The db->conn void * pointer.
|
|
|
|
*
|
|
|
|
* @return the actual sqlite3 connection.
|
|
|
|
*/
|
|
|
|
static inline
|
|
|
|
sqlite3 *conn2sql(void *conn)
|
|
|
|
{
|
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) conn;
|
|
|
|
return wrapper->conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replicate_statement(struct db_sqlite3 *wrapper,
|
|
|
|
const char *qry)
|
|
|
|
{
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!wrapper->backup_conn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sqlite3_prepare_v2(wrapper->backup_conn,
|
|
|
|
qry, -1, &stmt, NULL);
|
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (err != SQLITE_DONE)
|
|
|
|
db_fatal("Failed to replicate query: %s: %s: %s",
|
|
|
|
sqlite3_errstr(err),
|
|
|
|
sqlite3_errmsg(wrapper->backup_conn),
|
|
|
|
qry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void db_sqlite3_changes_add(struct db_sqlite3 *wrapper,
|
|
|
|
struct db_stmt *stmt,
|
|
|
|
const char *qry)
|
|
|
|
{
|
|
|
|
replicate_statement(wrapper, qry);
|
|
|
|
db_changes_add(stmt, qry);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if both sqlite3 databases have a data_version variable,
|
|
|
|
* *and* are the same.
|
|
|
|
*/
|
|
|
|
static bool have_same_data_version(sqlite3 *a, sqlite3 *b)
|
|
|
|
{
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
const char *qry = "SELECT intval FROM vars"
|
|
|
|
" WHERE name = 'data_version';";
|
|
|
|
int err;
|
|
|
|
|
|
|
|
u64 version_a;
|
|
|
|
u64 version_b;
|
|
|
|
|
|
|
|
sqlite3_prepare_v2(a, qry, -1, &stmt, NULL);
|
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
if (err != SQLITE_ROW) {
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
version_a = sqlite3_column_int64(stmt, 0);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
sqlite3_prepare_v2(b, qry, -1, &stmt, NULL);
|
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
if (err != SQLITE_ROW) {
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
version_b = sqlite3_column_int64(stmt, 0);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
return version_a == version_b;
|
|
|
|
}
|
|
|
|
|
2019-08-28 16:25:42 +02:00
|
|
|
#if !HAVE_SQLITE3_EXPANDED_SQL
|
|
|
|
/* Prior to sqlite3 v3.14, we have to use tracing to dump statements */
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3_trace {
|
|
|
|
struct db_sqlite3 *wrapper;
|
|
|
|
struct db_stmt *stmt;
|
|
|
|
};
|
|
|
|
|
2019-08-28 16:25:42 +02:00
|
|
|
static void trace_sqlite3(void *stmtv, const char *stmt)
|
2019-07-25 22:47:07 +02:00
|
|
|
{
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3_trace *trace = (struct db_sqlite3_trace *)stmtv;
|
|
|
|
struct db_sqlite3 *wrapper = trace->wrapper;
|
|
|
|
struct db_stmt *s = trace->stmt;
|
|
|
|
db_sqlite3_changes_add(wrapper, s, stmt);
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
2019-08-28 16:25:42 +02:00
|
|
|
#endif
|
2019-07-25 22:47:07 +02:00
|
|
|
|
|
|
|
static const char *db_sqlite3_fmt_error(struct db_stmt *stmt)
|
|
|
|
{
|
|
|
|
return tal_fmt(stmt, "%s: %s: %s", stmt->location, stmt->query->query,
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3_errmsg(conn2sql(stmt->db->conn)));
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
|
|
|
|
2019-08-08 13:42:39 +02:00
|
|
|
static bool db_sqlite3_setup(struct db *db)
|
|
|
|
{
|
2019-09-03 21:10:29 +02:00
|
|
|
char *filename;
|
2021-10-27 11:49:26 +02:00
|
|
|
char *sep;
|
|
|
|
char *backup_filename = NULL;
|
2019-08-08 13:42:39 +02:00
|
|
|
sqlite3_stmt *stmt;
|
2019-09-03 21:10:29 +02:00
|
|
|
sqlite3 *sql;
|
|
|
|
int err, flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
|
|
|
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3 *wrapper;
|
|
|
|
|
2019-09-03 21:10:29 +02:00
|
|
|
if (!strstarts(db->filename, "sqlite3://") || strlen(db->filename) < 10)
|
|
|
|
db_fatal("Could not parse the wallet DSN: %s", db->filename);
|
|
|
|
|
|
|
|
/* Strip the scheme from the dsn. */
|
|
|
|
filename = db->filename + strlen("sqlite3://");
|
2021-10-27 11:49:26 +02:00
|
|
|
/* Look for a replica specification. */
|
|
|
|
sep = strchr(filename, ':');
|
|
|
|
if (sep) {
|
|
|
|
/* Split at ':'. */
|
|
|
|
filename = tal_strndup(db, filename, sep - filename);
|
|
|
|
backup_filename = tal_strdup(db, sep + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapper = tal(db, struct db_sqlite3);
|
|
|
|
db->conn = wrapper;
|
2019-09-03 21:10:29 +02:00
|
|
|
|
|
|
|
err = sqlite3_open_v2(filename, &sql, flags, NULL);
|
|
|
|
|
|
|
|
if (err != SQLITE_OK) {
|
|
|
|
db_fatal("failed to open database %s: %s", filename,
|
|
|
|
sqlite3_errstr(err));
|
|
|
|
}
|
2021-10-27 11:49:26 +02:00
|
|
|
wrapper->conn = sql;
|
|
|
|
|
|
|
|
if (!backup_filename)
|
|
|
|
wrapper->backup_conn = NULL;
|
|
|
|
else {
|
|
|
|
err = sqlite3_open_v2(backup_filename,
|
|
|
|
&wrapper->backup_conn,
|
|
|
|
flags, NULL);
|
|
|
|
if (err != SQLITE_OK) {
|
|
|
|
db_fatal("failed to open backup database %s: %s",
|
|
|
|
backup_filename,
|
|
|
|
sqlite3_errstr(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_prepare_v2(wrapper->backup_conn,
|
|
|
|
"PRAGMA foreign_keys = ON;", -1, &stmt,
|
|
|
|
NULL);
|
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (err != SQLITE_DONE) {
|
|
|
|
db_fatal("failed to use backup database %s: %s",
|
|
|
|
backup_filename,
|
|
|
|
sqlite3_errstr(err));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have a backup db, but it does not have a matching
|
|
|
|
* data_version, copy over the main database. */
|
|
|
|
if (wrapper->backup_conn &&
|
|
|
|
!have_same_data_version(wrapper->conn, wrapper->backup_conn)) {
|
|
|
|
/* Copy the main database over the backup database. */
|
|
|
|
sqlite3_backup *copier = sqlite3_backup_init(wrapper->backup_conn,
|
|
|
|
"main",
|
|
|
|
wrapper->conn,
|
|
|
|
"main");
|
|
|
|
if (!copier) {
|
|
|
|
db_fatal("failed to initiate copy to %s: %s",
|
|
|
|
backup_filename,
|
|
|
|
sqlite3_errmsg(wrapper->backup_conn));
|
|
|
|
}
|
|
|
|
err = sqlite3_backup_step(copier, -1);
|
|
|
|
if (err != SQLITE_DONE) {
|
|
|
|
db_fatal("failed to copy database to %s: %s",
|
|
|
|
backup_filename,
|
|
|
|
sqlite3_errstr(err));
|
|
|
|
}
|
|
|
|
sqlite3_backup_finish(copier);
|
|
|
|
}
|
2019-09-03 21:10:29 +02:00
|
|
|
|
2021-10-17 00:29:27 +02:00
|
|
|
/* In case another process (litestream?) grabs a lock, we don't
|
2021-06-28 23:17:39 +02:00
|
|
|
* want to return SQLITE_BUSY immediately (which will cause a
|
2021-10-17 00:29:27 +02:00
|
|
|
* fatal error): give it 60 seconds.
|
|
|
|
* We *could* make this an option, but surely the user prefers a
|
|
|
|
* long timeout over an outright crash.
|
|
|
|
*/
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3_busy_timeout(conn2sql(db->conn), 60000);
|
2021-06-28 23:17:39 +02:00
|
|
|
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3_prepare_v2(conn2sql(db->conn),
|
|
|
|
"PRAGMA foreign_keys = ON;", -1, &stmt, NULL);
|
2019-08-08 13:42:39 +02:00
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return err == SQLITE_DONE;
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:47:07 +02:00
|
|
|
static bool db_sqlite3_query(struct db_stmt *stmt)
|
|
|
|
{
|
|
|
|
sqlite3_stmt *s;
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3 *conn = conn2sql(stmt->db->conn);
|
2019-07-25 22:47:07 +02:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = sqlite3_prepare_v2(conn, stmt->query->query, -1, &s, NULL);
|
|
|
|
|
|
|
|
for (size_t i=0; i<stmt->query->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;
|
2020-05-06 01:35:55 +02:00
|
|
|
bool success;
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) stmt->db->conn;
|
|
|
|
|
2019-08-28 16:25:42 +02:00
|
|
|
#if !HAVE_SQLITE3_EXPANDED_SQL
|
|
|
|
/* Register the tracing function if we don't have an explicit way of
|
|
|
|
* expanding the statement. */
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3_trace trace;
|
|
|
|
trace.wrapper = wrapper;
|
|
|
|
trace.stmt = stmt;
|
|
|
|
sqlite3_trace(conn2sql(stmt->db->conn), trace_sqlite3, &trace);
|
2019-08-28 16:25:42 +02:00
|
|
|
#endif
|
|
|
|
|
2019-07-25 22:47:07 +02:00
|
|
|
if (!db_sqlite3_query(stmt)) {
|
|
|
|
/* If the prepare step caused an error we hand it up. */
|
2020-05-06 01:35:55 +02:00
|
|
|
success = false;
|
|
|
|
goto done;
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
err = sqlite3_step(stmt->inner_stmt);
|
|
|
|
if (err != SQLITE_DONE) {
|
|
|
|
tal_free(stmt->error);
|
|
|
|
stmt->error = db_sqlite3_fmt_error(stmt);
|
2020-05-06 01:35:55 +02:00
|
|
|
success = false;
|
|
|
|
goto done;
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
|
|
|
|
2019-08-28 16:25:42 +02:00
|
|
|
#if HAVE_SQLITE3_EXPANDED_SQL
|
|
|
|
/* Manually expand and call the callback */
|
|
|
|
char *expanded_sql;
|
|
|
|
expanded_sql = sqlite3_expanded_sql(stmt->inner_stmt);
|
2021-10-27 11:49:26 +02:00
|
|
|
db_sqlite3_changes_add(wrapper, stmt, expanded_sql);
|
2019-08-28 16:25:42 +02:00
|
|
|
sqlite3_free(expanded_sql);
|
2020-05-06 01:35:55 +02:00
|
|
|
#endif
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
done:
|
|
|
|
#if !HAVE_SQLITE3_EXPANDED_SQL
|
2019-08-28 16:25:42 +02:00
|
|
|
/* Unregister the trace callback to avoid it accessing the potentially
|
|
|
|
* stale pointer to stmt */
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3_trace(conn2sql(stmt->db->conn), NULL, NULL);
|
2019-08-28 16:25:42 +02:00
|
|
|
#endif
|
|
|
|
|
2020-05-06 01:35:55 +02:00
|
|
|
return success;
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-10-27 11:49:26 +02:00
|
|
|
|
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn;
|
|
|
|
|
|
|
|
err = sqlite3_exec(conn2sql(db->conn),
|
|
|
|
"BEGIN TRANSACTION;", NULL, NULL, &errmsg);
|
2019-07-25 22:47:07 +02:00
|
|
|
if (err != SQLITE_OK) {
|
|
|
|
db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg);
|
|
|
|
return false;
|
|
|
|
}
|
2021-10-27 11:49:26 +02:00
|
|
|
replicate_statement(wrapper, "BEGIN TRANSACTION;");
|
2019-07-25 22:47:07 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool db_sqlite3_commit_tx(struct db *db)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
char *errmsg;
|
2021-10-27 11:49:26 +02:00
|
|
|
|
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn;
|
|
|
|
|
|
|
|
err = sqlite3_exec(conn2sql(db->conn),
|
|
|
|
"COMMIT;", NULL, NULL, &errmsg);
|
2019-07-25 22:47:07 +02:00
|
|
|
if (err != SQLITE_OK) {
|
2020-05-05 23:01:21 +02:00
|
|
|
db->error = tal_fmt(db, "Failed to commit a transaction: %s", errmsg);
|
2019-07-25 22:47:07 +02:00
|
|
|
return false;
|
|
|
|
}
|
2021-10-27 11:49:26 +02:00
|
|
|
replicate_statement(wrapper, "COMMIT;");
|
2019-07-25 22:47:07 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-03 16:34:03 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:47:07 +02:00
|
|
|
static void db_sqlite3_stmt_free(struct db_stmt *stmt)
|
|
|
|
{
|
|
|
|
if (stmt->inner_stmt)
|
|
|
|
sqlite3_finalize(stmt->inner_stmt);
|
2019-08-03 14:48:38 +02:00
|
|
|
stmt->inner_stmt = NULL;
|
2019-07-25 22:47:07 +02:00
|
|
|
}
|
|
|
|
|
2019-08-03 18:13:31 +02:00
|
|
|
static size_t db_sqlite3_count_changes(struct db_stmt *stmt)
|
|
|
|
{
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3 *s = conn2sql(stmt->db->conn);
|
2019-08-03 18:13:31 +02:00
|
|
|
return sqlite3_changes(s);
|
|
|
|
}
|
|
|
|
|
2019-08-08 13:42:39 +02:00
|
|
|
static void db_sqlite3_close(struct db *db)
|
|
|
|
{
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn;
|
|
|
|
|
|
|
|
if (wrapper->backup_conn)
|
|
|
|
sqlite3_close(wrapper->backup_conn);
|
|
|
|
sqlite3_close(wrapper->conn);
|
|
|
|
|
|
|
|
db->conn = tal_free(db->conn);
|
2019-08-08 13:42:39 +02:00
|
|
|
}
|
|
|
|
|
2019-08-20 19:38:09 +02:00
|
|
|
static u64 db_sqlite3_last_insert_id(struct db_stmt *stmt)
|
|
|
|
{
|
2021-10-27 11:49:26 +02:00
|
|
|
sqlite3 *s = conn2sql(stmt->db->conn);
|
2019-08-20 19:38:09 +02:00
|
|
|
return sqlite3_last_insert_rowid(s);
|
|
|
|
}
|
|
|
|
|
2021-10-13 05:45:36 +02:00
|
|
|
static bool db_sqlite3_vacuum(struct db *db)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
|
2021-10-27 11:49:26 +02:00
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn;
|
|
|
|
|
|
|
|
sqlite3_prepare_v2(conn2sql(db->conn), "VACUUM;", -1, &stmt, NULL);
|
2021-10-13 05:45:36 +02:00
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
if (err != SQLITE_DONE)
|
2021-10-27 11:49:26 +02:00
|
|
|
db->error = tal_fmt(db, "%s",
|
|
|
|
sqlite3_errmsg(conn2sql(db->conn)));
|
2021-10-13 05:45:36 +02:00
|
|
|
sqlite3_finalize(stmt);
|
2021-10-27 11:49:26 +02:00
|
|
|
replicate_statement(wrapper, "VACUUM;");
|
2021-10-13 05:45:36 +02:00
|
|
|
|
|
|
|
return err == SQLITE_DONE;
|
|
|
|
}
|
|
|
|
|
2021-11-14 19:04:46 +01:00
|
|
|
static bool colname_to_delete(const char **colnames,
|
|
|
|
size_t num_colnames,
|
|
|
|
const char *columnname)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < num_colnames; i++) {
|
|
|
|
if (streq(columnname, colnames[i]))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *find_column_name(const tal_t *ctx,
|
|
|
|
const char *sqlpart,
|
|
|
|
size_t *after)
|
|
|
|
{
|
|
|
|
size_t start = 0;
|
|
|
|
|
|
|
|
while (isspace(sqlpart[start]))
|
|
|
|
start++;
|
|
|
|
*after = strspn(sqlpart + start, "abcdefghijklmnopqrstuvwxyz_0123456789") + start;
|
|
|
|
if (*after == start)
|
|
|
|
return NULL;
|
|
|
|
return tal_strndup(ctx, sqlpart + start, *after - start);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move table out the way, return columns */
|
|
|
|
static char **prepare_table_manip(const tal_t *ctx,
|
|
|
|
struct db *db, const char *tablename)
|
|
|
|
{
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
const char *sql;
|
|
|
|
char *cmd, *bracket;
|
|
|
|
char **parts;
|
|
|
|
int err;
|
2021-11-18 00:59:40 +01:00
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *)db->conn;
|
2021-11-14 19:04:46 +01:00
|
|
|
|
|
|
|
/* Get schema. */
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, "SELECT sql FROM sqlite_master WHERE type = ? AND name = ?;", -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
sqlite3_bind_text(stmt, 1, "table", strlen("table"), SQLITE_TRANSIENT);
|
|
|
|
sqlite3_bind_text(stmt, 2, tablename, strlen(tablename), SQLITE_TRANSIENT);
|
2021-11-18 00:59:40 +01:00
|
|
|
|
2021-11-14 19:04:46 +01:00
|
|
|
err = sqlite3_step(stmt);
|
|
|
|
if (err != SQLITE_ROW) {
|
|
|
|
db->error = tal_fmt(db, "getting schema: %s",
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_errmsg(wrapper->conn));
|
2021-11-14 19:04:46 +01:00
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql = tal_strdup(tmpctx, (const char *)sqlite3_column_text(stmt, 0));
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
bracket = strchr(sql, '(');
|
|
|
|
if (!strstarts(sql, "CREATE TABLE") || !bracket) {
|
|
|
|
db->error = tal_fmt(db, "strange schema for %s: %s",
|
|
|
|
tablename, sql);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split after ( by commas: any lower case is assumed to be a field */
|
|
|
|
parts = tal_strsplit(ctx, bracket + 1, ",", STR_EMPTY_OK);
|
|
|
|
|
|
|
|
/* Turn off foreign keys first. */
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = OFF;", -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
cmd = tal_fmt(tmpctx, "ALTER TABLE %s RENAME TO temp_%s;",
|
|
|
|
tablename, tablename);
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, cmd, -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
2021-11-18 00:59:40 +01:00
|
|
|
/* Make sure we do the same to backup! */
|
|
|
|
replicate_statement(wrapper, "PRAGMA foreign_keys = OFF;");
|
|
|
|
replicate_statement(wrapper, cmd);
|
|
|
|
|
2021-11-14 19:04:46 +01:00
|
|
|
return parts;
|
|
|
|
|
|
|
|
sqlite_stmt_err:
|
2021-11-18 00:59:40 +01:00
|
|
|
db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn));
|
2021-11-14 19:04:46 +01:00
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool complete_table_manip(struct db *db,
|
|
|
|
const char *tablename,
|
|
|
|
const char **coldefs,
|
|
|
|
const char **oldcolnames)
|
|
|
|
{
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
char *create_cmd, *insert_cmd, *drop_cmd;
|
2021-11-18 00:59:40 +01:00
|
|
|
struct db_sqlite3 *wrapper = (struct db_sqlite3 *)db->conn;
|
2021-11-14 19:04:46 +01:00
|
|
|
|
|
|
|
/* Create table */
|
|
|
|
create_cmd = tal_fmt(tmpctx, "CREATE TABLE %s (", tablename);
|
|
|
|
for (size_t i = 0; i < tal_count(coldefs); i++) {
|
|
|
|
if (i != 0)
|
|
|
|
tal_append_fmt(&create_cmd, ", ");
|
|
|
|
tal_append_fmt(&create_cmd, "%s", coldefs[i]);
|
|
|
|
}
|
|
|
|
tal_append_fmt(&create_cmd, ";");
|
|
|
|
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, create_cmd, -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
2021-11-18 00:59:40 +01:00
|
|
|
/* Make sure we do the same to backup! */
|
|
|
|
replicate_statement(wrapper, create_cmd);
|
|
|
|
|
2021-11-14 19:04:46 +01:00
|
|
|
/* Populate table from old one */
|
|
|
|
insert_cmd = tal_fmt(tmpctx, "INSERT INTO %s SELECT ", tablename);
|
|
|
|
for (size_t i = 0; i < tal_count(oldcolnames); i++) {
|
|
|
|
if (i != 0)
|
|
|
|
tal_append_fmt(&insert_cmd, ", ");
|
|
|
|
tal_append_fmt(&insert_cmd, "%s", oldcolnames[i]);
|
|
|
|
}
|
|
|
|
tal_append_fmt(&insert_cmd, " FROM temp_%s;", tablename);
|
|
|
|
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, insert_cmd, -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
2021-11-18 00:59:40 +01:00
|
|
|
replicate_statement(wrapper, insert_cmd);
|
2021-11-14 19:04:46 +01:00
|
|
|
|
|
|
|
/* Cleanup temp table */
|
|
|
|
drop_cmd = tal_fmt(tmpctx, "DROP TABLE temp_%s;", tablename);
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, drop_cmd, -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
2021-11-18 00:59:40 +01:00
|
|
|
replicate_statement(wrapper, drop_cmd);
|
2021-11-14 19:04:46 +01:00
|
|
|
|
|
|
|
/* Allow links between them (esp. cascade deletes!) */
|
2021-11-18 00:59:40 +01:00
|
|
|
sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL);
|
2021-11-14 19:04:46 +01:00
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
|
|
|
goto sqlite_stmt_err;
|
|
|
|
sqlite3_finalize(stmt);
|
2021-11-18 00:59:40 +01:00
|
|
|
replicate_statement(wrapper, "PRAGMA foreign_keys = ON;");
|
2021-11-14 19:04:46 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
sqlite_stmt_err:
|
2021-11-18 00:59:40 +01:00
|
|
|
db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn));
|
2021-11-14 19:04:46 +01:00
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool db_sqlite3_rename_column(struct db *db,
|
|
|
|
const char *tablename,
|
|
|
|
const char *from, const char *to)
|
|
|
|
{
|
|
|
|
char **parts;
|
|
|
|
const char **coldefs, **oldcolnames;
|
|
|
|
bool colname_found = false;
|
|
|
|
|
|
|
|
parts = prepare_table_manip(tmpctx, db, tablename);
|
|
|
|
if (!parts)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
coldefs = tal_arr(tmpctx, const char *, 0);
|
|
|
|
oldcolnames = tal_arr(tmpctx, const char *, 0);
|
|
|
|
|
|
|
|
for (size_t i = 0; parts[i]; i++) {
|
|
|
|
/* columnname DETAILS */
|
|
|
|
size_t after_name;
|
|
|
|
const char *colname = find_column_name(tmpctx, parts[i],
|
|
|
|
&after_name);
|
|
|
|
|
|
|
|
/* Things like "PRIMARY KEY xxx" must be copied verbatim */
|
|
|
|
if (!colname) {
|
|
|
|
tal_arr_expand(&coldefs, parts[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (streq(colname, from)) {
|
|
|
|
char *newdef;
|
|
|
|
colname_found = true;
|
|
|
|
/* Create column with new name */
|
|
|
|
newdef = tal_fmt(coldefs,
|
|
|
|
"%s%s", to, parts[i] + after_name);
|
|
|
|
tal_arr_expand(&coldefs, newdef);
|
|
|
|
tal_arr_expand(&oldcolnames, colname);
|
|
|
|
} else {
|
|
|
|
/* Not mentioned, keep it as is! */
|
|
|
|
tal_arr_expand(&coldefs, parts[i]);
|
|
|
|
tal_arr_expand(&oldcolnames, colname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!colname_found) {
|
|
|
|
db->error = tal_fmt(db, "No column called %s", from);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return complete_table_manip(db, tablename, coldefs, oldcolnames);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool db_sqlite3_delete_columns(struct db *db,
|
|
|
|
const char *tablename,
|
|
|
|
const char **colnames, size_t num_cols)
|
|
|
|
{
|
|
|
|
char **parts;
|
|
|
|
const char **coldefs, **oldcolnames;
|
|
|
|
size_t colnames_found = 0;
|
|
|
|
|
|
|
|
parts = prepare_table_manip(tmpctx, db, tablename);
|
|
|
|
if (!parts)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
coldefs = tal_arr(tmpctx, const char *, 0);
|
|
|
|
oldcolnames = tal_arr(tmpctx, const char *, 0);
|
|
|
|
|
|
|
|
for (size_t i = 0; parts[i]; i++) {
|
|
|
|
/* columnname DETAILS */
|
|
|
|
size_t after_name;
|
|
|
|
const char *colname = find_column_name(tmpctx, parts[i],
|
|
|
|
&after_name);
|
|
|
|
|
|
|
|
/* Things like "PRIMARY KEY xxx" must be copied verbatim */
|
|
|
|
if (!colname) {
|
|
|
|
tal_arr_expand(&coldefs, parts[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't mention columns we're supposed to delete */
|
|
|
|
if (colname_to_delete(colnames, num_cols, colname)) {
|
|
|
|
colnames_found++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep it as is! */
|
|
|
|
tal_arr_expand(&coldefs, parts[i]);
|
|
|
|
tal_arr_expand(&oldcolnames, colname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (colnames_found != num_cols) {
|
|
|
|
db->error = tal_fmt(db, "Only %zu/%zu columns found",
|
|
|
|
colnames_found, num_cols);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return complete_table_manip(db, tablename, coldefs, oldcolnames);
|
|
|
|
}
|
|
|
|
|
2019-07-24 18:29:03 +02:00
|
|
|
struct db_config db_sqlite3_config = {
|
|
|
|
.name = "sqlite3",
|
2021-11-14 19:03:46 +01:00
|
|
|
.query_table = db_sqlite3_queries,
|
|
|
|
.query_table_size = ARRAY_SIZE(db_sqlite3_queries),
|
2019-07-25 22:47:07 +02:00
|
|
|
.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,
|
2019-08-03 16:34:03 +02:00
|
|
|
|
|
|
|
.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,
|
2019-08-03 18:13:31 +02:00
|
|
|
|
2019-08-20 19:38:09 +02:00
|
|
|
.last_insert_id_fn = &db_sqlite3_last_insert_id,
|
2019-08-03 18:13:31 +02:00
|
|
|
.count_changes_fn = &db_sqlite3_count_changes,
|
2019-08-08 13:42:39 +02:00
|
|
|
.setup_fn = &db_sqlite3_setup,
|
|
|
|
.teardown_fn = &db_sqlite3_close,
|
2021-10-13 05:45:36 +02:00
|
|
|
|
|
|
|
.vacuum_fn = db_sqlite3_vacuum,
|
2021-11-14 19:04:46 +01:00
|
|
|
.rename_column = db_sqlite3_rename_column,
|
|
|
|
.delete_columns = db_sqlite3_delete_columns,
|
2019-07-24 18:29:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
AUTODATA(db_backends, &db_sqlite3_config);
|
|
|
|
|
|
|
|
#endif
|