330 lines
7.7 KiB
Go
330 lines
7.7 KiB
Go
package testkv
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
|
|
"github.com/cosmos/cosmos-sdk/orm/internal/stablejson"
|
|
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
|
|
"github.com/cosmos/cosmos-sdk/orm/types/kv"
|
|
)
|
|
|
|
// Debugger is an interface that handles debug info from the debug store wrapper.
|
|
type Debugger interface {
|
|
// Log logs a single log message.
|
|
Log(string)
|
|
|
|
// Decode decodes a key-value entry into a debug string.
|
|
Decode(key, value []byte) string
|
|
}
|
|
|
|
// 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"),
|
|
ValidateHooks: hooks,
|
|
WriteHooks: hooks,
|
|
})
|
|
}
|
|
|
|
type debugStore struct {
|
|
store kv.Store
|
|
debugger Debugger
|
|
storeName string
|
|
}
|
|
|
|
// NewDebugStore wraps the store with the debugger instance returning a debug store wrapper.
|
|
func NewDebugStore(store kv.Store, debugger Debugger, storeName string) kv.Store {
|
|
return &debugStore{store: store, debugger: debugger, storeName: storeName}
|
|
}
|
|
|
|
func (t debugStore) Get(key []byte) ([]byte, error) {
|
|
val, err := t.store.Get(key)
|
|
if err != nil {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ERR on GET %s: %v", t.debugger.Decode(key, nil), err))
|
|
}
|
|
return nil, err
|
|
}
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("GET %x %x", key, val))
|
|
t.debugger.Log(fmt.Sprintf(" %s", t.debugger.Decode(key, val)))
|
|
}
|
|
return val, nil
|
|
}
|
|
|
|
func (t debugStore) Has(key []byte) (bool, error) {
|
|
has, err := t.store.Has(key)
|
|
if err != nil {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ERR on HAS %s: %v", t.debugger.Decode(key, nil), err))
|
|
}
|
|
return has, err
|
|
}
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("HAS %x", key))
|
|
t.debugger.Log(fmt.Sprintf(" %s", t.debugger.Decode(key, nil)))
|
|
}
|
|
return has, nil
|
|
}
|
|
|
|
func (t debugStore) Iterator(start, end []byte) (kv.Iterator, error) {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ITERATOR %x -> %x", start, end))
|
|
}
|
|
it, err := t.store.Iterator(start, end)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &debugIterator{
|
|
iterator: it,
|
|
storeName: t.storeName,
|
|
debugger: t.debugger,
|
|
}, nil
|
|
}
|
|
|
|
func (t debugStore) ReverseIterator(start, end []byte) (kv.Iterator, error) {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ITERATOR %x <- %x", start, end))
|
|
}
|
|
it, err := t.store.ReverseIterator(start, end)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &debugIterator{
|
|
iterator: it,
|
|
storeName: t.storeName,
|
|
debugger: t.debugger,
|
|
}, nil
|
|
}
|
|
|
|
func (t debugStore) Set(key, value []byte) error {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("SET %x %x", key, value))
|
|
t.debugger.Log(fmt.Sprintf(" %s", t.debugger.Decode(key, value)))
|
|
}
|
|
err := t.store.Set(key, value)
|
|
if err != nil {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ERR on SET %s: %v", t.debugger.Decode(key, value), err))
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t debugStore) Delete(key []byte) error {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("DEL %x", key))
|
|
t.debugger.Log(fmt.Sprintf("DEL %s", t.debugger.Decode(key, nil)))
|
|
}
|
|
err := t.store.Delete(key)
|
|
if err != nil {
|
|
if t.debugger != nil {
|
|
t.debugger.Log(fmt.Sprintf("ERR on SET %s: %v", t.debugger.Decode(key, nil), err))
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ kv.Store = &debugStore{}
|
|
|
|
type debugIterator struct {
|
|
iterator kv.Iterator
|
|
storeName string
|
|
debugger Debugger
|
|
}
|
|
|
|
func (d debugIterator) Domain() (start []byte, end []byte) {
|
|
start, end = d.iterator.Domain()
|
|
d.debugger.Log(fmt.Sprintf(" DOMAIN %x -> %x", start, end))
|
|
return start, end
|
|
}
|
|
|
|
func (d debugIterator) Valid() bool {
|
|
valid := d.iterator.Valid()
|
|
d.debugger.Log(fmt.Sprintf(" VALID %t", valid))
|
|
return valid
|
|
}
|
|
|
|
func (d debugIterator) Next() {
|
|
d.debugger.Log(" NEXT")
|
|
d.iterator.Next()
|
|
}
|
|
|
|
func (d debugIterator) Key() (key []byte) {
|
|
key = d.iterator.Key()
|
|
value := d.iterator.Value()
|
|
d.debugger.Log(fmt.Sprintf(" KEY %x %x", key, value))
|
|
d.debugger.Log(fmt.Sprintf(" %s", d.debugger.Decode(key, value)))
|
|
return key
|
|
}
|
|
|
|
func (d debugIterator) Value() (value []byte) {
|
|
return d.iterator.Value()
|
|
}
|
|
|
|
func (d debugIterator) Error() error {
|
|
err := d.iterator.Error()
|
|
d.debugger.Log(fmt.Sprintf(" ERR %+v", err))
|
|
return err
|
|
}
|
|
|
|
func (d debugIterator) Close() error {
|
|
d.debugger.Log(" CLOSE")
|
|
return d.iterator.Close()
|
|
}
|
|
|
|
var _ kv.Iterator = &debugIterator{}
|
|
|
|
// EntryCodecDebugger is a Debugger instance that uses an EntryCodec and Print
|
|
// function for debugging.
|
|
type EntryCodecDebugger struct {
|
|
EntryCodec ormkv.EntryCodec
|
|
Print func(string)
|
|
}
|
|
|
|
func (d *EntryCodecDebugger) Log(s string) {
|
|
if d.Print != nil {
|
|
d.Print(s)
|
|
} else {
|
|
fmt.Println(s)
|
|
}
|
|
}
|
|
|
|
func (d *EntryCodecDebugger) Decode(key, value []byte) string {
|
|
entry, err := d.EntryCodec.DecodeEntry(key, value)
|
|
if err != nil {
|
|
return fmt.Sprintf("ERR:%v", err)
|
|
}
|
|
|
|
return entry.String()
|
|
}
|
|
|
|
type debugHooks struct {
|
|
debugger Debugger
|
|
validateHooks ormtable.ValidateHooks
|
|
writeHooks ormtable.WriteHooks
|
|
}
|
|
|
|
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 BEFORE INSERT %s %s",
|
|
message.ProtoReflect().Descriptor().FullName(),
|
|
jsonBz,
|
|
))
|
|
if d.validateHooks != nil {
|
|
return d.validateHooks.ValidateInsert(context, message)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d debugHooks) ValidateUpdate(ctx context.Context, existing, new proto.Message) error {
|
|
existingJson, err := stablejson.Marshal(existing)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newJson, err := stablejson.Marshal(new)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.debugger.Log(fmt.Sprintf(
|
|
"ORM BEFORE UPDATE %s %s -> %s",
|
|
existing.ProtoReflect().Descriptor().FullName(),
|
|
existingJson,
|
|
newJson,
|
|
))
|
|
if d.validateHooks != nil {
|
|
return d.validateHooks.ValidateUpdate(ctx, existing, new)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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 BEFORE DELETE %s %s",
|
|
message.ProtoReflect().Descriptor().FullName(),
|
|
jsonBz,
|
|
))
|
|
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)
|
|
}
|
|
}
|