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  }