mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 16:23:27 +08:00
🐛 fix(schema): 修复 PostgreSQL 模式备份恢复与重命名
- 模式备份脚本写入安全转义的 CREATE SCHEMA IF NOT EXISTS - 支持仅大小写变化的 PostgreSQL 模式重命名 - 补充备份头顺序、非 PostgreSQL 隔离和模式 DDL 回归测试 Fixes #480
This commit is contained in:
@@ -322,7 +322,7 @@ func buildRenameSchemaSQLWithText(dbType string, oldSchemaName string, newSchema
|
||||
if oldSchemaName == "" || newSchemaName == "" {
|
||||
return "", fmt.Errorf("%s", text("db.backend.error.schema_name_required", nil))
|
||||
}
|
||||
if strings.EqualFold(oldSchemaName, newSchemaName) {
|
||||
if oldSchemaName == newSchemaName {
|
||||
return "", fmt.Errorf("%s", text("db.backend.error.schema_same_name", nil))
|
||||
}
|
||||
if !isPostgresSchemaDDLDBType(dbType) {
|
||||
|
||||
@@ -406,7 +406,7 @@ func TestMethodsDBSchemaDDLUsesEnglishMessages(t *testing.T) {
|
||||
t.Fatalf("expected localized schema name message, got %q", missingSchemaName.Message)
|
||||
}
|
||||
|
||||
sameSchemaName := app.RenameSchema(connection.ConnectionConfig{Type: "postgres", Database: "tenant"}, "", "sales", "SALES")
|
||||
sameSchemaName := app.RenameSchema(connection.ConnectionConfig{Type: "postgres", Database: "tenant"}, "", "sales", "sales")
|
||||
if sameSchemaName.Success {
|
||||
t.Fatalf("RenameSchema with same names returned success: %+v", sameSchemaName)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,17 @@ func TestBuildRenameSchemaSQL_PostgresQuotesIdentifiers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRenameSchemaSQL_PostgresAllowsCaseOnlyRename(t *testing.T) {
|
||||
got, err := buildRenameSchemaSQL("postgresql", "sales", "SALES")
|
||||
if err != nil {
|
||||
t.Fatalf("expected postgres case-only schema rename SQL, got error: %v", err)
|
||||
}
|
||||
const want = `ALTER SCHEMA "sales" RENAME TO "SALES"`
|
||||
if got != want {
|
||||
t.Fatalf("unexpected case-only rename schema SQL, want %q got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDropSchemaSQL_PostgresUsesCascade(t *testing.T) {
|
||||
got, err := buildDropSchemaSQL("postgresql", `sales"ops`)
|
||||
if err != nil {
|
||||
|
||||
@@ -3065,10 +3065,7 @@ func (a *App) ExportSchemaSQL(config connection.ConnectionConfig, dbName string,
|
||||
w := bufio.NewWriterSize(f, 1024*1024)
|
||||
defer w.Flush()
|
||||
|
||||
if err := writeSQLHeader(w, runConfig, dbName); err != nil {
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if _, err := w.WriteString(fmt.Sprintf("-- Schema: %s\n\n", safeSchemaName)); err != nil {
|
||||
if err := writeSQLSchemaExportHeader(w, runConfig, dbName, safeSchemaName); err != nil {
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
for _, objectName := range objects {
|
||||
@@ -3322,6 +3319,33 @@ func writeSQLHeader(w *bufio.Writer, config connection.ConnectionConfig, dbName
|
||||
return writeSQLHeaderWithDatabaseBootstrap(w, config, dbName, false)
|
||||
}
|
||||
|
||||
func writeSQLSchemaExportHeader(
|
||||
w *bufio.Writer,
|
||||
config connection.ConnectionConfig,
|
||||
dbName string,
|
||||
schemaName string,
|
||||
) error {
|
||||
safeSchemaName := strings.TrimSpace(schemaName)
|
||||
if safeSchemaName == "" {
|
||||
return errors.New("schema name is required")
|
||||
}
|
||||
if err := writeSQLHeader(w, config, dbName); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(fmt.Sprintf("-- Schema: %s\n\n", safeSchemaName)); err != nil {
|
||||
return err
|
||||
}
|
||||
dbType := resolveDDLDBType(config)
|
||||
if isPostgresSchemaDDLDBType(dbType) {
|
||||
_, err := w.WriteString(fmt.Sprintf(
|
||||
"CREATE SCHEMA IF NOT EXISTS %s;\n\n",
|
||||
quoteIdentByType(dbType, safeSchemaName),
|
||||
))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSQLDatabaseBackupHeader(w *bufio.Writer, config connection.ConnectionConfig, dbName string) error {
|
||||
return writeSQLHeaderWithDatabaseBootstrap(w, config, dbName, true)
|
||||
}
|
||||
|
||||
@@ -1717,3 +1717,53 @@ func TestFilterExportViewLookupBySchema_PostgresQualifiedViewsOnly(t *testing.T)
|
||||
t.Fatalf("expected public.v_users to be filtered out, got=%v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSQLSchemaExportHeaderPostgresCreatesQuotedSchema(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
writer := bufio.NewWriter(&output)
|
||||
if err := writeSQLSchemaExportHeader(
|
||||
writer,
|
||||
connection.ConnectionConfig{Type: "postgres"},
|
||||
"app_db",
|
||||
`Sales"Ops`,
|
||||
); err != nil {
|
||||
t.Fatalf("write postgres schema export header: %v", err)
|
||||
}
|
||||
if err := writer.Flush(); err != nil {
|
||||
t.Fatalf("flush postgres schema export header: %v", err)
|
||||
}
|
||||
|
||||
content := output.String()
|
||||
if !strings.Contains(content, `-- Schema: Sales"Ops`) {
|
||||
t.Fatalf("schema export header must describe the selected schema, content=%q", content)
|
||||
}
|
||||
if !strings.Contains(content, `CREATE SCHEMA IF NOT EXISTS "Sales""Ops";`) {
|
||||
t.Fatalf("schema export header must bootstrap the quoted schema, content=%q", content)
|
||||
}
|
||||
databaseIndex := strings.Index(content, "-- Database: app_db")
|
||||
schemaIndex := strings.Index(content, `-- Schema: Sales"Ops`)
|
||||
createIndex := strings.Index(content, `CREATE SCHEMA IF NOT EXISTS "Sales""Ops";`)
|
||||
if databaseIndex < 0 || schemaIndex < databaseIndex || createIndex < schemaIndex {
|
||||
t.Fatalf("schema bootstrap must follow the database and schema metadata, content=%q", content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSQLSchemaExportHeaderDoesNotBootstrapNonPostgresSchema(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
writer := bufio.NewWriter(&output)
|
||||
if err := writeSQLSchemaExportHeader(
|
||||
writer,
|
||||
connection.ConnectionConfig{Type: "mysql"},
|
||||
"app_db",
|
||||
"sales",
|
||||
); err != nil {
|
||||
t.Fatalf("write non-postgres schema export header: %v", err)
|
||||
}
|
||||
if err := writer.Flush(); err != nil {
|
||||
t.Fatalf("flush non-postgres schema export header: %v", err)
|
||||
}
|
||||
|
||||
if strings.Contains(output.String(), "CREATE SCHEMA") {
|
||||
t.Fatalf("non-postgres schema export must not inject postgres bootstrap SQL, content=%q", output.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user