feat(server): cmd/migrate up 接入 codes 库自建迁移(ApplyCodesLibMigrations)(#codes-lib)

顺带修 000020 sqlite 索引名冲突:ALTER TABLE RENAME 不会带着重命名索引,
legacy_codes 上遗留的 idx_codes_status 与 codes 库自己迁移里同名索引撞名,
一并改成 idx_legacy_codes_status(up/down 对称)。
This commit is contained in:
wangjia
2026-07-10 13:55:14 +08:00
parent 665749a90b
commit 5a9b5f1d8e
6 changed files with 67 additions and 0 deletions
+4
View File
@@ -12,6 +12,7 @@
package main
import (
"context"
"fmt"
"log"
"os"
@@ -44,6 +45,9 @@ func main() {
if err := store.MigrateUp(db, driver); err != nil {
log.Fatalf("migrate up: %v", err)
}
if err := store.ApplyCodesLibMigrations(context.Background(), db, driver); err != nil {
log.Fatalf("migrate up (codes lib): %v", err)
}
log.Println("migrate: up — done")
case "down":
+1
View File
@@ -17,6 +17,7 @@ require (
github.com/testcontainers/testcontainers-go v0.43.0
github.com/testcontainers/testcontainers-go/modules/mysql v0.43.0
github.com/testcontainers/testcontainers-go/modules/redis v0.43.0
github.com/wangjia/codes v0.0.0-00010101000000-000000000000
golang.org/x/crypto v0.52.0
golang.org/x/term v0.43.0
google.golang.org/grpc v1.81.1
@@ -0,0 +1,41 @@
package store_test
import (
"context"
"testing"
"github.com/wangjia/pangolin/server/internal/config"
"github.com/wangjia/pangolin/server/internal/store"
)
// TestApplyCodesLibMigrations verifies the shared codes library's tables are
// created on top of a fully-migrated pangolin sqlite DB (post-000020, the
// legacy tables are renamed away so the lib's `codes` table name is free).
func TestApplyCodesLibMigrations(t *testing.T) {
ctx := context.Background()
db, err := store.Open(&config.Config{Driver: "sqlite", DSN: ":memory:"})
if err != nil {
t.Fatalf("open: %v", err)
}
defer db.Close()
if err := store.MigrateUp(db, "sqlite"); err != nil {
t.Fatalf("MigrateUp: %v", err)
}
if err := store.ApplyCodesLibMigrations(ctx, db, "sqlite"); err != nil {
t.Fatalf("ApplyCodesLibMigrations: %v", err)
}
// 幂等:重复调用不报错(库自带 codes_schema_migrations 追踪表)。
if err := store.ApplyCodesLibMigrations(ctx, db, "sqlite"); err != nil {
t.Fatalf("ApplyCodesLibMigrations 2nd: %v", err)
}
for _, tbl := range []string{"codes", "codes_batches", "codes_audit_log", "codes_schema_migrations"} {
var name string
if err := db.QueryRow(
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, tbl,
).Scan(&name); err != nil {
t.Errorf("lib table %q missing: %v", tbl, err)
}
}
}
+14
View File
@@ -1,6 +1,7 @@
package store
import (
"context"
"database/sql"
"errors"
"fmt"
@@ -11,6 +12,7 @@ import (
migratemysql "github.com/golang-migrate/migrate/v4/database/mysql"
migratesqlite "github.com/golang-migrate/migrate/v4/database/sqlite"
"github.com/golang-migrate/migrate/v4/source/iofs"
libcodes "github.com/wangjia/codes"
"github.com/wangjia/pangolin/server/internal/db"
"github.com/wangjia/pangolin/server/migrations"
@@ -24,6 +26,18 @@ func MigrateUp(database *sql.DB, driver string) error {
})
}
// ApplyCodesLibMigrations creates/updates the shared codes library's own
// tables (codes / codes_batches / codes_audit_log, tracked by the lib's
// codes_schema_migrations). Runs AFTER pangolin's golang-migrate set — 000020
// must have renamed the legacy `codes` table away first. Idempotent.
func ApplyCodesLibMigrations(ctx context.Context, database *sql.DB, driver string) error {
d := libcodes.DialectMySQL
if db.Normalize(driver) == "sqlite" {
d = libcodes.DialectSQLite
}
return libcodes.ApplyMigrations(ctx, database, d)
}
// MigrateDown rolls back all applied migrations. ErrNoChange = success.
func MigrateDown(database *sql.DB, driver string) error {
return runMigration(database, driver, func(m *migrate.Migrate) error {
@@ -1,2 +1,4 @@
DROP INDEX idx_legacy_codes_status;
ALTER TABLE legacy_code_batches RENAME TO code_batches;
ALTER TABLE legacy_codes RENAME TO codes;
CREATE INDEX idx_codes_status ON codes (status);
@@ -1,3 +1,8 @@
-- 同 mysql 版注释;sqlite ≥3.25 的 RENAME 会自动更新其他表 FK 引用。
ALTER TABLE codes RENAME TO legacy_codes;
ALTER TABLE code_batches RENAME TO legacy_code_batches;
-- sqlite 的索引名是库级全局命名空间,ALTER TABLE RENAME 不会连带重命名索引:
-- idx_codes_status 仍挂在 legacy_codes 上但保留旧名,会跟 codes 库自己迁移里
-- 同名的 idx_codes_status(建在它的新 codes 表上)冲突。这里随表改名一并重建索引名。
DROP INDEX idx_codes_status;
CREATE INDEX idx_legacy_codes_status ON legacy_codes (status);