163 lines
3.4 KiB
Go
163 lines
3.4 KiB
Go
package query
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
db "github.com/tendermint/tm-db"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"github.com/cosmos/cosmos-sdk/store/types"
|
|
)
|
|
|
|
// 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)
|
|
}
|