Enable Binding-scoped agent push/pull, empty-table schema ensure, SyncPage inspect/drop-table, default module import, and agent-bound publish docs from the 宇恒联调意见. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package dbsync
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
// DropTableResult 控制台删表结果。
|
||
type DropTableResult struct {
|
||
OK bool `json:"ok"`
|
||
Side string `json:"side"`
|
||
Table string `json:"table"`
|
||
Dropped bool `json:"dropped"`
|
||
MetaCleared int64 `json:"meta_cleared,omitempty"`
|
||
Message string `json:"message,omitempty"`
|
||
}
|
||
|
||
// DropTableOnEndpoint 删除业务表(禁止 _ajz_*);并清理 _ajz_sync_meta 中该表残留。
|
||
// 注意:仅作用于当前 endpoint(线上 A 或本机 B),不向另一侧同步 DDL。
|
||
func DropTableOnEndpoint(ctx context.Context, ep Endpoint, side, table string) (*DropTableResult, error) {
|
||
table = strings.TrimSpace(table)
|
||
side = strings.TrimSpace(side)
|
||
if side == "" {
|
||
side = "remote"
|
||
}
|
||
if table == "" {
|
||
return nil, fmt.Errorf("table required")
|
||
}
|
||
if strings.HasPrefix(table, "_ajz_") {
|
||
return nil, fmt.Errorf("同步系统表不可删: %s", table)
|
||
}
|
||
if strings.EqualFold(table, "sqlite_master") || strings.HasPrefix(strings.ToLower(table), "sqlite_") {
|
||
return nil, fmt.Errorf("系统表不可删: %s", table)
|
||
}
|
||
|
||
db, owned, err := openInspectDB(ep)
|
||
if err != nil {
|
||
return &DropTableResult{OK: false, Side: side, Table: table, Message: err.Error()}, err
|
||
}
|
||
if owned {
|
||
defer db.Close()
|
||
}
|
||
|
||
exists, err := tableExists(ctx, db, ep.Driver, table)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if !exists {
|
||
return &DropTableResult{
|
||
OK: true,
|
||
Side: side,
|
||
Table: table,
|
||
Dropped: false,
|
||
Message: "表不存在(可能已删)",
|
||
}, nil
|
||
}
|
||
|
||
ddl := fmt.Sprintf(`DROP TABLE IF EXISTS %s`, quoteIdent(ep.Driver, table))
|
||
if _, err := db.ExecContext(ctx, ddl); err != nil {
|
||
return nil, fmt.Errorf("drop table: %w", err)
|
||
}
|
||
|
||
var metaCleared int64
|
||
if metaExists, _ := tableExists(ctx, db, ep.Driver, MetaTable); metaExists {
|
||
q := fmt.Sprintf(`DELETE FROM %s WHERE table_name = ?`, quoteIdent(ep.Driver, MetaTable))
|
||
if ep.Driver == DriverPostgres {
|
||
q = fmt.Sprintf(`DELETE FROM %s WHERE table_name = $1`, quoteIdent(ep.Driver, MetaTable))
|
||
}
|
||
res, err := db.ExecContext(ctx, q, table)
|
||
if err == nil && res != nil {
|
||
metaCleared, _ = res.RowsAffected()
|
||
}
|
||
}
|
||
|
||
// 若该 DSN 在 remote 池里,丢掉缓存连接,避免旧 schema 缓存感。
|
||
InvalidateRemote(ep.Driver, ep.DSN)
|
||
|
||
return &DropTableResult{
|
||
OK: true,
|
||
Side: side,
|
||
Table: table,
|
||
Dropped: true,
|
||
MetaCleared: metaCleared,
|
||
Message: "已删除;若另一侧仍有同名表,不会自动同步删除,需分别处理或避免再次 push/ensure",
|
||
}, nil
|
||
}
|