feat: implement smart delete with shadow table support
This commit is contained in:
parent
8d75cf7be5
commit
d84495af2e
34
DB.go
34
DB.go
@ -610,18 +610,30 @@ func (db *DB) Update(table string, data any, conditions string, args ...any) *Ex
|
||||
}
|
||||
|
||||
func (db *DB) Delete(table string, conditions string, args ...any) *ExecResult {
|
||||
if conditions != "" {
|
||||
conditions = " where " + conditions
|
||||
}
|
||||
query := fmt.Sprintf("delete from %s%s", db.Quote(table), conditions)
|
||||
r := baseExec(db.conn, nil, query, args...)
|
||||
r.logger = db.logger
|
||||
if r.Error != nil {
|
||||
db.logger.LogQueryError(r.Error.Error(), query, args, r.usedTime)
|
||||
} else {
|
||||
if db.Config.LogSlow > 0 && r.usedTime >= float32(db.Config.LogSlow.TimeDuration()/time.Millisecond) {
|
||||
db.logger.LogQuery(query, args, r.usedTime)
|
||||
ts := db.getTable(table)
|
||||
if !ts.HasShadowTable {
|
||||
if conditions != "" {
|
||||
conditions = " where " + conditions
|
||||
}
|
||||
query := fmt.Sprintf("delete from %s%s", db.Quote(table), conditions)
|
||||
r := baseExec(db.conn, nil, query, args...)
|
||||
r.logger = db.logger
|
||||
if r.Error != nil {
|
||||
db.logger.LogQueryError(r.Error.Error(), query, args, r.usedTime)
|
||||
} else {
|
||||
if db.Config.LogSlow > 0 && r.usedTime >= float32(db.Config.LogSlow.TimeDuration()/time.Millisecond) {
|
||||
db.logger.LogQuery(query, args, r.usedTime)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Shadow delete
|
||||
tx := db.Begin()
|
||||
defer tx.CheckFinished()
|
||||
r := tx.Delete(table, conditions, args...)
|
||||
if r.Error == nil {
|
||||
tx.Commit()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
17
Tx.go
17
Tx.go
@ -165,10 +165,23 @@ func (tx *Tx) Update(table string, data any, conditions string, args ...any) *Ex
|
||||
}
|
||||
|
||||
func (tx *Tx) Delete(table string, conditions string, args ...any) *ExecResult {
|
||||
ts := tx.db.getTable(table)
|
||||
where := ""
|
||||
if conditions != "" {
|
||||
conditions = " where " + conditions
|
||||
where = " where " + conditions
|
||||
}
|
||||
query := fmt.Sprintf("delete from %s%s", tx.Quote(table), conditions)
|
||||
|
||||
if ts.HasShadowTable {
|
||||
// Move to shadow table
|
||||
moveQuery := fmt.Sprintf("insert into %s select * from %s%s", tx.Quote(table+"_deleted"), tx.Quote(table), where)
|
||||
r := baseExec(nil, tx.conn, moveQuery, args...)
|
||||
if r.Error != nil {
|
||||
tx.logger.LogQueryError(r.Error.Error(), moveQuery, args, r.usedTime)
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("delete from %s%s", tx.Quote(table), where)
|
||||
tx.lastSql = &query
|
||||
tx.lastArgs = args
|
||||
r := baseExec(nil, tx.conn, query, args...)
|
||||
|
||||
54
delete_test.go
Normal file
54
delete_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package db_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"apigo.cc/go/db"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestSmartDelete(t *testing.T) {
|
||||
dbInst := db.GetDB("sqlite://:memory:", nil)
|
||||
|
||||
// Create table and shadow table
|
||||
dbInst.Exec("CREATE TABLE orders (id INTEGER PRIMARY KEY, item TEXT)")
|
||||
dbInst.Exec("CREATE TABLE orders_deleted (id INTEGER PRIMARY KEY, item TEXT)")
|
||||
|
||||
t.Run("ShadowDelete", func(t *testing.T) {
|
||||
dbInst.Exec("INSERT INTO orders (id, item) VALUES (1, 'Phone')")
|
||||
|
||||
res := dbInst.Delete("orders", "id = 1")
|
||||
if res.Error != nil {
|
||||
t.Fatalf("Delete failed: %v", res.Error)
|
||||
}
|
||||
if res.Changes() != 1 {
|
||||
t.Errorf("Expected 1 change, got %d", res.Changes())
|
||||
}
|
||||
|
||||
// Verify it's gone from main table
|
||||
qr := dbInst.Query("SELECT COUNT(*) FROM orders WHERE id = 1")
|
||||
count, _ := db.ToValue[int](qr)
|
||||
if count != 0 {
|
||||
t.Errorf("Expected 0 records in main table, got %d", count)
|
||||
}
|
||||
|
||||
// Verify it's in shadow table
|
||||
qr2 := dbInst.Query("SELECT COUNT(*) FROM orders_deleted WHERE id = 1")
|
||||
countDeleted, _ := db.ToValue[int](qr2)
|
||||
if countDeleted != 1 {
|
||||
t.Errorf("Expected 1 record in shadow table, got %d", countDeleted)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PhysicalDelete", func(t *testing.T) {
|
||||
dbInst.Exec("CREATE TABLE logs (id INTEGER PRIMARY KEY, msg TEXT)")
|
||||
dbInst.Exec("INSERT INTO logs (id, msg) VALUES (1, 'Login')")
|
||||
|
||||
dbInst.Delete("logs", "id = 1")
|
||||
|
||||
qr := dbInst.Query("SELECT COUNT(*) FROM logs WHERE id = 1")
|
||||
count, _ := db.ToValue[int](qr)
|
||||
if count != 0 {
|
||||
t.Errorf("Expected 0 records in logs, got %d", count)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user