forked from influxdata/kapacitor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
query.go
180 lines (162 loc) · 3.89 KB
/
query.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
package kapacitor
import (
"fmt"
"time"
"github.com/influxdata/influxdb/influxql"
"github.com/influxdata/kapacitor/tick/ast"
)
type Query struct {
startTL *influxql.TimeLiteral
stopTL *influxql.TimeLiteral
stmt *influxql.SelectStatement
}
func NewQuery(queryString string) (*Query, error) {
query := &Query{}
// Parse and validate query
q, err := influxql.ParseQuery(queryString)
if err != nil {
return nil, err
}
if l := len(q.Statements); l != 1 {
return nil, fmt.Errorf("query must be a single select statement, got %d statements", l)
}
var ok bool
query.stmt, ok = q.Statements[0].(*influxql.SelectStatement)
if !ok {
return nil, fmt.Errorf("query is not a select statement %q", q)
}
// Add in time condition nodes
query.startTL = &influxql.TimeLiteral{}
startExpr := &influxql.BinaryExpr{
Op: influxql.GTE,
LHS: &influxql.VarRef{Val: "time"},
RHS: query.startTL,
}
query.stopTL = &influxql.TimeLiteral{}
stopExpr := &influxql.BinaryExpr{
Op: influxql.LT,
LHS: &influxql.VarRef{Val: "time"},
RHS: query.stopTL,
}
if query.stmt.Condition != nil {
query.stmt.Condition = &influxql.BinaryExpr{
Op: influxql.AND,
LHS: query.stmt.Condition,
RHS: &influxql.BinaryExpr{
Op: influxql.AND,
LHS: startExpr,
RHS: stopExpr,
},
}
} else {
query.stmt.Condition = &influxql.BinaryExpr{
Op: influxql.AND,
LHS: startExpr,
RHS: stopExpr,
}
}
return query, nil
}
// Return the db rp pairs of the query
func (q *Query) DBRPs() ([]DBRP, error) {
dbrps := make([]DBRP, len(q.stmt.Sources))
for i, s := range q.stmt.Sources {
m, ok := s.(*influxql.Measurement)
if !ok {
return nil, fmt.Errorf("unknown query source %T", s)
}
dbrps[i] = DBRP{
Database: m.Database,
RetentionPolicy: m.RetentionPolicy,
}
}
return dbrps, nil
}
// Set the start time of the query
func (q *Query) Start(s time.Time) {
q.startTL.Val = s
}
// Set the stop time of the query
func (q *Query) Stop(s time.Time) {
q.stopTL.Val = s
}
// Set the dimensions on the query
func (q *Query) Dimensions(dims []interface{}) error {
q.stmt.Dimensions = q.stmt.Dimensions[:0]
// Add in dimensions
hasTime := false
for _, d := range dims {
switch dim := d.(type) {
case time.Duration:
if hasTime {
return fmt.Errorf("groupBy cannot have more than one time dimension")
}
// Add time dimension
hasTime = true
q.stmt.Dimensions = append(q.stmt.Dimensions,
&influxql.Dimension{
Expr: &influxql.Call{
Name: "time",
Args: []influxql.Expr{
&influxql.DurationLiteral{
Val: dim,
},
},
},
})
case string:
q.stmt.Dimensions = append(q.stmt.Dimensions,
&influxql.Dimension{
Expr: &influxql.VarRef{
Val: dim,
},
})
case *ast.StarNode:
q.stmt.Dimensions = append(q.stmt.Dimensions,
&influxql.Dimension{
Expr: &influxql.Wildcard{},
})
case TimeDimension:
q.stmt.Dimensions = append(q.stmt.Dimensions,
&influxql.Dimension{
Expr: &influxql.Call{
Name: "time",
Args: []influxql.Expr{
&influxql.DurationLiteral{
Val: dim.Length,
},
&influxql.DurationLiteral{
Val: dim.Offset,
},
},
},
})
default:
return fmt.Errorf("invalid dimension type:%T, must be string or time.Duration", d)
}
}
return nil
}
func (q *Query) Fill(option influxql.FillOption, value interface{}) {
q.stmt.Fill = option
q.stmt.FillValue = value
}
func (q *Query) String() string {
return q.stmt.String()
}
type TimeDimension struct {
Length time.Duration
Offset time.Duration
}
func groupByTime(length time.Duration, offset ...time.Duration) (TimeDimension, error) {
var o time.Duration
if l := len(offset); l == 1 {
o = offset[0]
} else if l != 0 {
return TimeDimension{}, fmt.Errorf("time() function expects 1 or 2 args, got %d", l+1)
}
return TimeDimension{
Length: length,
Offset: o,
}, nil
}