修复云端实库迁移的文本主键序列处理并校正日志测试
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-20 01:52:59 +08:00
parent b062ca645e
commit 65e61a3c6d
4 changed files with 24 additions and 5 deletions

View File

@ -182,12 +182,18 @@ func copyPortableTables(ctx context.Context, source *sql.Conn, target *sql.DB, p
return err
}
if plan.Target.Type == config.DatabaseTypePostgreSQL && types["id"] != "" {
// 显式复制主键后必须推进序列,否则重启后的新增记录会冲突。
quoted := quotePostgreSQLIdentifier(table)
query := "SELECT setval(pg_get_serial_sequence($1, 'id'), COALESCE(MAX(id), 1), MAX(id) IS NOT NULL) FROM " + quoted
if _, err := transaction.ExecContext(ctx, query, quoted); err != nil {
var sequence sql.NullString
if err := transaction.QueryRowContext(ctx, "SELECT pg_get_serial_sequence($1, 'id')", quoted).Scan(&sequence); err != nil {
return ErrSQLDataVerification
}
// 只有绑定序列的主键需要推进;日志等文本主键不能参与数值聚合。
if sequence.Valid {
query := "SELECT setval($1::regclass, COALESCE(MAX(id), 1), MAX(id) IS NOT NULL) FROM " + quoted
if _, err := transaction.ExecContext(ctx, query, sequence.String); err != nil {
return ErrSQLDataVerification
}
}
}
}
if err := synchronizeSequences(ctx, transaction, plan, sequences); err != nil {

View File

@ -94,9 +94,11 @@ func runPortableIntegrationChain(t *testing.T, inputs []TargetInput) {
}
var subject string
queryErr := connection.QueryRowContext(ctx, "SELECT subject FROM email WHERE id=82").Scan(&subject)
var logPayload string
logErr := connection.QueryRowContext(ctx, "SELECT payload FROM mail_log WHERE id='migration-log'").Scan(&logPayload)
_, insertErr := connection.ExecContext(ctx, "INSERT INTO "+quoteDatabaseIdentifier(target.Type, "user")+" (account,name,password) VALUES ('next_hop_"+string(rune('a'+index))+"','sequence','test-only')")
_ = connection.Close()
if queryErr != nil || subject != "中文主题" || insertErr != nil {
if queryErr != nil || subject != "中文主题" || logErr != nil || logPayload != `{"message":"中文日志"}` || insertErr != nil {
t.Fatal("target content or identity sequence was not preserved")
}
source = target

View File

@ -34,6 +34,7 @@ func portableFixture(t *testing.T) (string, config.DatabaseConfig) {
`INSERT INTO "user" (id,account,name,password) VALUES (40,'review','测试','hash')`,
`INSERT INTO email (id,subject,text,create_time) VALUES (82,'中文主题','正文😀','2026-09-05 12:30:00.123456')`,
`INSERT INTO sessions (token,data,expiry) VALUES ('token',x'0001ff',julianday('2026-09-12 12:00:00'))`,
`INSERT INTO mail_log (id,created_at,kind,actor_id,action,section,result,payload) VALUES ('migration-log',1,'mail','40','迁移验证','','success','{"message":"中文日志"}')`,
`INSERT INTO plugin_user_setting (plugin_id,user_id,schema_version,settings_ciphertext,settings_hash,enabled,created_at,updated_at) VALUES ('test',40,1,'cipher','hash',1,'2026-09-05 12:00:00','2026-09-05 12:00:00')`,
} {
if _, err := database.Exec(query); err != nil {

View File

@ -73,7 +73,17 @@ func TestLogStoreServerDatabaseIntegration(t *testing.T) {
t.Fatal("dedicated test database is unavailable")
}
migrations := migration.DefaultMigrations()
for _, statement := range migrations[len(migrations)-2].StatementsFor(test.dialect) {
var logSchema []string
for _, item := range migrations {
if item.Name == "create_managed_logs" {
logSchema = item.StatementsFor(test.dialect)
break
}
}
if len(logSchema) == 0 {
t.Fatal("managed log schema migration is missing")
}
for _, statement := range logSchema {
if strings.HasPrefix(statement, "CREATE TABLE IF NOT EXISTS ") {
statement = strings.Replace(statement, "CREATE TABLE IF NOT EXISTS ", "CREATE TEMPORARY TABLE ", 1)
}