2022-05-01 22:47:00 +02:00
|
|
|
module db
|
|
|
|
|
|
|
|
import sqlite
|
2022-05-28 22:22:55 +02:00
|
|
|
import time
|
2022-05-01 22:47:00 +02:00
|
|
|
|
2022-06-22 20:17:11 +02:00
|
|
|
pub struct VieterDb {
|
2022-05-01 22:47:00 +02:00
|
|
|
conn sqlite.DB
|
|
|
|
}
|
|
|
|
|
2022-05-24 22:48:17 +02:00
|
|
|
struct MigrationVersion {
|
|
|
|
id int [primary]
|
|
|
|
version int
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2022-06-14 22:25:40 +02:00
|
|
|
migrations_up = [
|
|
|
|
$embed_file('migrations/001-initial/up.sql'),
|
|
|
|
$embed_file('migrations/002-rename-to-targets/up.sql'),
|
2022-06-17 13:45:21 +02:00
|
|
|
$embed_file('migrations/003-target-url-type/up.sql'),
|
|
|
|
]
|
|
|
|
migrations_down = [
|
|
|
|
$embed_file('migrations/001-initial/down.sql'),
|
|
|
|
$embed_file('migrations/002-rename-to-targets/down.sql'),
|
|
|
|
$embed_file('migrations/003-target-url-type/down.sql'),
|
2022-06-14 22:25:40 +02:00
|
|
|
]
|
2022-05-24 22:48:17 +02:00
|
|
|
)
|
|
|
|
|
2022-05-03 19:55:52 +02:00
|
|
|
// init initializes a database & adds the correct tables.
|
2022-11-01 21:59:18 +01:00
|
|
|
pub fn init(db_path string) !VieterDb {
|
|
|
|
conn := sqlite.connect(db_path)!
|
2022-05-01 22:47:00 +02:00
|
|
|
|
|
|
|
sql conn {
|
2022-05-24 22:48:17 +02:00
|
|
|
create table MigrationVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_version := sql conn {
|
|
|
|
select from MigrationVersion limit 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's no row yet, we add it here
|
|
|
|
if cur_version == MigrationVersion{} {
|
|
|
|
sql conn {
|
|
|
|
insert cur_version into MigrationVersion
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply each migration in order
|
|
|
|
for i in cur_version.version .. db.migrations_up.len {
|
|
|
|
migration := db.migrations_up[i].to_string()
|
|
|
|
|
|
|
|
version_num := i + 1
|
|
|
|
|
|
|
|
// vfmt does not like these dots
|
|
|
|
println('Applying migration $version_num' + '...')
|
|
|
|
|
|
|
|
// The sqlite library seems to not like it when multiple statements are
|
|
|
|
// passed in a single exec. Therefore, we split them & run them all
|
|
|
|
// separately.
|
|
|
|
for part in migration.split(';').map(it.trim_space()).filter(it != '') {
|
|
|
|
res := conn.exec_none(part)
|
|
|
|
|
|
|
|
if res != sqlite.sqlite_done {
|
|
|
|
return error('An error occurred while applying migration $version_num')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The where clause doesn't really matter, as there will always only be
|
|
|
|
// one entry anyways.
|
|
|
|
sql conn {
|
|
|
|
update MigrationVersion set version = version_num where id > 0
|
|
|
|
}
|
2022-05-01 22:47:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return VieterDb{
|
|
|
|
conn: conn
|
|
|
|
}
|
|
|
|
}
|
2022-05-28 22:22:55 +02:00
|
|
|
|
2022-05-29 22:16:39 +02:00
|
|
|
// row_into<T> converts an sqlite.Row into a given type T by parsing each field
|
|
|
|
// from a string according to its type.
|
2022-05-28 22:22:55 +02:00
|
|
|
pub fn row_into<T>(row sqlite.Row) T {
|
|
|
|
mut i := 0
|
|
|
|
mut out := T{}
|
|
|
|
|
|
|
|
$for field in T.fields {
|
|
|
|
$if field.typ is string {
|
|
|
|
out.$(field.name) = row.vals[i]
|
|
|
|
} $else $if field.typ is int {
|
|
|
|
out.$(field.name) = row.vals[i].int()
|
|
|
|
} $else $if field.typ is time.Time {
|
|
|
|
out.$(field.name) = time.unix(row.vals[i].int())
|
|
|
|
}
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|