Basically, `devtools/reduce-includes.sh */*.c`. Build time from make clean (RUST=0) (includes building external libs): Before: real 0m38.944000-40.416000(40.1131+/-0.4)s user 3m6.790000-17.159000(15.0571+/-2.8)s sys 0m35.304000-37.336000(36.8942+/-0.57)s After: real 0m37.872000-39.974000(39.5466+/-0.59)s user 3m1.211000-14.968000(12.4556+/-3.9)s sys 0m35.008000-36.830000(36.4143+/-0.5)s Build time after touch config.vars (RUST=0): Before: real 0m19.831000-21.862000(21.5528+/-0.58)s user 2m15.361000-30.731000(28.4798+/-4.4)s sys 0m21.056000-22.339000(22.0346+/-0.35)s After: real 0m18.384000-21.307000(20.8605+/-0.92)s user 2m5.585000-26.843000(23.6017+/-6.7)s sys 0m19.650000-22.003000(21.4943+/-0.69)s Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
168 lines
4.5 KiB
C
168 lines
4.5 KiB
C
#include "config.h"
|
|
#include <db/bindings.h>
|
|
#include <db/common.h>
|
|
#include <db/exec.h>
|
|
#include <db/utils.h>
|
|
|
|
/**
|
|
* db_get_version - Determine the current DB schema version
|
|
*
|
|
* Will attempt to determine the current schema version of the
|
|
* database @db by querying the `version` table. If the table does not
|
|
* exist it'll return schema version -1, so that migration 0 is
|
|
* applied, which should create the `version` table.
|
|
*/
|
|
int db_get_version(struct db *db)
|
|
{
|
|
int res = -1;
|
|
struct db_stmt *stmt = db_prepare_v2(db, SQL("SELECT version FROM version LIMIT 1"));
|
|
|
|
/*
|
|
* Tentatively execute a query, but allow failures. Some databases
|
|
* like postgres will terminate the DB transaction if there is an
|
|
* error during the execution of a query, e.g., trying to access a
|
|
* table that doesn't exist yet, so we need to terminate and restart
|
|
* the DB transaction.
|
|
*/
|
|
if (!db_query_prepared_canfail(stmt)) {
|
|
db_commit_transaction(stmt->db);
|
|
db_begin_transaction(stmt->db);
|
|
tal_free(stmt);
|
|
return res;
|
|
}
|
|
|
|
if (db_step(stmt))
|
|
res = db_col_int(stmt, "version");
|
|
|
|
tal_free(stmt);
|
|
return res;
|
|
}
|
|
|
|
u32 db_data_version_get(struct db *db)
|
|
{
|
|
struct db_stmt *stmt;
|
|
u32 version;
|
|
stmt = db_prepare_v2(db, SQL("SELECT intval FROM vars WHERE name = 'data_version'"));
|
|
/* postgres will act upset if the table doesn't exist yet. */
|
|
if (!db_query_prepared_canfail(stmt)) {
|
|
tal_free(stmt);
|
|
return 0;
|
|
}
|
|
/* This fails on uninitialized db, so "0" */
|
|
if (db_step(stmt))
|
|
version = db_col_int(stmt, "intval");
|
|
else
|
|
version = 0;
|
|
tal_free(stmt);
|
|
return version;
|
|
}
|
|
|
|
void db_set_intvar(struct db *db, const char *varname, s64 val)
|
|
{
|
|
size_t changes;
|
|
struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET intval=? WHERE name=?;"));
|
|
db_bind_int(stmt, val);
|
|
db_bind_text(stmt, varname);
|
|
db_exec_prepared_v2(stmt);
|
|
changes = db_count_changes(stmt);
|
|
tal_free(stmt);
|
|
|
|
if (changes == 0) {
|
|
stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, intval) VALUES (?, ?);"));
|
|
db_bind_text(stmt, varname);
|
|
db_bind_int(stmt, val);
|
|
db_exec_prepared_v2(stmt);
|
|
tal_free(stmt);
|
|
}
|
|
}
|
|
|
|
s64 db_get_intvar(struct db *db, const char *varname, s64 defval)
|
|
{
|
|
s64 res = defval;
|
|
struct db_stmt *stmt = db_prepare_v2(
|
|
db, SQL("SELECT intval FROM vars WHERE name= ? LIMIT 1"));
|
|
db_bind_text(stmt, varname);
|
|
if (db_query_prepared_canfail(stmt) && db_step(stmt))
|
|
res = db_col_int(stmt, "intval");
|
|
|
|
tal_free(stmt);
|
|
return res;
|
|
}
|
|
|
|
/* Leak tracking. */
|
|
|
|
/* By making the update conditional on the current value we expect we
|
|
* are implementing an optimistic lock: if the update results in
|
|
* changes on the DB we know that the data_version did not change
|
|
* under our feet and no other transaction ran in the meantime.
|
|
*
|
|
* Notice that this update effectively locks the row, so that other
|
|
* operations attempting to change this outside the transaction will
|
|
* wait for this transaction to complete. The external change will
|
|
* ultimately fail the changes test below, it'll just delay its abort
|
|
* until our transaction is committed.
|
|
*/
|
|
static void db_data_version_incr(struct db *db)
|
|
{
|
|
struct db_stmt *stmt = db_prepare_v2(
|
|
db, SQL("UPDATE vars "
|
|
"SET intval = intval + 1 "
|
|
"WHERE name = 'data_version'"
|
|
" AND intval = ?"));
|
|
db_bind_int(stmt, db->data_version);
|
|
db_exec_prepared_v2(stmt);
|
|
if (db_count_changes(stmt) != 1)
|
|
db_fatal(stmt->db, "Optimistic lock on the database failed. There"
|
|
" may be a concurrent access to the database."
|
|
" Aborting since concurrent access is unsafe.");
|
|
tal_free(stmt);
|
|
db->data_version++;
|
|
}
|
|
|
|
void db_begin_transaction_(struct db *db, const char *location)
|
|
{
|
|
bool ok;
|
|
if (db->in_transaction)
|
|
db_fatal(db, "Already in transaction from %s", db->in_transaction);
|
|
|
|
/* No writes yet. */
|
|
db->dirty = false;
|
|
|
|
db_prepare_for_changes(db);
|
|
ok = db->config->begin_tx_fn(db);
|
|
if (!ok)
|
|
db_fatal(db, "Failed to start DB transaction: %s", db->error);
|
|
|
|
db->in_transaction = location;
|
|
}
|
|
|
|
bool db_in_transaction(struct db *db)
|
|
{
|
|
return db->in_transaction;
|
|
}
|
|
|
|
void db_set_readonly(struct db *db, bool readonly)
|
|
{
|
|
db->readonly = readonly;
|
|
}
|
|
|
|
void db_commit_transaction(struct db *db)
|
|
{
|
|
bool ok;
|
|
assert(db->in_transaction);
|
|
db_assert_no_outstanding_statements(db);
|
|
|
|
/* Increment before reporting changes to an eventual plugin. */
|
|
if (db->dirty)
|
|
db_data_version_incr(db);
|
|
|
|
db_report_changes(db, NULL, 0);
|
|
ok = db->config->commit_tx_fn(db);
|
|
|
|
if (!ok)
|
|
db_fatal(db, "Failed to commit DB transaction: %s", db->error);
|
|
|
|
db->in_transaction = NULL;
|
|
db->dirty = false;
|
|
}
|