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