2017-11-01 02:21:56 +01:00
|
|
|
#include <lightningd/log.h>
|
|
|
|
|
2018-08-09 04:27:20 +02:00
|
|
|
static void db_test_fatal(const char *fmt, ...);
|
|
|
|
#define db_fatal db_test_fatal
|
2017-11-01 02:21:56 +01:00
|
|
|
|
2019-11-17 12:41:33 +01:00
|
|
|
static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const struct node_id *node_id UNUSED, bool call_notifier UNUSED, const char *fmt UNUSED, ...)
|
2018-01-02 05:03:42 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
#define log_ db_log_
|
|
|
|
|
2017-12-12 04:42:37 +01:00
|
|
|
#include "wallet/db.c"
|
2017-05-12 20:24:18 +02:00
|
|
|
|
2017-12-12 04:42:37 +01:00
|
|
|
#include "test_utils.h"
|
2017-05-31 15:26:30 +02:00
|
|
|
|
2020-12-02 01:10:04 +01:00
|
|
|
#include <common/setup.h>
|
2017-05-12 20:24:18 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-12-12 04:42:38 +01:00
|
|
|
/* AUTOGENERATED MOCKS START */
|
2020-09-09 09:20:53 +02:00
|
|
|
/* Generated stub for derive_channel_id */
|
|
|
|
void derive_channel_id(struct channel_id *channel_id UNNEEDED,
|
2021-10-13 05:45:36 +02:00
|
|
|
const struct bitcoin_outpoint *outpoint UNNEEDED)
|
2020-09-09 09:20:53 +02:00
|
|
|
{ fprintf(stderr, "derive_channel_id called!\n"); abort(); }
|
2019-07-25 22:45:02 +02:00
|
|
|
/* Generated stub for fatal */
|
|
|
|
void fatal(const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "fatal called!\n"); abort(); }
|
2021-03-01 15:47:55 +01:00
|
|
|
/* Generated stub for fromwire_hsmd_get_channel_basepoints_reply */
|
|
|
|
bool fromwire_hsmd_get_channel_basepoints_reply(const void *p UNNEEDED, struct basepoints *basepoints UNNEEDED, struct pubkey *funding_pubkey UNNEEDED)
|
|
|
|
{ fprintf(stderr, "fromwire_hsmd_get_channel_basepoints_reply called!\n"); abort(); }
|
2020-08-25 03:55:38 +02:00
|
|
|
/* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */
|
|
|
|
bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED)
|
|
|
|
{ fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); }
|
2020-05-22 05:45:14 +02:00
|
|
|
/* Generated stub for get_channel_basepoints */
|
|
|
|
void get_channel_basepoints(struct lightningd *ld UNNEEDED,
|
|
|
|
const struct node_id *peer_id UNNEEDED,
|
|
|
|
const u64 dbid UNNEEDED,
|
|
|
|
struct basepoints *local_basepoints UNNEEDED,
|
|
|
|
struct pubkey *local_funding_pubkey UNNEEDED)
|
|
|
|
{ fprintf(stderr, "get_channel_basepoints called!\n"); abort(); }
|
2019-11-18 01:27:17 +01:00
|
|
|
/* Generated stub for new_log */
|
|
|
|
struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED,
|
|
|
|
const struct node_id *default_node_id UNNEEDED,
|
|
|
|
const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "new_log called!\n"); abort(); }
|
2021-03-01 15:47:55 +01:00
|
|
|
/* Generated stub for towire_hsmd_get_channel_basepoints */
|
|
|
|
u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct node_id *peerid UNNEEDED, u64 dbid UNNEEDED)
|
|
|
|
{ fprintf(stderr, "towire_hsmd_get_channel_basepoints called!\n"); abort(); }
|
2020-08-25 03:55:38 +02:00
|
|
|
/* Generated stub for towire_hsmd_get_output_scriptpubkey */
|
|
|
|
u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
|
|
|
|
{ fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); }
|
2020-07-29 04:03:03 +02:00
|
|
|
/* Generated stub for wire_sync_read */
|
|
|
|
u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED)
|
|
|
|
{ fprintf(stderr, "wire_sync_read called!\n"); abort(); }
|
|
|
|
/* Generated stub for wire_sync_write */
|
|
|
|
bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED)
|
|
|
|
{ fprintf(stderr, "wire_sync_write called!\n"); abort(); }
|
2017-12-12 04:42:38 +01:00
|
|
|
/* AUTOGENERATED MOCKS END */
|
|
|
|
|
2017-11-01 02:21:56 +01:00
|
|
|
static char *db_err;
|
2018-08-09 04:27:20 +02:00
|
|
|
static void db_test_fatal(const char *fmt, ...)
|
2017-11-01 02:21:56 +01:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
/* Fail hard if we're complaining about not being in transaction */
|
|
|
|
assert(!strstarts(fmt, "No longer in transaction"));
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
db_err = tal_vfmt(NULL, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2019-12-18 11:06:09 +01:00
|
|
|
void plugin_hook_db_sync(struct db *db UNNEEDED)
|
2019-03-18 03:40:32 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-02-21 16:36:21 +01:00
|
|
|
static struct db *create_test_db(void)
|
2017-05-12 20:24:18 +02:00
|
|
|
{
|
|
|
|
struct db *db;
|
2019-09-03 21:10:29 +02:00
|
|
|
char *dsn, filename[] = "/tmp/ldb-XXXXXX";
|
2017-05-12 20:24:18 +02:00
|
|
|
|
|
|
|
int fd = mkstemp(filename);
|
|
|
|
if (fd == -1)
|
|
|
|
return NULL;
|
|
|
|
close(fd);
|
|
|
|
|
2019-09-03 21:10:29 +02:00
|
|
|
dsn = tal_fmt(NULL, "sqlite3://%s", filename);
|
|
|
|
db = db_open(NULL, dsn);
|
2019-12-18 19:45:24 +01:00
|
|
|
db->data_version = 0;
|
2019-09-03 21:10:29 +02:00
|
|
|
tal_free(dsn);
|
2017-05-12 20:24:18 +02:00
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
2019-03-05 00:39:21 +01:00
|
|
|
static bool test_empty_db_migrate(struct lightningd *ld)
|
2017-05-12 20:24:18 +02:00
|
|
|
{
|
2018-02-21 16:36:21 +01:00
|
|
|
struct db *db = create_test_db();
|
2020-07-29 03:20:30 +02:00
|
|
|
const struct ext_key *bip32_base = NULL;
|
2017-05-31 15:26:30 +02:00
|
|
|
CHECK(db);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_begin_transaction(db);
|
2017-05-31 15:26:30 +02:00
|
|
|
CHECK(db_get_version(db) == -1);
|
2020-07-29 03:20:30 +02:00
|
|
|
db_migrate(ld, db, bip32_base);
|
2019-12-18 18:57:37 +01:00
|
|
|
db_commit_transaction(db);
|
|
|
|
|
2017-11-01 02:21:56 +01:00
|
|
|
db_begin_transaction(db);
|
2019-02-24 06:12:09 +01:00
|
|
|
CHECK(db_get_version(db) == ARRAY_SIZE(dbmigrations) - 1);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_commit_transaction(db);
|
2017-05-12 20:24:18 +02:00
|
|
|
|
|
|
|
tal_free(db);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-13 15:43:38 +02:00
|
|
|
static bool test_primitives(void)
|
|
|
|
{
|
2019-08-13 16:00:46 +02:00
|
|
|
struct db_stmt *stmt;
|
2018-02-21 16:36:21 +01:00
|
|
|
struct db *db = create_test_db();
|
2019-08-13 16:00:46 +02:00
|
|
|
db_err = NULL;
|
2017-11-01 02:21:56 +01:00
|
|
|
db_begin_transaction(db);
|
2017-06-13 15:43:38 +02:00
|
|
|
CHECK(db->in_transaction);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_commit_transaction(db);
|
2017-06-13 15:43:38 +02:00
|
|
|
CHECK(!db->in_transaction);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_begin_transaction(db);
|
|
|
|
db_commit_transaction(db);
|
|
|
|
|
|
|
|
db_begin_transaction(db);
|
2019-08-13 16:00:46 +02:00
|
|
|
stmt = db_prepare_v2(db, SQL("SELECT name FROM sqlite_master WHERE type='table';"));
|
|
|
|
CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed");
|
2017-11-01 02:21:56 +01:00
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
2019-08-13 16:00:46 +02:00
|
|
|
tal_free(stmt);
|
2017-06-13 15:43:38 +02:00
|
|
|
|
2019-08-13 16:00:46 +02:00
|
|
|
stmt = db_prepare_v2(db, SQL("not a valid SQL statement"));
|
|
|
|
CHECK_MSG(!db_exec_prepared_v2(stmt), "db_exec_prepared must fail");
|
2017-11-01 02:21:56 +01:00
|
|
|
CHECK_MSG(db_err, "Failing SQL command");
|
2019-08-13 16:00:46 +02:00
|
|
|
tal_free(stmt);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_err = tal_free(db_err);
|
2019-12-18 19:45:24 +01:00
|
|
|
|
|
|
|
/* We didn't migrate the DB, so don't have the vars table. Pretend we
|
|
|
|
* didn't change anything so we don't bump the data_version. */
|
|
|
|
db->dirty = false;
|
2017-11-01 02:21:56 +01:00
|
|
|
db_commit_transaction(db);
|
2017-06-13 15:43:38 +02:00
|
|
|
CHECK(!db->in_transaction);
|
|
|
|
tal_free(db);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-05 00:39:21 +01:00
|
|
|
static bool test_vars(struct lightningd *ld)
|
2017-06-01 16:10:40 +02:00
|
|
|
{
|
2018-02-21 16:36:21 +01:00
|
|
|
struct db *db = create_test_db();
|
2017-06-01 16:10:40 +02:00
|
|
|
char *varname = "testvar";
|
2020-07-29 03:20:30 +02:00
|
|
|
const struct ext_key *bip32_base = NULL;
|
2017-06-01 16:10:40 +02:00
|
|
|
CHECK(db);
|
|
|
|
|
2017-11-01 02:21:56 +01:00
|
|
|
db_begin_transaction(db);
|
2020-07-29 03:20:30 +02:00
|
|
|
db_migrate(ld, db, bip32_base);
|
2017-06-01 16:10:40 +02:00
|
|
|
/* Check default behavior */
|
|
|
|
CHECK(db_get_intvar(db, varname, 42) == 42);
|
|
|
|
|
|
|
|
/* Check setting and getting */
|
2017-11-01 02:21:56 +01:00
|
|
|
db_set_intvar(db, varname, 1);
|
2017-06-01 16:10:40 +02:00
|
|
|
CHECK(db_get_intvar(db, varname, 42) == 1);
|
|
|
|
|
|
|
|
/* Check updating */
|
2017-11-01 02:21:56 +01:00
|
|
|
db_set_intvar(db, varname, 2);
|
2017-06-01 16:10:40 +02:00
|
|
|
CHECK(db_get_intvar(db, varname, 42) == 2);
|
2017-11-01 02:21:56 +01:00
|
|
|
db_commit_transaction(db);
|
2017-06-01 16:10:40 +02:00
|
|
|
|
|
|
|
tal_free(db);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-14 19:04:46 +01:00
|
|
|
static bool test_manip_columns(void)
|
|
|
|
{
|
|
|
|
struct db_stmt *stmt;
|
|
|
|
struct db *db = create_test_db();
|
|
|
|
const char *field1 = "field1";
|
|
|
|
|
|
|
|
db_begin_transaction(db);
|
|
|
|
/* tablea refers to tableb */
|
|
|
|
stmt = db_prepare_v2(db, SQL("CREATE TABLE tablea ("
|
|
|
|
" id BIGSERIAL"
|
|
|
|
", field1 INTEGER"
|
|
|
|
", PRIMARY KEY (id))"));
|
|
|
|
CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
tal_free(stmt);
|
|
|
|
|
|
|
|
stmt = db_prepare_v2(db, SQL("INSERT INTO tablea (id, field1) VALUES (0, 1);"));
|
|
|
|
CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
tal_free(stmt);
|
|
|
|
|
|
|
|
stmt = db_prepare_v2(db, SQL("CREATE TABLE tableb ("
|
|
|
|
" id REFERENCES tablea(id) ON DELETE CASCADE"
|
|
|
|
", field1 INTEGER"
|
|
|
|
", field2 INTEGER);"));
|
|
|
|
CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
tal_free(stmt);
|
|
|
|
|
|
|
|
stmt = db_prepare_v2(db, SQL("INSERT INTO tableb (id, field1, field2) VALUES (0, 1, 2);"));
|
|
|
|
CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
tal_free(stmt);
|
|
|
|
/* Don't let it try to set a version field (we don't have one!) */
|
|
|
|
db->dirty = false;
|
|
|
|
db->changes = tal_arr(db, const char *, 0);
|
|
|
|
db_commit_transaction(db);
|
|
|
|
|
|
|
|
/* Rename tablea.field1 -> table1.field1a. */
|
|
|
|
CHECK(db->config->rename_column(db, "tablea", "field1", "field1a"));
|
|
|
|
/* Remove tableb.field1. */
|
|
|
|
CHECK(db->config->delete_columns(db, "tableb", &field1, 1));
|
|
|
|
|
|
|
|
db_begin_transaction(db);
|
|
|
|
stmt = db_prepare_v2(db, SQL("SELECT id, field1a FROM tablea;"));
|
|
|
|
CHECK_MSG(db_query_prepared(stmt), "db_query_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
CHECK(db_step(stmt));
|
|
|
|
CHECK(db_col_u64(stmt, "id") == 0);
|
|
|
|
CHECK(db_col_u64(stmt, "field1a") == 1);
|
|
|
|
CHECK(!db_step(stmt));
|
|
|
|
tal_free(stmt);
|
|
|
|
|
|
|
|
stmt = db_prepare_v2(db, SQL("SELECT id, field2 FROM tableb;"));
|
|
|
|
CHECK_MSG(db_query_prepared(stmt), "db_query_prepared must succeed");
|
|
|
|
CHECK_MSG(!db_err, "Simple correct SQL command");
|
|
|
|
CHECK(db_step(stmt));
|
|
|
|
CHECK(db_col_u64(stmt, "id") == 0);
|
|
|
|
CHECK(db_col_u64(stmt, "field2") == 2);
|
|
|
|
CHECK(!db_step(stmt));
|
|
|
|
tal_free(stmt);
|
|
|
|
db->dirty = false;
|
|
|
|
db->changes = tal_arr(db, const char *, 0);
|
|
|
|
db_commit_transaction(db);
|
|
|
|
|
|
|
|
db_begin_transaction(db);
|
|
|
|
/* This will actually fail */
|
|
|
|
stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tablea;"));
|
|
|
|
CHECK_MSG(!db_query_prepared(stmt), "db_query_prepared must fail");
|
|
|
|
db->dirty = false;
|
|
|
|
db->changes = tal_arr(db, const char *, 0);
|
|
|
|
db_commit_transaction(db);
|
|
|
|
|
|
|
|
db_begin_transaction(db);
|
|
|
|
/* This will actually fail */
|
|
|
|
stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tableb;"));
|
|
|
|
CHECK_MSG(!db_query_prepared(stmt), "db_query_prepared must fail");
|
|
|
|
db->dirty = false;
|
|
|
|
db->changes = tal_arr(db, const char *, 0);
|
|
|
|
db_commit_transaction(db);
|
|
|
|
|
|
|
|
tal_free(db);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-02 01:10:04 +01:00
|
|
|
int main(int argc, char *argv[])
|
2017-05-12 20:24:18 +02:00
|
|
|
{
|
|
|
|
bool ok = true;
|
2019-03-05 00:39:21 +01:00
|
|
|
/* Dummy for migration hooks */
|
|
|
|
struct lightningd *ld = tal(NULL, struct lightningd);
|
2020-12-02 01:10:04 +01:00
|
|
|
|
|
|
|
common_setup(argv[0]);
|
2019-03-13 17:20:54 +01:00
|
|
|
ld->config = test_config;
|
2017-05-12 20:24:18 +02:00
|
|
|
|
2019-03-05 00:39:21 +01:00
|
|
|
ok &= test_empty_db_migrate(ld);
|
|
|
|
ok &= test_vars(ld);
|
2017-06-13 15:43:38 +02:00
|
|
|
ok &= test_primitives();
|
2021-11-14 19:04:46 +01:00
|
|
|
ok &= test_manip_columns();
|
2017-05-12 20:24:18 +02:00
|
|
|
|
2019-03-05 00:39:21 +01:00
|
|
|
tal_free(ld);
|
2020-12-02 01:10:04 +01:00
|
|
|
common_shutdown();
|
2017-05-12 20:24:18 +02:00
|
|
|
return !ok;
|
|
|
|
}
|