feat: Add Table-Store (aka ORM) package - AutoUInt64Table and PrimaryKeyTable (#10415)
<!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description ref: #9237, #9156 This PR is a follow-up of #9751. It introduces 2 new public table types: `AutoUInt64Table` and `PrimaryKeyTable` based on the parent `table` struct introduced by #9751. Upcoming work will include: - multi-key secondary indexes - iterator and pagination - import/export genesis --- ### 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... - [x] 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 - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] 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) - [x] 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` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] 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)
This commit is contained in:
+8
-4
@@ -17,7 +17,7 @@ require github.com/tendermint/tm-db v0.6.4
|
||||
|
||||
require (
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/armon/go-metrics v0.3.9 // indirect
|
||||
github.com/armon/go-metrics v0.3.10 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/btcsuite/btcd v0.22.0-beta // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
@@ -27,12 +27,13 @@ require (
|
||||
github.com/cosmos/iavl v0.17.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/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-kit/kit v0.10.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
@@ -44,6 +45,7 @@ require (
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
@@ -56,7 +58,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.31.1 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
@@ -69,7 +71,7 @@ require (
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tendermint/tendermint v0.34.13 // indirect
|
||||
github.com/tendermint/tendermint v0.34.14 // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
|
||||
@@ -87,3 +89,5 @@ replace google.golang.org/grpc => google.golang.org/grpc v1.33.2
|
||||
replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
|
||||
|
||||
replace github.com/cosmos/cosmos-sdk => ../../
|
||||
|
||||
replace github.com/cosmos/cosmos-sdk/db => ../../db
|
||||
|
||||
+18
-10
@@ -101,8 +101,8 @@ github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18=
|
||||
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
@@ -162,8 +162,8 @@ github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b80
|
||||
github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.6.10 h1:rgHD/nHjxLh0lMEdfGDqpTtlvtSBwULqrrZ2qPdNaCM=
|
||||
github.com/coinbase/rosetta-sdk-go v0.6.10/go.mod h1:J/JFMsfcePrjJZkwQFLh+hJErkAmdm9Iyy3D5Y0LfXo=
|
||||
github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg=
|
||||
github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE=
|
||||
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8=
|
||||
@@ -217,9 +217,11 @@ github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFM
|
||||
github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
|
||||
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
@@ -300,6 +302,7 @@ github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
|
||||
github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -342,6 +345,7 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -480,7 +484,7 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH
|
||||
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jhump/protoreflect v1.10.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
github.com/jhump/protoreflect v1.10.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
@@ -506,8 +510,9 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
@@ -520,6 +525,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554 h1:nDOkLO7klmnEw1s4AyKt1Arvpgyh33uj1JmkYlJaDsk=
|
||||
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554/go.mod h1:9+Pb2/tg1PvEgW7aFx4bFhDE4bvbI03zuJ8kb7nJ9Jc=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@@ -689,8 +696,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
|
||||
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.31.1 h1:d18hG4PkHnNAKNMOmFuXFaiY8Us0nird/2m60uS1AMs=
|
||||
github.com/prometheus/common v0.31.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@@ -809,8 +816,9 @@ github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM
|
||||
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
|
||||
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
|
||||
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
|
||||
github.com/tendermint/tendermint v0.34.13 h1:fu+tsHudbOr5PvepjH0q47Jae59hQAvn3IqAHv2EbC8=
|
||||
github.com/tendermint/tendermint v0.34.13/go.mod h1:6RVVRBqwtKhA+H59APKumO+B7Nye4QXSFc6+TYxAxCI=
|
||||
github.com/tendermint/tendermint v0.34.14 h1:GCXmlS8Bqd2Ix3TQCpwYLUNHe+Y+QyJsm5YE+S/FkPo=
|
||||
github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0=
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
|
||||
github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ=
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var _ Indexable = &AutoUInt64Table{}
|
||||
|
||||
// AutoUInt64Table is the table type with an auto incrementing ID.
|
||||
type AutoUInt64Table struct {
|
||||
*table
|
||||
seq Sequence
|
||||
}
|
||||
|
||||
// NewAutoUInt64Table creates a new AutoUInt64Table.
|
||||
func NewAutoUInt64Table(prefixData [2]byte, prefixSeq byte, model codec.ProtoMarshaler, cdc codec.Codec) (*AutoUInt64Table, error) {
|
||||
table, err := newTable(prefixData, model, cdc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AutoUInt64Table{
|
||||
table: table,
|
||||
seq: NewSequence(prefixSeq),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create a new persistent object with an auto generated uint64 primary key. The
|
||||
// key is returned.
|
||||
//
|
||||
// Create iterates through the registered callbacks that may add secondary index
|
||||
// keys.
|
||||
func (a AutoUInt64Table) Create(store sdk.KVStore, obj codec.ProtoMarshaler) (uint64, error) {
|
||||
autoIncID := a.seq.NextVal(store)
|
||||
err := a.table.Create(store, EncodeSequence(autoIncID), obj)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return autoIncID, nil
|
||||
}
|
||||
|
||||
// Update updates the given object under the rowID key. It expects the key to
|
||||
// exists already and fails with an `ErrNotFound` otherwise. Any caller must
|
||||
// therefore make sure that this contract is fulfilled. Parameters must not be
|
||||
// nil.
|
||||
//
|
||||
// Update iterates through the registered callbacks that may add or remove
|
||||
// secondary index keys.
|
||||
func (a AutoUInt64Table) Update(store sdk.KVStore, rowID uint64, newValue codec.ProtoMarshaler) error {
|
||||
return a.table.Update(store, EncodeSequence(rowID), newValue)
|
||||
}
|
||||
|
||||
// Delete removes the object under the rowID key. It expects the key to exists already
|
||||
// and fails with a `ErrNotFound` otherwise. Any caller must therefore make sure that this contract
|
||||
// is fulfilled.
|
||||
//
|
||||
// Delete iterates though the registered callbacks and removes secondary index keys by them.
|
||||
func (a AutoUInt64Table) Delete(store sdk.KVStore, rowID uint64) error {
|
||||
return a.table.Delete(store, EncodeSequence(rowID))
|
||||
}
|
||||
|
||||
// Has checks if a rowID exists.
|
||||
func (a AutoUInt64Table) Has(store sdk.KVStore, rowID uint64) bool {
|
||||
return a.table.Has(store, EncodeSequence(rowID))
|
||||
}
|
||||
|
||||
// GetOne load the object persisted for the given RowID into the dest parameter.
|
||||
// If none exists `ErrNotFound` is returned instead. Parameters must not be nil.
|
||||
func (a AutoUInt64Table) GetOne(store sdk.KVStore, rowID uint64, dest codec.ProtoMarshaler) (RowID, error) {
|
||||
rawRowID := EncodeSequence(rowID)
|
||||
if err := a.table.GetOne(store, rawRowID, dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rawRowID, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
type TestKeeper struct {
|
||||
autoUInt64Table *AutoUInt64Table
|
||||
primaryKeyTable *PrimaryKeyTable
|
||||
}
|
||||
|
||||
var (
|
||||
AutoUInt64TableTablePrefix [2]byte = [2]byte{0x0}
|
||||
PrimaryKeyTablePrefix [2]byte = [2]byte{0x1}
|
||||
AutoUInt64TableSeqPrefix byte = 0x2
|
||||
)
|
||||
|
||||
func NewTestKeeper(cdc codec.Codec) TestKeeper {
|
||||
k := TestKeeper{}
|
||||
|
||||
autoUInt64Table, err := NewAutoUInt64Table(AutoUInt64TableTablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
k.autoUInt64Table = autoUInt64Table
|
||||
|
||||
primaryKeyTable, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
k.primaryKeyTable = primaryKeyTable
|
||||
|
||||
return k
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
// genTableModel generates a new table model. At the moment it doesn't
|
||||
// generate empty strings for Name.
|
||||
var genTableModel = rapid.Custom(func(t *rapid.T) *testdata.TableModel {
|
||||
return &testdata.TableModel{
|
||||
Id: rapid.Uint64().Draw(t, "id").(uint64),
|
||||
Name: rapid.StringN(1, 100, 150).Draw(t, "name").(string),
|
||||
Number: rapid.Uint64().Draw(t, "number ").(uint64),
|
||||
Metadata: []byte(rapid.StringN(1, 100, 150).Draw(t, "metadata").(string)),
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,75 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// MaxBytesLen is the maximum allowed length for a key part of type []byte
|
||||
const MaxBytesLen = 255
|
||||
|
||||
// buildKeyFromParts encodes and concatenates primary key and index parts.
|
||||
// They can be []byte, string, and integer types. The function will return
|
||||
// an error if there is a part of any other type.
|
||||
// Key parts, except the last part, follow these rules:
|
||||
// - []byte is encoded with a single byte length prefix
|
||||
// - strings are null-terminated
|
||||
// - integers are encoded using 8 byte big endian.
|
||||
func buildKeyFromParts(parts []interface{}) ([]byte, error) {
|
||||
bytesSlice := make([][]byte, len(parts))
|
||||
totalLen := 0
|
||||
var err error
|
||||
for i, part := range parts {
|
||||
bytesSlice[i], err = keyPartBytes(part, len(parts) > 1 && i == len(parts)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalLen += len(bytesSlice[i])
|
||||
}
|
||||
key := make([]byte, 0, totalLen)
|
||||
for _, bs := range bytesSlice {
|
||||
key = append(key, bs...)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func keyPartBytes(part interface{}, last bool) ([]byte, error) {
|
||||
switch v := part.(type) {
|
||||
case []byte:
|
||||
if last || len(v) == 0 {
|
||||
return v, nil
|
||||
}
|
||||
return AddLengthPrefix(v), nil
|
||||
case string:
|
||||
if last || len(v) == 0 {
|
||||
return []byte(v), nil
|
||||
}
|
||||
return NullTerminatedBytes(v), nil
|
||||
case uint64:
|
||||
return EncodeSequence(v), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("type %T not allowed as key part", v)
|
||||
}
|
||||
}
|
||||
|
||||
// AddLengthPrefix prefixes the byte array with its length as 8 bytes. The function will panic
|
||||
// if the bytes length is bigger than 255.
|
||||
func AddLengthPrefix(bytes []byte) []byte {
|
||||
byteLen := len(bytes)
|
||||
if byteLen > MaxBytesLen {
|
||||
panic(errors.Wrap(errors.ErrORMKeyMaxLength, "Cannot create key part with an []byte of length greater than 255 bytes. Try again with a smaller []byte."))
|
||||
}
|
||||
|
||||
prefixedBytes := make([]byte, 1+len(bytes))
|
||||
copy(prefixedBytes, []byte{uint8(byteLen)})
|
||||
copy(prefixedBytes[1:], bytes)
|
||||
return prefixedBytes
|
||||
}
|
||||
|
||||
// NullTerminatedBytes converts string to byte array and null terminate it
|
||||
func NullTerminatedBytes(s string) []byte {
|
||||
bytes := make([]byte, len(s)+1)
|
||||
copy(bytes, s)
|
||||
return bytes
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddLengthPrefix(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
in []byte
|
||||
expected []byte
|
||||
}{
|
||||
{"empty", []byte{}, []byte{0}},
|
||||
{"nil", nil, []byte{0}},
|
||||
{"some data", []byte{0, 1, 100, 200}, []byte{4, 0, 1, 100, 200}},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out := AddLengthPrefix(tc.in)
|
||||
require.Equal(t, tc.expected, out)
|
||||
})
|
||||
}
|
||||
|
||||
require.Panics(t, func() {
|
||||
AddLengthPrefix(make([]byte, 256))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNullTerminatedBytes(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
in string
|
||||
expected []byte
|
||||
}{
|
||||
{"empty", "", []byte{0}},
|
||||
{"some data", "abc", []byte{0x61, 0x62, 0x63, 0}},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out := NullTerminatedBytes(tc.in)
|
||||
require.Equal(t, tc.expected, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
func TestKeeperEndToEndWithAutoUInt64Table(t *testing.T) {
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
|
||||
ctx := NewMockContext()
|
||||
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
||||
|
||||
k := NewTestKeeper(cdc)
|
||||
|
||||
tm := testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "name",
|
||||
Number: 123,
|
||||
Metadata: []byte("metadata"),
|
||||
}
|
||||
// when stored
|
||||
rowID, err := k.autoUInt64Table.Create(store, &tm)
|
||||
require.NoError(t, err)
|
||||
// then we should find it
|
||||
exists := k.autoUInt64Table.Has(store, rowID)
|
||||
require.True(t, exists)
|
||||
|
||||
// and load it
|
||||
var loaded testdata.TableModel
|
||||
|
||||
binKey, err := k.autoUInt64Table.GetOne(store, rowID, &loaded)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
|
||||
require.Equal(t, tm, loaded)
|
||||
|
||||
// when updated
|
||||
tm.Name = "new name"
|
||||
err = k.autoUInt64Table.Update(store, rowID, &tm)
|
||||
require.NoError(t, err)
|
||||
|
||||
binKey, err = k.autoUInt64Table.GetOne(store, rowID, &loaded)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
|
||||
require.Equal(t, tm, loaded)
|
||||
|
||||
// when deleted
|
||||
err = k.autoUInt64Table.Delete(store, rowID)
|
||||
require.NoError(t, err)
|
||||
|
||||
exists = k.autoUInt64Table.Has(store, rowID)
|
||||
require.False(t, exists)
|
||||
}
|
||||
|
||||
func TestKeeperEndToEndWithPrimaryKeyTable(t *testing.T) {
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
|
||||
ctx := NewMockContext()
|
||||
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
||||
|
||||
k := NewTestKeeper(cdc)
|
||||
|
||||
tm := testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "name",
|
||||
Number: 123,
|
||||
Metadata: []byte("metadata"),
|
||||
}
|
||||
// when stored
|
||||
err := k.primaryKeyTable.Create(store, &tm)
|
||||
require.NoError(t, err)
|
||||
// then we should find it by primary key
|
||||
primaryKey := PrimaryKey(&tm)
|
||||
exists := k.primaryKeyTable.Has(store, primaryKey)
|
||||
require.True(t, exists)
|
||||
|
||||
// and load it by primary key
|
||||
var loaded testdata.TableModel
|
||||
err = k.primaryKeyTable.GetOne(store, primaryKey, &loaded)
|
||||
require.NoError(t, err)
|
||||
|
||||
// then values should match expectations
|
||||
require.Equal(t, tm, loaded)
|
||||
|
||||
// and when we create another entry with the same primary key
|
||||
err = k.primaryKeyTable.Create(store, &tm)
|
||||
// then it should fail as the primary key must be unique
|
||||
require.True(t, errors.ErrORMUniqueConstraint.Is(err), err)
|
||||
|
||||
// and when entity updated with new primary key
|
||||
updatedMember := &testdata.TableModel{
|
||||
Id: 2,
|
||||
Name: tm.Name,
|
||||
Number: tm.Number,
|
||||
Metadata: tm.Metadata,
|
||||
}
|
||||
// then it should fail as the primary key is immutable
|
||||
err = k.primaryKeyTable.Update(store, updatedMember)
|
||||
require.Error(t, err)
|
||||
|
||||
// and when entity updated with non primary key attribute modified
|
||||
updatedMember = &testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "new name",
|
||||
Number: tm.Number,
|
||||
Metadata: tm.Metadata,
|
||||
}
|
||||
// then it should not fail
|
||||
err = k.primaryKeyTable.Update(store, updatedMember)
|
||||
require.NoError(t, err)
|
||||
|
||||
// and when entity deleted
|
||||
err = k.primaryKeyTable.Delete(store, &tm)
|
||||
require.NoError(t, err)
|
||||
|
||||
exists = k.primaryKeyTable.Has(store, primaryKey)
|
||||
require.False(t, exists)
|
||||
}
|
||||
|
||||
func TestGasCostsPrimaryKeyTable(t *testing.T) {
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
|
||||
ctx := NewMockContext()
|
||||
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
||||
|
||||
k := NewTestKeeper(cdc)
|
||||
|
||||
tm := testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "name",
|
||||
Number: 123,
|
||||
Metadata: []byte("metadata"),
|
||||
}
|
||||
rowID, err := k.autoUInt64Table.Create(store, &tm)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), rowID)
|
||||
|
||||
gCtx := NewGasCountingMockContext()
|
||||
err = k.primaryKeyTable.Create(gCtx.KVStore(store), &tm)
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas consumed on create: %d", gCtx.GasConsumed())
|
||||
|
||||
// get by primary key
|
||||
gCtx.ResetGasMeter()
|
||||
var loaded testdata.TableModel
|
||||
err = k.primaryKeyTable.GetOne(gCtx.KVStore(store), PrimaryKey(&tm), &loaded)
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas consumed on get by primary key: %d", gCtx.GasConsumed())
|
||||
|
||||
// delete
|
||||
gCtx.ResetGasMeter()
|
||||
err = k.primaryKeyTable.Delete(gCtx.KVStore(store), &tm)
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas consumed on delete by primary key: %d", gCtx.GasConsumed())
|
||||
|
||||
// with 3 elements
|
||||
var tms []testdata.TableModel
|
||||
for i := 1; i < 4; i++ {
|
||||
gCtx.ResetGasMeter()
|
||||
tm := testdata.TableModel{
|
||||
Id: uint64(i),
|
||||
Name: fmt.Sprintf("name%d", i),
|
||||
Number: 123,
|
||||
Metadata: []byte("metadata"),
|
||||
}
|
||||
err = k.primaryKeyTable.Create(gCtx.KVStore(store), &tm)
|
||||
require.NoError(t, err)
|
||||
t.Logf("%d: gas consumed on create: %d", i, gCtx.GasConsumed())
|
||||
tms = append(tms, tm)
|
||||
}
|
||||
|
||||
for i := 1; i < 4; i++ {
|
||||
gCtx.ResetGasMeter()
|
||||
tm := testdata.TableModel{
|
||||
Id: uint64(i),
|
||||
Name: fmt.Sprintf("name%d", i),
|
||||
Number: 123,
|
||||
Metadata: []byte("metadata"),
|
||||
}
|
||||
err = k.primaryKeyTable.GetOne(gCtx.KVStore(store), PrimaryKey(&tm), &loaded)
|
||||
require.NoError(t, err)
|
||||
t.Logf("%d: gas consumed on get by primary key: %d", i, gCtx.GasConsumed())
|
||||
}
|
||||
|
||||
// delete
|
||||
for i, m := range tms {
|
||||
gCtx.ResetGasMeter()
|
||||
|
||||
err = k.primaryKeyTable.Delete(gCtx.KVStore(store), &m)
|
||||
require.NoError(t, err)
|
||||
t.Logf("%d: gas consumed on delete: %d", i, gCtx.GasConsumed())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var _ Indexable = &PrimaryKeyTable{}
|
||||
|
||||
// PrimaryKeyTable provides simpler object style orm methods without passing database RowIDs.
|
||||
// Entries are persisted and loaded with a reference to their unique primary key.
|
||||
type PrimaryKeyTable struct {
|
||||
*table
|
||||
}
|
||||
|
||||
// NewPrimaryKeyTable creates a new PrimaryKeyTable.
|
||||
func NewPrimaryKeyTable(prefixData [2]byte, model PrimaryKeyed, cdc codec.Codec) (*PrimaryKeyTable, error) {
|
||||
table, err := newTable(prefixData, model, cdc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PrimaryKeyTable{
|
||||
table: table,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PrimaryKeyed defines an object type that is aware of its immutable primary key.
|
||||
type PrimaryKeyed interface {
|
||||
// PrimaryKeyFields returns the fields of the object that will make up
|
||||
// the primary key. The PrimaryKey function will encode and concatenate
|
||||
// the fields to build the primary key.
|
||||
//
|
||||
// PrimaryKey parts can be []byte, string, and integer types. []byte is
|
||||
// encoded with a length prefix, strings are null-terminated, and
|
||||
// integers are encoded using 8 byte big endian.
|
||||
//
|
||||
// IMPORTANT: []byte parts are encoded with a single byte length prefix,
|
||||
// so cannot be longer than 255 bytes.
|
||||
PrimaryKeyFields() []interface{}
|
||||
codec.ProtoMarshaler
|
||||
}
|
||||
|
||||
// PrimaryKey returns the immutable and serialized primary key of this object.
|
||||
// The primary key has to be unique within it's domain so that not two with same
|
||||
// value can exist in the same table. This means PrimaryKeyFields() has to
|
||||
// return a unique value for each object.
|
||||
func PrimaryKey(obj PrimaryKeyed) []byte {
|
||||
fields := obj.PrimaryKeyFields()
|
||||
key, err := buildKeyFromParts(fields)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// Create persists the given object under their primary key. It checks if the
|
||||
// key already exists and may return an `ErrUniqueConstraint`.
|
||||
//
|
||||
// Create iterates through the registered callbacks that may add secondary
|
||||
// index keys.
|
||||
func (a PrimaryKeyTable) Create(store sdk.KVStore, obj PrimaryKeyed) error {
|
||||
rowID := PrimaryKey(obj)
|
||||
return a.table.Create(store, rowID, obj)
|
||||
}
|
||||
|
||||
// Update updates the given object under the primary key. It expects the key to
|
||||
// exists already and fails with an `ErrNotFound` otherwise. Any caller must
|
||||
// therefore make sure that this contract is fulfilled. Parameters must not be
|
||||
// nil.
|
||||
//
|
||||
// Update iterates through the registered callbacks that may add or remove
|
||||
// secondary index keys.
|
||||
func (a PrimaryKeyTable) Update(store sdk.KVStore, newValue PrimaryKeyed) error {
|
||||
return a.table.Update(store, PrimaryKey(newValue), newValue)
|
||||
}
|
||||
|
||||
// Set persists the given object under the rowID key. It does not check if the
|
||||
// key already exists and overwrites the value if it does.
|
||||
//
|
||||
// Set iterates through the registered callbacks that may add secondary index
|
||||
// keys.
|
||||
func (a PrimaryKeyTable) Set(store sdk.KVStore, newValue PrimaryKeyed) error {
|
||||
return a.table.Set(store, PrimaryKey(newValue), newValue)
|
||||
}
|
||||
|
||||
// Delete removes the object. It expects the primary key to exists already and
|
||||
// fails with a `ErrNotFound` otherwise. Any caller must therefore make sure
|
||||
// that this contract is fulfilled.
|
||||
//
|
||||
// Delete iterates through the registered callbacks that remove secondary index
|
||||
// keys.
|
||||
func (a PrimaryKeyTable) Delete(store sdk.KVStore, obj PrimaryKeyed) error {
|
||||
return a.table.Delete(store, PrimaryKey(obj))
|
||||
}
|
||||
|
||||
// Has checks if a key exists. Always returns false on nil or empty key.
|
||||
func (a PrimaryKeyTable) Has(store sdk.KVStore, primaryKey RowID) bool {
|
||||
return a.table.Has(store, primaryKey)
|
||||
}
|
||||
|
||||
// Contains returns true when an object with same type and primary key is persisted in this table.
|
||||
func (a PrimaryKeyTable) Contains(store sdk.KVStore, obj PrimaryKeyed) bool {
|
||||
if err := assertCorrectType(a.table.model, obj); err != nil {
|
||||
return false
|
||||
}
|
||||
return a.table.Has(store, PrimaryKey(obj))
|
||||
}
|
||||
|
||||
// GetOne loads the object persisted for the given primary Key into the dest parameter.
|
||||
// If none exists `ErrNotFound` is returned instead. Parameters must not be nil.
|
||||
func (a PrimaryKeyTable) GetOne(store sdk.KVStore, primKey RowID, dest codec.ProtoMarshaler) error {
|
||||
return a.table.GetOne(store, primKey, dest)
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
func TestPrimaryKeyTable(t *testing.T) {
|
||||
rapid.Check(t, rapid.Run(&primaryKeyMachine{}))
|
||||
}
|
||||
|
||||
// primaryKeyMachine is a state machine model of the PrimaryKeyTable. The state
|
||||
// is modelled as a map of strings to TableModels.
|
||||
type primaryKeyMachine struct {
|
||||
store sdk.KVStore
|
||||
table *PrimaryKeyTable
|
||||
state map[string]*testdata.TableModel
|
||||
}
|
||||
|
||||
// stateKeys gets all the keys in the model map
|
||||
func (m *primaryKeyMachine) stateKeys() []string {
|
||||
keys := make([]string, len(m.state))
|
||||
|
||||
i := 0
|
||||
for k := range m.state {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// Generate a TableModel that has a 50% chance of being a part of the existing
|
||||
// state
|
||||
func (m *primaryKeyMachine) genTableModel() *rapid.Generator {
|
||||
genStateTableModel := rapid.Custom(func(t *rapid.T) *testdata.TableModel {
|
||||
pk := rapid.SampledFrom(m.stateKeys()).Draw(t, "key").(string)
|
||||
return m.state[pk]
|
||||
})
|
||||
|
||||
if len(m.stateKeys()) == 0 {
|
||||
return genTableModel
|
||||
} else {
|
||||
return rapid.OneOf(genTableModel, genStateTableModel)
|
||||
}
|
||||
}
|
||||
|
||||
// Init creates a new instance of the state machine model by building the real
|
||||
// table and making the empty model map
|
||||
func (m *primaryKeyMachine) Init(t *rapid.T) {
|
||||
// Create context
|
||||
ctx := NewMockContext()
|
||||
m.store = ctx.KVStore(sdk.NewKVStoreKey("test"))
|
||||
|
||||
// Create primary key table
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
table, err := NewPrimaryKeyTable(
|
||||
[2]byte{0x1},
|
||||
&testdata.TableModel{},
|
||||
cdc,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
m.table = table
|
||||
|
||||
// Create model state
|
||||
m.state = make(map[string]*testdata.TableModel)
|
||||
}
|
||||
|
||||
// Check that the real values match the state values.
|
||||
func (m *primaryKeyMachine) Check(t *rapid.T) {
|
||||
for i := range m.state {
|
||||
has := m.table.Has(m.store, []byte(i))
|
||||
require.Equal(t, true, has)
|
||||
}
|
||||
}
|
||||
|
||||
// Create is one of the model commands. It adds an object to the table, creating
|
||||
// an error if it already exists.
|
||||
func (m *primaryKeyMachine) Create(t *rapid.T) {
|
||||
g := genTableModel.Draw(t, "g").(*testdata.TableModel)
|
||||
pk := string(PrimaryKey(g))
|
||||
|
||||
t.Logf("pk: %v", pk)
|
||||
t.Logf("m.state: %v", m.state)
|
||||
|
||||
err := m.table.Create(m.store, g)
|
||||
|
||||
if m.state[pk] != nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
m.state[pk] = g
|
||||
}
|
||||
}
|
||||
|
||||
// Update is one of the model commands. It updates the value at a given primary
|
||||
// key and fails if that primary key doesn't already exist in the table.
|
||||
func (m *primaryKeyMachine) Update(t *rapid.T) {
|
||||
tm := m.genTableModel().Draw(t, "tm").(*testdata.TableModel)
|
||||
|
||||
newName := rapid.StringN(1, 100, 150).Draw(t, "newName").(string)
|
||||
tm.Name = newName
|
||||
|
||||
// Perform the real Update
|
||||
err := m.table.Update(m.store, tm)
|
||||
|
||||
if m.state[string(PrimaryKey(tm))] == nil {
|
||||
// If there's no value in the model, we expect an error
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
// If we have a value in the model, expect no error
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the model with the new value
|
||||
m.state[string(PrimaryKey(tm))] = tm
|
||||
}
|
||||
}
|
||||
|
||||
// Set is one of the model commands. It sets the value at a key in the table
|
||||
// whether it exists or not.
|
||||
func (m *primaryKeyMachine) Set(t *rapid.T) {
|
||||
g := genTableModel.Draw(t, "g").(*testdata.TableModel)
|
||||
pk := string(PrimaryKey(g))
|
||||
|
||||
err := m.table.Set(m.store, g)
|
||||
|
||||
require.NoError(t, err)
|
||||
m.state[pk] = g
|
||||
}
|
||||
|
||||
// Delete is one of the model commands. It removes the object with the given
|
||||
// primary key from the table and returns an error if that primary key doesn't
|
||||
// already exist in the table.
|
||||
func (m *primaryKeyMachine) Delete(t *rapid.T) {
|
||||
tm := m.genTableModel().Draw(t, "tm").(*testdata.TableModel)
|
||||
|
||||
// Perform the real Delete
|
||||
err := m.table.Delete(m.store, tm)
|
||||
|
||||
if m.state[string(PrimaryKey(tm))] == nil {
|
||||
// If there's no value in the model, we expect an error
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
// If we have a value in the model, expect no error
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete the value from the model
|
||||
delete(m.state, string(PrimaryKey(tm)))
|
||||
}
|
||||
}
|
||||
|
||||
// Has is one of the model commands. It checks whether a key already exists in
|
||||
// the table.
|
||||
func (m *primaryKeyMachine) Has(t *rapid.T) {
|
||||
pk := PrimaryKey(m.genTableModel().Draw(t, "g").(*testdata.TableModel))
|
||||
|
||||
realHas := m.table.Has(m.store, pk)
|
||||
modelHas := m.state[string(pk)] != nil
|
||||
|
||||
require.Equal(t, realHas, modelHas)
|
||||
}
|
||||
|
||||
// GetOne is one of the model commands. It fetches an object from the table by
|
||||
// its primary key and returns an error if that primary key isn't in the table.
|
||||
func (m *primaryKeyMachine) GetOne(t *rapid.T) {
|
||||
pk := PrimaryKey(m.genTableModel().Draw(t, "tm").(*testdata.TableModel))
|
||||
|
||||
var tm testdata.TableModel
|
||||
|
||||
err := m.table.GetOne(m.store, pk, &tm)
|
||||
t.Logf("tm: %v", tm)
|
||||
|
||||
if m.state[string(pk)] == nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, *m.state[string(pk)], tm)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
interfaceRegistry := types.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
|
||||
ctx := NewMockContext()
|
||||
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
||||
|
||||
tb, err := NewPrimaryKeyTable([2]byte{0x1}, &testdata.TableModel{}, cdc)
|
||||
require.NoError(t, err)
|
||||
|
||||
obj := testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "Some name",
|
||||
}
|
||||
err = tb.Create(store, &obj)
|
||||
require.NoError(t, err)
|
||||
|
||||
specs := map[string]struct {
|
||||
src PrimaryKeyed
|
||||
exp bool
|
||||
}{
|
||||
|
||||
"same object": {src: &obj, exp: true},
|
||||
"clone": {
|
||||
src: &testdata.TableModel{
|
||||
Id: 1,
|
||||
Name: "Some name",
|
||||
},
|
||||
exp: true,
|
||||
},
|
||||
"different primary key": {
|
||||
src: &testdata.TableModel{
|
||||
Id: 2,
|
||||
Name: "Some name",
|
||||
},
|
||||
exp: false,
|
||||
},
|
||||
"different type, same key": {
|
||||
src: mockPrimaryKeyed{&obj},
|
||||
exp: false,
|
||||
},
|
||||
}
|
||||
for msg, spec := range specs {
|
||||
t.Run(msg, func(t *testing.T) {
|
||||
got := tb.Contains(store, spec.src)
|
||||
assert.Equal(t, spec.exp, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockPrimaryKeyed struct {
|
||||
*testdata.TableModel
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Table
|
||||
|
||||
A table can be built given a `codec.ProtoMarshaler` model type, a prefix to access the underlying prefix store used to store table data as well as a `Codec` for marshalling/unmarshalling.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/9f78f16ae75cc42fc5fe636bde18a453ba74831f/x/group/internal/orm/table.go#L24-L30
|
||||
|
||||
In the prefix store, entities should be stored by an unique identifier called `RowID` which can be based either on an `uint64` auto-increment counter, string or dynamic size bytes.
|
||||
Regular CRUD operations can be performed on a table, these methods take a `sdk.KVStore` as parameter to get the table prefix store.
|
||||
|
||||
The `table` struct does not:
|
||||
- enforce uniqueness of the `RowID`
|
||||
- enforce prefix uniqueness of keys, i.e. not allowing one key to be a prefix
|
||||
of another
|
||||
- optimize Gas usage conditions
|
||||
The `table` struct is private, so that we only have custom tables built on top of it, that do satisfy these requirements.
|
||||
|
||||
## AutoUInt64Table
|
||||
|
||||
`AutoUInt64Table` is a table type with an auto incrementing `uint64` ID.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/9f78f16ae75cc42fc5fe636bde18a453ba74831f/x/group/internal/orm/auto_uint64.go#L11-L14
|
||||
|
||||
It's based on the `Sequence` struct which is a persistent unique key generator based on a counter encoded using 8 byte big endian.
|
||||
|
||||
## PrimaryKeyTable
|
||||
|
||||
`PrimaryKeyTable` provides simpler object style orm methods where are persisted and loaded with a reference to their unique primary key.
|
||||
|
||||
The model provided for creating a `PrimaryKeyTable` should implement the `PrimaryKeyed` interface:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/9f78f16ae75cc42fc5fe636bde18a453ba74831f/x/group/internal/orm/primary_key.go#L28-L41
|
||||
|
||||
`PrimaryKeyFields()` method returns the list of key parts for a given object.
|
||||
The primary key parts can be []byte, string, and `uint64` types.
|
||||
Key parts, except the last part, follow these rules:
|
||||
- []byte is encoded with a single byte length prefix
|
||||
- strings are null-terminated
|
||||
- `uint64` are encoded using 8 byte big endian.
|
||||
@@ -1,26 +1,9 @@
|
||||
## Abstract
|
||||
# Abstract
|
||||
|
||||
The orm package provides a framework for creating relational database tables with primary and secondary keys.
|
||||
|
||||
### Tables
|
||||
## Contents
|
||||
|
||||
```go
|
||||
type table struct {
|
||||
model reflect.Type
|
||||
prefix [2]byte
|
||||
afterSet []AfterSetInterceptor
|
||||
afterDelete []AfterDeleteInterceptor
|
||||
cdc codec.Codec
|
||||
}
|
||||
```
|
||||
|
||||
A table can be built given a `codec.ProtoMarshaler` model type, a prefix to access the underlying prefix store used to store table data as well as a `Codec` for marshalling/unmarshalling.
|
||||
In the prefix store, entities should be stored by an unique identifier called `RowID` which can be based either on an `uint64` auto-increment counter, string or dynamic size bytes.
|
||||
Regular CRUD operations can be performed on a table, these methods take a `sdk.KVStore` as parameter to get the table prefix store.
|
||||
|
||||
The `table` struct does not:
|
||||
- enforce uniqueness of the `RowID`
|
||||
- enforce prefix uniqueness of keys, i.e. not allowing one key to be a prefix
|
||||
of another
|
||||
- optimize Gas usage conditions
|
||||
The `table` struct is private, so that we only have custom tables built on top of it, that do satisfy these requirements.
|
||||
1. **[Table](01_table.md)**
|
||||
- [AutoUInt64Table](01_table.md#autouint64table)
|
||||
- [PrimaryKeyTable](01_table.md#primarykeytable)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/store/gaskv"
|
||||
"github.com/cosmos/cosmos-sdk/store/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@@ -31,3 +34,70 @@ func (m MockContext) KVStore(key storetypes.StoreKey) sdk.KVStore {
|
||||
}
|
||||
return m.store.GetCommitKVStore(key)
|
||||
}
|
||||
|
||||
type debuggingGasMeter struct {
|
||||
g types.GasMeter
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) GasConsumed() types.Gas {
|
||||
return d.g.GasConsumed()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) GasRemaining() types.Gas {
|
||||
return d.g.GasRemaining()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) GasConsumedToLimit() types.Gas {
|
||||
return d.g.GasConsumedToLimit()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) RefundGas(amount uint64, descriptor string) {
|
||||
d.g.RefundGas(amount, descriptor)
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) Limit() types.Gas {
|
||||
return d.g.Limit()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) ConsumeGas(amount types.Gas, descriptor string) {
|
||||
fmt.Printf("++ Consuming gas: %q :%d\n", descriptor, amount)
|
||||
d.g.ConsumeGas(amount, descriptor)
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) IsPastLimit() bool {
|
||||
return d.g.IsPastLimit()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) IsOutOfGas() bool {
|
||||
return d.g.IsOutOfGas()
|
||||
}
|
||||
|
||||
func (d debuggingGasMeter) String() string {
|
||||
return d.g.String()
|
||||
}
|
||||
|
||||
type GasCountingMockContext struct {
|
||||
GasMeter sdk.GasMeter
|
||||
}
|
||||
|
||||
func NewGasCountingMockContext() *GasCountingMockContext {
|
||||
return &GasCountingMockContext{
|
||||
GasMeter: &debuggingGasMeter{sdk.NewInfiniteGasMeter()},
|
||||
}
|
||||
}
|
||||
|
||||
func (g GasCountingMockContext) KVStore(store sdk.KVStore) sdk.KVStore {
|
||||
return gaskv.NewStore(store, g.GasMeter, types.KVGasConfig())
|
||||
}
|
||||
|
||||
func (g GasCountingMockContext) GasConsumed() types.Gas {
|
||||
return g.GasMeter.GasConsumed()
|
||||
}
|
||||
|
||||
func (g GasCountingMockContext) GasRemaining() types.Gas {
|
||||
return g.GasMeter.GasRemaining()
|
||||
}
|
||||
|
||||
func (g *GasCountingMockContext) ResetGasMeter() {
|
||||
g.GasMeter = sdk.NewInfiniteGasMeter()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ type Iterator interface {
|
||||
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
|
||||
// are no more items the ErrORMIteratorDone error is returned
|
||||
// The key is the rowID.
|
||||
LoadNext(dest codec.ProtoMarshaler) (RowID, error)
|
||||
LoadNext(store sdk.KVStore, dest codec.ProtoMarshaler) (RowID, error)
|
||||
// Close releases the iterator and should be called at the end of iteration
|
||||
io.Closer
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user