package dbsync import ( "context" "database/sql" "fmt" "time" ) const FlagTable = "_ajz_sync_flag" // EnsureFlagTable:SQLite 用表标记「正在应用远端变更」,避免触发器回声。 func EnsureFlagTable(ctx context.Context, db *sql.DB, driver Driver) error { if driver != DriverSQLite { return nil } _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS `+FlagTable+` ( k TEXT PRIMARY KEY, v INTEGER NOT NULL DEFAULT 0 );`) if err != nil { return err } _, _ = db.ExecContext(ctx, `INSERT OR IGNORE INTO `+FlagTable+`(k,v) VALUES('applying',0)`) return nil } // WithApplying 在写入目标库时置位,使触发器不写 outbox(防 A↔B 回声导致「多」)。 func WithApplying(ctx context.Context, db *sql.DB, driver Driver, fn func() error) error { switch driver { case DriverSQLite: if err := EnsureFlagTable(ctx, db, driver); err != nil { return err } if _, err := db.ExecContext(ctx, `UPDATE `+FlagTable+` SET v=1 WHERE k='applying'`); err != nil { return err } defer func() { _, _ = db.ExecContext(context.Background(), `UPDATE `+FlagTable+` SET v=0 WHERE k='applying'`) }() return fn() case DriverMySQL: if _, err := db.ExecContext(ctx, `SET @ajz_applying = 1`); err != nil { return err } defer func() { _, _ = db.ExecContext(context.Background(), `SET @ajz_applying = 0`) }() return fn() case DriverPostgres: if _, err := db.ExecContext(ctx, `SELECT set_config('ajz.applying', '1', false)`); err != nil { return err } defer func() { _, _ = db.ExecContext(context.Background(), `SELECT set_config('ajz.applying', '0', false)`) }() return fn() default: return fn() } } func EnqueueOutbox(ctx context.Context, db *sql.DB, driver Driver, table, pk, op, payload string, version int64) error { if version <= 0 { version = time.Now().UnixMilli() } switch driver { case DriverPostgres: _, err := db.ExecContext(ctx, fmt.Sprintf(` INSERT INTO %s(table_name,row_pk,op,payload,version) VALUES($1,$2,$3,$4,$5)`, OutboxTable), table, pk, op, payload, version) return err default: _, err := db.ExecContext(ctx, fmt.Sprintf(` INSERT INTO %s(table_name,row_pk,op,payload,version) VALUES(?,?,?,?,?)`, OutboxTable), table, pk, op, payload, version) return err } }