go/mirko: add SQL migrations machinery
This uses github.com/golang-migrate/migrate and adds a Source that
allows using go_embed data files.
We also provide a test/example.
Change-Id: Icd2b6c7f7d0f728073b3fdf39b432b33ce61a3cd
diff --git a/go/mirko/tests/sql/BUILD.bazel b/go/mirko/tests/sql/BUILD.bazel
new file mode 100644
index 0000000..5831dcd
--- /dev/null
+++ b/go/mirko/tests/sql/BUILD.bazel
@@ -0,0 +1,10 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_test")
+
+go_test(
+ name = "go_default_test",
+ srcs = ["sql_test.go"],
+ deps = [
+ "//go/mirko/tests/sql/migrations:go_default_library",
+ "@com_github_golang_migrate_migrate_v4//database/sqlite3:go_default_library",
+ ],
+)
diff --git a/go/mirko/tests/sql/migrations/1564669958_one.down.sql b/go/mirko/tests/sql/migrations/1564669958_one.down.sql
new file mode 100644
index 0000000..cc1f647
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669958_one.down.sql
@@ -0,0 +1 @@
+DROP TABLE users;
diff --git a/go/mirko/tests/sql/migrations/1564669958_one.up.sql b/go/mirko/tests/sql/migrations/1564669958_one.up.sql
new file mode 100644
index 0000000..ddefda2
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669958_one.up.sql
@@ -0,0 +1,4 @@
+CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ username STRING
+);
diff --git a/go/mirko/tests/sql/migrations/1564669976_two.down.sql b/go/mirko/tests/sql/migrations/1564669976_two.down.sql
new file mode 100644
index 0000000..bfb3c33
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669976_two.down.sql
@@ -0,0 +1 @@
+DELETE FROM users WHERE username = 'q3k';
diff --git a/go/mirko/tests/sql/migrations/1564669976_two.up.sql b/go/mirko/tests/sql/migrations/1564669976_two.up.sql
new file mode 100644
index 0000000..b24d2fa
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669976_two.up.sql
@@ -0,0 +1 @@
+INSERT INTO users (username) VALUES ("q3k");
diff --git a/go/mirko/tests/sql/migrations/1564669988_three.down.sql b/go/mirko/tests/sql/migrations/1564669988_three.down.sql
new file mode 100644
index 0000000..345612e
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669988_three.down.sql
@@ -0,0 +1,7 @@
+ALTER TABLE users RENAME TO users_old;
+CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ username STRING
+);
+INSERT INTO users (username) SELECT (username) FROM users_old;
+DROP TABLE users_old;
diff --git a/go/mirko/tests/sql/migrations/1564669988_three.up.sql b/go/mirko/tests/sql/migrations/1564669988_three.up.sql
new file mode 100644
index 0000000..8cef579
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/1564669988_three.up.sql
@@ -0,0 +1 @@
+ALTER TABLE users ADD cool BOOLEAN NULL;
diff --git a/go/mirko/tests/sql/migrations/BUILD.bazel b/go/mirko/tests/sql/migrations/BUILD.bazel
new file mode 100644
index 0000000..585ea48
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//extras:embed_data.bzl", "go_embed_data")
+
+go_embed_data(
+ name = "migrations_data",
+ srcs = glob(["*.sql"]),
+ package = "migrations",
+ flatten = True,
+)
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "migrations.go",
+ ":migrations_data", # keep
+ ],
+ importpath = "code.hackerspace.pl/hscloud/go/mirko/tests/sql/migrations",
+ visibility = ["//go/mirko/tests/sql:__subpackages__"],
+ deps = [
+ "//go/mirko:go_default_library",
+ "@com_github_golang_migrate_migrate_v4//:go_default_library",
+ ],
+)
diff --git a/go/mirko/tests/sql/migrations/migrations.go b/go/mirko/tests/sql/migrations/migrations.go
new file mode 100644
index 0000000..1782c2e
--- /dev/null
+++ b/go/mirko/tests/sql/migrations/migrations.go
@@ -0,0 +1,17 @@
+package migrations
+
+import (
+ "fmt"
+
+ "code.hackerspace.pl/hscloud/go/mirko"
+
+ "github.com/golang-migrate/migrate/v4"
+)
+
+func New(dburl string) (*migrate.Migrate, error) {
+ source, err := mirko.NewMigrationsFromBazel(Data)
+ if err != nil {
+ return nil, fmt.Errorf("could not create migrations: %v", err)
+ }
+ return migrate.NewWithSourceInstance("bazel", source, dburl)
+}
diff --git a/go/mirko/tests/sql/sql_test.go b/go/mirko/tests/sql/sql_test.go
new file mode 100644
index 0000000..c76c3c1
--- /dev/null
+++ b/go/mirko/tests/sql/sql_test.go
@@ -0,0 +1,38 @@
+package sql
+
+import (
+ "testing"
+
+ _ "github.com/golang-migrate/migrate/v4/database/sqlite3"
+
+ "code.hackerspace.pl/hscloud/go/mirko/tests/sql/migrations"
+)
+
+// TestOkay goes up and down fully through migrations.
+func TestOkay(t *testing.T) {
+ m, err := migrations.New("sqlite3://:memory:")
+ if err != nil {
+ t.Fatalf("migrations.New: %v", err)
+ }
+
+ err = m.Up()
+ if err != nil {
+ t.Fatalf("m.Up() failed: %v", err)
+ }
+
+ vers, dirty, err := m.Version()
+ if err != nil {
+ t.Fatalf("m.Version() failed: %v", err)
+ }
+ if dirty {
+ t.Errorf("database migration shouldn't be dirty")
+ }
+ if want, got := uint(1564669988), vers; want != got {
+ t.Errorf("got database version %d, want %d", want, got)
+ }
+
+ err = m.Down()
+ if err != nil {
+ t.Fatalf("m.Down() failed: %v", err)
+ }
+}