mirror of
https://github.com/NixOS/nix.git
synced 2024-11-21 14:22:29 +00:00
Support fine-grained database schema migrations
Backward-compatible schema changes (e.g. those that add tables or nullable columns) now no longer need a change to the global schema file (/nix/var/nix/db/schema). Thus, old Nix versions can continue to access the database. This is especially useful for schema changes required by experimental features. In particular, it replaces the ad-hoc handling of the schema changes for CA derivations (i.e. the file /nix/var/nix/db/ca-schema). Schema versions 8 and 10 could have been handled by this mechanism in a backward-compatible way as well.
This commit is contained in:
parent
3db75b0060
commit
27ea437813
@ -95,51 +95,6 @@ struct LocalStore::State::Stmts {
|
|||||||
SQLiteStmt AddRealisationReference;
|
SQLiteStmt AddRealisationReference;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int getSchema(Path schemaPath)
|
|
||||||
{
|
|
||||||
int curSchema = 0;
|
|
||||||
if (pathExists(schemaPath)) {
|
|
||||||
auto s = readFile(schemaPath);
|
|
||||||
auto n = string2Int<int>(s);
|
|
||||||
if (!n)
|
|
||||||
throw Error("'%1%' is corrupt", schemaPath);
|
|
||||||
curSchema = *n;
|
|
||||||
}
|
|
||||||
return curSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
|
||||||
{
|
|
||||||
const int nixCASchemaVersion = 4;
|
|
||||||
int curCASchema = getSchema(schemaPath);
|
|
||||||
if (curCASchema != nixCASchemaVersion) {
|
|
||||||
if (curCASchema > nixCASchemaVersion) {
|
|
||||||
throw Error("current Nix store ca-schema is version %1%, but I only support %2%",
|
|
||||||
curCASchema, nixCASchemaVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lockFile(lockFd.get(), ltWrite, false)) {
|
|
||||||
printInfo("waiting for exclusive access to the Nix store for ca drvs...");
|
|
||||||
lockFile(lockFd.get(), ltNone, false); // We have acquired a shared lock; release it to prevent deadlocks
|
|
||||||
lockFile(lockFd.get(), ltWrite, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curCASchema == 0) {
|
|
||||||
static const char schema[] =
|
|
||||||
#include "ca-specific-schema.sql.gen.hh"
|
|
||||||
;
|
|
||||||
db.exec(schema);
|
|
||||||
curCASchema = nixCASchemaVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curCASchema < 4)
|
|
||||||
throw Error("experimental CA schema version %d is no longer supported", curCASchema);
|
|
||||||
|
|
||||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion), 0666, true);
|
|
||||||
lockFile(lockFd.get(), ltRead, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalStore::LocalStore(
|
LocalStore::LocalStore(
|
||||||
std::string_view scheme,
|
std::string_view scheme,
|
||||||
PathView path,
|
PathView path,
|
||||||
@ -316,6 +271,10 @@ LocalStore::LocalStore(
|
|||||||
|
|
||||||
openDB(*state, false);
|
openDB(*state, false);
|
||||||
|
|
||||||
|
/* Legacy database schema migrations. Don't bump 'schema' for
|
||||||
|
new migrations; instead, add a migration to
|
||||||
|
upgradeDBSchema(). */
|
||||||
|
|
||||||
if (curSchema < 8) {
|
if (curSchema < 8) {
|
||||||
SQLiteTxn txn(state->db);
|
SQLiteTxn txn(state->db);
|
||||||
state->db.exec("alter table ValidPaths add column ultimate integer");
|
state->db.exec("alter table ValidPaths add column ultimate integer");
|
||||||
@ -342,13 +301,7 @@ LocalStore::LocalStore(
|
|||||||
|
|
||||||
else openDB(*state, false);
|
else openDB(*state, false);
|
||||||
|
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
upgradeDBSchema(*state);
|
||||||
if (!readOnly) {
|
|
||||||
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
|
|
||||||
} else {
|
|
||||||
throw Error("need to migrate to content-addressed schema, but this cannot be done in read-only mode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare SQL statements. */
|
/* Prepare SQL statements. */
|
||||||
state->stmts->RegisterValidPath.create(state->db,
|
state->stmts->RegisterValidPath.create(state->db,
|
||||||
@ -483,7 +436,17 @@ std::string LocalStore::getUri()
|
|||||||
|
|
||||||
|
|
||||||
int LocalStore::getSchema()
|
int LocalStore::getSchema()
|
||||||
{ return nix::getSchema(schemaPath); }
|
{
|
||||||
|
int curSchema = 0;
|
||||||
|
if (pathExists(schemaPath)) {
|
||||||
|
auto s = readFile(schemaPath);
|
||||||
|
auto n = string2Int<int>(s);
|
||||||
|
if (!n)
|
||||||
|
throw Error("'%1%' is corrupt", schemaPath);
|
||||||
|
curSchema = *n;
|
||||||
|
}
|
||||||
|
return curSchema;
|
||||||
|
}
|
||||||
|
|
||||||
void LocalStore::openDB(State & state, bool create)
|
void LocalStore::openDB(State & state, bool create)
|
||||||
{
|
{
|
||||||
@ -566,6 +529,42 @@ void LocalStore::openDB(State & state, bool create)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LocalStore::upgradeDBSchema(State & state)
|
||||||
|
{
|
||||||
|
state.db.exec("create table if not exists SchemaMigrations (migration text primary key not null);");
|
||||||
|
|
||||||
|
std::set<std::string> schemaMigrations;
|
||||||
|
|
||||||
|
{
|
||||||
|
SQLiteStmt querySchemaMigrations;
|
||||||
|
querySchemaMigrations.create(state.db, "select migration from SchemaMigrations;");
|
||||||
|
auto useQuerySchemaMigrations(querySchemaMigrations.use());
|
||||||
|
while (useQuerySchemaMigrations.next())
|
||||||
|
schemaMigrations.insert(useQuerySchemaMigrations.getStr(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto doUpgrade = [&](const std::string & migrationName, const std::string & stmt)
|
||||||
|
{
|
||||||
|
if (schemaMigrations.contains(migrationName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
debug("executing Nix database schema migration '%s'...", migrationName);
|
||||||
|
|
||||||
|
SQLiteTxn txn(state.db);
|
||||||
|
state.db.exec(stmt + fmt(";\ninsert into SchemaMigrations values('%s')", migrationName));
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
schemaMigrations.insert(migrationName);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||||
|
doUpgrade(
|
||||||
|
"20220326-ca-derivations",
|
||||||
|
#include "ca-specific-schema.sql.gen.hh"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* To improve purity, users may want to make the Nix store a read-only
|
/* To improve purity, users may want to make the Nix store a read-only
|
||||||
bind mount. So make the Nix store writable for this process. */
|
bind mount. So make the Nix store writable for this process. */
|
||||||
void LocalStore::makeStoreWritable()
|
void LocalStore::makeStoreWritable()
|
||||||
|
@ -356,6 +356,8 @@ private:
|
|||||||
|
|
||||||
void openDB(State & state, bool create);
|
void openDB(State & state, bool create);
|
||||||
|
|
||||||
|
void upgradeDBSchema(State & state);
|
||||||
|
|
||||||
void makeStoreWritable();
|
void makeStoreWritable();
|
||||||
|
|
||||||
uint64_t queryValidPathId(State & state, const StorePath & path);
|
uint64_t queryValidPathId(State & state, const StorePath & path);
|
||||||
|
Loading…
Reference in New Issue
Block a user