fix(migrations): support MySQL 8.4 checks (#140)
This commit is contained in:
@@ -1,11 +1,26 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go-admin/app/goauto/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const mysqlCheckConstraintQuery = `
|
||||
SELECT cc.check_clause
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.check_constraints AS cc
|
||||
ON cc.constraint_catalog = tc.constraint_catalog
|
||||
AND cc.constraint_schema = tc.constraint_schema
|
||||
AND cc.constraint_name = tc.constraint_name
|
||||
WHERE tc.constraint_schema = DATABASE()
|
||||
AND tc.table_name = ?
|
||||
AND tc.constraint_type = 'CHECK'
|
||||
AND tc.constraint_name = ?
|
||||
LIMIT 1`
|
||||
|
||||
// Migrate creates the complete GoAuto MVP schema. AutoMigrate is intentionally
|
||||
// kept behind this package so the CLI migration and tests use the same model set.
|
||||
// MigratedModels is the single source of truth for which models make up the
|
||||
@@ -68,21 +83,23 @@ func ensureMySQLDirectSelectConstraint(db *gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
const name = "ck_purchase_task_spec_source"
|
||||
var total, current int64
|
||||
base := `SELECT COUNT(*) FROM information_schema.check_constraints WHERE constraint_schema = DATABASE() AND table_name = 'purchase_task' AND constraint_name = ?`
|
||||
if err := db.Raw(base, name).Scan(&total).Error; err != nil {
|
||||
var constraints []struct {
|
||||
CheckClause string `gorm:"column:check_clause"`
|
||||
}
|
||||
if err := db.Raw(mysqlCheckConstraintQuery, "purchase_task", name).Scan(&constraints).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Raw(base+` AND check_clause LIKE '%direct_select%'`, name).Scan(¤t).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if current > 0 {
|
||||
if len(constraints) > 0 && checkConstraintAllowsDirectSelect(constraints[0].CheckClause) {
|
||||
return nil
|
||||
}
|
||||
if total > 0 {
|
||||
if len(constraints) > 0 {
|
||||
if err := db.Exec("ALTER TABLE purchase_task DROP CHECK " + name).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return db.Exec("ALTER TABLE purchase_task ADD CONSTRAINT " + name + " CHECK (spec_source IN ('unresolved','manual_mapping','exact_match','ai_match','direct_select'))").Error
|
||||
}
|
||||
|
||||
func checkConstraintAllowsDirectSelect(clause string) bool {
|
||||
return strings.Contains(strings.ToLower(clause), "direct_select")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMySQLCheckConstraintQueryUsesTableConstraintsForTableName(t *testing.T) {
|
||||
query := strings.ToLower(mysqlCheckConstraintQuery)
|
||||
for _, required := range []string{
|
||||
"from information_schema.table_constraints as tc",
|
||||
"join information_schema.check_constraints as cc",
|
||||
"tc.table_name = ?",
|
||||
"tc.constraint_type = 'check'",
|
||||
"cc.constraint_schema = tc.constraint_schema",
|
||||
"cc.constraint_name = tc.constraint_name",
|
||||
} {
|
||||
if !strings.Contains(query, required) {
|
||||
t.Fatalf("metadata query missing %q: %s", required, mysqlCheckConstraintQuery)
|
||||
}
|
||||
}
|
||||
if strings.Contains(query, "cc.table_name") {
|
||||
t.Fatalf("MySQL 8.4 CHECK_CONSTRAINTS has no TABLE_NAME column: %s", mysqlCheckConstraintQuery)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckConstraintAllowsDirectSelect(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
clause string
|
||||
want bool
|
||||
}{
|
||||
{name: "legacy constraint", clause: "spec_source in ('unresolved','manual_mapping','exact_match','ai_match')", want: false},
|
||||
{name: "upgraded constraint", clause: "spec_source in ('unresolved','manual_mapping','exact_match','ai_match','direct_select')", want: true},
|
||||
{name: "mysql charset prefix", clause: "(`spec_source` in (_utf8mb4'DIRECT_SELECT',_utf8mb4'unresolved'))", want: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := checkConstraintAllowsDirectSelect(test.clause); got != test.want {
|
||||
t.Fatalf("checkConstraintAllowsDirectSelect(%q) = %v, want %v", test.clause, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user