fediglam/src/main/migrations.zig

187 lines
5.4 KiB
Zig
Raw Normal View History

2022-09-15 01:12:07 +00:00
const std = @import("std");
2022-07-30 07:26:35 +00:00
const sql = @import("sql");
const DateTime = @import("util").DateTime;
pub const Migration = struct {
2022-09-15 01:12:07 +00:00
name: [:0]const u8,
2022-07-30 07:26:35 +00:00
up: []const u8,
down: []const u8,
};
fn firstIndexOf(str: []const u8, char: u8) ?usize {
for (str) |ch, i| {
if (ch == char) return i;
}
return null;
}
2022-09-15 01:12:07 +00:00
fn execStmt(tx: sql.Tx, stmt: []const u8, alloc: std.mem.Allocator) !void {
const stmt_null = try std.cstr.addNullByte(alloc, stmt);
defer alloc.free(stmt_null);
try tx.exec(stmt_null, .{}, null);
2022-07-30 07:26:35 +00:00
}
2022-09-15 01:12:07 +00:00
fn execScript(db: sql.Db, script: []const u8, alloc: std.mem.Allocator) !void {
const tx = try db.begin();
errdefer tx.rollback();
2022-07-30 07:26:35 +00:00
var remaining = script;
while (firstIndexOf(remaining, ';')) |last| {
2022-09-15 01:12:07 +00:00
try execStmt(tx, remaining[0 .. last + 1], alloc);
2022-07-30 07:26:35 +00:00
remaining = remaining[last + 1 ..];
}
2022-09-15 01:12:07 +00:00
if (remaining.len > 1) try execStmt(tx, remaining, alloc);
2022-07-30 07:26:35 +00:00
2022-09-15 01:12:07 +00:00
try tx.commit();
2022-07-30 07:26:35 +00:00
}
2022-09-15 01:12:07 +00:00
fn wasMigrationRan(db: sql.Db, name: []const u8, alloc: std.mem.Allocator) !bool {
const row = (try db.queryRow(&.{i32}, "SELECT COUNT(*) FROM migration WHERE name = $1", .{name}, alloc)) orelse return false;
return row[0] != 0;
2022-07-30 07:26:35 +00:00
}
2022-09-15 01:12:07 +00:00
pub fn up(db: sql.Db) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
std.log.info("Running migrations...", .{});
try execScript(db, create_migration_table, gpa.allocator());
2022-07-30 07:26:35 +00:00
for (migrations) |migration| {
2022-09-15 01:12:07 +00:00
const was_ran = try wasMigrationRan(db, migration.name, gpa.allocator());
if (!was_ran) {
std.log.info("Running migration {s}", .{migration.name});
try execScript(db, migration.up, gpa.allocator());
try db.insert("migration", .{ .name = migration.name });
2022-07-30 07:26:35 +00:00
}
}
}
const create_migration_table =
\\CREATE TABLE IF NOT EXISTS
\\migration(
\\ name TEXT NOT NULL PRIMARY KEY,
2022-09-15 01:12:07 +00:00
\\ applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-05 09:15:16 +00:00
\\);
2022-07-30 07:26:35 +00:00
;
// NOTE: Until the first public release, i may collapse multiple
// migrations into a single one. this will require db recreation
const migrations: []const Migration = &.{
.{
2022-09-07 23:14:52 +00:00
.name = "users",
2022-07-30 07:26:35 +00:00
.up =
\\CREATE TABLE user(
\\ id TEXT NOT NULL PRIMARY KEY,
\\ username TEXT NOT NULL,
\\
2022-09-15 01:12:07 +00:00
\\ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-05 08:52:49 +00:00
\\);
2022-07-30 07:26:35 +00:00
\\
2022-09-07 23:14:52 +00:00
\\CREATE TABLE local_user(
2022-07-30 07:26:35 +00:00
\\ user_id TEXT NOT NULL PRIMARY KEY REFERENCES user(id),
2022-09-07 23:14:52 +00:00
\\
\\ email TEXT
2022-09-05 08:52:49 +00:00
\\);
2022-07-30 07:26:35 +00:00
\\
2022-09-07 23:14:52 +00:00
\\CREATE TABLE account_password(
2022-07-30 07:26:35 +00:00
\\ user_id TEXT NOT NULL PRIMARY KEY REFERENCES user(id),
\\
2022-09-07 23:14:52 +00:00
\\ hashed_password BLOB NOT NULL
2022-09-05 08:52:49 +00:00
\\);
2022-07-30 07:26:35 +00:00
,
.down =
2022-09-07 23:14:52 +00:00
\\DROP TABLE account_password;
2022-07-30 07:26:35 +00:00
\\DROP TABLE local_user;
\\DROP TABLE user;
,
},
.{
.name = "notes",
.up =
\\CREATE TABLE note(
\\ id TEXT NOT NULL,
\\
\\ content TEXT NOT NULL,
2022-09-07 23:14:52 +00:00
\\ author_id TEXT NOT NULL REFERENCES user(id),
2022-07-30 07:26:35 +00:00
\\
2022-09-15 01:12:07 +00:00
\\ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-07 23:14:52 +00:00
\\);
2022-07-30 07:26:35 +00:00
,
.down = "DROP TABLE note;",
},
.{
.name = "note reactions",
.up =
\\CREATE TABLE reaction(
\\ id TEXT NOT NULL PRIMARY KEY,
\\
2022-09-07 23:14:52 +00:00
\\ user_id TEXT NOT NULL REFERENCES user(id),
2022-07-30 07:26:35 +00:00
\\ note_id TEXT NOT NULL REFERENCES note(id),
\\
2022-09-15 01:12:07 +00:00
\\ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-07 23:14:52 +00:00
\\);
2022-07-30 07:26:35 +00:00
,
.down = "DROP TABLE reaction;",
},
.{
.name = "user tokens",
.up =
\\CREATE TABLE token(
2022-09-07 23:14:52 +00:00
\\ hash TEXT NOT NULL PRIMARY KEY,
2022-07-30 07:26:35 +00:00
\\ user_id TEXT NOT NULL REFERENCES local_user(id),
\\
2022-09-15 01:12:07 +00:00
\\ issued_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-07 23:14:52 +00:00
\\);
2022-07-30 07:26:35 +00:00
,
.down = "DROP TABLE token;",
},
.{
.name = "user invites",
.up =
\\CREATE TABLE invite(
\\ id TEXT NOT NULL PRIMARY KEY,
\\
\\ name TEXT NOT NULL,
2022-09-08 06:56:29 +00:00
\\ code TEXT NOT NULL UNIQUE,
2022-07-30 07:26:35 +00:00
\\ created_by TEXT NOT NULL REFERENCES local_user(id),
\\
\\ max_uses INTEGER,
\\
2022-09-15 01:12:07 +00:00
\\ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
\\ expires_at TIMESTAMPTZ,
2022-09-08 06:56:29 +00:00
\\
\\ type TEXT NOT NULL CHECK (type in ('system', 'community_owner', 'user'))
2022-09-07 23:14:52 +00:00
\\);
2022-07-30 07:26:35 +00:00
\\ALTER TABLE local_user ADD COLUMN invite_id TEXT REFERENCES invite(id);
,
.down =
\\ALTER TABLE local_user DROP COLUMN invite_id;
\\DROP TABLE invite;
,
},
2022-08-02 04:33:23 +00:00
.{
.name = "communities",
.up =
\\CREATE TABLE community(
\\ id TEXT NOT NULL PRIMARY KEY,
\\
2022-09-08 06:56:29 +00:00
\\ owner_id TEXT REFERENCES user(id),
2022-08-02 04:33:23 +00:00
\\ name TEXT NOT NULL,
\\ host TEXT NOT NULL UNIQUE,
\\ scheme TEXT NOT NULL CHECK (scheme IN ('http', 'https')),
\\
2022-09-15 01:12:07 +00:00
\\ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
2022-09-05 09:15:16 +00:00
\\);
2022-08-02 04:33:23 +00:00
\\ALTER TABLE user ADD COLUMN community_id TEXT REFERENCES community(id);
\\ALTER TABLE invite ADD COLUMN to_community TEXT REFERENCES community(id);
,
.down =
\\ALTER TABLE invite DROP COLUMN to_community;
\\ALTER TABLE user DROP COLUMN community_id;
\\DROP TABLE community;
,
},
2022-07-30 07:26:35 +00:00
};