cosmos-sdk/types/query/pagination.go

163 lines
3.4 KiB
Go
Raw Normal View History

package query
import (
"fmt"
"math"
db "github.com/tendermint/tm-db"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2021-04-08 12:22:00 -07:00
"github.com/cosmos/cosmos-sdk/store/types"
)
feat!: remove legacy REST (#9594) <!-- 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: #7517 * [x] Remove the x/{module}/client/rest folder * [x] Remove all glue code between simapp/modules and the REST server <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### 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 - [x] 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) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - see #9615 - [x] reviewed "Files changed" and left comments if necessary - [x] 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... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [x] reviewed API design and naming - [ ] reviewed documentation is accurate - see #9615 - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-07-06 03:04:54 -07:00
// DefaultPage is the default `page` number for queries.
// If the `page` number is not supplied, `DefaultPage` will be used.
const DefaultPage = 1
// DefaultLimit is the default `limit` for queries
// if the `limit` is not supplied, paginate will use `DefaultLimit`
const DefaultLimit = 100
// MaxLimit is the maximum limit the paginate function can handle
// which equals the maximum value that can be stored in uint64
const MaxLimit = math.MaxUint64
// ParsePagination validate PageRequest and returns page number & limit.
func ParsePagination(pageReq *PageRequest) (page, limit int, err error) {
offset := 0
limit = DefaultLimit
if pageReq != nil {
offset = int(pageReq.Offset)
limit = int(pageReq.Limit)
}
if offset < 0 {
return 1, 0, status.Error(codes.InvalidArgument, "offset must greater than 0")
}
if limit < 0 {
return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0")
} else if limit == 0 {
limit = DefaultLimit
}
page = offset/limit + 1
return page, limit, nil
}
// Paginate does pagination of all the results in the PrefixStore based on the
// provided PageRequest. onResult should be used to do actual unmarshaling.
func Paginate(
prefixStore types.KVStore,
pageRequest *PageRequest,
onResult func(key []byte, value []byte) error,
) (*PageResponse, error) {
// if the PageRequest is nil, use default PageRequest
if pageRequest == nil {
pageRequest = &PageRequest{}
}
offset := pageRequest.Offset
key := pageRequest.Key
limit := pageRequest.Limit
countTotal := pageRequest.CountTotal
reverse := pageRequest.Reverse
if offset > 0 && key != nil {
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
}
if limit == 0 {
limit = DefaultLimit
// count total results when the limit is zero/not supplied
countTotal = true
}
if len(key) != 0 {
iterator := getIterator(prefixStore, key, reverse)
defer iterator.Close()
var count uint64
var nextKey []byte
for ; iterator.Valid(); iterator.Next() {
if count == limit {
nextKey = iterator.Key()
break
}
if iterator.Error() != nil {
return nil, iterator.Error()
}
err := onResult(iterator.Key(), iterator.Value())
if err != nil {
return nil, err
}
count++
}
return &PageResponse{
NextKey: nextKey,
}, nil
}
iterator := getIterator(prefixStore, nil, reverse)
defer iterator.Close()
end := offset + limit
var count uint64
var nextKey []byte
for ; iterator.Valid(); iterator.Next() {
count++
if count <= offset {
continue
}
if count <= end {
err := onResult(iterator.Key(), iterator.Value())
if err != nil {
return nil, err
}
} else if count == end+1 {
nextKey = iterator.Key()
if !countTotal {
break
}
}
if iterator.Error() != nil {
return nil, iterator.Error()
}
}
res := &PageResponse{NextKey: nextKey}
if countTotal {
res.Total = count
}
return res, nil
}
func getIterator(prefixStore types.KVStore, start []byte, reverse bool) db.Iterator {
if reverse {
var end []byte
if start != nil {
itr := prefixStore.Iterator(start, nil)
defer itr.Close()
if itr.Valid() {
itr.Next()
end = itr.Key()
}
}
return prefixStore.ReverseIterator(nil, end)
}
return prefixStore.Iterator(start, nil)
}