diff --git a/orm/internal/testkv/debug.go b/orm/internal/testkv/debug.go index 8ed9dc05d..8cdc032f5 100644 --- a/orm/internal/testkv/debug.go +++ b/orm/internal/testkv/debug.go @@ -1,6 +1,7 @@ package testkv import ( + "context" "fmt" "google.golang.org/protobuf/proto" @@ -23,10 +24,16 @@ type Debugger interface { // NewDebugBackend wraps both stores from a Backend with a debugger. func NewDebugBackend(backend ormtable.Backend, debugger Debugger) ormtable.Backend { + hooks := debugHooks{ + debugger: debugger, + validateHooks: backend.ValidateHooks(), + writeHooks: backend.WriteHooks(), + } return ormtable.NewBackend(ormtable.BackendOptions{ CommitmentStore: NewDebugStore(backend.CommitmentStore(), debugger, "commit"), IndexStore: NewDebugStore(backend.IndexStore(), debugger, "index"), - Hooks: debugHooks{debugger: debugger, hooks: backend.Hooks()}, + ValidateHooks: hooks, + WriteHooks: hooks, }) } @@ -206,28 +213,29 @@ func (d *EntryCodecDebugger) Decode(key, value []byte) string { } type debugHooks struct { - debugger Debugger - hooks ormtable.Hooks + debugger Debugger + validateHooks ormtable.ValidateHooks + writeHooks ormtable.WriteHooks } -func (d debugHooks) OnInsert(message proto.Message) error { +func (d debugHooks) ValidateInsert(context context.Context, message proto.Message) error { jsonBz, err := stablejson.Marshal(message) if err != nil { return err } d.debugger.Log(fmt.Sprintf( - "ORM INSERT %s %s", + "ORM BEFORE INSERT %s %s", message.ProtoReflect().Descriptor().FullName(), jsonBz, )) - if d.hooks != nil { - return d.hooks.OnInsert(message) + if d.validateHooks != nil { + return d.validateHooks.ValidateInsert(context, message) } return nil } -func (d debugHooks) OnUpdate(existing, new proto.Message) error { +func (d debugHooks) ValidateUpdate(ctx context.Context, existing, new proto.Message) error { existingJson, err := stablejson.Marshal(existing) if err != nil { return err @@ -239,30 +247,84 @@ func (d debugHooks) OnUpdate(existing, new proto.Message) error { } d.debugger.Log(fmt.Sprintf( - "ORM UPDATE %s %s -> %s", + "ORM BEFORE UPDATE %s %s -> %s", existing.ProtoReflect().Descriptor().FullName(), existingJson, newJson, )) - if d.hooks != nil { - return d.hooks.OnUpdate(existing, new) + if d.validateHooks != nil { + return d.validateHooks.ValidateUpdate(ctx, existing, new) } return nil } -func (d debugHooks) OnDelete(message proto.Message) error { +func (d debugHooks) ValidateDelete(ctx context.Context, message proto.Message) error { jsonBz, err := stablejson.Marshal(message) if err != nil { return err } d.debugger.Log(fmt.Sprintf( - "ORM DELETE %s %s", + "ORM BEFORE DELETE %s %s", message.ProtoReflect().Descriptor().FullName(), jsonBz, )) - if d.hooks != nil { - return d.hooks.OnDelete(message) + if d.validateHooks != nil { + return d.validateHooks.ValidateDelete(ctx, message) } return nil } + +func (d debugHooks) OnInsert(ctx context.Context, message proto.Message) { + jsonBz, err := stablejson.Marshal(message) + if err != nil { + panic(err) + } + + d.debugger.Log(fmt.Sprintf( + "ORM AFTER INSERT %s %s", + message.ProtoReflect().Descriptor().FullName(), + jsonBz, + )) + if d.writeHooks != nil { + d.writeHooks.OnInsert(ctx, message) + } +} + +func (d debugHooks) OnUpdate(ctx context.Context, existing, new proto.Message) { + existingJson, err := stablejson.Marshal(existing) + if err != nil { + panic(err) + } + + newJson, err := stablejson.Marshal(new) + if err != nil { + panic(err) + } + + d.debugger.Log(fmt.Sprintf( + "ORM AFTER UPDATE %s %s -> %s", + existing.ProtoReflect().Descriptor().FullName(), + existingJson, + newJson, + )) + if d.writeHooks != nil { + d.writeHooks.OnUpdate(ctx, existing, new) + } +} + +func (d debugHooks) OnDelete(ctx context.Context, message proto.Message) { + jsonBz, err := stablejson.Marshal(message) + if err != nil { + panic(err) + } + + d.debugger.Log(fmt.Sprintf( + "ORM AFTER DELETE %s %s", + message.ProtoReflect().Descriptor().FullName(), + jsonBz, + )) + if d.writeHooks != nil { + d.writeHooks.OnDelete(ctx, message) + } +} diff --git a/orm/model/ormdb/module_test.go b/orm/model/ormdb/module_test.go index 5fadde77a..e70f4e244 100644 --- a/orm/model/ormdb/module_test.go +++ b/orm/model/ormdb/module_test.go @@ -275,8 +275,11 @@ func TestHooks(t *testing.T) { ctrl := gomock.NewController(t) db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{}) assert.NilError(t, err) - hooks := ormmocks.NewMockHooks(ctrl) - ctx := ormtable.WrapContextDefault(ormtest.NewMemoryBackend().WithHooks(hooks)) + validateHooks := ormmocks.NewMockValidateHooks(ctrl) + writeHooks := ormmocks.NewMockWriteHooks(ctrl) + ctx := ormtable.WrapContextDefault(ormtest.NewMemoryBackend(). + WithValidateHooks(validateHooks). + WithWriteHooks(writeHooks)) k, err := NewKeeper(db) assert.NilError(t, err) @@ -284,24 +287,48 @@ func TestHooks(t *testing.T) { acct1 := "bob" acct2 := "sally" - hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10})) - hooks.EXPECT().OnInsert(ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10})) + validateHooks.EXPECT().ValidateInsert(gomock.Any(), ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10})) + validateHooks.EXPECT().ValidateInsert(gomock.Any(), ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10})) + writeHooks.EXPECT().OnInsert(gomock.Any(), ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10})) + writeHooks.EXPECT().OnInsert(gomock.Any(), ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10})) assert.NilError(t, k.Mint(ctx, acct1, denom, 10)) - hooks.EXPECT().OnUpdate( + validateHooks.EXPECT().ValidateUpdate( + gomock.Any(), ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}), ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}), ) - hooks.EXPECT().OnInsert( + validateHooks.EXPECT().ValidateInsert( + gomock.Any(), + ormmocks.Eq(&testpb.Balance{Address: acct2, Denom: denom, Amount: 5}), + ) + writeHooks.EXPECT().OnUpdate( + gomock.Any(), + ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 10}), + ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}), + ) + writeHooks.EXPECT().OnInsert( + gomock.Any(), ormmocks.Eq(&testpb.Balance{Address: acct2, Denom: denom, Amount: 5}), ) assert.NilError(t, k.Send(ctx, acct1, acct2, denom, 5)) - hooks.EXPECT().OnUpdate( + validateHooks.EXPECT().ValidateUpdate( + gomock.Any(), ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}), ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 5}), ) - hooks.EXPECT().OnDelete( + validateHooks.EXPECT().ValidateDelete( + gomock.Any(), + ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}), + ) + writeHooks.EXPECT().OnUpdate( + gomock.Any(), + ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 10}), + ormmocks.Eq(&testpb.Supply{Denom: denom, Amount: 5}), + ) + writeHooks.EXPECT().OnDelete( + gomock.Any(), ormmocks.Eq(&testpb.Balance{Address: acct1, Denom: denom, Amount: 5}), ) assert.NilError(t, k.Burn(ctx, acct1, denom, 5)) diff --git a/orm/model/ormdb/testdata/bank_scenario.golden b/orm/model/ormdb/testdata/bank_scenario.golden index 8bd51f49e..29c61ebb9 100644 --- a/orm/model/ormdb/testdata/bank_scenario.golden +++ b/orm/model/ormdb/testdata/bank_scenario.golden @@ -2,18 +2,20 @@ GET 010200666f6f PK testpb.Supply foo -> {"denom":"foo"} GET 010200666f6f PK testpb.Supply foo -> {"denom":"foo"} -ORM INSERT testpb.Supply {"denom":"foo","amount":100} +ORM BEFORE INSERT testpb.Supply {"denom":"foo","amount":100} SET 010200666f6f 1064 PK testpb.Supply foo -> {"denom":"foo","amount":100} +ORM AFTER INSERT testpb.Supply {"denom":"foo","amount":100} GET 010100626f6200666f6f PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo"} GET 010100626f6200666f6f PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo"} -ORM INSERT testpb.Balance {"address":"bob","denom":"foo","amount":100} +ORM BEFORE INSERT testpb.Balance {"address":"bob","denom":"foo","amount":100} SET 010100626f6200666f6f 1864 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":100} SET 010101666f6f00626f62 IDX testpb.Balance denom/address : foo/bob -> bob/foo +ORM AFTER INSERT testpb.Balance {"address":"bob","denom":"foo","amount":100} GET 010100626f6200666f6f 1864 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":100} GET 010200666f6f 1064 @@ -22,18 +24,20 @@ GET 010100626f6200666f6f 1864 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":100} GET 010100626f6200666f6f 1864 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":100} -ORM UPDATE testpb.Balance {"address":"bob","denom":"foo","amount":100} -> {"address":"bob","denom":"foo","amount":70} +ORM BEFORE UPDATE testpb.Balance {"address":"bob","denom":"foo","amount":100} -> {"address":"bob","denom":"foo","amount":70} SET 010100626f6200666f6f 1846 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":70} +ORM AFTER UPDATE testpb.Balance {"address":"bob","denom":"foo","amount":100} -> {"address":"bob","denom":"foo","amount":70} GET 01010073616c6c7900666f6f PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo"} GET 01010073616c6c7900666f6f PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo"} -ORM INSERT testpb.Balance {"address":"sally","denom":"foo","amount":30} +ORM BEFORE INSERT testpb.Balance {"address":"sally","denom":"foo","amount":30} SET 01010073616c6c7900666f6f 181e PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo","amount":30} SET 010101666f6f0073616c6c79 IDX testpb.Balance denom/address : foo/sally -> sally/foo +ORM AFTER INSERT testpb.Balance {"address":"sally","denom":"foo","amount":30} GET 010100626f6200666f6f 1846 PK testpb.Balance bob/foo -> {"address":"bob","denom":"foo","amount":70} GET 01010073616c6c7900666f6f 181e @@ -42,16 +46,18 @@ GET 010200666f6f 1064 PK testpb.Supply foo -> {"denom":"foo","amount":100} GET 010200666f6f 1064 PK testpb.Supply foo -> {"denom":"foo","amount":100} -ORM UPDATE testpb.Supply {"denom":"foo","amount":100} -> {"denom":"foo","amount":97} +ORM BEFORE UPDATE testpb.Supply {"denom":"foo","amount":100} -> {"denom":"foo","amount":97} SET 010200666f6f 1061 PK testpb.Supply foo -> {"denom":"foo","amount":97} +ORM AFTER UPDATE testpb.Supply {"denom":"foo","amount":100} -> {"denom":"foo","amount":97} GET 01010073616c6c7900666f6f 181e PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo","amount":30} GET 01010073616c6c7900666f6f 181e PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo","amount":30} -ORM UPDATE testpb.Balance {"address":"sally","denom":"foo","amount":30} -> {"address":"sally","denom":"foo","amount":27} +ORM BEFORE UPDATE testpb.Balance {"address":"sally","denom":"foo","amount":30} -> {"address":"sally","denom":"foo","amount":27} SET 01010073616c6c7900666f6f 181b PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo","amount":27} +ORM AFTER UPDATE testpb.Balance {"address":"sally","denom":"foo","amount":30} -> {"address":"sally","denom":"foo","amount":27} GET 01010073616c6c7900666f6f 181b PK testpb.Balance sally/foo -> {"address":"sally","denom":"foo","amount":27} GET 010200666f6f 1061 diff --git a/orm/model/ormtable/auto_increment.go b/orm/model/ormtable/auto_increment.go index ac6750b84..7eeb32f2d 100644 --- a/orm/model/ormtable/auto_increment.go +++ b/orm/model/ormtable/auto_increment.go @@ -28,7 +28,7 @@ func (t autoIncrementTable) InsertReturningID(ctx context.Context, message proto return 0, err } - return t.save(backend, message, saveModeInsert) + return t.save(ctx, backend, message, saveModeInsert) } func (t autoIncrementTable) Save(ctx context.Context, message proto.Message) error { @@ -37,7 +37,7 @@ func (t autoIncrementTable) Save(ctx context.Context, message proto.Message) err return err } - _, err = t.save(backend, message, saveModeDefault) + _, err = t.save(ctx, backend, message, saveModeDefault) return err } @@ -47,7 +47,7 @@ func (t autoIncrementTable) Insert(ctx context.Context, message proto.Message) e return err } - _, err = t.save(backend, message, saveModeInsert) + _, err = t.save(ctx, backend, message, saveModeInsert) return err } @@ -57,11 +57,11 @@ func (t autoIncrementTable) Update(ctx context.Context, message proto.Message) e return err } - _, err = t.save(backend, message, saveModeUpdate) + _, err = t.save(ctx, backend, message, saveModeUpdate) return err } -func (t *autoIncrementTable) save(backend Backend, message proto.Message, mode saveMode) (newId uint64, err error) { +func (t *autoIncrementTable) save(ctx context.Context, backend Backend, message proto.Message, mode saveMode) (newId uint64, err error) { messageRef := message.ProtoReflect() val := messageRef.Get(t.autoIncField).Uint() writer := newBatchIndexCommitmentWriter(backend) @@ -87,7 +87,7 @@ func (t *autoIncrementTable) save(backend Backend, message proto.Message, mode s mode = saveModeUpdate } - return newId, t.tableImpl.doSave(writer, message, mode) + return newId, t.tableImpl.doSave(ctx, writer, message, mode) } func (t *autoIncrementTable) curSeqValue(kv kv.ReadonlyStore) (uint64, error) { @@ -148,7 +148,7 @@ func (t autoIncrementTable) ImportJSON(ctx context.Context, reader io.Reader) er if id == 0 { // we don't have an ID in the JSON, so we call Save to insert and // generate one - _, err = t.save(backend, message, saveModeInsert) + _, err = t.save(ctx, backend, message, saveModeInsert) return err } else { if id > maxID { @@ -158,7 +158,7 @@ func (t autoIncrementTable) ImportJSON(ctx context.Context, reader io.Reader) er // either no ID or SAVE_MODE_UPDATE. So instead we drop one level // down and insert using tableImpl which doesn't know about // auto-incrementing IDs - return t.tableImpl.save(backend, message, saveModeInsert) + return t.tableImpl.save(ctx, backend, message, saveModeInsert) } }) } diff --git a/orm/model/ormtable/backend.go b/orm/model/ormtable/backend.go index 7de1d8c0d..7d6967a74 100644 --- a/orm/model/ormtable/backend.go +++ b/orm/model/ormtable/backend.go @@ -30,11 +30,15 @@ type Backend interface { // otherwise it the commitment store. IndexStore() kv.Store - // Hooks returns a Hooks instance or nil. - Hooks() Hooks + // ValidateHooks returns a ValidateHooks instance or nil. + ValidateHooks() ValidateHooks - // WithHooks returns a copy of this backend with the provided hooks. - WithHooks(Hooks) Backend + // WithValidateHooks returns a copy of this backend with the provided hooks. + WithValidateHooks(ValidateHooks) Backend + + WriteHooks() WriteHooks + + WithWriteHooks(WriteHooks) Backend } // ReadBackendOptions defines options for creating a ReadBackend. @@ -82,11 +86,25 @@ func NewReadBackend(options ReadBackendOptions) ReadBackend { type backend struct { commitmentStore kv.Store indexStore kv.Store - hooks Hooks + validateHooks ValidateHooks + writeHooks WriteHooks } -func (c backend) WithHooks(hooks Hooks) Backend { - c.hooks = hooks +func (c backend) ValidateHooks() ValidateHooks { + return c.validateHooks +} + +func (c backend) WithValidateHooks(hooks ValidateHooks) Backend { + c.validateHooks = hooks + return c +} + +func (c backend) WriteHooks() WriteHooks { + return c.writeHooks +} + +func (c backend) WithWriteHooks(hooks WriteHooks) Backend { + c.writeHooks = hooks return c } @@ -108,10 +126,6 @@ func (c backend) IndexStore() kv.Store { return c.indexStore } -func (c backend) Hooks() Hooks { - return c.hooks -} - // BackendOptions defines options for creating a Backend. // Context can optionally define two stores - a commitment store // that is backed by a merkle tree and an index store that isn't. @@ -126,8 +140,10 @@ type BackendOptions struct { // If it is nil the CommitmentStore will be used. IndexStore kv.Store - // Hooks are optional hooks into ORM insert, update and delete operations. - Hooks Hooks + // ValidateHooks are optional hooks into ORM insert, update and delete operations. + ValidateHooks ValidateHooks + + WriteHooks WriteHooks } // NewBackend creates a new Backend. @@ -139,7 +155,8 @@ func NewBackend(options BackendOptions) Backend { return &backend{ commitmentStore: options.CommitmentStore, indexStore: indexStore, - hooks: options.Hooks, + validateHooks: options.ValidateHooks, + writeHooks: options.WriteHooks, } } diff --git a/orm/model/ormtable/batch.go b/orm/model/ormtable/batch.go index 567780498..c34dbf444 100644 --- a/orm/model/ormtable/batch.go +++ b/orm/model/ormtable/batch.go @@ -62,7 +62,9 @@ func flushWrites(store kv.Store, writer *batchStoreWriter) error { func flushBuf(store kv.Store, writes []*batchWriterEntry) error { for _, write := range writes { - if !write.delete { + if write.hookCall != nil { + write.hookCall() + } else if !write.delete { err := store.Set(write.key, write.value) if err != nil { return err @@ -73,6 +75,7 @@ func flushBuf(store kv.Store, writes []*batchWriterEntry) error { return err } } + } return nil } @@ -89,6 +92,7 @@ func (w *batchIndexCommitmentWriter) Close() { type batchWriterEntry struct { key, value []byte delete bool + hookCall func() } type batchStoreWriter struct { @@ -109,6 +113,10 @@ func (b *batchStoreWriter) Delete(key []byte) error { return nil } +func (w *batchIndexCommitmentWriter) enqueueHook(f func()) { + w.indexWriter.append(&batchWriterEntry{hookCall: f}) +} + func (b *batchStoreWriter) append(entry *batchWriterEntry) { if len(b.curBuf) == capacity { b.prevBufs = append(b.prevBufs, b.curBuf) diff --git a/orm/model/ormtable/hooks.go b/orm/model/ormtable/hooks.go index 93881bf37..288bfd2b4 100644 --- a/orm/model/ormtable/hooks.go +++ b/orm/model/ormtable/hooks.go @@ -1,21 +1,42 @@ package ormtable -import "google.golang.org/protobuf/proto" +import ( + "context" -// Hooks defines an interface for a table hooks which can intercept -// insert, update and delete operations. Table.Save and Table.Delete methods will -// do a type assertion on kvstore.IndexCommitmentStore and if the Hooks -// interface is defined call the appropriate hooks. -type Hooks interface { - // OnInsert is called before the message is inserted. - // If error is not nil the operation will fail. - OnInsert(proto.Message) error + "google.golang.org/protobuf/proto" +) - // OnUpdate is called before the existing message is updated with the new one. - // If error is not nil the operation will fail. - OnUpdate(existing, new proto.Message) error +// ValidateHooks defines an interface for a table hooks which can intercept +// insert, update and delete operations and possibly return an error. +type ValidateHooks interface { - // OnDelete is called before the message is deleted. - // If error is not nil the operation will fail. - OnDelete(proto.Message) error + // ValidateInsert is called before the message is inserted. + // If error is not nil the insertion will fail. + ValidateInsert(context.Context, proto.Message) error + + // ValidateUpdate is called before the existing message is updated with the new one. + // If error is not nil the update will fail. + ValidateUpdate(ctx context.Context, existing, new proto.Message) error + + // ValidateDelete is called before the message is deleted. + // If error is not nil the deletion will fail. + ValidateDelete(context.Context, proto.Message) error +} + +// WriteHooks defines an interface for listening to insertions, updates and +// deletes after they are written to the store. This can be used for indexing +// state in another database. Indexers should make sure they coordinate with +// transactions at live at the next level above the ORM as they write hooks +// may be called but the enclosing transaction may still fail. The context +// is provided in each method to help coordinate this. +type WriteHooks interface { + + // OnInsert is called after an message is inserted into the store. + OnInsert(context.Context, proto.Message) + + // OnUpdate is called after the entity is updated in the store. + OnUpdate(ctx context.Context, existing, new proto.Message) + + // OnDelete is called after the entity is deleted from the store. + OnDelete(context.Context, proto.Message) } diff --git a/orm/model/ormtable/primary_key.go b/orm/model/ormtable/primary_key.go index 37a4e0537..8c8b8b8a3 100644 --- a/orm/model/ormtable/primary_key.go +++ b/orm/model/ormtable/primary_key.go @@ -127,7 +127,7 @@ func (p primaryKeyIndex) doDelete(ctx context.Context, primaryKeyValues []protor return nil } - err = p.doDeleteWithWriteBatch(backend, writer, pk, msg) + err = p.doDeleteWithWriteBatch(ctx, backend, writer, pk, msg) if err != nil { return err } @@ -135,9 +135,9 @@ func (p primaryKeyIndex) doDelete(ctx context.Context, primaryKeyValues []protor return writer.Write() } -func (p primaryKeyIndex) doDeleteWithWriteBatch(backend Backend, writer *batchIndexCommitmentWriter, primaryKeyBz []byte, message proto.Message) error { - if hooks := backend.Hooks(); hooks != nil { - err := hooks.OnDelete(message) +func (p primaryKeyIndex) doDeleteWithWriteBatch(ctx context.Context, backend Backend, writer *batchIndexCommitmentWriter, primaryKeyBz []byte, message proto.Message) error { + if hooks := backend.ValidateHooks(); hooks != nil { + err := hooks.ValidateDelete(ctx, message) if err != nil { return err } @@ -159,6 +159,12 @@ func (p primaryKeyIndex) doDeleteWithWriteBatch(backend Backend, writer *batchIn } } + if writeHooks := backend.WriteHooks(); writeHooks != nil { + writer.enqueueHook(func() { + writeHooks.OnDelete(ctx, message) + }) + } + return nil } @@ -209,7 +215,7 @@ func (p primaryKeyIndex) deleteByIterator(ctx context.Context, it Iterator) erro return err } - err = p.doDeleteWithWriteBatch(backend, writer, pkBz, msg) + err = p.doDeleteWithWriteBatch(ctx, backend, writer, pkBz, msg) if err != nil { return err } diff --git a/orm/model/ormtable/singleton.go b/orm/model/ormtable/singleton.go index cab54f7b2..3c7418492 100644 --- a/orm/model/ormtable/singleton.go +++ b/orm/model/ormtable/singleton.go @@ -60,7 +60,7 @@ func (t singleton) ImportJSON(ctx context.Context, reader io.Reader) error { return err } - return t.save(backend, msg, saveModeDefault) + return t.save(ctx, backend, msg, saveModeDefault) } func (t singleton) ExportJSON(ctx context.Context, writer io.Writer) error { diff --git a/orm/model/ormtable/table.go b/orm/model/ormtable/table.go index 2fb3c07ee..4c9910d64 100644 --- a/orm/model/ormtable/table.go +++ b/orm/model/ormtable/table.go @@ -60,8 +60,8 @@ type Table interface { // Save saves the provided entry in the store either inserting it or // updating it if needed. // - // If store implement the Hooks interface, the appropriate OnInsert or - // OnUpdate hook method will be called. + // If store implement the ValidateHooks interface, the appropriate ValidateInsert or + // ValidateUpdate hook method will be called. // // Save attempts to be atomic with respect to the underlying store, // meaning that either the full save operation is written or the store is @@ -81,7 +81,7 @@ type Table interface { // if one exists. Other fields besides the primary key fields will not // be used for retrieval. // - // If store implement the Hooks interface, the OnDelete hook method will + // If store implement the ValidateHooks interface, the ValidateDelete hook method will // be called. // // Delete attempts to be atomic with respect to the underlying store, diff --git a/orm/model/ormtable/table_impl.go b/orm/model/ormtable/table_impl.go index ab5468662..b0a5d6f49 100644 --- a/orm/model/ormtable/table_impl.go +++ b/orm/model/ormtable/table_impl.go @@ -53,7 +53,7 @@ func (t tableImpl) Save(ctx context.Context, message proto.Message) error { return err } - return t.save(backend, message, saveModeDefault) + return t.save(ctx, backend, message, saveModeDefault) } func (t tableImpl) Insert(ctx context.Context, message proto.Message) error { @@ -62,7 +62,7 @@ func (t tableImpl) Insert(ctx context.Context, message proto.Message) error { return err } - return t.save(backend, message, saveModeInsert) + return t.save(ctx, backend, message, saveModeInsert) } func (t tableImpl) Update(ctx context.Context, message proto.Message) error { @@ -71,16 +71,16 @@ func (t tableImpl) Update(ctx context.Context, message proto.Message) error { return err } - return t.save(backend, message, saveModeUpdate) + return t.save(ctx, backend, message, saveModeUpdate) } -func (t tableImpl) save(backend Backend, message proto.Message, mode saveMode) error { +func (t tableImpl) save(ctx context.Context, backend Backend, message proto.Message, mode saveMode) error { writer := newBatchIndexCommitmentWriter(backend) defer writer.Close() - return t.doSave(writer, message, mode) + return t.doSave(ctx, writer, message, mode) } -func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Message, mode saveMode) error { +func (t tableImpl) doSave(ctx context.Context, writer *batchIndexCommitmentWriter, message proto.Message, mode saveMode) error { mref := message.ProtoReflect() pkValues, pk, err := t.EncodeKeyFromMessage(mref) if err != nil { @@ -98,8 +98,8 @@ func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Mess return ormerrors.PrimaryKeyConstraintViolation.Wrapf("%q:%+v", mref.Descriptor().FullName(), pkValues) } - if hooks := writer.Hooks(); hooks != nil { - err = hooks.OnUpdate(existing, message) + if validateHooks := writer.ValidateHooks(); validateHooks != nil { + err = validateHooks.ValidateUpdate(ctx, existing, message) if err != nil { return err } @@ -109,8 +109,8 @@ func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Mess return ormerrors.NotFoundOnUpdate.Wrapf("%q", mref.Descriptor().FullName()) } - if hooks := writer.Hooks(); hooks != nil { - err = hooks.OnInsert(message) + if validateHooks := writer.ValidateHooks(); validateHooks != nil { + err = validateHooks.ValidateInsert(ctx, message) if err != nil { return err } @@ -140,6 +140,11 @@ func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Mess } } + if writeHooks := writer.WriteHooks(); writeHooks != nil { + writer.enqueueHook(func() { + writeHooks.OnInsert(ctx, message) + }) + } } else { existingMref := existing.ProtoReflect() for _, idx := range t.indexers { @@ -148,6 +153,11 @@ func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Mess return err } } + if writeHooks := writer.WriteHooks(); writeHooks != nil { + writer.enqueueHook(func() { + writeHooks.OnUpdate(ctx, existing, message) + }) + } } return writer.Write() @@ -283,7 +293,7 @@ func (t tableImpl) ImportJSON(ctx context.Context, reader io.Reader) error { } return t.decodeJson(reader, func(message proto.Message) error { - return t.save(backend, message, saveModeDefault) + return t.save(ctx, backend, message, saveModeDefault) }) } diff --git a/orm/model/ormtable/testdata/test_auto_inc.golden b/orm/model/ormtable/testdata/test_auto_inc.golden index a1b669347..07a1fc466 100644 --- a/orm/model/ormtable/testdata/test_auto_inc.golden +++ b/orm/model/ormtable/testdata/test_auto_inc.golden @@ -4,7 +4,7 @@ GET 03808002 SEQ testpb.ExampleAutoIncrementTable 0 GET 03000001 PK testpb.ExampleAutoIncrementTable 1 -> {"id":1} -ORM INSERT testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} +ORM BEFORE INSERT testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} HAS 0301666f6f ERR:EOF SET 03000001 1203666f6f1805 @@ -13,11 +13,12 @@ SET 03808002 01 SEQ testpb.ExampleAutoIncrementTable 1 SET 0301666f6f 0001 UNIQ testpb.ExampleAutoIncrementTable x : foo -> 1 +ORM AFTER INSERT testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} GET 03808002 01 SEQ testpb.ExampleAutoIncrementTable 1 GET 03000002 PK testpb.ExampleAutoIncrementTable 2 -> {"id":2} -ORM INSERT testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} +ORM BEFORE INSERT testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} HAS 0301626172 ERR:EOF SET 03000002 1203626172180a @@ -26,6 +27,7 @@ SET 03808002 02 SEQ testpb.ExampleAutoIncrementTable 2 SET 0301626172 0002 UNIQ testpb.ExampleAutoIncrementTable x : bar -> 2 +ORM AFTER INSERT testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} GET 03808002 02 SEQ testpb.ExampleAutoIncrementTable 2 ITERATOR 0300 -> 0301 @@ -54,18 +56,20 @@ ITERATOR 0300 -> 0301 VALID false GET 03000001 1203666f6f1805 PK testpb.ExampleAutoIncrementTable 1 -> {"id":1,"x":"foo","y":5} -ORM DELETE testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} +ORM BEFORE DELETE testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} DEL 03000001 DEL PK testpb.ExampleAutoIncrementTable 1 -> {"id":1} DEL 0301666f6f DEL ERR:EOF +ORM AFTER DELETE testpb.ExampleAutoIncrementTable {"id":1,"x":"foo","y":5} GET 03000002 1203626172180a PK testpb.ExampleAutoIncrementTable 2 -> {"id":2,"x":"bar","y":10} -ORM DELETE testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} +ORM BEFORE DELETE testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} DEL 03000002 DEL PK testpb.ExampleAutoIncrementTable 2 -> {"id":2} DEL 0301626172 DEL ERR:EOF +ORM AFTER DELETE testpb.ExampleAutoIncrementTable {"id":2,"x":"bar","y":10} GET 03808002 02 SEQ testpb.ExampleAutoIncrementTable 2 ITERATOR 0300 -> 0301 diff --git a/orm/model/ormtable/testdata/test_scenario.golden b/orm/model/ormtable/testdata/test_scenario.golden index be7a6b74e..d6dad07db 100644 --- a/orm/model/ormtable/testdata/test_scenario.golden +++ b/orm/model/ormtable/testdata/test_scenario.golden @@ -1,6 +1,6 @@ GET 010000047ffffffffffffffe616263 PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"str":"abc","i64":-2} -ORM INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} +ORM BEFORE INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} HAS 01010007616263 ERR:EOF SET 010000047ffffffffffffffe616263 1007 @@ -11,6 +11,7 @@ SET 01026162630000047ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abc/4/-2 -> 4/-2/abc SET 0103006162630000047ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/4/-2 -> 4/-2/abc +ORM AFTER INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} ITERATOR 0100 -> 0101 VALID true KEY 010000047ffffffffffffffe616263 1007 @@ -19,7 +20,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000047ffffffffffffffe616264 PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"str":"abd","i64":-2} -ORM INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} +ORM BEFORE INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} HAS 01010007616264 ERR:EOF SET 010000047ffffffffffffffe616264 1007 @@ -30,6 +31,7 @@ SET 01026162640000047ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd SET 0103006162640000047ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/4/-2 -> 4/-2/abd +ORM AFTER INSERT testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} ITERATOR 0100 -> 0101 VALID true KEY 010000047ffffffffffffffe616263 1007 @@ -42,7 +44,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000047fffffffffffffff616263 PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"str":"abc","i64":-1} -ORM INSERT testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} +ORM BEFORE INSERT testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} HAS 01010008616263 ERR:EOF SET 010000047fffffffffffffff616263 1008 @@ -53,9 +55,10 @@ SET 01026162630000047fffffffffffffff IDX testpb.ExampleTable str/u32/i64 : abc/4/-1 -> 4/-1/abc SET 0103006162630000047fffffffffffffff IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/4/-1 -> 4/-1/abc +ORM AFTER INSERT testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} GET 010000057ffffffffffffffe616264 PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"str":"abd","i64":-2} -ORM INSERT testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} +ORM BEFORE INSERT testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} HAS 01010008616264 ERR:EOF SET 010000057ffffffffffffffe616264 1008 @@ -66,9 +69,10 @@ SET 01026162640000057ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd SET 0103006162640000057ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/5/-2 -> 5/-2/abd +ORM AFTER INSERT testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} GET 010000057ffffffffffffffe616265 PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"str":"abe","i64":-2} -ORM INSERT testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} +ORM BEFORE INSERT testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} HAS 01010009616265 ERR:EOF SET 010000057ffffffffffffffe616265 1009 @@ -79,9 +83,10 @@ SET 01026162650000057ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abe/5/-2 -> 5/-2/abe SET 0103006162650000057ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : []/abe/5/-2 -> 5/-2/abe +ORM AFTER INSERT testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} GET 010000077ffffffffffffffe616265 PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"str":"abe","i64":-2} -ORM INSERT testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} +ORM BEFORE INSERT testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} HAS 0101000a616265 ERR:EOF SET 010000077ffffffffffffffe616265 100a @@ -92,9 +97,10 @@ SET 01026162650000077ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abe/7/-2 -> 7/-2/abe SET 0103006162650000077ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : []/abe/7/-2 -> 7/-2/abe +ORM AFTER INSERT testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} GET 010000077fffffffffffffff616265 PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"str":"abe","i64":-1} -ORM INSERT testpb.ExampleTable {"u32":7,"u64":11,"str":"abe","i64":-1} +ORM BEFORE INSERT testpb.ExampleTable {"u32":7,"u64":11,"str":"abe","i64":-1} HAS 0101000b616265 ERR:EOF SET 010000077fffffffffffffff616265 100b @@ -105,9 +111,10 @@ SET 01026162650000077fffffffffffffff IDX testpb.ExampleTable str/u32/i64 : abe/7/-1 -> 7/-1/abe SET 0103006162650000077fffffffffffffff IDX testpb.ExampleTable bz/str/u32/i64 : []/abe/7/-1 -> 7/-1/abe +ORM AFTER INSERT testpb.ExampleTable {"u32":7,"u64":11,"str":"abe","i64":-1} GET 010000087ffffffffffffffc616263 PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"str":"abc","i64":-4} -ORM INSERT testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} +ORM BEFORE INSERT testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} HAS 0101000b616263 ERR:EOF SET 010000087ffffffffffffffc616263 100b @@ -118,9 +125,10 @@ SET 01026162630000087ffffffffffffffc IDX testpb.ExampleTable str/u32/i64 : abc/8/-4 -> 8/-4/abc SET 0103006162630000087ffffffffffffffc IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/-4 -> 8/-4/abc +ORM AFTER INSERT testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} GET 010000088000000000000001616263 PK testpb.ExampleTable 8/1/abc -> {"u32":8,"str":"abc","i64":1} -ORM INSERT testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} +ORM BEFORE INSERT testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} HAS 0101000c616263 ERR:EOF SET 010000088000000000000001616263 100c @@ -131,9 +139,10 @@ SET 01026162630000088000000000000001 IDX testpb.ExampleTable str/u32/i64 : abc/8/1 -> 8/1/abc SET 0103006162630000088000000000000001 IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/1 -> 8/1/abc +ORM AFTER INSERT testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} GET 010000088000000000000001616264 PK testpb.ExampleTable 8/1/abd -> {"u32":8,"str":"abd","i64":1} -ORM INSERT testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} +ORM BEFORE INSERT testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} HAS 0101000a616264 ERR:EOF SET 010000088000000000000001616264 100a @@ -144,6 +153,7 @@ SET 01026162640000088000000000000001 IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd SET 0103006162640000088000000000000001 IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/8/1 -> 8/1/abd +ORM AFTER INSERT testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} ITERATOR 01000008 -> 01000009 VALID true KEY 010000087ffffffffffffffc616263 100b @@ -614,7 +624,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000047ffffffffffffffe616263 1007 PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2} -ORM UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} HAS 0101000e616263 ERR:EOF SET 010000047ffffffffffffffe616263 100e2203616263 @@ -627,9 +637,10 @@ DEL 0103006162630000047ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/4/-2 -> 4/-2/abc SET 0103036162636162630000047ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 99]/abc/4/-2 -> 4/-2/abc +ORM AFTER UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} GET 010000047ffffffffffffffe616264 1007 PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":7,"str":"abd","i64":-2} -ORM UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} -> {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} -> {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} HAS 0101000e616264 ERR:EOF SET 010000047ffffffffffffffe616264 100e2203616264 @@ -642,9 +653,10 @@ DEL 0103006162640000047ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/4/-2 -> 4/-2/abd SET 0103036162646162640000047ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/4/-2 -> 4/-2/abd +ORM AFTER UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abd","i64":-2} -> {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} GET 010000047fffffffffffffff616263 1008 PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1} -ORM UPDATE testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} -> {"u32":4,"u64":16,"str":"abc","bz":"abc","i64":-1} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} -> {"u32":4,"u64":16,"str":"abc","bz":"abc","i64":-1} HAS 01010010616263 ERR:EOF SET 010000047fffffffffffffff616263 10102203616263 @@ -657,9 +669,10 @@ DEL 0103006162630000047fffffffffffffff DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/4/-1 -> 4/-1/abc SET 0103036162636162630000047fffffffffffffff IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 99]/abc/4/-1 -> 4/-1/abc +ORM AFTER UPDATE testpb.ExampleTable {"u32":4,"u64":8,"str":"abc","i64":-1} -> {"u32":4,"u64":16,"str":"abc","bz":"abc","i64":-1} GET 010000057ffffffffffffffe616264 1008 PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2} -ORM UPDATE testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} -> {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} -> {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} HAS 01010010616264 ERR:EOF SET 010000057ffffffffffffffe616264 10102203616264 @@ -672,9 +685,10 @@ DEL 0103006162640000057ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/5/-2 -> 5/-2/abd SET 0103036162646162640000057ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/5/-2 -> 5/-2/abd +ORM AFTER UPDATE testpb.ExampleTable {"u32":5,"u64":8,"str":"abd","i64":-2} -> {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} GET 010000057ffffffffffffffe616265 1009 PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2} -ORM UPDATE testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} -> {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} -> {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} HAS 01010012616265 ERR:EOF SET 010000057ffffffffffffffe616265 10122203616265 @@ -687,6 +701,7 @@ DEL 0103006162650000057ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abe/5/-2 -> 5/-2/abe SET 0103036162656162650000057ffffffffffffffe IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 101]/abe/5/-2 -> 5/-2/abe +ORM AFTER UPDATE testpb.ExampleTable {"u32":5,"u64":9,"str":"abe","i64":-2} -> {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} ITERATOR 0100 -> 0101 VALID true KEY 010000047ffffffffffffffe616263 100e2203616263 @@ -731,7 +746,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000098000000000000000 PK testpb.ExampleTable 9/0/ -> {"u32":9} -ORM INSERT testpb.ExampleTable {"u32":9} +ORM BEFORE INSERT testpb.ExampleTable {"u32":9} HAS 01010000 ERR:EOF SET 010000098000000000000000 @@ -742,13 +757,15 @@ SET 01020000098000000000000000 IDX testpb.ExampleTable str/u32/i64 : /9/0 -> 9/0/ SET 0103000000098000000000000000 IDX testpb.ExampleTable bz/str/u32/i64 : []//9/0 -> 9/0/ +ORM AFTER INSERT testpb.ExampleTable {"u32":9} GET 010000098000000000000000 PK testpb.ExampleTable 9/0/ -> {"u32":9} GET 010000098000000000000000 PK testpb.ExampleTable 9/0/ -> {"u32":9} -ORM UPDATE testpb.ExampleTable {"u32":9} -> {"u32":9,"b":true} +ORM BEFORE UPDATE testpb.ExampleTable {"u32":9} -> {"u32":9,"b":true} SET 010000098000000000000000 7801 PK testpb.ExampleTable 9/0/ -> {"u32":9,"b":true} +ORM AFTER UPDATE testpb.ExampleTable {"u32":9} -> {"u32":9,"b":true} GET 010000098000000000000000 7801 PK testpb.ExampleTable 9/0/ -> {"u32":9,"b":true} ITERATOR 0100 -> 0101 @@ -913,7 +930,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000077ffffffffffffffe616265 100a PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2} -ORM DELETE testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} +ORM BEFORE DELETE testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} DEL 010000077ffffffffffffffe616265 DEL PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"str":"abe","i64":-2} DEL 0101000a616265 @@ -922,6 +939,7 @@ DEL 01026162650000077ffffffffffffffe DEL IDX testpb.ExampleTable str/u32/i64 : abe/7/-2 -> 7/-2/abe DEL 0103006162650000077ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abe/7/-2 -> 7/-2/abe +ORM AFTER DELETE testpb.ExampleTable {"u32":7,"u64":10,"str":"abe","i64":-2} HAS 010000077ffffffffffffffe616265 PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"str":"abe","i64":-2} ITERATOR 0100 -> 0101 @@ -992,21 +1010,21 @@ ITERATOR 010261626400 -> 010261626401 IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd GET 010000047ffffffffffffffe616264 100e2203616264 PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} -ORM DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} +ORM BEFORE DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} NEXT VALID true KEY 01026162640000057ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd GET 010000057ffffffffffffffe616264 10102203616264 PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} -ORM DELETE testpb.ExampleTable {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} +ORM BEFORE DELETE testpb.ExampleTable {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} NEXT VALID true KEY 01026162640000088000000000000001 IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd GET 010000088000000000000001616264 100a PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1} -ORM DELETE testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} +ORM BEFORE DELETE testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} NEXT VALID false CLOSE @@ -1022,18 +1040,21 @@ DEL 01026162640000047ffffffffffffffe DEL IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd DEL 0103036162646162640000047ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/4/-2 -> 4/-2/abd +ORM AFTER DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} DEL 01010010616264 DEL ERR:EOF DEL 01026162640000057ffffffffffffffe DEL IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd DEL 0103036162646162640000057ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/5/-2 -> 5/-2/abd +ORM AFTER DELETE testpb.ExampleTable {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} DEL 0101000a616264 DEL ERR:EOF DEL 01026162640000088000000000000001 DEL IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd DEL 0103006162640000088000000000000001 DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/8/1 -> 8/1/abd +ORM AFTER DELETE testpb.ExampleTable {"u32":8,"u64":10,"str":"abd","i64":1} ITERATOR 0100 -> 0101 VALID true KEY 010000047ffffffffffffffe616263 100e2203616263 @@ -1070,21 +1091,21 @@ ITERATOR 0102616263000008 -> 0102616265000006 IDX testpb.ExampleTable str/u32/i64 : abc/8/-4 -> 8/-4/abc GET 010000087ffffffffffffffc616263 100b PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4} -ORM DELETE testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} +ORM BEFORE DELETE testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} NEXT VALID true KEY 01026162630000088000000000000001 IDX testpb.ExampleTable str/u32/i64 : abc/8/1 -> 8/1/abc GET 010000088000000000000001616263 100c PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1} -ORM DELETE testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} +ORM BEFORE DELETE testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} NEXT VALID true KEY 01026162650000057ffffffffffffffe IDX testpb.ExampleTable str/u32/i64 : abe/5/-2 -> 5/-2/abe GET 010000057ffffffffffffffe616265 10122203616265 PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} -ORM DELETE testpb.ExampleTable {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} +ORM BEFORE DELETE testpb.ExampleTable {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} NEXT VALID false CLOSE @@ -1100,18 +1121,21 @@ DEL 01026162630000087ffffffffffffffc DEL IDX testpb.ExampleTable str/u32/i64 : abc/8/-4 -> 8/-4/abc DEL 0103006162630000087ffffffffffffffc DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/-4 -> 8/-4/abc +ORM AFTER DELETE testpb.ExampleTable {"u32":8,"u64":11,"str":"abc","i64":-4} DEL 0101000c616263 DEL ERR:EOF DEL 01026162630000088000000000000001 DEL IDX testpb.ExampleTable str/u32/i64 : abc/8/1 -> 8/1/abc DEL 0103006162630000088000000000000001 DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/1 -> 8/1/abc +ORM AFTER DELETE testpb.ExampleTable {"u32":8,"u64":12,"str":"abc","i64":1} DEL 01010012616265 DEL ERR:EOF DEL 01026162650000057ffffffffffffffe DEL IDX testpb.ExampleTable str/u32/i64 : abe/5/-2 -> 5/-2/abe DEL 0103036162656162650000057ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 101]/abe/5/-2 -> 5/-2/abe +ORM AFTER DELETE testpb.ExampleTable {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} ITERATOR 0100 -> 0101 VALID true KEY 010000047ffffffffffffffe616263 100e2203616263 @@ -1132,7 +1156,7 @@ ITERATOR 0100 -> 0101 VALID false GET 010000047ffffffffffffffe616263 100e2203616263 PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} -ORM DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} +ORM BEFORE DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} DEL 010000047ffffffffffffffe616263 DEL PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"str":"abc","i64":-2} DEL 0101000e616263 @@ -1141,6 +1165,7 @@ DEL 01026162630000047ffffffffffffffe DEL IDX testpb.ExampleTable str/u32/i64 : abc/4/-2 -> 4/-2/abc DEL 0103036162636162630000047ffffffffffffffe DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 99]/abc/4/-2 -> 4/-2/abc +ORM AFTER DELETE testpb.ExampleTable {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} ITERATOR 0100 -> 0101 VALID true KEY 010000047fffffffffffffff616263 10102203616263 diff --git a/orm/testing/ormmocks/docs.go b/orm/testing/ormmocks/docs.go index 751da39a2..7065a2fed 100644 --- a/orm/testing/ormmocks/docs.go +++ b/orm/testing/ormmocks/docs.go @@ -1,5 +1,5 @@ // Package ormmocks contains generated mocks for orm types that can be used -// in testing. Right now, this package only contains a mock for ormtable.Hooks +// in testing. Right now, this package only contains a mock for ormtable.ValidateHooks // as this useful way for unit testing using an in-memory database. Rather // than attempting to mock a whole table or database instance, instead // a mock Hook instance can be passed in to verify that the expected @@ -9,5 +9,5 @@ // be used in gomock EXPECT functions. // // See TestHooks in ormdb/module_test.go for examples of how to use -// mock Hooks in a real-world scenario. +// mock ValidateHooks in a real-world scenario. package ormmocks diff --git a/orm/testing/ormmocks/hooks.go b/orm/testing/ormmocks/hooks.go index 1a63fab52..c5ad0e6f0 100644 --- a/orm/testing/ormmocks/hooks.go +++ b/orm/testing/ormmocks/hooks.go @@ -5,73 +5,133 @@ package ormmocks import ( + context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" proto "google.golang.org/protobuf/proto" ) -// MockHooks is a mock of Hooks interface. -type MockHooks struct { +// MockValidateHooks is a mock of ValidateHooks interface. +type MockValidateHooks struct { ctrl *gomock.Controller - recorder *MockHooksMockRecorder + recorder *MockValidateHooksMockRecorder } -// MockHooksMockRecorder is the mock recorder for MockHooks. -type MockHooksMockRecorder struct { - mock *MockHooks +// MockValidateHooksMockRecorder is the mock recorder for MockValidateHooks. +type MockValidateHooksMockRecorder struct { + mock *MockValidateHooks } -// NewMockHooks creates a new mock instance. -func NewMockHooks(ctrl *gomock.Controller) *MockHooks { - mock := &MockHooks{ctrl: ctrl} - mock.recorder = &MockHooksMockRecorder{mock} +// NewMockValidateHooks creates a new mock instance. +func NewMockValidateHooks(ctrl *gomock.Controller) *MockValidateHooks { + mock := &MockValidateHooks{ctrl: ctrl} + mock.recorder = &MockValidateHooksMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockHooks) EXPECT() *MockHooksMockRecorder { +func (m *MockValidateHooks) EXPECT() *MockValidateHooksMockRecorder { + return m.recorder +} + +// ValidateDelete mocks base method. +func (m *MockValidateHooks) ValidateDelete(arg0 context.Context, arg1 proto.Message) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateDelete", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateDelete indicates an expected call of ValidateDelete. +func (mr *MockValidateHooksMockRecorder) ValidateDelete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDelete", reflect.TypeOf((*MockValidateHooks)(nil).ValidateDelete), arg0, arg1) +} + +// ValidateInsert mocks base method. +func (m *MockValidateHooks) ValidateInsert(arg0 context.Context, arg1 proto.Message) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateInsert", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateInsert indicates an expected call of ValidateInsert. +func (mr *MockValidateHooksMockRecorder) ValidateInsert(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateInsert", reflect.TypeOf((*MockValidateHooks)(nil).ValidateInsert), arg0, arg1) +} + +// ValidateUpdate mocks base method. +func (m *MockValidateHooks) ValidateUpdate(ctx context.Context, existing, new proto.Message) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateUpdate", ctx, existing, new) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateUpdate indicates an expected call of ValidateUpdate. +func (mr *MockValidateHooksMockRecorder) ValidateUpdate(ctx, existing, new interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUpdate", reflect.TypeOf((*MockValidateHooks)(nil).ValidateUpdate), ctx, existing, new) +} + +// MockWriteHooks is a mock of WriteHooks interface. +type MockWriteHooks struct { + ctrl *gomock.Controller + recorder *MockWriteHooksMockRecorder +} + +// MockWriteHooksMockRecorder is the mock recorder for MockWriteHooks. +type MockWriteHooksMockRecorder struct { + mock *MockWriteHooks +} + +// NewMockWriteHooks creates a new mock instance. +func NewMockWriteHooks(ctrl *gomock.Controller) *MockWriteHooks { + mock := &MockWriteHooks{ctrl: ctrl} + mock.recorder = &MockWriteHooksMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWriteHooks) EXPECT() *MockWriteHooksMockRecorder { return m.recorder } // OnDelete mocks base method. -func (m *MockHooks) OnDelete(arg0 proto.Message) error { +func (m *MockWriteHooks) OnDelete(arg0 context.Context, arg1 proto.Message) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OnDelete", arg0) - ret0, _ := ret[0].(error) - return ret0 + m.ctrl.Call(m, "OnDelete", arg0, arg1) } // OnDelete indicates an expected call of OnDelete. -func (mr *MockHooksMockRecorder) OnDelete(arg0 interface{}) *gomock.Call { +func (mr *MockWriteHooksMockRecorder) OnDelete(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockHooks)(nil).OnDelete), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockWriteHooks)(nil).OnDelete), arg0, arg1) } // OnInsert mocks base method. -func (m *MockHooks) OnInsert(arg0 proto.Message) error { +func (m *MockWriteHooks) OnInsert(arg0 context.Context, arg1 proto.Message) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OnInsert", arg0) - ret0, _ := ret[0].(error) - return ret0 + m.ctrl.Call(m, "OnInsert", arg0, arg1) } // OnInsert indicates an expected call of OnInsert. -func (mr *MockHooksMockRecorder) OnInsert(arg0 interface{}) *gomock.Call { +func (mr *MockWriteHooksMockRecorder) OnInsert(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnInsert", reflect.TypeOf((*MockHooks)(nil).OnInsert), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnInsert", reflect.TypeOf((*MockWriteHooks)(nil).OnInsert), arg0, arg1) } // OnUpdate mocks base method. -func (m *MockHooks) OnUpdate(existing, new proto.Message) error { +func (m *MockWriteHooks) OnUpdate(ctx context.Context, existing, new proto.Message) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OnUpdate", existing, new) - ret0, _ := ret[0].(error) - return ret0 + m.ctrl.Call(m, "OnUpdate", ctx, existing, new) } // OnUpdate indicates an expected call of OnUpdate. -func (mr *MockHooksMockRecorder) OnUpdate(existing, new interface{}) *gomock.Call { +func (mr *MockWriteHooksMockRecorder) OnUpdate(ctx, existing, new interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnUpdate", reflect.TypeOf((*MockHooks)(nil).OnUpdate), existing, new) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnUpdate", reflect.TypeOf((*MockWriteHooks)(nil).OnUpdate), ctx, existing, new) }