Run DB migrations on startup
This commit is contained in:
parent
c9f7675308
commit
c7344a9e37
@ -2,7 +2,11 @@
|
||||
|
||||
local migrate = require "migrate"
|
||||
|
||||
migrate.migrate()
|
||||
local DB_FILENAME = "peachy.db"
|
||||
sqlite3 = require "lsqlite3"
|
||||
local db = sqlite3.open(DB_FILENAME)
|
||||
db:exec("PRAGMA journal_mode=WAL;")
|
||||
migrate.migrate(db)
|
||||
|
||||
fm = require "fullmoon"
|
||||
fm.setTemplate("hello", "Hello, {%& name %}")
|
||||
|
@ -3,30 +3,92 @@ local path = require "path"
|
||||
|
||||
local filenameRegex = re.compile([=[^([0-9]+).*\.sql$]=])
|
||||
|
||||
local function migrate()
|
||||
local function throwSqlError(db, prefix)
|
||||
local code = db:errcode()
|
||||
if code ~= sqlite3.OK and code ~= sqlite3.ROW and code ~= sqlite3.DONE then
|
||||
if prefix then
|
||||
error(string.format("%s: %s", prefix, db:errmsg()))
|
||||
else
|
||||
error(db:errmsg())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function transact(db, func, errorPrefix)
|
||||
local name = string.format("savepoint_%d", math.abs(math.random(0)))
|
||||
db:exec(string.format("savepoint "..name))
|
||||
throwSqlError(db, errorPrefix)
|
||||
local success, result = pcall(func)
|
||||
if not success then
|
||||
db:exec("rollback to "..name)
|
||||
error(result, errorPrefix)
|
||||
end
|
||||
db:exec("release "..name)
|
||||
return result
|
||||
end
|
||||
|
||||
local function getCurrentMigration(db)
|
||||
for seqnum in db:urows("select max(seqnum) from migrations") do return seqnum end
|
||||
throwSqlError(db, "failed to get last migration sequence number", db)
|
||||
return -1
|
||||
end
|
||||
|
||||
local function recordMigration(db, seqnum)
|
||||
local stmt = db:prepare("insert into migrations (seqnum) values (?)")
|
||||
stmt:bind_values(seqnum)
|
||||
throwSqlError(db, "failed to record migration")
|
||||
stmt:step()
|
||||
end
|
||||
|
||||
local function migrate(db)
|
||||
local migrations = {}
|
||||
local seqnums = {}
|
||||
local paths = GetZipPaths("/migrations/")
|
||||
local MIGRATION_PATH = "/migrations/"
|
||||
local paths = GetZipPaths(MIGRATION_PATH)
|
||||
for _, p in ipairs(paths) do
|
||||
-- basename -> sequence number
|
||||
-- check that sequence number doesn't already exist
|
||||
local basename = path.basename(p)
|
||||
local _, seqnum = filenameRegex:search(basename)
|
||||
seqnum = tonumber(seqnum)
|
||||
if seqnum and not seqnums[seqnum] then
|
||||
table.insert(seqnums, seqnum)
|
||||
table.insert(migrations, {
|
||||
filename = p,
|
||||
seqnum = seqnum,
|
||||
})
|
||||
else
|
||||
print(string.format("found weird migration name: %s", p))
|
||||
if p ~= MIGRATION_PATH then
|
||||
local basename = path.basename(p)
|
||||
local _, seqnum = filenameRegex:search(basename)
|
||||
seqnum = tonumber(seqnum)
|
||||
if seqnum and not seqnums[seqnum] then
|
||||
table.insert(seqnums, seqnum)
|
||||
table.insert(migrations, {
|
||||
filename = p,
|
||||
seqnum = seqnum,
|
||||
})
|
||||
else
|
||||
print(string.format("found weird migration name: %s", p))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
db:exec[[
|
||||
create table if not exists migrations (
|
||||
seqnum integer primary key
|
||||
) without rowid
|
||||
]]
|
||||
throwSqlError(db, "failed to create migrations table")
|
||||
|
||||
table.sort(migrations, function(a, b) return a.seqnum < b.seqnum end)
|
||||
local announced
|
||||
for i,mig in ipairs(migrations) do
|
||||
print(string.format("%d: %s", i, mig.filename))
|
||||
local seqnum = mig.seqnum
|
||||
local lastSeqnum = getCurrentMigration(db)
|
||||
if seqnum > lastSeqnum then
|
||||
if not announced then
|
||||
print"Applying migrations:"
|
||||
announced = true
|
||||
end
|
||||
print(mig.filename)
|
||||
local migsql = LoadAsset(mig.filename)
|
||||
transact(db, function()
|
||||
db:exec(migsql)
|
||||
throwSqlError(db, "migration failed")
|
||||
recordMigration(db, seqnum)
|
||||
seqnum = seqnum + 1
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
create table node (
|
||||
id INT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
body TEXT NOT NULL
|
||||
);
|
||||
insert into node(title, body) values ("First post", "This is the beginning of a beautiful relationship.");
|
@ -0,0 +1,9 @@
|
||||
create table node_type (
|
||||
id INT PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
insert into node_type(id, name) values (0, "Basic post");
|
||||
|
||||
alter table node
|
||||
add column node_type_id INT NOT NULL references node_type(id) default 0;
|
Loading…
Reference in New Issue
Block a user