github.com/KYVENetwork/cometbft/v38@v38.0.3/state/indexer/query_range.go (about)

     1  package indexer
     2  
     3  import (
     4  	"math/big"
     5  	"time"
     6  
     7  	"github.com/KYVENetwork/cometbft/v38/libs/pubsub/query/syntax"
     8  	"github.com/KYVENetwork/cometbft/v38/types"
     9  )
    10  
    11  // QueryRanges defines a mapping between a composite event key and a QueryRange.
    12  //
    13  // e.g.account.number => queryRange{lowerBound: 1, upperBound: 5}
    14  type QueryRanges map[string]QueryRange
    15  
    16  // QueryRange defines a range within a query condition.
    17  type QueryRange struct {
    18  	LowerBound        interface{} // int || time.Time
    19  	UpperBound        interface{} // int || time.Time
    20  	Key               string
    21  	IncludeLowerBound bool
    22  	IncludeUpperBound bool
    23  }
    24  
    25  // AnyBound returns either the lower bound if non-nil, otherwise the upper bound.
    26  func (qr QueryRange) AnyBound() interface{} {
    27  	if qr.LowerBound != nil {
    28  		return qr.LowerBound
    29  	}
    30  
    31  	return qr.UpperBound
    32  }
    33  
    34  // LowerBoundValue returns the value for the lower bound. If the lower bound is
    35  // nil, nil will be returned.
    36  func (qr QueryRange) LowerBoundValue() interface{} {
    37  	if qr.LowerBound == nil {
    38  		return nil
    39  	}
    40  
    41  	if qr.IncludeLowerBound {
    42  		return qr.LowerBound
    43  	}
    44  
    45  	switch t := qr.LowerBound.(type) {
    46  	case int64:
    47  		return t + 1
    48  	case *big.Int:
    49  		tmp := new(big.Int)
    50  		return tmp.Add(t, big.NewInt(1))
    51  
    52  	case *big.Float:
    53  		// For floats we cannot simply add one as the float to float
    54  		// comparison is more finegrained.
    55  		// When comparing to integers, adding one is also incorrect:
    56  		// example: x >100.2 ; x = 101 float increased to 101.2 and condition
    57  		// is not satisfied
    58  		return t
    59  	case time.Time:
    60  		return t.Unix() + 1
    61  
    62  	default:
    63  		panic("not implemented")
    64  	}
    65  }
    66  
    67  // UpperBoundValue returns the value for the upper bound. If the upper bound is
    68  // nil, nil will be returned.
    69  func (qr QueryRange) UpperBoundValue() interface{} {
    70  	if qr.UpperBound == nil {
    71  		return nil
    72  	}
    73  
    74  	if qr.IncludeUpperBound {
    75  		return qr.UpperBound
    76  	}
    77  
    78  	switch t := qr.UpperBound.(type) {
    79  	case int64:
    80  		return t - 1
    81  	case *big.Int:
    82  		tmp := new(big.Int)
    83  		return tmp.Sub(t, big.NewInt(1))
    84  	case *big.Float:
    85  		return t
    86  	case time.Time:
    87  		return t.Unix() - 1
    88  
    89  	default:
    90  		panic("not implemented")
    91  	}
    92  }
    93  
    94  // LookForRangesWithHeight returns a mapping of QueryRanges and the matching indexes in
    95  // the provided query conditions.
    96  func LookForRangesWithHeight(conditions []syntax.Condition) (queryRange QueryRanges, indexes []int, heightRange QueryRange) {
    97  	queryRange = make(QueryRanges)
    98  	for i, c := range conditions {
    99  		if IsRangeOperation(c.Op) {
   100  			heightKey := c.Tag == types.BlockHeightKey || c.Tag == types.TxHeightKey
   101  			r, ok := queryRange[c.Tag]
   102  			if !ok {
   103  				r = QueryRange{Key: c.Tag}
   104  				if c.Tag == types.BlockHeightKey || c.Tag == types.TxHeightKey {
   105  					heightRange = QueryRange{Key: c.Tag}
   106  				}
   107  			}
   108  
   109  			switch c.Op {
   110  			case syntax.TGt:
   111  				if heightKey {
   112  					heightRange.LowerBound = conditionArg(c)
   113  				}
   114  				r.LowerBound = conditionArg(c)
   115  
   116  			case syntax.TGeq:
   117  				r.IncludeLowerBound = true
   118  				r.LowerBound = conditionArg(c)
   119  				if heightKey {
   120  					heightRange.IncludeLowerBound = true
   121  					heightRange.LowerBound = conditionArg(c)
   122  				}
   123  
   124  			case syntax.TLt:
   125  				r.UpperBound = conditionArg(c)
   126  				if heightKey {
   127  					heightRange.UpperBound = conditionArg(c)
   128  				}
   129  
   130  			case syntax.TLeq:
   131  				r.IncludeUpperBound = true
   132  				r.UpperBound = conditionArg(c)
   133  				if heightKey {
   134  					heightRange.IncludeUpperBound = true
   135  					heightRange.UpperBound = conditionArg(c)
   136  				}
   137  			}
   138  
   139  			queryRange[c.Tag] = r
   140  			indexes = append(indexes, i)
   141  		}
   142  	}
   143  
   144  	return queryRange, indexes, heightRange
   145  }
   146  
   147  // Deprecated: This function is not used anymore and will be replaced with LookForRangesWithHeight
   148  func LookForRanges(conditions []syntax.Condition) (ranges QueryRanges, indexes []int) {
   149  	ranges = make(QueryRanges)
   150  	for i, c := range conditions {
   151  		if IsRangeOperation(c.Op) {
   152  			r, ok := ranges[c.Tag]
   153  			if !ok {
   154  				r = QueryRange{Key: c.Tag}
   155  			}
   156  
   157  			switch c.Op {
   158  			case syntax.TGt:
   159  				r.LowerBound = conditionArg(c)
   160  
   161  			case syntax.TGeq:
   162  				r.IncludeLowerBound = true
   163  				r.LowerBound = conditionArg(c)
   164  
   165  			case syntax.TLt:
   166  				r.UpperBound = conditionArg(c)
   167  
   168  			case syntax.TLeq:
   169  				r.IncludeUpperBound = true
   170  				r.UpperBound = conditionArg(c)
   171  			}
   172  
   173  			ranges[c.Tag] = r
   174  			indexes = append(indexes, i)
   175  		}
   176  	}
   177  
   178  	return ranges, indexes
   179  }
   180  
   181  // IsRangeOperation returns a boolean signifying if a query Operator is a range
   182  // operation or not.
   183  func IsRangeOperation(op syntax.Token) bool {
   184  	switch op {
   185  	case syntax.TGt, syntax.TGeq, syntax.TLt, syntax.TLeq:
   186  		return true
   187  
   188  	default:
   189  		return false
   190  	}
   191  }
   192  
   193  func conditionArg(c syntax.Condition) interface{} {
   194  	if c.Arg == nil {
   195  		return nil
   196  	}
   197  	switch c.Arg.Type {
   198  	case syntax.TNumber:
   199  		return c.Arg.Number()
   200  	case syntax.TTime, syntax.TDate:
   201  		return c.Arg.Time()
   202  	default:
   203  		return c.Arg.Value() // string
   204  	}
   205  }