@@ -16,6 +16,7 @@ import (
16
16
"github.com/prometheus/prometheus/util/annotations"
17
17
"github.com/prometheus/prometheus/util/httputil"
18
18
v1 "github.com/prometheus/prometheus/web/api/v1"
19
+ "github.com/thanos-io/promql-engine/logicalplan"
19
20
"github.com/weaveworks/common/httpgrpc"
20
21
21
22
"github.com/cortexproject/cortex/pkg/engine"
@@ -26,7 +27,7 @@ import (
26
27
27
28
type QueryAPI struct {
28
29
queryable storage.SampleAndChunkQueryable
29
- queryEngine promql .QueryEngine
30
+ queryEngine engine .QueryEngine
30
31
now func () time.Time
31
32
statsRenderer v1.StatsRenderer
32
33
logger log.Logger
@@ -35,7 +36,7 @@ type QueryAPI struct {
35
36
}
36
37
37
38
func NewQueryAPI (
38
- qe promql .QueryEngine ,
39
+ qe engine .QueryEngine ,
39
40
q storage.SampleAndChunkQueryable ,
40
41
statsRenderer v1.StatsRenderer ,
41
42
logger log.Logger ,
@@ -101,10 +102,29 @@ func (q *QueryAPI) RangeQueryHandler(r *http.Request) (result apiFuncResult) {
101
102
102
103
ctx = engine .AddEngineTypeToContext (ctx , r )
103
104
ctx = querier .AddBlockStoreTypeToContext (ctx , r .Header .Get (querier .BlockStoreTypeHeader ))
104
- qry , err := q .queryEngine .NewRangeQuery (ctx , q .queryable , opts , r .FormValue ("query" ), convertMsToTime (start ), convertMsToTime (end ), convertMsToDuration (step ))
105
- if err != nil {
106
- return invalidParamError (httpgrpc .Errorf (http .StatusBadRequest , "%s" , err .Error ()), "query" )
105
+
106
+ var qry promql.Query
107
+ startTime := convertMsToTime (start )
108
+ endTime := convertMsToTime (end )
109
+ stepDuration := convertMsToDuration (step )
110
+
111
+ byteLP := []byte (r .PostFormValue ("plan" ))
112
+ if len (byteLP ) != 0 {
113
+ logicalPlan , err := logicalplan .Unmarshal (byteLP )
114
+ if err != nil {
115
+ return apiFuncResult {nil , & apiError {errorInternal , fmt .Errorf ("invalid logical plan: %v" , err )}, nil , nil }
116
+ }
117
+ qry , err = q .queryEngine .MakeRangeQueryFromPlan (ctx , q .queryable , opts , logicalPlan , startTime , endTime , stepDuration , r .FormValue ("query" ))
118
+ if err != nil {
119
+ return apiFuncResult {nil , & apiError {errorInternal , fmt .Errorf ("failed to create range query from logical plan: %v" , err )}, nil , nil }
120
+ }
121
+ } else { // if there is logical plan field is empty, fall back
122
+ qry , err = q .queryEngine .NewRangeQuery (ctx , q .queryable , opts , r .FormValue ("query" ), startTime , endTime , stepDuration )
123
+ if err != nil {
124
+ return invalidParamError (httpgrpc .Errorf (http .StatusBadRequest , "%s" , err .Error ()), "query" )
125
+ }
107
126
}
127
+
108
128
// From now on, we must only return with a finalizer in the result (to
109
129
// be called by the caller) or call qry.Close ourselves (which is
110
130
// required in the case of a panic).
@@ -157,9 +177,25 @@ func (q *QueryAPI) InstantQueryHandler(r *http.Request) (result apiFuncResult) {
157
177
158
178
ctx = engine .AddEngineTypeToContext (ctx , r )
159
179
ctx = querier .AddBlockStoreTypeToContext (ctx , r .Header .Get (querier .BlockStoreTypeHeader ))
160
- qry , err := q .queryEngine .NewInstantQuery (ctx , q .queryable , opts , r .FormValue ("query" ), convertMsToTime (ts ))
161
- if err != nil {
162
- return invalidParamError (httpgrpc .Errorf (http .StatusBadRequest , "%s" , err .Error ()), "query" )
180
+
181
+ var qry promql.Query
182
+ tsTime := convertMsToTime (ts )
183
+
184
+ byteLP := []byte (r .PostFormValue ("plan" ))
185
+ if len (byteLP ) != 0 {
186
+ logicalPlan , err := logicalplan .Unmarshal (byteLP )
187
+ if err != nil {
188
+ return apiFuncResult {nil , & apiError {errorInternal , fmt .Errorf ("invalid logical plan: %v" , err )}, nil , nil }
189
+ }
190
+ qry , err = q .queryEngine .MakeInstantQueryFromPlan (ctx , q .queryable , opts , logicalPlan , tsTime , r .FormValue ("query" ))
191
+ if err != nil {
192
+ return apiFuncResult {nil , & apiError {errorInternal , fmt .Errorf ("failed to create instant query from logical plan: %v" , err )}, nil , nil }
193
+ }
194
+ } else { // if there is logical plan field is empty, fall back
195
+ qry , err = q .queryEngine .NewInstantQuery (ctx , q .queryable , opts , r .FormValue ("query" ), tsTime )
196
+ if err != nil {
197
+ return invalidParamError (httpgrpc .Errorf (http .StatusBadRequest , "%s" , err .Error ()), "query" )
198
+ }
163
199
}
164
200
165
201
// From now on, we must only return with a finalizer in the result (to
0 commit comments