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  }