feat: implement smart delete with shadow table support
This commit is contained in:
parent
8d75cf7be5
commit
d84495af2e
12
DB.go
12
DB.go
@ -610,6 +610,8 @@ func (db *DB) Update(table string, data any, conditions string, args ...any) *Ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Delete(table string, conditions string, args ...any) *ExecResult {
|
func (db *DB) Delete(table string, conditions string, args ...any) *ExecResult {
|
||||||
|
ts := db.getTable(table)
|
||||||
|
if !ts.HasShadowTable {
|
||||||
if conditions != "" {
|
if conditions != "" {
|
||||||
conditions = " where " + conditions
|
conditions = " where " + conditions
|
||||||
}
|
}
|
||||||
@ -624,6 +626,16 @@ func (db *DB) Delete(table string, conditions string, args ...any) *ExecResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shadow delete
|
||||||
|
tx := db.Begin()
|
||||||
|
defer tx.CheckFinished()
|
||||||
|
r := tx.Delete(table, conditions, args...)
|
||||||
|
if r.Error == nil {
|
||||||
|
tx.Commit()
|
||||||
|
}
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) getTable(table string) *TableStruct {
|
func (db *DB) getTable(table string) *TableStruct {
|
||||||
|
|||||||
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 {
|
func (tx *Tx) Delete(table string, conditions string, args ...any) *ExecResult {
|
||||||
|
ts := tx.db.getTable(table)
|
||||||
|
where := ""
|
||||||
if conditions != "" {
|
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.lastSql = &query
|
||||||
tx.lastArgs = args
|
tx.lastArgs = args
|
||||||
r := baseExec(nil, tx.conn, query, 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