From 872d9247b85f6e39ebcf7189d3d8d8a73ba43b10 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Thu, 17 Mar 2022 14:57:55 -0400 Subject: [PATCH] feat(orm): gRPC codes for save errors (#11386) ## Description Ref: #11088. First PR of several to add gRPC error codes and make sure that errors are a well-defined part of the API. Also introduces Cucumber-style BDD acceptance tests into the SDK via https://github.com/regen-network/gocuke. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- orm/features/table/saving.feature | 49 ++ orm/go.mod | 10 +- orm/go.sum | 22 +- orm/internal/testpb/bank.pulsar.go | 2 +- orm/internal/testpb/test_schema.cosmos_orm.go | 162 +++++ orm/internal/testpb/test_schema.proto | 126 ++-- orm/internal/testpb/test_schema.pulsar.go | 682 +++++++++++++++++- orm/model/ormtable/auto_increment_test.go | 2 +- orm/model/ormtable/save_test.go | 68 ++ orm/model/ormtable/table.go | 9 + orm/model/ormtable/table_impl.go | 4 +- orm/model/ormtable/unique.go | 4 +- orm/types/ormerrors/errors.go | 12 +- 13 files changed, 1050 insertions(+), 102 deletions(-) create mode 100644 orm/features/table/saving.feature create mode 100644 orm/model/ormtable/save_test.go diff --git a/orm/features/table/saving.feature b/orm/features/table/saving.feature new file mode 100644 index 000000000..59c615197 --- /dev/null +++ b/orm/features/table/saving.feature @@ -0,0 +1,49 @@ +Feature: inserting, updating and saving entities + + Scenario: can't insert an entity with a duplicate primary key + Given an existing entity + """ + {"name": "foo", "not_unique": "bar"} + """ + When I insert + """ + {"name": "foo", "not_unique": "baz"} + """ + Then expect a "already exists" error + And expect grpc error code "ALREADY_EXISTS" + + Scenario: can't update entity that doesn't exist + When I update + """ + {"name":"foo"} + """ + Then expect a "not found" error + And expect grpc error code "NOT_FOUND" +# + Scenario: can't violate unique constraint on insert + Given an existing entity + """ + {"name": "foo", "unique": "bar"} + """ + When I insert + """ + {"name": "baz", "unique": "bar"} + """ + Then expect a "unique key violation" error + And expect grpc error code "FAILED_PRECONDITION" + + Scenario: can't violate unique constraint on update + Given an existing entity + """ + {"name": "foo", "unique": "bar"} + """ + And an existing entity + """ + {"name": "baz", "unique": "bam"} + """ + When I update + """ + {"name": "baz", "unique": "bar"} + """ + Then expect a "unique key violation" error + And expect grpc error code "FAILED_PRECONDITION" diff --git a/orm/go.mod b/orm/go.mod index 24050e7f6..2930d41da 100644 --- a/orm/go.mod +++ b/orm/go.mod @@ -5,12 +5,14 @@ go 1.17 require ( github.com/cosmos/cosmos-proto v1.0.0-alpha7 github.com/cosmos/cosmos-sdk/api v0.1.0-alpha5 - github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.3 + github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.4 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.7 github.com/iancoleman/strcase v0.2.0 + github.com/regen-network/gocuke v0.6.1 github.com/stretchr/testify v1.7.0 github.com/tendermint/tm-db v0.6.7 + google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 gotest.tools/v3 v3.1.0 pgregory.net/rapid v0.4.7 @@ -18,13 +20,18 @@ require ( require ( github.com/DataDog/zstd v1.4.5 // indirect + github.com/alecthomas/participle/v2 v2.0.0-alpha7 // indirect github.com/cespare/xxhash v1.1.0 // indirect + github.com/cockroachdb/apd/v3 v3.1.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect + github.com/cucumber/messages-go/v16 v16.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/badger/v2 v2.2007.2 // indirect github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 // indirect @@ -39,7 +46,6 @@ require ( golang.org/x/text v0.3.6 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf // indirect - google.golang.org/grpc v1.44.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/orm/go.sum b/orm/go.sum index 6e9c0f4d9..68f77c745 100644 --- a/orm/go.sum +++ b/orm/go.sum @@ -6,6 +6,10 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= +github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -20,6 +24,8 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= +github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -27,11 +33,16 @@ github.com/cosmos/cosmos-proto v1.0.0-alpha7 h1:yqYUOHF2jopwZh4dVQp3xgqwftE5/2hk github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw= github.com/cosmos/cosmos-sdk/api v0.1.0-alpha5 h1:UlK7NOHGlEbvbDTCM0cN7BJN6hhqeXZIuLv6KapFYTc= github.com/cosmos/cosmos-sdk/api v0.1.0-alpha5/go.mod h1:gZu6sOu2vl4Fd7I+BjDSx2bxndwPgFLGfOegek3SQQo= -github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.3 h1:Ep7FHNViVwwGnwLFEPewZYsyN2CJNVMmMvFmtNQtbnw= -github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.3/go.mod h1:HFea93YKmoMJ/mNKtkSeJZDtyJ4inxBsUK928KONcqo= +github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.4 h1:Kjv3QD2Y3C7TvxDh1+Yg9cXefwFbTOUypUtB1tMJRco= +github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.4/go.mod h1:HFea93YKmoMJ/mNKtkSeJZDtyJ4inxBsUK928KONcqo= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE= +github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw= +github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g= +github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY= +github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -62,6 +73,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -113,6 +126,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -126,12 +141,15 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/regen-network/gocuke v0.6.1 h1:SEsXbZDg7/DXpy/hPsLbVvfdObpK13PsJ8Pq3ko+S4s= +github.com/regen-network/gocuke v0.6.1/go.mod h1:+i/R+pDBMLx1M7rL3fV7FC18gzyVTdGu3rNLUSOzHIo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= diff --git a/orm/internal/testpb/bank.pulsar.go b/orm/internal/testpb/bank.pulsar.go index b66d2cf34..1a347bf55 100644 --- a/orm/internal/testpb/bank.pulsar.go +++ b/orm/internal/testpb/bank.pulsar.go @@ -1016,7 +1016,7 @@ func (x *fastReflection_Supply) ProtoMethods() *protoiface.Methods { // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.0 -// protoc v3.19.1 +// protoc (unknown) // source: testpb/bank.proto const ( diff --git a/orm/internal/testpb/test_schema.cosmos_orm.go b/orm/internal/testpb/test_schema.cosmos_orm.go index 4ad28f452..5f62658a0 100644 --- a/orm/internal/testpb/test_schema.cosmos_orm.go +++ b/orm/internal/testpb/test_schema.cosmos_orm.go @@ -530,11 +530,162 @@ func NewExampleTimestampTable(db ormtable.Schema) (ExampleTimestampTable, error) return exampleTimestampTable{table.(ormtable.AutoIncrementTable)}, nil } +type SimpleExampleTable interface { + Insert(ctx context.Context, simpleExample *SimpleExample) error + Update(ctx context.Context, simpleExample *SimpleExample) error + Save(ctx context.Context, simpleExample *SimpleExample) error + Delete(ctx context.Context, simpleExample *SimpleExample) error + Has(ctx context.Context, name string) (found bool, err error) + // Get returns nil and an error which responds true to ormerrors.IsNotFound() if the record was not found. + Get(ctx context.Context, name string) (*SimpleExample, error) + HasByUnique(ctx context.Context, unique string) (found bool, err error) + // GetByUnique returns nil and an error which responds true to ormerrors.IsNotFound() if the record was not found. + GetByUnique(ctx context.Context, unique string) (*SimpleExample, error) + List(ctx context.Context, prefixKey SimpleExampleIndexKey, opts ...ormlist.Option) (SimpleExampleIterator, error) + ListRange(ctx context.Context, from, to SimpleExampleIndexKey, opts ...ormlist.Option) (SimpleExampleIterator, error) + DeleteBy(ctx context.Context, prefixKey SimpleExampleIndexKey) error + DeleteRange(ctx context.Context, from, to SimpleExampleIndexKey) error + + doNotImplement() +} + +type SimpleExampleIterator struct { + ormtable.Iterator +} + +func (i SimpleExampleIterator) Value() (*SimpleExample, error) { + var simpleExample SimpleExample + err := i.UnmarshalMessage(&simpleExample) + return &simpleExample, err +} + +type SimpleExampleIndexKey interface { + id() uint32 + values() []interface{} + simpleExampleIndexKey() +} + +// primary key starting index.. +type SimpleExamplePrimaryKey = SimpleExampleNameIndexKey + +type SimpleExampleNameIndexKey struct { + vs []interface{} +} + +func (x SimpleExampleNameIndexKey) id() uint32 { return 0 } +func (x SimpleExampleNameIndexKey) values() []interface{} { return x.vs } +func (x SimpleExampleNameIndexKey) simpleExampleIndexKey() {} + +func (this SimpleExampleNameIndexKey) WithName(name string) SimpleExampleNameIndexKey { + this.vs = []interface{}{name} + return this +} + +type SimpleExampleUniqueIndexKey struct { + vs []interface{} +} + +func (x SimpleExampleUniqueIndexKey) id() uint32 { return 1 } +func (x SimpleExampleUniqueIndexKey) values() []interface{} { return x.vs } +func (x SimpleExampleUniqueIndexKey) simpleExampleIndexKey() {} + +func (this SimpleExampleUniqueIndexKey) WithUnique(unique string) SimpleExampleUniqueIndexKey { + this.vs = []interface{}{unique} + return this +} + +type simpleExampleTable struct { + table ormtable.Table +} + +func (this simpleExampleTable) Insert(ctx context.Context, simpleExample *SimpleExample) error { + return this.table.Insert(ctx, simpleExample) +} + +func (this simpleExampleTable) Update(ctx context.Context, simpleExample *SimpleExample) error { + return this.table.Update(ctx, simpleExample) +} + +func (this simpleExampleTable) Save(ctx context.Context, simpleExample *SimpleExample) error { + return this.table.Save(ctx, simpleExample) +} + +func (this simpleExampleTable) Delete(ctx context.Context, simpleExample *SimpleExample) error { + return this.table.Delete(ctx, simpleExample) +} + +func (this simpleExampleTable) Has(ctx context.Context, name string) (found bool, err error) { + return this.table.PrimaryKey().Has(ctx, name) +} + +func (this simpleExampleTable) Get(ctx context.Context, name string) (*SimpleExample, error) { + var simpleExample SimpleExample + found, err := this.table.PrimaryKey().Get(ctx, &simpleExample, name) + if err != nil { + return nil, err + } + if !found { + return nil, ormerrors.NotFound + } + return &simpleExample, nil +} + +func (this simpleExampleTable) HasByUnique(ctx context.Context, unique string) (found bool, err error) { + return this.table.GetIndexByID(1).(ormtable.UniqueIndex).Has(ctx, + unique, + ) +} + +func (this simpleExampleTable) GetByUnique(ctx context.Context, unique string) (*SimpleExample, error) { + var simpleExample SimpleExample + found, err := this.table.GetIndexByID(1).(ormtable.UniqueIndex).Get(ctx, &simpleExample, + unique, + ) + if err != nil { + return nil, err + } + if !found { + return nil, ormerrors.NotFound + } + return &simpleExample, nil +} + +func (this simpleExampleTable) List(ctx context.Context, prefixKey SimpleExampleIndexKey, opts ...ormlist.Option) (SimpleExampleIterator, error) { + it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...) + return SimpleExampleIterator{it}, err +} + +func (this simpleExampleTable) ListRange(ctx context.Context, from, to SimpleExampleIndexKey, opts ...ormlist.Option) (SimpleExampleIterator, error) { + it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...) + return SimpleExampleIterator{it}, err +} + +func (this simpleExampleTable) DeleteBy(ctx context.Context, prefixKey SimpleExampleIndexKey) error { + return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...) +} + +func (this simpleExampleTable) DeleteRange(ctx context.Context, from, to SimpleExampleIndexKey) error { + return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values()) +} + +func (this simpleExampleTable) doNotImplement() {} + +var _ SimpleExampleTable = simpleExampleTable{} + +func NewSimpleExampleTable(db ormtable.Schema) (SimpleExampleTable, error) { + table := db.GetTable(&SimpleExample{}) + if table == nil { + return nil, ormerrors.TableNotFound.Wrap(string((&SimpleExample{}).ProtoReflect().Descriptor().FullName())) + } + return simpleExampleTable{table}, nil +} + type TestSchemaStore interface { ExampleTableTable() ExampleTableTable ExampleAutoIncrementTableTable() ExampleAutoIncrementTableTable ExampleSingletonTable() ExampleSingletonTable ExampleTimestampTable() ExampleTimestampTable + SimpleExampleTable() SimpleExampleTable doNotImplement() } @@ -544,6 +695,7 @@ type testSchemaStore struct { exampleAutoIncrementTable ExampleAutoIncrementTableTable exampleSingleton ExampleSingletonTable exampleTimestamp ExampleTimestampTable + simpleExample SimpleExampleTable } func (x testSchemaStore) ExampleTableTable() ExampleTableTable { @@ -562,6 +714,10 @@ func (x testSchemaStore) ExampleTimestampTable() ExampleTimestampTable { return x.exampleTimestamp } +func (x testSchemaStore) SimpleExampleTable() SimpleExampleTable { + return x.simpleExample +} + func (testSchemaStore) doNotImplement() {} var _ TestSchemaStore = testSchemaStore{} @@ -587,10 +743,16 @@ func NewTestSchemaStore(db ormtable.Schema) (TestSchemaStore, error) { return nil, err } + simpleExampleTable, err := NewSimpleExampleTable(db) + if err != nil { + return nil, err + } + return testSchemaStore{ exampleTableTable, exampleAutoIncrementTableTable, exampleSingletonTable, exampleTimestampTable, + simpleExampleTable, }, nil } diff --git a/orm/internal/testpb/test_schema.proto b/orm/internal/testpb/test_schema.proto index 47792619b..522e801f9 100644 --- a/orm/internal/testpb/test_schema.proto +++ b/orm/internal/testpb/test_schema.proto @@ -9,68 +9,68 @@ import "cosmos/orm/v1alpha1/orm.proto"; message ExampleTable { option (cosmos.orm.v1alpha1.table) = { id: 1; -primary_key: { -fields: - "u32,i64,str" -} -index: { -id: - 1; -fields: - "u64,str" unique: true -} -index: { -id: - 2; -fields: - "str,u32" -} -index: { -id: - 3; -fields: - "bz,str" -} -}; + primary_key: { + fields: + "u32,i64,str" + } + index: { + id: + 1; + fields: + "u64,str" unique: true + } + index: { + id: + 2; + fields: + "str,u32" + } + index: { + id: + 3; + fields: + "bz,str" + } + }; -// Valid key fields: -uint32 u32 = 1; -uint64 u64 = 2; -string str = 3; -bytes bz = 4; -google.protobuf.Timestamp ts = 5; -google.protobuf.Duration dur = 6; -int32 i32 = 7; -sint32 s32 = 8; -sfixed32 sf32 = 9; -int64 i64 = 10; -sint64 s64 = 11; -sfixed64 sf64 = 12; -fixed32 f32 = 13; -fixed64 f64 = 14; -bool b = 15; -Enum e = 16; + // Valid key fields: + uint32 u32 = 1; + uint64 u64 = 2; + string str = 3; + bytes bz = 4; + google.protobuf.Timestamp ts = 5; + google.protobuf.Duration dur = 6; + int32 i32 = 7; + sint32 s32 = 8; + sfixed32 sf32 = 9; + int64 i64 = 10; + sint64 s64 = 11; + sfixed64 sf64 = 12; + fixed32 f32 = 13; + fixed64 f64 = 14; + bool b = 15; + Enum e = 16; -// Invalid key fields: -repeated uint32 repeated = 17; -map map = 18; -ExampleMessage msg = 19; -oneof sum { - uint32 oneof = 20; -} + // Invalid key fields: + repeated uint32 repeated = 17; + map map = 18; + ExampleMessage msg = 19; + oneof sum { + uint32 oneof = 20; + } -message ExampleMessage { - string foo = 1; - int32 bar = 2; -} + message ExampleMessage { + string foo = 1; + int32 bar = 2; + } } enum Enum { ENUM_UNSPECIFIED = 0; - ENUM_ONE = 1; - ENUM_TWO = 2; - ENUM_FIVE = 5; - ENUM_NEG_THREE = -3; + ENUM_ONE = 1; + ENUM_TWO = 2; + ENUM_FIVE = 5; + ENUM_NEG_THREE = -3; } message ExampleAutoIncrementTable { @@ -81,8 +81,8 @@ message ExampleAutoIncrementTable { }; uint64 id = 1; - string x = 2; - int32 y = 3; + string x = 2; + int32 y = 3; } message ExampleSingleton { @@ -104,3 +104,15 @@ message ExampleTimestamp { string name = 2; google.protobuf.Timestamp ts = 3; } + +message SimpleExample { + option (cosmos.orm.v1alpha1.table) = { + id: 5 + primary_key: {fields: "name"} + index: {id: 1, fields: "unique", unique: true} + }; + + string name = 1; + string unique = 2; + string not_unique = 3; +} \ No newline at end of file diff --git a/orm/internal/testpb/test_schema.pulsar.go b/orm/internal/testpb/test_schema.pulsar.go index 7fb309825..6cb4e98cb 100644 --- a/orm/internal/testpb/test_schema.pulsar.go +++ b/orm/internal/testpb/test_schema.pulsar.go @@ -1872,7 +1872,7 @@ func (x *ExampleTable_ExampleMessage) ProtoReflect() protoreflect.Message { } func (x *ExampleTable_ExampleMessage) slowProtoReflect() protoreflect.Message { - mi := &file_testpb_test_schema_proto_msgTypes[5] + mi := &file_testpb_test_schema_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3849,10 +3849,558 @@ func (x *fastReflection_ExampleTimestamp) ProtoMethods() *protoiface.Methods { } } +var ( + md_SimpleExample protoreflect.MessageDescriptor + fd_SimpleExample_name protoreflect.FieldDescriptor + fd_SimpleExample_unique protoreflect.FieldDescriptor + fd_SimpleExample_not_unique protoreflect.FieldDescriptor +) + +func init() { + file_testpb_test_schema_proto_init() + md_SimpleExample = File_testpb_test_schema_proto.Messages().ByName("SimpleExample") + fd_SimpleExample_name = md_SimpleExample.Fields().ByName("name") + fd_SimpleExample_unique = md_SimpleExample.Fields().ByName("unique") + fd_SimpleExample_not_unique = md_SimpleExample.Fields().ByName("not_unique") +} + +var _ protoreflect.Message = (*fastReflection_SimpleExample)(nil) + +type fastReflection_SimpleExample SimpleExample + +func (x *SimpleExample) ProtoReflect() protoreflect.Message { + return (*fastReflection_SimpleExample)(x) +} + +func (x *SimpleExample) slowProtoReflect() protoreflect.Message { + mi := &file_testpb_test_schema_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_SimpleExample_messageType fastReflection_SimpleExample_messageType +var _ protoreflect.MessageType = fastReflection_SimpleExample_messageType{} + +type fastReflection_SimpleExample_messageType struct{} + +func (x fastReflection_SimpleExample_messageType) Zero() protoreflect.Message { + return (*fastReflection_SimpleExample)(nil) +} +func (x fastReflection_SimpleExample_messageType) New() protoreflect.Message { + return new(fastReflection_SimpleExample) +} +func (x fastReflection_SimpleExample_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_SimpleExample +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_SimpleExample) Descriptor() protoreflect.MessageDescriptor { + return md_SimpleExample +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_SimpleExample) Type() protoreflect.MessageType { + return _fastReflection_SimpleExample_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_SimpleExample) New() protoreflect.Message { + return new(fastReflection_SimpleExample) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_SimpleExample) Interface() protoreflect.ProtoMessage { + return (*SimpleExample)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_SimpleExample) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Name != "" { + value := protoreflect.ValueOfString(x.Name) + if !f(fd_SimpleExample_name, value) { + return + } + } + if x.Unique != "" { + value := protoreflect.ValueOfString(x.Unique) + if !f(fd_SimpleExample_unique, value) { + return + } + } + if x.NotUnique != "" { + value := protoreflect.ValueOfString(x.NotUnique) + if !f(fd_SimpleExample_not_unique, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_SimpleExample) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "testpb.SimpleExample.name": + return x.Name != "" + case "testpb.SimpleExample.unique": + return x.Unique != "" + case "testpb.SimpleExample.not_unique": + return x.NotUnique != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_SimpleExample) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "testpb.SimpleExample.name": + x.Name = "" + case "testpb.SimpleExample.unique": + x.Unique = "" + case "testpb.SimpleExample.not_unique": + x.NotUnique = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_SimpleExample) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "testpb.SimpleExample.name": + value := x.Name + return protoreflect.ValueOfString(value) + case "testpb.SimpleExample.unique": + value := x.Unique + return protoreflect.ValueOfString(value) + case "testpb.SimpleExample.not_unique": + value := x.NotUnique + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_SimpleExample) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "testpb.SimpleExample.name": + x.Name = value.Interface().(string) + case "testpb.SimpleExample.unique": + x.Unique = value.Interface().(string) + case "testpb.SimpleExample.not_unique": + x.NotUnique = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_SimpleExample) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "testpb.SimpleExample.name": + panic(fmt.Errorf("field name of message testpb.SimpleExample is not mutable")) + case "testpb.SimpleExample.unique": + panic(fmt.Errorf("field unique of message testpb.SimpleExample is not mutable")) + case "testpb.SimpleExample.not_unique": + panic(fmt.Errorf("field not_unique of message testpb.SimpleExample is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_SimpleExample) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "testpb.SimpleExample.name": + return protoreflect.ValueOfString("") + case "testpb.SimpleExample.unique": + return protoreflect.ValueOfString("") + case "testpb.SimpleExample.not_unique": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: testpb.SimpleExample")) + } + panic(fmt.Errorf("message testpb.SimpleExample does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_SimpleExample) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in testpb.SimpleExample", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_SimpleExample) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_SimpleExample) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_SimpleExample) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_SimpleExample) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*SimpleExample) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Name) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Unique) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.NotUnique) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*SimpleExample) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.NotUnique) > 0 { + i -= len(x.NotUnique) + copy(dAtA[i:], x.NotUnique) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.NotUnique))) + i-- + dAtA[i] = 0x1a + } + if len(x.Unique) > 0 { + i -= len(x.Unique) + copy(dAtA[i:], x.Unique) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Unique))) + i-- + dAtA[i] = 0x12 + } + if len(x.Name) > 0 { + i -= len(x.Name) + copy(dAtA[i:], x.Name) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Name))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*SimpleExample) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: SimpleExample: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: SimpleExample: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Unique", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Unique = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field NotUnique", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.NotUnique = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.0 -// protoc v3.19.1 +// protoc (unknown) // source: testpb/test_schema.proto const ( @@ -4270,6 +4818,57 @@ func (x *ExampleTimestamp) GetTs() *timestamppb.Timestamp { return nil } +type SimpleExample struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Unique string `protobuf:"bytes,2,opt,name=unique,proto3" json:"unique,omitempty"` + NotUnique string `protobuf:"bytes,3,opt,name=not_unique,json=notUnique,proto3" json:"not_unique,omitempty"` +} + +func (x *SimpleExample) Reset() { + *x = SimpleExample{} + if protoimpl.UnsafeEnabled { + mi := &file_testpb_test_schema_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SimpleExample) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SimpleExample) ProtoMessage() {} + +// Deprecated: Use SimpleExample.ProtoReflect.Descriptor instead. +func (*SimpleExample) Descriptor() ([]byte, []int) { + return file_testpb_test_schema_proto_rawDescGZIP(), []int{4} +} + +func (x *SimpleExample) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SimpleExample) GetUnique() string { + if x != nil { + return x.Unique + } + return "" +} + +func (x *SimpleExample) GetNotUnique() string { + if x != nil { + return x.NotUnique + } + return "" +} + type ExampleTable_ExampleMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4282,7 +4881,7 @@ type ExampleTable_ExampleMessage struct { func (x *ExampleTable_ExampleMessage) Reset() { *x = ExampleTable_ExampleMessage{} if protoimpl.UnsafeEnabled { - mi := &file_testpb_test_schema_proto_msgTypes[5] + mi := &file_testpb_test_schema_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4386,22 +4985,30 @@ var file_testpb_test_schema_proto_rawDesc = []byte{ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x73, 0x3a, 0x18, 0xf2, 0x9e, 0xd3, 0x8e, 0x03, 0x12, 0x0a, 0x06, 0x0a, 0x02, 0x69, 0x64, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, - 0x74, 0x73, 0x10, 0x01, 0x18, 0x04, 0x2a, 0x64, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x14, - 0x0a, 0x10, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4f, 0x4e, 0x45, - 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x57, 0x4f, 0x10, 0x02, - 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, - 0x1b, 0x0a, 0x0e, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4e, 0x45, 0x47, 0x5f, 0x54, 0x48, 0x52, 0x45, - 0x45, 0x10, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x42, 0x87, 0x01, 0x0a, - 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x42, 0x0f, 0x54, 0x65, 0x73, - 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, - 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x6f, 0x72, 0x6d, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, - 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xca, - 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xe2, 0x02, 0x12, 0x54, 0x65, 0x73, 0x74, 0x70, - 0x62, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, - 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x73, 0x10, 0x01, 0x18, 0x04, 0x22, 0x7a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, + 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x6e, 0x69, + 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x55, 0x6e, 0x69, 0x71, + 0x75, 0x65, 0x3a, 0x1e, 0xf2, 0x9e, 0xd3, 0x8e, 0x03, 0x18, 0x0a, 0x06, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x0c, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x10, 0x01, 0x18, 0x01, + 0x18, 0x05, 0x2a, 0x64, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x4e, + 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0c, + 0x0a, 0x08, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x57, 0x4f, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, + 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x0e, 0x45, + 0x4e, 0x55, 0x4d, 0x5f, 0x4e, 0x45, 0x47, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x45, 0x10, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x42, 0x87, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x42, 0x0f, 0x54, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x54, + 0x58, 0x58, 0xaa, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xca, 0x02, 0x06, 0x54, 0x65, + 0x73, 0x74, 0x70, 0x62, 0xe2, 0x02, 0x12, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4417,25 +5024,26 @@ func file_testpb_test_schema_proto_rawDescGZIP() []byte { } var file_testpb_test_schema_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_testpb_test_schema_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_testpb_test_schema_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_testpb_test_schema_proto_goTypes = []interface{}{ (Enum)(0), // 0: testpb.Enum (*ExampleTable)(nil), // 1: testpb.ExampleTable (*ExampleAutoIncrementTable)(nil), // 2: testpb.ExampleAutoIncrementTable (*ExampleSingleton)(nil), // 3: testpb.ExampleSingleton (*ExampleTimestamp)(nil), // 4: testpb.ExampleTimestamp - nil, // 5: testpb.ExampleTable.MapEntry - (*ExampleTable_ExampleMessage)(nil), // 6: testpb.ExampleTable.ExampleMessage - (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 8: google.protobuf.Duration + (*SimpleExample)(nil), // 5: testpb.SimpleExample + nil, // 6: testpb.ExampleTable.MapEntry + (*ExampleTable_ExampleMessage)(nil), // 7: testpb.ExampleTable.ExampleMessage + (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 9: google.protobuf.Duration } var file_testpb_test_schema_proto_depIdxs = []int32{ - 7, // 0: testpb.ExampleTable.ts:type_name -> google.protobuf.Timestamp - 8, // 1: testpb.ExampleTable.dur:type_name -> google.protobuf.Duration + 8, // 0: testpb.ExampleTable.ts:type_name -> google.protobuf.Timestamp + 9, // 1: testpb.ExampleTable.dur:type_name -> google.protobuf.Duration 0, // 2: testpb.ExampleTable.e:type_name -> testpb.Enum - 5, // 3: testpb.ExampleTable.map:type_name -> testpb.ExampleTable.MapEntry - 6, // 4: testpb.ExampleTable.msg:type_name -> testpb.ExampleTable.ExampleMessage - 7, // 5: testpb.ExampleTimestamp.ts:type_name -> google.protobuf.Timestamp + 6, // 3: testpb.ExampleTable.map:type_name -> testpb.ExampleTable.MapEntry + 7, // 4: testpb.ExampleTable.msg:type_name -> testpb.ExampleTable.ExampleMessage + 8, // 5: testpb.ExampleTimestamp.ts:type_name -> google.protobuf.Timestamp 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name @@ -4497,7 +5105,19 @@ func file_testpb_test_schema_proto_init() { return nil } } - file_testpb_test_schema_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_testpb_test_schema_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SimpleExample); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_testpb_test_schema_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExampleTable_ExampleMessage); i { case 0: return &v.state @@ -4519,7 +5139,7 @@ func file_testpb_test_schema_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_testpb_test_schema_proto_rawDesc, NumEnums: 1, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/orm/model/ormtable/auto_increment_test.go b/orm/model/ormtable/auto_increment_test.go index c18280892..4f1556f4f 100644 --- a/orm/model/ormtable/auto_increment_test.go +++ b/orm/model/ormtable/auto_increment_test.go @@ -47,7 +47,7 @@ func runAutoIncrementScenario(t *testing.T, table ormtable.AutoIncrementTable, c assert.NilError(t, err) err = store.Save(ctx, &testpb.ExampleAutoIncrementTable{Id: 5}) - assert.ErrorContains(t, err, "update") + assert.ErrorContains(t, err, "not found") ex1 := &testpb.ExampleAutoIncrementTable{X: "foo", Y: 5} assert.NilError(t, store.Save(ctx, ex1)) diff --git a/orm/model/ormtable/save_test.go b/orm/model/ormtable/save_test.go new file mode 100644 index 000000000..dc3a24edd --- /dev/null +++ b/orm/model/ormtable/save_test.go @@ -0,0 +1,68 @@ +package ormtable_test + +import ( + "context" + "fmt" + "github.com/cosmos/cosmos-sdk/orm/model/ormtable" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" + "testing" + + "github.com/regen-network/gocuke" + "gotest.tools/v3/assert" + + "github.com/cosmos/cosmos-sdk/orm/internal/testpb" + "github.com/cosmos/cosmos-sdk/orm/testing/ormtest" +) + +func TestSave(t *testing.T) { + gocuke.NewRunner(t, &suite{}).Path("../../features/table/saving.feature").Run() +} + +type suite struct { + gocuke.TestingT + table ormtable.Table + ctx context.Context + err error +} + +func (s *suite) Before() { + var err error + s.table, err = ormtable.Build(ormtable.Options{ + MessageType: (&testpb.SimpleExample{}).ProtoReflect().Type(), + }) + assert.NilError(s, err) + s.ctx = ormtable.WrapContextDefault(ormtest.NewMemoryBackend()) +} + +func (s *suite) AnExistingEntity(docString gocuke.DocString) { + existing := s.simpleExampleFromDocString(docString) + assert.NilError(s, s.table.Insert(s.ctx, existing)) +} + +func (s suite) simpleExampleFromDocString(docString gocuke.DocString) *testpb.SimpleExample { + ex := &testpb.SimpleExample{} + assert.NilError(s, protojson.Unmarshal([]byte(docString.Content), ex)) + return ex +} + +func (s *suite) IInsert(a gocuke.DocString) { + ex := s.simpleExampleFromDocString(a) + s.err = s.table.Insert(s.ctx, ex) +} + +func (s *suite) IUpdate(a gocuke.DocString) { + ex := s.simpleExampleFromDocString(a) + s.err = s.table.Update(s.ctx, ex) +} + +func (s *suite) ExpectAError(a string) { + assert.ErrorContains(s, s.err, a) +} + +func (s *suite) ExpectGrpcErrorCode(a string) { + var code codes.Code + assert.NilError(s, code.UnmarshalJSON([]byte(fmt.Sprintf("%q", a)))) + assert.Equal(s, code, status.Code(s.err)) +} diff --git a/orm/model/ormtable/table.go b/orm/model/ormtable/table.go index 4c9910d64..7594fcadb 100644 --- a/orm/model/ormtable/table.go +++ b/orm/model/ormtable/table.go @@ -66,15 +66,24 @@ type Table interface { // Save attempts to be atomic with respect to the underlying store, // meaning that either the full save operation is written or the store is // left unchanged, unless there is an error with the underlying store. + // + // If a unique key constraint is violated, ormerrors.UniqueKeyViolation + // (or an error wrapping it) will be returned. Save(context context.Context, message proto.Message) error // Insert inserts the provided entry in the store and fails if there is // an unique key violation. See Save for more details on behavior. + // + // If an entity with the same primary key exists, an error wrapping + // ormerrors.AlreadyExists will be returned. Insert(ctx context.Context, message proto.Message) error // Update updates the provided entry in the store and fails if an entry // with a matching primary key does not exist. See Save for more details // on behavior. + // + // If an entity with the same primary key does not exist, ormerrors.NotFound + // (or an error wrapping it) will be returned. Update(ctx context.Context, message proto.Message) error // Delete deletes the entry with the with primary key fields set on message diff --git a/orm/model/ormtable/table_impl.go b/orm/model/ormtable/table_impl.go index 34d75aa74..b598161b6 100644 --- a/orm/model/ormtable/table_impl.go +++ b/orm/model/ormtable/table_impl.go @@ -95,7 +95,7 @@ func (t tableImpl) doSave(ctx context.Context, writer *batchIndexCommitmentWrite if haveExisting { if mode == saveModeInsert { - return ormerrors.PrimaryKeyConstraintViolation.Wrapf("%q:%+v", mref.Descriptor().FullName(), pkValues) + return ormerrors.AlreadyExists.Wrapf("%q:%+v", mref.Descriptor().FullName(), pkValues) } if validateHooks := writer.ValidateHooks(); validateHooks != nil { @@ -106,7 +106,7 @@ func (t tableImpl) doSave(ctx context.Context, writer *batchIndexCommitmentWrite } } else { if mode == saveModeUpdate { - return ormerrors.NotFoundOnUpdate.Wrapf("%q", mref.Descriptor().FullName()) + return ormerrors.NotFound.Wrapf("%q", mref.Descriptor().FullName()) } if validateHooks := writer.ValidateHooks(); validateHooks != nil { diff --git a/orm/model/ormtable/unique.go b/orm/model/ormtable/unique.go index d61a9150b..63386942a 100644 --- a/orm/model/ormtable/unique.go +++ b/orm/model/ormtable/unique.go @@ -118,7 +118,7 @@ func (u uniqueKeyIndex) onInsert(store kv.Store, message protoreflect.Message) e } if has { - return ormerrors.UniqueKeyViolation + return ormerrors.UniqueKeyViolation.Wrapf("%q", u.fields) } return store.Set(k, v) @@ -143,7 +143,7 @@ func (u uniqueKeyIndex) onUpdate(store kv.Store, new, existing protoreflect.Mess } if has { - return ormerrors.UniqueKeyViolation + return ormerrors.UniqueKeyViolation.Wrapf("%q", u.fields) } existingKey, err := keyCodec.EncodeKey(existingValues) diff --git a/orm/types/ormerrors/errors.go b/orm/types/ormerrors/errors.go index fb2d597d3..bcb432a74 100644 --- a/orm/types/ormerrors/errors.go +++ b/orm/types/ormerrors/errors.go @@ -1,6 +1,9 @@ package ormerrors -import "github.com/cosmos/cosmos-sdk/errors" +import ( + "github.com/cosmos/cosmos-sdk/errors" + "google.golang.org/grpc/codes" +) var codespace = "orm" @@ -19,7 +22,6 @@ var ( InvalidIndexId = errors.New(codespace, 7, "invalid or missing index id, need a value >= 0 and < 32768") DuplicateIndexId = errors.New(codespace, 8, "duplicate index id") PrimaryKeyConstraintViolation = errors.New(codespace, 9, "object with primary key already exists") - NotFoundOnUpdate = errors.New(codespace, 10, "can't update object which doesn't exist") PrimaryKeyInvalidOnUpdate = errors.New(codespace, 11, "can't update object with missing or invalid primary key") AutoIncrementKeyAlreadySet = errors.New(codespace, 12, "can't create with auto-increment primary key already set") CantFindIndex = errors.New(codespace, 13, "can't find index") @@ -33,11 +35,13 @@ var ( UnexpectedError = errors.New(codespace, 21, "unexpected error") InvalidRangeIterationKeys = errors.New(codespace, 22, "invalid range iteration keys") JSONImportError = errors.New(codespace, 23, "json import error") - UniqueKeyViolation = errors.New(codespace, 24, "unique key violation") + UniqueKeyViolation = errors.RegisterWithGRPCCode(codespace, 24, codes.FailedPrecondition, "unique key violation") InvalidTableDefinition = errors.New(codespace, 25, "invalid table definition") InvalidFileDescriptorID = errors.New(codespace, 26, "invalid file descriptor ID") TableNotFound = errors.New(codespace, 27, "table not found") JSONValidationError = errors.New(codespace, 28, "invalid JSON") - NotFound = errors.New(codespace, 29, "not found") + NotFound = errors.RegisterWithGRPCCode(codespace, 29, codes.NotFound, "not found") ReadOnly = errors.New(codespace, 30, "database is read-only") + AlreadyExists = errors.RegisterWithGRPCCode(codespace, 31, codes.AlreadyExists, "already exists") + ConstraintViolation = errors.RegisterWithGRPCCode(codespace, 32, codes.FailedPrecondition, "failed precondition") )