github.com/cosmos/cosmos-sdk@v0.50.10/types/query/pagination.go (about) 1 package query 2 3 import ( 4 "fmt" 5 "math" 6 7 db "github.com/cosmos/cosmos-db" 8 "google.golang.org/grpc/codes" 9 "google.golang.org/grpc/status" 10 11 "cosmossdk.io/store/types" 12 ) 13 14 // DefaultPage is the default `page` number for queries. 15 // If the `page` number is not supplied, `DefaultPage` will be used. 16 const DefaultPage = 1 17 18 // DefaultLimit is the default `limit` for queries 19 // if the `limit` is not supplied, paginate will use `DefaultLimit` 20 const DefaultLimit = 100 21 22 // PaginationMaxLimit is the maximum limit the paginate function can handle 23 // which equals the maximum value that can be stored in uint64 24 var PaginationMaxLimit uint64 = math.MaxUint64 25 26 // ParsePagination validate PageRequest and returns page number & limit. 27 func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { 28 offset := 0 29 limit = DefaultLimit 30 31 if pageReq != nil { 32 offset = int(pageReq.Offset) 33 limit = int(pageReq.Limit) 34 } 35 if offset < 0 { 36 return 1, 0, status.Error(codes.InvalidArgument, "offset must greater than 0") 37 } 38 39 if limit < 0 { 40 return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0") 41 } else if limit == 0 { 42 limit = DefaultLimit 43 } 44 45 page = offset/limit + 1 46 47 return page, limit, nil 48 } 49 50 // Paginate does pagination of all the results in the PrefixStore based on the 51 // provided PageRequest. onResult should be used to do actual unmarshaling. 52 func Paginate( 53 prefixStore types.KVStore, 54 pageRequest *PageRequest, 55 onResult func(key, value []byte) error, 56 ) (*PageResponse, error) { 57 pageRequest = initPageRequestDefaults(pageRequest) 58 59 if pageRequest.Offset > 0 && pageRequest.Key != nil { 60 return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") 61 } 62 63 iterator := getIterator(prefixStore, pageRequest.Key, pageRequest.Reverse) 64 defer iterator.Close() 65 66 var count uint64 67 var nextKey []byte 68 69 if len(pageRequest.Key) != 0 { 70 for ; iterator.Valid(); iterator.Next() { 71 if count == pageRequest.Limit { 72 nextKey = iterator.Key() 73 break 74 } 75 if iterator.Error() != nil { 76 return nil, iterator.Error() 77 } 78 err := onResult(iterator.Key(), iterator.Value()) 79 if err != nil { 80 return nil, err 81 } 82 83 count++ 84 } 85 86 return &PageResponse{ 87 NextKey: nextKey, 88 }, nil 89 } 90 91 end := pageRequest.Offset + pageRequest.Limit 92 93 for ; iterator.Valid(); iterator.Next() { 94 count++ 95 96 if count <= pageRequest.Offset { 97 continue 98 } 99 if count <= end { 100 err := onResult(iterator.Key(), iterator.Value()) 101 if err != nil { 102 return nil, err 103 } 104 } else if count == end+1 { 105 nextKey = iterator.Key() 106 107 if !pageRequest.CountTotal { 108 break 109 } 110 } 111 if iterator.Error() != nil { 112 return nil, iterator.Error() 113 } 114 } 115 116 res := &PageResponse{NextKey: nextKey} 117 if pageRequest.CountTotal { 118 res.Total = count 119 } 120 121 return res, nil 122 } 123 124 func getIterator(prefixStore types.KVStore, start []byte, reverse bool) db.Iterator { 125 if reverse { 126 var end []byte 127 if start != nil { 128 itr := prefixStore.Iterator(start, nil) 129 defer itr.Close() 130 if itr.Valid() { 131 itr.Next() 132 end = itr.Key() 133 } 134 } 135 return prefixStore.ReverseIterator(nil, end) 136 } 137 return prefixStore.Iterator(start, nil) 138 } 139 140 // initPageRequestDefaults initializes a PageRequest's defaults when those are not set. 141 func initPageRequestDefaults(pageRequest *PageRequest) *PageRequest { 142 // if the PageRequest is nil, use default PageRequest 143 if pageRequest == nil { 144 pageRequest = &PageRequest{} 145 } 146 147 pageRequestCopy := *pageRequest 148 if len(pageRequestCopy.Key) == 0 { 149 pageRequestCopy.Key = nil 150 } 151 152 if pageRequestCopy.Limit == 0 { 153 pageRequestCopy.Limit = DefaultLimit 154 155 // count total results when the limit is zero/not supplied 156 pageRequestCopy.CountTotal = true 157 } 158 159 return &pageRequestCopy 160 }