mirror of
https://github.com/mudita/MuditaOS.git
synced 2025-12-25 15:08:02 -05:00
Fixed issues with restoring from legacy backups and cases when the database set fetched from backup package is different than the system one.
176 lines
5.7 KiB
Lua
176 lines
5.7 KiB
Lua
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
|
|
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
--- Database migration scripts
|
|
-- @module migration
|
|
local sqlite3 = require("lsqlite3complete")
|
|
local lfs = require("lfs")
|
|
local helpers = require("helpers")
|
|
|
|
local up_script = "up.sql"
|
|
local down_script = "down.sql"
|
|
|
|
migration = {}
|
|
|
|
--- Return codes
|
|
-- @table retcode
|
|
migration.retcode = {
|
|
OK = 0,
|
|
ALREADY_UP_TO_DATE = 1,
|
|
WRONG_TARGET_VERSION = 2
|
|
}
|
|
|
|
local function build_script_array(path, db_name, filename)
|
|
local scripts = {}
|
|
for file in lfs.dir(path .. "/" .. db_name) do
|
|
if file ~= "." and file ~= ".." and tonumber(file) then
|
|
local f = path .. '/' .. db_name .. '/' .. file .. '/' .. filename
|
|
local attr = lfs.attributes(f)
|
|
assert(type(attr) == "table")
|
|
scripts[tonumber(file)] = helpers.read_whole_file(f)
|
|
end
|
|
end
|
|
return scripts
|
|
end
|
|
|
|
local function build_database_path(path, db_name)
|
|
return path .. "/" .. db_name .. ".db"
|
|
end
|
|
|
|
local function db_exec(file, script, version)
|
|
local db = assert(sqlite3.open(file))
|
|
assert(db:exec(script) == sqlite3.OK, string.format("Script execution failed:\n%s\n", script))
|
|
assert(db:exec(string.format("PRAGMA user_version=%u;", version)) == sqlite3.OK,
|
|
string.format("Setting database version: %d failed", version))
|
|
db:close()
|
|
end
|
|
|
|
local function read_db_version(file)
|
|
local db = assert(sqlite3.open(file))
|
|
local stmt = assert(db:prepare("PRAGMA user_version;"))
|
|
local ret = {}
|
|
for v in stmt:urows() do
|
|
ret = v
|
|
end
|
|
db:close()
|
|
return ret
|
|
end
|
|
|
|
local function db_migrate_up(db_path, scripts, target_version)
|
|
local current_version = read_db_version(db_path)
|
|
|
|
if current_version == target_version then
|
|
return migration.retcode.ALREADY_UP_TO_DATE
|
|
end
|
|
|
|
if current_version > target_version then
|
|
return migration.retcode.WRONG_TARGET_VERSION
|
|
end
|
|
|
|
for v = current_version, target_version - 1 do
|
|
db_exec(db_path, scripts[v + 1], v + 1)
|
|
end
|
|
return migration.retcode.OK
|
|
end
|
|
|
|
local function db_migrate_down(db_path, scripts, target_version)
|
|
local current_version = read_db_version(db_path)
|
|
|
|
if current_version == target_version then
|
|
return migration.retcode.ALREADY_UP_TO_DATE
|
|
end
|
|
|
|
if current_version < target_version then
|
|
return migration.retcode.WRONG_TARGET_VERSION
|
|
end
|
|
|
|
for v = current_version, target_version + 1, -1 do
|
|
db_exec(db_path, scripts[v], v - 1)
|
|
end
|
|
return migration.retcode.OK
|
|
end
|
|
|
|
local function print_db_set(db_set)
|
|
print("Database set(name,version):")
|
|
for name, version in pairs(db_set) do
|
|
print(string.format(" '%s':%d", name, version))
|
|
end
|
|
end
|
|
|
|
local function validate_inputs(migration_dir, db_dir)
|
|
assert(helpers.exists(migration_dir), "Migration directory does not exist")
|
|
assert(helpers.exists(db_dir), "Databases directory does not exist")
|
|
return true
|
|
end
|
|
|
|
local function migrate(db_path, scripts_up, scripts_down, target_version)
|
|
local db_version = read_db_version(db_path)
|
|
|
|
if db_version > target_version then
|
|
print(string.format("Performing migration-down of '%s' from version %d to %d", db_path, db_version,
|
|
target_version))
|
|
return migration.down(db_path, scripts_down, target_version)
|
|
end
|
|
if db_version < target_version then
|
|
print(
|
|
string.format("Performing migration-up of '%s' from version %d to %d", db_path, db_version, target_version))
|
|
return migration.up(db_path, scripts_up, target_version)
|
|
end
|
|
|
|
print(string.format("Migration not needed, '%s' is already the newest version", db_path))
|
|
return migration.retcode.OK
|
|
end
|
|
|
|
--- Perform database migration from lower to higher version
|
|
-- @function up
|
|
-- @param db_path path to the database we want to perform migration on
|
|
-- @param scripts array of {""}
|
|
-- @param target_version database version we want to migrate in to
|
|
-- @return @{retcode}
|
|
function migration.up(db_path, scripts, target_version)
|
|
return db_migrate_up(db_path, scripts, target_version)
|
|
end
|
|
|
|
--- Perform database migration from higher to lower version
|
|
-- @function down
|
|
-- @param db_path path to the database we want to perform migration on
|
|
-- @param scripts array of {""}
|
|
-- @param target_version database version we want to migrate in to
|
|
-- @return @{retcode}
|
|
function migration.down(db_path, scripts, target_version)
|
|
return db_migrate_down(db_path, scripts, target_version)
|
|
end
|
|
|
|
--- Read the database version
|
|
-- @function get_db_version
|
|
-- @param db_path path to the database
|
|
-- @return database version number
|
|
function migration.get_db_version(db_path)
|
|
return read_db_version(db_path)
|
|
end
|
|
|
|
--- Perform databases migration(up/down) automatically
|
|
-- @function migrate
|
|
-- @param db_dir location of the databases
|
|
-- @param scripts_dir location of the database migration scripts
|
|
-- @param db_set array of {<"database_name"> = <db_target_version>} entries
|
|
-- @return @{retcode}
|
|
function migration.migrate(db_dir, scripts_dir, db_set)
|
|
print(string.format("Migration scripts directory: '%s'", scripts_dir))
|
|
print(string.format("Databases directory: '%s'", db_dir))
|
|
print_db_set(db_set)
|
|
|
|
for name, version in pairs(db_set) do
|
|
local database_path = build_database_path(db_dir, name)
|
|
local scripts_up = build_script_array(scripts_dir, name, up_script)
|
|
local scripts_down = build_script_array(scripts_dir, name, down_script)
|
|
local ret = migrate(database_path, scripts_up, scripts_down, version)
|
|
if ret ~= migration.retcode.OK then
|
|
return ret
|
|
end
|
|
end
|
|
|
|
return migration.retcode.OK
|
|
end
|
|
|
|
return migration
|