diff --git a/test/config.json b/test/config.json index 9818731..5d74007 100644 --- a/test/config.json +++ b/test/config.json @@ -5,6 +5,11 @@ "name": "FSW", "path": "./dataw" }, + { + "type": "fs", + "name": "Migrations", + "path": "./migrations" + }, { "type": "kv", "name": "KV", diff --git a/test/migrations/manual.1.sql b/test/migrations/manual.1.sql new file mode 100644 index 0000000..94b5359 --- /dev/null +++ b/test/migrations/manual.1.sql @@ -0,0 +1 @@ +CREATE TABLE manual(id); diff --git a/test/migrations/manual.2.sql b/test/migrations/manual.2.sql new file mode 100644 index 0000000..691bef1 --- /dev/null +++ b/test/migrations/manual.2.sql @@ -0,0 +1 @@ +DROP TABLE manual; diff --git a/test/migrations/schema.1.sql b/test/migrations/schema.1.sql new file mode 100644 index 0000000..8dc2c09 --- /dev/null +++ b/test/migrations/schema.1.sql @@ -0,0 +1,3 @@ +CREATE TABLE test(data); + +PRAGMA user_version = 1; diff --git a/test/migrations/schema.10.sql b/test/migrations/schema.10.sql new file mode 100644 index 0000000..790bef0 --- /dev/null +++ b/test/migrations/schema.10.sql @@ -0,0 +1,3 @@ +DROP INDEX idx_test; + +PRAGMA user_version = 10; diff --git a/test/migrations/schema.2.sql b/test/migrations/schema.2.sql new file mode 100644 index 0000000..6053b29 --- /dev/null +++ b/test/migrations/schema.2.sql @@ -0,0 +1,3 @@ +CREATE INDEX idx_test ON test(data); + +PRAGMA user_version = 2; diff --git a/test/templates/.migration.html b/test/templates/.migration.html new file mode 100644 index 0000000..129f7c8 --- /dev/null +++ b/test/templates/.migration.html @@ -0,0 +1,55 @@ +{{define "create-migration-tables"}} +{{.DB.Exec `CREATE TABLE IF NOT EXISTS migration_files(id INT PRIMARY KEY, name TEXT NOT NULL) STRICT;`}} +{{.DB.Exec `CREATE TABLE IF NOT EXISTS migrations(id ANY, run_date INT NOT NULL, stmt TEXT NOT NULL, error TEXT, result TEXT, ok INT GENERATED ALWAYS AS (error IS NULL)) STRICT;`}} +{{.DB.Exec `CREATE INDEX IF NOT EXISTS successful_migrations ON migrations(id) WHERE ok;`}} +{{end}} + + +{{define "migrate"}} +{{$applied := .DB.QueryVal `SELECT COUNT(id) FROM migrations WHERE ok AND id=?;` .ID}} +{{if eq $applied 0}} +{{$result := try .DB `Exec` .STMT}} +{{if $result.OK}} +{{$.DB.Exec `INSERT INTO migrations VALUES(?,?,?,NULL,?);` .ID now .STMT ($result.Value | toJson)}} +{{else}} +{{$.DB.Exec `INSERT INTO migrations VALUES(?,?,?,?,NULL);` .ID now .STMT ($result.Error | toJson)}} +{{failf "failed to apply migration. id: %d, sqlerror: %s" .ID $result.Error}} +{{end}} +{{end}} +{{end}} + + +{{define "migrate-dir"}} +{{range .DIR.List "."}} + {{$id := atoi (regexReplaceAll $.RE .Name "$1")}} + {{if eq $id 0}}{{continue}}{{end}} + {{.DB.Exec `INSERT INTO migration_files VALUES(?1,?2) ON CONFLICT DO UPDATE SET name = ?2;` $id .Name}} +{{end}} +{{range .DB.QueryRows `SELECT name,id FROM migration_files WHERE id > ? ORDER BY id;` (.DB.QueryVal `PRAGMA user_version;`)}} + {{$stmt := $.DIR.Read .name}} + {{$result := try $.DB `Exec` $stmt}} + {{if $result.OK}} + {{$.DB.Exec `INSERT INTO migrations VALUES(?,?,?,NULL,?);` .id now $stmt ($result.Value | toJson)}} + {{$.DB.Exec (printf `PRAGMA user_version=%d;` .id)}} + {{else}} + {{$.DB.Exec `INSERT INTO migrations VALUES(?,?,?,?,NULL);` .id now $stmt ($result.Error | toJson)}} + {{failf "failed to apply migration. id: %d, sqlerror: %s" .ID $result.Error}} + {{end}} +{{end}} +{{end}} diff --git a/test/templates/db/.init.html b/test/templates/db/.init.html new file mode 100644 index 0000000..e25e16b --- /dev/null +++ b/test/templates/db/.init.html @@ -0,0 +1,5 @@ + +{{define "INIT db"}} +{{template "create-migration-tables" .}} +{{template "migrate-dir" (dict "DB" .DB "DIR" .Migrations "RE" `^schema\.(\d+)\.sql$`)}} +{{end}} diff --git a/test/templates/db/index.html b/test/templates/db/index.html new file mode 100644 index 0000000..b31de58 --- /dev/null +++ b/test/templates/db/index.html @@ -0,0 +1,5 @@ + + +

{{.DB.Migrate .Migrations `^schema\.(\d+)\.sql$` (.DB.QueryVal `PRAGMA user_version;`)}} migrations applied.

+ +

Current user_version: {{.DB.QueryVal `PRAGMA user_version`}}

diff --git a/test/templates/db/manual.html b/test/templates/db/manual.html new file mode 100644 index 0000000..cc688ed --- /dev/null +++ b/test/templates/db/manual.html @@ -0,0 +1,32 @@ + + + +{{$RE := `^manual\.(\d+)\.sql$`}} +

Run a manual migration:

+{{range .Migrations.List "."}} +{{$id := atoi (regexReplaceAll $RE .Name "$1")}} +{{if eq $id 0}}{{continue}}{{end}} +
+{{end}} + +

Results:

+ + +{{define "POST /db/run"}} +{{.Req.ParseForm}} +{{$id := atoi (.Req.FormValue "id")}} +{{if eq $id 0}}
  • Invalid id: {{$id}}
  • {{return}}{{end}} +{{$file := printf "manual.%d.sql" $id}} +{{$result := try .Migrations `Read` $file}} +{{if not $result.OK}}
  • Failed to read file {{$file}}: {{$result.Error}}
  • {{return}}{{end}} +{{$stmt := $result.Value}} +{{$result := try .DB `Exec` $stmt}} +
  • {{now | date "2006-01-02 15:04:05"}}: +{{if $result.OK}} +Applied migration {{$id}}. +{{else}} +Migration error: {{$result.Error}} +{{end}} +
  • +{{end}} diff --git a/test/templates/index{$}.html b/test/templates/index{$}.html index 25df9ad..756aee8 100644 --- a/test/templates/index{$}.html +++ b/test/templates/index{$}.html @@ -7,6 +7,7 @@
    Navigate to different tests: