fix(11-07): hand after-commit callbacks a clean statement
- lagoon.Transaction, the lagoon:after_commit flush and the immediate AfterCommit path pass a handle with an empty statement on the write's connection (Session NewDB+Context, Clauses(), Session NewDB) - a WithContext query through the handle no longer continues from the written model's statement (deferred from 11-05) - TestTransactionAfterCommit/callback_handle_has_a_clean_statement covers the implicit, plain-transaction and lagoon.Transaction paths
This commit is contained in:
@@ -18,6 +18,13 @@ type acItem struct {
|
||||
|
||||
func (acItem) TableName() string { return "lagoon_ac_items" }
|
||||
|
||||
type acOther struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Label string `gorm:"column:label"`
|
||||
}
|
||||
|
||||
func (acOther) TableName() string { return "lagoon_ac_others" }
|
||||
|
||||
// recorder collects after-commit callback names and whether the row they
|
||||
// were registered for was visible to another connection when they ran.
|
||||
type recorder struct {
|
||||
@@ -188,8 +195,11 @@ func TestTransactionAfterCommit(t *testing.T) {
|
||||
var got *gorm.DB
|
||||
err := gdb.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
AfterCommit(ctx, tx, func(_ context.Context, d *gorm.DB) { got = d })
|
||||
if got != tx {
|
||||
t.Error("callback did not run immediately with the tx handle")
|
||||
if got == nil {
|
||||
t.Fatal("callback did not run immediately")
|
||||
}
|
||||
if got.Statement.ConnPool != tx.Statement.ConnPool {
|
||||
t.Error("callback did not run on the transaction's connection")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -198,6 +208,65 @@ func TestTransactionAfterCommit(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// The handle a callback receives has an empty statement on the write's
|
||||
// connection: a query through it (with WithContext, as application code
|
||||
// writes it) must not continue from the written model's statement.
|
||||
t.Run("callback_handle_has_a_clean_statement", func(t *testing.T) {
|
||||
if err := gdb.Exec(`CREATE TABLE lagoon_ac_others (id SERIAL PRIMARY KEY, label TEXT NOT NULL)`).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := gdb.Exec(`INSERT INTO lagoon_ac_others (label) VALUES ('other')`).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
g3, err := Use(ctx, db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var mu sync.Mutex
|
||||
labels := map[string]string{}
|
||||
if err := g3.Callback().Create().Before("gorm:create").Register("lagoon_test:clean_handle", func(stmt *gorm.DB) {
|
||||
item, ok := stmt.Statement.Dest.(*acItem)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name := item.Name
|
||||
AfterCommit(stmt.Statement.Context, stmt, func(ctx context.Context, d *gorm.DB) {
|
||||
var o acOther
|
||||
label := ""
|
||||
if err := d.WithContext(ctx).Take(&o).Error; err != nil {
|
||||
label = "error: " + err.Error()
|
||||
} else {
|
||||
label = o.Label
|
||||
}
|
||||
mu.Lock()
|
||||
labels[name] = label
|
||||
mu.Unlock()
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := g3.WithContext(ctx).Create(&acItem{Name: "clean-implicit"}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := g3.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return tx.Create(&acItem{Name: "clean-plain-tx"}).Error
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := Transaction(ctx, g3, func(ctx context.Context, tx *gorm.DB) error {
|
||||
return tx.WithContext(ctx).Create(&acItem{Name: "clean-lagoon-tx"}).Error
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for _, name := range []string{"clean-implicit", "clean-plain-tx", "clean-lagoon-tx"} {
|
||||
if got := labels[name]; got != "other" {
|
||||
t.Errorf("%s: query through the callback handle read %q, want the lagoon_ac_others row \"other\"", name, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("outside_transaction_runs_now", func(t *testing.T) {
|
||||
ran := false
|
||||
AfterCommit(ctx, gdb, func(context.Context, *gorm.DB) { ran = true })
|
||||
|
||||
Reference in New Issue
Block a user