Enable auto default sync channels on agent activate, bind-code/phone confirm flows, publish ALTER, and align admin/yuheng docs with the production bind path.
93 lines
2.2 KiB
Go
93 lines
2.2 KiB
Go
package schema
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"aijianzhan/platform/internal/blueprint"
|
||
)
|
||
|
||
// BuildPostgresAlterDDL 对比蓝图与已有表,生成 ADD COLUMN IF NOT EXISTS(Z11a)。
|
||
// db 可为 nil:则只返回空(调用方应先 CREATE TABLE)。
|
||
func BuildPostgresAlterDDL(ctx context.Context, db *sql.DB, bp *blueprint.Blueprint) ([]string, error) {
|
||
if bp == nil || bp.Storage.SchemaName == "" {
|
||
return nil, fmt.Errorf("schema_name empty")
|
||
}
|
||
if db == nil {
|
||
return nil, nil
|
||
}
|
||
schemaName := bp.Storage.SchemaName
|
||
var stmts []string
|
||
for _, e := range bp.Entities {
|
||
existing, err := listTableColumns(ctx, db, schemaName, e.Table)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if existing == nil {
|
||
// 表尚不存在:由 CREATE TABLE IF NOT EXISTS 处理
|
||
continue
|
||
}
|
||
for _, f := range e.Fields {
|
||
if _, ok := existing[f.Name]; ok {
|
||
continue
|
||
}
|
||
sqlType, err := mapType(f)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 存量表补列一律可空,避免非空约束导致迁库失败
|
||
stmts = append(stmts, fmt.Sprintf(
|
||
`ALTER TABLE %s.%s ADD COLUMN IF NOT EXISTS %s %s`,
|
||
quoteIdent(schemaName), quoteIdent(e.Table), quoteIdent(f.Name), sqlType,
|
||
))
|
||
}
|
||
}
|
||
return stmts, nil
|
||
}
|
||
|
||
func listTableColumns(ctx context.Context, db *sql.DB, schemaName, table string) (map[string]struct{}, error) {
|
||
const q = `
|
||
SELECT column_name FROM information_schema.columns
|
||
WHERE table_schema = $1 AND table_name = $2`
|
||
rows, err := db.QueryContext(ctx, q, schemaName, table)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
out := map[string]struct{}{}
|
||
found := false
|
||
for rows.Next() {
|
||
found = true
|
||
var name string
|
||
if err := rows.Scan(&name); err != nil {
|
||
return nil, err
|
||
}
|
||
out[name] = struct{}{}
|
||
}
|
||
if err := rows.Err(); err != nil {
|
||
return nil, err
|
||
}
|
||
if !found {
|
||
return nil, nil
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
// MergeDDL 将 alter 语句追加到 create ddl 之后。
|
||
func MergeDDL(create, alter []string) []string {
|
||
if len(alter) == 0 {
|
||
return create
|
||
}
|
||
out := make([]string, 0, len(create)+len(alter))
|
||
out = append(out, create...)
|
||
out = append(out, alter...)
|
||
return out
|
||
}
|
||
|
||
// Quote for tests
|
||
func normalizeSchemaTable(s string) string {
|
||
return strings.TrimSpace(s)
|
||
}
|