diff --git a/orm/internal/codegen/table.go b/orm/internal/codegen/table.go index b5472538c7..970a02260c 100644 --- a/orm/internal/codegen/table.go +++ b/orm/internal/codegen/table.go @@ -65,11 +65,14 @@ func (t tableGen) genStoreInterface() { t.P("Delete(ctx ", contextPkg.Ident("Context"), ", ", t.param(t.msg.GoIdent.GoName), " *", t.QualifiedGoIdent(t.msg.GoIdent), ") error") t.P("Has(ctx ", contextPkg.Ident("Context"), ", ", t.fieldsArgs(t.primaryKeyFields.Names()), ") (found bool, err error)") t.P("Get(ctx ", contextPkg.Ident("Context"), ", ", t.fieldsArgs(t.primaryKeyFields.Names()), ") (*", t.QualifiedGoIdent(t.msg.GoIdent), ", error)") + for _, idx := range t.uniqueIndexes { t.genUniqueIndexSig(idx) } t.P("List(ctx ", contextPkg.Ident("Context"), ", prefixKey ", t.indexKeyInterfaceName(), ", opts ...", ormListPkg.Ident("Option"), ") ", "(", t.iteratorName(), ", error)") t.P("ListRange(ctx ", contextPkg.Ident("Context"), ", from, to ", t.indexKeyInterfaceName(), ", opts ...", ormListPkg.Ident("Option"), ") ", "(", t.iteratorName(), ", error)") + t.P("DeleteBy(ctx ", contextPkg.Ident("Context"), ", prefixKey ", t.indexKeyInterfaceName(), ") error") + t.P("DeleteRange(ctx ", contextPkg.Ident("Context"), ", from, to ", t.indexKeyInterfaceName(), ") error") t.P() t.P("doNotImplement()") t.P("}") @@ -77,9 +80,9 @@ func (t tableGen) genStoreInterface() { } // returns the has and get (in that order) function signature for unique indexes. -func (t tableGen) uniqueIndexSig(idx *ormv1alpha1.SecondaryIndexDescriptor) (string, string) { - fieldsSlc := strings.Split(idx.Fields, ",") - camelFields := t.fieldsToCamelCase(idx.Fields) +func (t tableGen) uniqueIndexSig(idxFields string) (string, string) { + fieldsSlc := strings.Split(idxFields, ",") + camelFields := t.fieldsToCamelCase(idxFields) hasFuncName := "HasBy" + camelFields getFuncName := "GetBy" + camelFields @@ -91,7 +94,7 @@ func (t tableGen) uniqueIndexSig(idx *ormv1alpha1.SecondaryIndexDescriptor) (str } func (t tableGen) genUniqueIndexSig(idx *ormv1alpha1.SecondaryIndexDescriptor) { - hasSig, getSig := t.uniqueIndexSig(idx) + hasSig, getSig := t.uniqueIndexSig(idx.Fields) t.P(hasSig) t.P(getSig) } @@ -197,7 +200,7 @@ func (t tableGen) genStoreImpl() { for _, idx := range t.uniqueIndexes { fields := strings.Split(idx.Fields, ",") - hasName, getName := t.uniqueIndexSig(idx) + hasName, getName := t.uniqueIndexSig(idx.Fields) // has t.P("func (", receiverVar, " ", t.messageStoreReceiverName(t.msg), ") ", hasName, "{") @@ -243,6 +246,20 @@ func (t tableGen) genStoreImpl() { t.P("}") t.P() + // DeleteBy + t.P(receiver, "DeleteBy(ctx ", contextPkg.Ident("Context"), ", prefixKey ", t.indexKeyInterfaceName(), ") error {") + t.P("return ", receiverVar, ".table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...)") + t.P("}") + t.P() + t.P() + + // DeleteRange + t.P(receiver, "DeleteRange(ctx ", contextPkg.Ident("Context"), ", from, to ", t.indexKeyInterfaceName(), ") error {") + t.P("return ", receiverVar, ".table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values())") + t.P("}") + t.P() + t.P() + t.P(receiver, "doNotImplement() {}") t.P() } diff --git a/orm/internal/testpb/bank.cosmos_orm.go b/orm/internal/testpb/bank.cosmos_orm.go index f48041ff1d..3ef8959470 100644 --- a/orm/internal/testpb/bank.cosmos_orm.go +++ b/orm/internal/testpb/bank.cosmos_orm.go @@ -20,6 +20,8 @@ type BalanceStore interface { Get(ctx context.Context, address string, denom string) (*Balance, error) List(ctx context.Context, prefixKey BalanceIndexKey, opts ...ormlist.Option) (BalanceIterator, error) ListRange(ctx context.Context, from, to BalanceIndexKey, opts ...ormlist.Option) (BalanceIterator, error) + DeleteBy(ctx context.Context, prefixKey BalanceIndexKey) error + DeleteRange(ctx context.Context, from, to BalanceIndexKey) error doNotImplement() } @@ -117,6 +119,14 @@ func (this balanceStore) ListRange(ctx context.Context, from, to BalanceIndexKey return BalanceIterator{it}, err } +func (this balanceStore) DeleteBy(ctx context.Context, prefixKey BalanceIndexKey) error { + return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...) +} + +func (this balanceStore) DeleteRange(ctx context.Context, from, to BalanceIndexKey) error { + return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values()) +} + func (this balanceStore) doNotImplement() {} var _ BalanceStore = balanceStore{} @@ -138,6 +148,8 @@ type SupplyStore interface { Get(ctx context.Context, denom string) (*Supply, error) List(ctx context.Context, prefixKey SupplyIndexKey, opts ...ormlist.Option) (SupplyIterator, error) ListRange(ctx context.Context, from, to SupplyIndexKey, opts ...ormlist.Option) (SupplyIterator, error) + DeleteBy(ctx context.Context, prefixKey SupplyIndexKey) error + DeleteRange(ctx context.Context, from, to SupplyIndexKey) error doNotImplement() } @@ -217,6 +229,14 @@ func (this supplyStore) ListRange(ctx context.Context, from, to SupplyIndexKey, return SupplyIterator{it}, err } +func (this supplyStore) DeleteBy(ctx context.Context, prefixKey SupplyIndexKey) error { + return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...) +} + +func (this supplyStore) DeleteRange(ctx context.Context, from, to SupplyIndexKey) error { + return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values()) +} + func (this supplyStore) doNotImplement() {} var _ SupplyStore = supplyStore{} diff --git a/orm/internal/testpb/test_schema.cosmos_orm.go b/orm/internal/testpb/test_schema.cosmos_orm.go index 6b45f8aec3..6ebf57c45d 100644 --- a/orm/internal/testpb/test_schema.cosmos_orm.go +++ b/orm/internal/testpb/test_schema.cosmos_orm.go @@ -22,6 +22,8 @@ type ExampleTableStore interface { GetByU64Str(ctx context.Context, u64 uint64, str string) (*ExampleTable, error) List(ctx context.Context, prefixKey ExampleTableIndexKey, opts ...ormlist.Option) (ExampleTableIterator, error) ListRange(ctx context.Context, from, to ExampleTableIndexKey, opts ...ormlist.Option) (ExampleTableIterator, error) + DeleteBy(ctx context.Context, prefixKey ExampleTableIndexKey) error + DeleteRange(ctx context.Context, from, to ExampleTableIndexKey) error doNotImplement() } @@ -184,6 +186,14 @@ func (this exampleTableStore) ListRange(ctx context.Context, from, to ExampleTab return ExampleTableIterator{it}, err } +func (this exampleTableStore) DeleteBy(ctx context.Context, prefixKey ExampleTableIndexKey) error { + return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...) +} + +func (this exampleTableStore) DeleteRange(ctx context.Context, from, to ExampleTableIndexKey) error { + return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values()) +} + func (this exampleTableStore) doNotImplement() {} var _ ExampleTableStore = exampleTableStore{} @@ -208,6 +218,8 @@ type ExampleAutoIncrementTableStore interface { GetByX(ctx context.Context, x string) (*ExampleAutoIncrementTable, error) List(ctx context.Context, prefixKey ExampleAutoIncrementTableIndexKey, opts ...ormlist.Option) (ExampleAutoIncrementTableIterator, error) ListRange(ctx context.Context, from, to ExampleAutoIncrementTableIndexKey, opts ...ormlist.Option) (ExampleAutoIncrementTableIterator, error) + DeleteBy(ctx context.Context, prefixKey ExampleAutoIncrementTableIndexKey) error + DeleteRange(ctx context.Context, from, to ExampleAutoIncrementTableIndexKey) error doNotImplement() } @@ -321,6 +333,14 @@ func (this exampleAutoIncrementTableStore) ListRange(ctx context.Context, from, return ExampleAutoIncrementTableIterator{it}, err } +func (this exampleAutoIncrementTableStore) DeleteBy(ctx context.Context, prefixKey ExampleAutoIncrementTableIndexKey) error { + return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...) +} + +func (this exampleAutoIncrementTableStore) DeleteRange(ctx context.Context, from, to ExampleAutoIncrementTableIndexKey) error { + return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values()) +} + func (this exampleAutoIncrementTableStore) doNotImplement() {} var _ ExampleAutoIncrementTableStore = exampleAutoIncrementTableStore{} diff --git a/orm/model/ormtable/batch.go b/orm/model/ormtable/batch.go index 62653fa7ed..6fb2115d17 100644 --- a/orm/model/ormtable/batch.go +++ b/orm/model/ormtable/batch.go @@ -11,14 +11,13 @@ type batchIndexCommitmentWriter struct { func newBatchIndexCommitmentWriter(store Backend) *batchIndexCommitmentWriter { return &batchIndexCommitmentWriter{ Backend: store, - // optimal array capacities are estimated here: commitmentWriter: &batchStoreWriter{ ReadonlyStore: store.CommitmentStoreReader(), - writes: make([]batchWriterEntry, 0, 2), + curBuf: make([]*batchWriterEntry, 0, capacity), }, indexWriter: &batchStoreWriter{ ReadonlyStore: store.IndexStoreReader(), - writes: make([]batchWriterEntry, 0, 16), + curBuf: make([]*batchWriterEntry, 0, capacity), }, } } @@ -33,12 +32,12 @@ func (w *batchIndexCommitmentWriter) IndexStore() kv.Store { // Write flushes any pending writes. func (w *batchIndexCommitmentWriter) Write() error { - err := flushWrites(w.Backend.CommitmentStore(), w.commitmentWriter.writes) + err := flushWrites(w.Backend.CommitmentStore(), w.commitmentWriter) if err != nil { return err } - err = flushWrites(w.Backend.IndexStore(), w.indexWriter.writes) + err = flushWrites(w.Backend.IndexStore(), w.indexWriter) if err != nil { return err } @@ -49,15 +48,25 @@ func (w *batchIndexCommitmentWriter) Write() error { return err } -func flushWrites(writer kv.Store, writes []batchWriterEntry) error { +func flushWrites(store kv.Store, writer *batchStoreWriter) error { + for _, buf := range writer.prevBufs { + err := flushBuf(store, buf) + if err != nil { + return err + } + } + return flushBuf(store, writer.curBuf) +} + +func flushBuf(store kv.Store, writes []*batchWriterEntry) error { for _, write := range writes { if !write.delete { - err := writer.Set(write.key, write.value) + err := store.Set(write.key, write.value) if err != nil { return err } } else { - err := writer.Delete(write.key) + err := store.Delete(write.key) if err != nil { return err } @@ -69,8 +78,10 @@ func flushWrites(writer kv.Store, writes []batchWriterEntry) error { // Close discards any pending writes and should generally be called using // a defer statement. func (w *batchIndexCommitmentWriter) Close() { - w.commitmentWriter.writes = nil - w.indexWriter.writes = nil + w.commitmentWriter.prevBufs = nil + w.commitmentWriter.curBuf = nil + w.indexWriter.prevBufs = nil + w.indexWriter.curBuf = nil } type batchWriterEntry struct { @@ -80,17 +91,29 @@ type batchWriterEntry struct { type batchStoreWriter struct { kv.ReadonlyStore - writes []batchWriterEntry + prevBufs [][]*batchWriterEntry + curBuf []*batchWriterEntry } +const capacity = 16 + func (b *batchStoreWriter) Set(key, value []byte) error { - b.writes = append(b.writes, batchWriterEntry{key: key, value: value}) + b.append(&batchWriterEntry{key: key, value: value}) return nil } func (b *batchStoreWriter) Delete(key []byte) error { - b.writes = append(b.writes, batchWriterEntry{key: key, delete: true}) + b.append(&batchWriterEntry{key: key, delete: true}) return nil } +func (b *batchStoreWriter) append(entry *batchWriterEntry) { + if len(b.curBuf) == capacity { + b.prevBufs = append(b.prevBufs, b.curBuf) + b.curBuf = make([]*batchWriterEntry, 0, capacity) + } + + b.curBuf = append(b.curBuf, entry) +} + var _ Backend = &batchIndexCommitmentWriter{} diff --git a/orm/model/ormtable/index.go b/orm/model/ormtable/index.go index 23918c6301..a594ad6d8f 100644 --- a/orm/model/ormtable/index.go +++ b/orm/model/ormtable/index.go @@ -38,6 +38,12 @@ type Index interface { // Range iteration is inclusive at both ends. ListRange(ctx context.Context, from, to []interface{}, options ...ormlist.Option) (Iterator, error) + // DeleteBy deletes any entries which match the provided prefix key. + DeleteBy(context context.Context, prefixKey ...interface{}) error + + // DeleteRange deletes any entries between the provided range keys. + DeleteRange(context context.Context, from, to []interface{}) error + // MessageType returns the protobuf message type of the index. MessageType() protoreflect.MessageType @@ -64,9 +70,6 @@ type UniqueIndex interface { // Get retrieves the message if one exists for the provided key values. Get(context context.Context, message proto.Message, keyValues ...interface{}) (found bool, err error) - - // DeleteByKey deletes the message if one exists in for the provided key values. - DeleteByKey(context context.Context, keyValues ...interface{}) error } type indexer interface { diff --git a/orm/model/ormtable/index_impl.go b/orm/model/ormtable/index_impl.go index a2a892d86a..3f554c4a9a 100644 --- a/orm/model/ormtable/index_impl.go +++ b/orm/model/ormtable/index_impl.go @@ -24,6 +24,24 @@ type indexKeyIndex struct { getReadBackend func(context.Context) (ReadBackend, error) } +func (i indexKeyIndex) DeleteBy(ctx context.Context, keyValues ...interface{}) error { + it, err := i.List(ctx, keyValues) + if err != nil { + return err + } + + return i.primaryKey.deleteByIterator(ctx, it) +} + +func (i indexKeyIndex) DeleteRange(ctx context.Context, from, to []interface{}) error { + it, err := i.ListRange(ctx, from, to) + if err != nil { + return err + } + + return i.primaryKey.deleteByIterator(ctx, it) +} + func (i indexKeyIndex) List(ctx context.Context, prefixKey []interface{}, options ...ormlist.Option) (Iterator, error) { backend, err := i.getReadBackend(ctx) if err != nil { diff --git a/orm/model/ormtable/iterator.go b/orm/model/ormtable/iterator.go index b8135cd671..35019f11b5 100644 --- a/orm/model/ormtable/iterator.go +++ b/orm/model/ormtable/iterator.go @@ -15,6 +15,10 @@ import ( ) // Iterator defines the interface for iterating over indexes. +// +// WARNING: it is generally unsafe to mutate a table while iterating over it. +// Instead you should do reads and writes separately, or use a helper +// function like DeleteBy which does this efficiently. type Iterator interface { // Next advances the iterator and returns true if a valid entry is found. @@ -213,6 +217,7 @@ func (i *indexIterator) Next() bool { i.started = true } else { i.iterator.Next() + i.indexValues = nil } return i.iterator.Valid() diff --git a/orm/model/ormtable/primary_key.go b/orm/model/ormtable/primary_key.go index 65c921a8e4..37a4e0537f 100644 --- a/orm/model/ormtable/primary_key.go +++ b/orm/model/ormtable/primary_key.go @@ -80,16 +80,38 @@ func (p primaryKeyIndex) get(backend ReadBackend, message proto.Message, values return p.getByKeyBytes(backend, key, values, message) } -func (p primaryKeyIndex) DeleteByKey(ctx context.Context, primaryKeyValues ...interface{}) error { - return p.doDeleteByKey(ctx, encodeutil.ValuesOf(primaryKeyValues...)) +func (p primaryKeyIndex) DeleteBy(ctx context.Context, primaryKeyValues ...interface{}) error { + if len(primaryKeyValues) == len(p.GetFieldNames()) { + return p.doDelete(ctx, encodeutil.ValuesOf(primaryKeyValues...)) + } + + it, err := p.List(ctx, primaryKeyValues) + if err != nil { + return err + } + + return p.deleteByIterator(ctx, it) } -func (p primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []protoreflect.Value) error { +func (p primaryKeyIndex) DeleteRange(ctx context.Context, from, to []interface{}) error { + it, err := p.ListRange(ctx, from, to) + if err != nil { + return err + } + + return p.deleteByIterator(ctx, it) +} + +func (p primaryKeyIndex) doDelete(ctx context.Context, primaryKeyValues []protoreflect.Value) error { backend, err := p.getBackend(ctx) if err != nil { return err } + // delete object + writer := newBatchIndexCommitmentWriter(backend) + defer writer.Close() + pk, err := p.EncodeKey(primaryKeyValues) if err != nil { return err @@ -105,23 +127,30 @@ func (p primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []p return nil } + err = p.doDeleteWithWriteBatch(backend, writer, pk, msg) + if err != nil { + return err + } + + 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(msg) + err := hooks.OnDelete(message) if err != nil { return err } } // delete object - writer := newBatchIndexCommitmentWriter(backend) - defer writer.Close() - err = writer.CommitmentStore().Delete(pk) + err := writer.CommitmentStore().Delete(primaryKeyBz) if err != nil { return err } // clear indexes - mref := msg.ProtoReflect() + mref := message.ProtoReflect() indexStoreWriter := writer.IndexStore() for _, idx := range p.indexers { err := idx.onDelete(indexStoreWriter, mref) @@ -130,7 +159,7 @@ func (p primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []p } } - return writer.Write() + return nil } func (p primaryKeyIndex) getByKeyBytes(store ReadBackend, key []byte, keyValues []protoreflect.Value, message proto.Message) (found bool, err error) { @@ -154,4 +183,42 @@ func (p primaryKeyIndex) Fields() string { return p.fields.String() } +func (p primaryKeyIndex) deleteByIterator(ctx context.Context, it Iterator) error { + backend, err := p.getBackend(ctx) + if err != nil { + return err + } + + // we batch writes while the iterator is still open + writer := newBatchIndexCommitmentWriter(backend) + defer writer.Close() + + for it.Next() { + _, pk, err := it.Keys() + if err != nil { + return err + } + + msg, err := it.GetMessage() + if err != nil { + return err + } + + pkBz, err := p.EncodeKey(pk) + if err != nil { + return err + } + + err = p.doDeleteWithWriteBatch(backend, writer, pkBz, msg) + if err != nil { + return err + } + } + + // close iterator + it.Close() + // then write batch + return writer.Write() +} + var _ UniqueIndex = &primaryKeyIndex{} diff --git a/orm/model/ormtable/table_impl.go b/orm/model/ormtable/table_impl.go index 0026cc4ded..7923aad74d 100644 --- a/orm/model/ormtable/table_impl.go +++ b/orm/model/ormtable/table_impl.go @@ -155,7 +155,7 @@ func (t tableImpl) doSave(writer *batchIndexCommitmentWriter, message proto.Mess func (t tableImpl) Delete(context context.Context, message proto.Message) error { pk := t.PrimaryKeyCodec.GetKeyValues(message.ProtoReflect()) - return t.DeleteByKey(context, pk) + return t.DeleteBy(context, pk) } func (t tableImpl) GetIndex(fields string) Index { diff --git a/orm/model/ormtable/table_test.go b/orm/model/ormtable/table_test.go index 775e040fc6..8eb7d19241 100644 --- a/orm/model/ormtable/table_test.go +++ b/orm/model/ormtable/table_test.go @@ -336,7 +336,6 @@ func runTestScenario(t *testing.T, table ormtable.Table, backend ormtable.Backen data = append(data, &testpb.ExampleTable{U32: 9}) err = store.Save(ctx, data[10]) assert.NilError(t, err) - pkIndex := table.GetUniqueIndex("u32,i64,str") a, err = store.Get(ctx, 9, 0, "") assert.NilError(t, err) assert.Assert(t, a != nil) @@ -366,17 +365,36 @@ func runTestScenario(t *testing.T, table ormtable.Table, backend ormtable.Backen assertTablesEqual(t, table, ctx, store2) // let's delete item 5 - key5 := []interface{}{uint32(7), int64(-2), "abe"} - err = pkIndex.DeleteByKey(ctx, key5...) + err = store.DeleteBy(ctx, testpb.ExampleTableU32I64StrIndexKey{}.WithU32I64Str(7, -2, "abe")) assert.NilError(t, err) // it should be gone - found, err = pkIndex.Has(ctx, key5...) + found, err = store.Has(ctx, 7, -2, "abe") assert.NilError(t, err) assert.Assert(t, !found) // and missing from the iterator it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}) assert.NilError(t, err) assertIteratorItems(it, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10) + + // let's do a batch delete + // first iterate over the items we'll delete to check that iterator + it, err = store.List(ctx, testpb.ExampleTableStrU32IndexKey{}.WithStr("abd")) + assert.NilError(t, err) + assertIteratorItems(it, 1, 3, 9) + // now delete them + assert.NilError(t, store.DeleteBy(ctx, testpb.ExampleTableStrU32IndexKey{}.WithStr("abd"))) + it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}) + assert.NilError(t, err) + assertIteratorItems(it, 0, 2, 4, 6, 7, 8, 10) + + // Let's do a range delete + assert.NilError(t, store.DeleteRange(ctx, + testpb.ExampleTableStrU32IndexKey{}.WithStrU32("abc", 8), + testpb.ExampleTableStrU32IndexKey{}.WithStrU32("abe", 5), + )) + it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}) + assert.NilError(t, err) + assertIteratorItems(it, 0, 2, 6, 10) } func TestRandomTableData(t *testing.T) { diff --git a/orm/model/ormtable/testdata/test_scenario.golden b/orm/model/ormtable/testdata/test_scenario.golden index 1bb1e27123..15a8cb7fd1 100644 --- a/orm/model/ormtable/testdata/test_scenario.golden +++ b/orm/model/ormtable/testdata/test_scenario.golden @@ -934,6 +934,170 @@ ITERATOR 0100 -> 0101 PK testpb.ExampleTable 9/0/ -> {"u32":9,"b":true} NEXT VALID false +ITERATOR 010261626400 -> 010261626401 + VALID true + KEY 010261626400000000047ffffffffffffffe + IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd +GET 0100000000047ffffffffffffffe616264 100e2203616264 + PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":14,"str":"abd","bz":"abd","i64":-2} + NEXT + VALID true + KEY 010261626400000000057ffffffffffffffe + IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd +GET 0100000000057ffffffffffffffe616264 10102203616264 + PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":16,"str":"abd","bz":"abd","i64":-2} + NEXT + VALID true + KEY 010261626400000000088000000000000001 + IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd +GET 0100000000088000000000000001616264 100a + PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1} + NEXT + VALID false +ITERATOR 010261626400 -> 010261626401 + VALID true + KEY 010261626400000000047ffffffffffffffe + IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd +GET 0100000000047ffffffffffffffe616264 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} + NEXT + VALID true + KEY 010261626400000000057ffffffffffffffe + IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd +GET 0100000000057ffffffffffffffe616264 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} + NEXT + VALID true + KEY 010261626400000000088000000000000001 + IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd +GET 0100000000088000000000000001616264 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} + NEXT + VALID false + CLOSE +DEL 0100000000047ffffffffffffffe616264 +DEL PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"str":"abd","i64":-2} +DEL 0100000000057ffffffffffffffe616264 +DEL PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"str":"abd","i64":-2} +DEL 0100000000088000000000000001616264 +DEL PK testpb.ExampleTable 8/1/abd -> {"u32":8,"str":"abd","i64":1} +DEL 0101000000000000000e616264 +DEL ERR:EOF +DEL 010261626400000000047ffffffffffffffe +DEL IDX testpb.ExampleTable str/u32/i64 : abd/4/-2 -> 4/-2/abd +DEL 01030361626461626400000000047ffffffffffffffe +DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/4/-2 -> 4/-2/abd +DEL 01010000000000000010616264 +DEL ERR:EOF +DEL 010261626400000000057ffffffffffffffe +DEL IDX testpb.ExampleTable str/u32/i64 : abd/5/-2 -> 5/-2/abd +DEL 01030361626461626400000000057ffffffffffffffe +DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 100]/abd/5/-2 -> 5/-2/abd +DEL 0101000000000000000a616264 +DEL ERR:EOF +DEL 010261626400000000088000000000000001 +DEL IDX testpb.ExampleTable str/u32/i64 : abd/8/1 -> 8/1/abd +DEL 01030061626400000000088000000000000001 +DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abd/8/1 -> 8/1/abd +ITERATOR 0100 -> 0101 + VALID true + KEY 0100000000047ffffffffffffffe616263 100e2203616263 + PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} + NEXT + VALID true + KEY 0100000000047fffffffffffffff616263 10102203616263 + PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":16,"str":"abc","bz":"abc","i64":-1} + NEXT + VALID true + KEY 0100000000057ffffffffffffffe616265 10122203616265 + PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":18,"str":"abe","bz":"abe","i64":-2} + NEXT + VALID true + KEY 0100000000077fffffffffffffff616265 100b + PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1} + NEXT + VALID true + KEY 0100000000087ffffffffffffffc616263 100b + PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4} + NEXT + VALID true + KEY 0100000000088000000000000001616263 100c + PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1} + NEXT + VALID true + KEY 0100000000098000000000000000 7801 + PK testpb.ExampleTable 9/0/ -> {"u32":9,"b":true} + NEXT + VALID false +ITERATOR 01026162630000000008 -> 01026162650000000006 + VALID true + KEY 010261626300000000087ffffffffffffffc + IDX testpb.ExampleTable str/u32/i64 : abc/8/-4 -> 8/-4/abc +GET 0100000000087ffffffffffffffc616263 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} + NEXT + VALID true + KEY 010261626300000000088000000000000001 + IDX testpb.ExampleTable str/u32/i64 : abc/8/1 -> 8/1/abc +GET 0100000000088000000000000001616263 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} + NEXT + VALID true + KEY 010261626500000000057ffffffffffffffe + IDX testpb.ExampleTable str/u32/i64 : abe/5/-2 -> 5/-2/abe +GET 0100000000057ffffffffffffffe616265 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} + NEXT + VALID false + CLOSE +DEL 0100000000087ffffffffffffffc616263 +DEL PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"str":"abc","i64":-4} +DEL 0100000000088000000000000001616263 +DEL PK testpb.ExampleTable 8/1/abc -> {"u32":8,"str":"abc","i64":1} +DEL 0100000000057ffffffffffffffe616265 +DEL PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"str":"abe","i64":-2} +DEL 0101000000000000000b616263 +DEL ERR:EOF +DEL 010261626300000000087ffffffffffffffc +DEL IDX testpb.ExampleTable str/u32/i64 : abc/8/-4 -> 8/-4/abc +DEL 01030061626300000000087ffffffffffffffc +DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/-4 -> 8/-4/abc +DEL 0101000000000000000c616263 +DEL ERR:EOF +DEL 010261626300000000088000000000000001 +DEL IDX testpb.ExampleTable str/u32/i64 : abc/8/1 -> 8/1/abc +DEL 01030061626300000000088000000000000001 +DEL IDX testpb.ExampleTable bz/str/u32/i64 : []/abc/8/1 -> 8/1/abc +DEL 01010000000000000012616265 +DEL ERR:EOF +DEL 010261626500000000057ffffffffffffffe +DEL IDX testpb.ExampleTable str/u32/i64 : abe/5/-2 -> 5/-2/abe +DEL 01030361626561626500000000057ffffffffffffffe +DEL IDX testpb.ExampleTable bz/str/u32/i64 : [97 98 101]/abe/5/-2 -> 5/-2/abe +ITERATOR 0100 -> 0101 + VALID true + KEY 0100000000047ffffffffffffffe616263 100e2203616263 + PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2} + NEXT + VALID true + KEY 0100000000047fffffffffffffff616263 10102203616263 + PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":16,"str":"abc","bz":"abc","i64":-1} + NEXT + VALID true + KEY 0100000000077fffffffffffffff616265 100b + PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1} + NEXT + VALID true + KEY 0100000000098000000000000000 7801 + PK testpb.ExampleTable 9/0/ -> {"u32":9,"b":true} + NEXT + VALID false CLOSE CLOSE CLOSE diff --git a/orm/model/ormtable/unique.go b/orm/model/ormtable/unique.go index a3c671a16e..d05cec96f0 100644 --- a/orm/model/ormtable/unique.go +++ b/orm/model/ormtable/unique.go @@ -87,33 +87,22 @@ func (u uniqueKeyIndex) Get(ctx context.Context, message proto.Message, keyValue return u.primaryKey.get(backend, message, pk) } -func (u uniqueKeyIndex) DeleteByKey(ctx context.Context, keyValues ...interface{}) error { - backend, err := u.getReadBackend(ctx) +func (u uniqueKeyIndex) DeleteBy(ctx context.Context, keyValues ...interface{}) error { + it, err := u.List(ctx, keyValues) if err != nil { return err } - key, err := u.GetKeyCodec().EncodeKey(encodeutil.ValuesOf(keyValues...)) + return u.primaryKey.deleteByIterator(ctx, it) +} + +func (u uniqueKeyIndex) DeleteRange(ctx context.Context, from, to []interface{}) error { + it, err := u.ListRange(ctx, from, to) if err != nil { return err } - value, err := backend.IndexStoreReader().Get(key) - if err != nil { - return err - } - - // for unique keys, value can be empty and the entry still exists - if value == nil { - return nil - } - - _, pk, err := u.DecodeIndexKey(key, value) - if err != nil { - return err - } - - return u.primaryKey.doDeleteByKey(ctx, pk) + return u.primaryKey.deleteByIterator(ctx, it) } func (u uniqueKeyIndex) onInsert(store kv.Store, message protoreflect.Message) error {