Skip to content

Commit

Permalink
Add example migration templates
Browse files Browse the repository at this point in the history
  • Loading branch information
infogulch committed May 29, 2024
1 parent 90ab0aa commit 17115ea
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 0 deletions.
5 changes: 5 additions & 0 deletions test/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"name": "FSW",
"path": "./dataw"
},
{
"type": "fs",
"name": "Migrations",
"path": "./migrations"
},
{
"type": "kv",
"name": "KV",
Expand Down
1 change: 1 addition & 0 deletions test/migrations/manual.1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE TABLE manual(id);
1 change: 1 addition & 0 deletions test/migrations/manual.2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE manual;
3 changes: 3 additions & 0 deletions test/migrations/schema.1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE test(data);

PRAGMA user_version = 1;
3 changes: 3 additions & 0 deletions test/migrations/schema.10.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP INDEX idx_test;

PRAGMA user_version = 10;
3 changes: 3 additions & 0 deletions test/migrations/schema.2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE INDEX idx_test ON test(data);

PRAGMA user_version = 2;
55 changes: 55 additions & 0 deletions test/templates/.migration.html
Original file line number Diff line number Diff line change
@@ -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}}

<!--
'migrate' accepts a dot value with fields DB, ID, and STMT. Performs a database
migration by executing the statement if a migration with the same ID has not
succeeded. Must call `create-migration-tables` once before calling this
template.
Invoke like: {{template "migrate" (dict "DB" .DB "ID" 2024032816100 "STMT" `ALTER TABLE user ADD last_login DATETIME`)}}
-->
{{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}}

<!--
'migrate-dir' accepts a dot value with fields DB, DIR, and RE. Performs a
database migration using the files listed in DIR that match the regex pattern
RE. The RE patternn must have a subpattern that matches a nonzero numeric string
within the file name which is the migration id. Migration IDs greater than the
current `PRAGMA user_version` are run in id order. Must call
`create-migration-tables` once before calling this template.
Invoke like {{template "migrate-dir" (dict "DB" .DB "DIR" .FS "RE" `^schema\.(\d+)\.sql$`)}}
-->
{{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}}
5 changes: 5 additions & 0 deletions test/templates/db/.init.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- this runs at startup -->
{{define "INIT db"}}
{{template "create-migration-tables" .}}
{{template "migrate-dir" (dict "DB" .DB "DIR" .Migrations "RE" `^schema\.(\d+)\.sql$`)}}
{{end}}
5 changes: 5 additions & 0 deletions test/templates/db/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!DOCTYPE html>

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

<p>Current user_version: {{.DB.QueryVal `PRAGMA user_version`}}</p>
32 changes: 32 additions & 0 deletions test/templates/db/manual.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<script src="https://unpkg.com/htmx.org@1.9.12"></script>

{{$RE := `^manual\.(\d+)\.sql$`}}
<p>Run a manual migration:</p>
{{range .Migrations.List "."}}
{{$id := atoi (regexReplaceAll $RE .Name "$1")}}
{{if eq $id 0}}{{continue}}{{end}}
<form><button hx-post="/db/run" hx-target="#results" hx-swap="beforeend">{{.Name}} ({{$id}})</button><input type="hidden" name="id" value="{{$id}}"></form>
{{end}}

<p>Results:</p>
<ul id="results">
</ul>

{{define "POST /db/run"}}
{{.Req.ParseForm}}
{{$id := atoi (.Req.FormValue "id")}}
{{if eq $id 0}}<li>Invalid id: {{$id}}</li>{{return}}{{end}}
{{$file := printf "manual.%d.sql" $id}}
{{$result := try .Migrations `Read` $file}}
{{if not $result.OK}}<li>Failed to read file <code>{{$file}}</code>: <code>{{$result.Error}}</code></li>{{return}}{{end}}
{{$stmt := $result.Value}}
{{$result := try .DB `Exec` $stmt}}
<li>{{now | date "2006-01-02 15:04:05"}}:
{{if $result.OK}}
Applied migration {{$id}}.
{{else}}
Migration error: <code>{{$result.Error}}</code>
{{end}}
</li>
{{end}}
1 change: 1 addition & 0 deletions test/templates/index{$}.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<div>
Navigate to different tests:
<ul>
<li><a href="/db/migrate-manual">DB</a></li>
<li><a href="/fs/">FS</a></li>
<li><a href="/kv/">KV</a></li>
<li><a href="/nats/">Nats</a></li>
Expand Down
15 changes: 15 additions & 0 deletions test/tests/db.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
GET http://localhost:8080/db/manual

HTTP 200
[Asserts]
body contains "Run a manual migration"
body contains "manual.1.sql (1)"


POST http://localhost:8080/db/run
[FormParams]
id: 1

HTTP 200
[Asserts]
body contains "Applied migration 1."

0 comments on commit 17115ea

Please sign in to comment.