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