@@ -11,7 +11,6 @@ import (
11
11
"github.com/featurebasedb/featurebase/v3/sql3"
12
12
"github.com/featurebasedb/featurebase/v3/sql3/parser"
13
13
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
14
- "github.com/pkg/errors"
15
14
)
16
15
17
16
// compileSelectStatment compiles a parser.SelectStatment AST into a PlanOperator
@@ -20,12 +19,12 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
20
19
21
20
aggregates := make ([]types.PlanExpression , 0 )
22
21
23
- // handle projections
22
+ // compile select list and generate a list of projections
24
23
projections := make ([]types.PlanExpression , 0 )
25
24
for _ , c := range stmt .Columns {
26
25
planExpr , err := p .compileExpr (c .Expr )
27
26
if err != nil {
28
- return nil , errors . Wrap ( err , "planning select column expression" )
27
+ return nil , err
29
28
}
30
29
if c .Alias != nil {
31
30
planExpr = newAliasPlanExpression (c .Alias .Name , planExpr )
@@ -34,7 +33,7 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
34
33
aggregates = p .gatherExprAggregates (planExpr , aggregates )
35
34
}
36
35
37
- // group by clause.
36
+ // compile group by clause and generate a list of group by expressions
38
37
groupByExprs := make ([]types.PlanExpression , 0 )
39
38
for _ , expr := range stmt .GroupByExprs {
40
39
switch expr := expr .(type ) {
@@ -44,32 +43,32 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
44
43
return nil , sql3 .NewErrInternalf ("unsupported expression type in GROUP BY clause: %T" , expr )
45
44
}
46
45
}
47
- var err error
48
46
49
- // handle the where clause
47
+ // compile the where clause
50
48
where , err := p .compileExpr (stmt .WhereExpr )
51
49
if err != nil {
52
50
return nil , err
53
51
}
54
52
55
- // source expression
53
+ // compile source expression
56
54
source , err := p .compileSource (query , stmt .Source )
57
55
if err != nil {
58
56
return nil , err
59
57
}
60
58
61
- // if we did have a where, insert the filter op
59
+ // if we did have a where, insert the filter op after source
62
60
if where != nil {
63
61
aggregates = p .gatherExprAggregates (where , aggregates )
64
62
source = NewPlanOpFilter (p , where , source )
65
63
}
66
64
67
- // handle the having clause
65
+ // compile the having clause
68
66
having , err := p .compileExpr (stmt .HavingExpr )
69
67
if err != nil {
70
68
return nil , err
71
69
}
72
70
71
+ // if we have a having, check references
73
72
if having != nil {
74
73
// gather aggregates
75
74
aggregates = p .gatherExprAggregates (having , aggregates )
@@ -129,9 +128,49 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
129
128
}
130
129
}
131
130
132
- // do we have straight projection or a group by?
131
+ // compile order by and generate a list of ordering expressions
132
+ orderByExprs := make ([]* OrderByExpression , 0 )
133
+ nonReferenceOrderByExpressions := make ([]types.PlanExpression , 0 )
134
+ if len (stmt .OrderingTerms ) > 0 {
135
+ for _ , ot := range stmt .OrderingTerms {
136
+ // compile the ordering term
137
+ expr , err := p .compileOrderingTermExpr (ot .X , projections , stmt .Source )
138
+ if err != nil {
139
+ return nil , err
140
+ }
141
+
142
+ f := & OrderByExpression {
143
+ Expr : expr ,
144
+ }
145
+ f .Order = orderByAsc
146
+ if ot .Desc .IsValid () {
147
+ f .Order = orderByDesc
148
+ }
149
+ orderByExprs = append (orderByExprs , f )
150
+ }
151
+
152
+ // if the expression is just references, we
153
+ // can put the sort directly after the source
154
+ for _ , oe := range orderByExprs {
155
+ _ , ok := oe .Expr .(* qualifiedRefPlanExpression )
156
+ if ! ok {
157
+ nonReferenceOrderByExpressions = append (nonReferenceOrderByExpressions , oe .Expr )
158
+ }
159
+ }
160
+
161
+ // all the order by expressions are references, so we can put the order by before the
162
+ // projection
163
+ if len (nonReferenceOrderByExpressions ) == 0 {
164
+ source = NewPlanOpOrderBy (orderByExprs , source )
165
+ }
166
+ }
167
+
133
168
var compiledOp types.PlanOperator
169
+
170
+ // do we have straight projection or a group by?
134
171
if len (aggregates ) > 0 {
172
+ // we have a group by
173
+
135
174
//check that any projections that are not aggregates are in the group by list
136
175
nonAggregateReferences := make ([]* qualifiedRefPlanExpression , 0 )
137
176
for _ , expr := range projections {
@@ -172,37 +211,98 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
172
211
}
173
212
compiledOp = NewPlanOpProjection (projections , groupByOp )
174
213
} else {
214
+ // no group by, just a straight projection
175
215
compiledOp = NewPlanOpProjection (projections , source )
176
216
}
177
217
178
- // handle order by
179
- if len (stmt .OrderingTerms ) > 0 {
180
- orderByFields := make ([]* OrderByExpression , 0 )
181
- for _ , ot := range stmt .OrderingTerms {
182
- index , err := p .compileOrderingTermExpr (ot .X )
183
- if err != nil {
184
- return nil , err
218
+ // handle the case where we have order by expressions and they are not references
219
+ // in this case we need to put the order by after the projection
220
+ if len (orderByExprs ) > 0 && len (nonReferenceOrderByExpressions ) > 0 {
221
+
222
+ // if the order by expressions contain a reference not in the projection list,
223
+ // we have to create a new projection, add references to current projection,
224
+ // and place the new order by in between
225
+
226
+ // get a list of all the refs for the order by exprs
227
+ orderByRefs := make (map [string ]* qualifiedRefPlanExpression )
228
+ for _ , oe := range orderByExprs {
229
+ ex , ok := oe .Expr .(* qualifiedRefPlanExpression )
230
+ if ok {
231
+ orderByRefs [ex .String ()] = ex
185
232
}
186
- // get the data type from the projection
187
- projDataType := projections [index ].Type ()
233
+ }
234
+
235
+ // get a list of all the projection refs
236
+ projRefs := make (map [string ]* qualifiedRefPlanExpression )
237
+ for _ , p := range projections {
238
+ InspectExpression (p , func (expr types.PlanExpression ) bool {
239
+ switch ex := expr .(type ) {
240
+ case * qualifiedRefPlanExpression :
241
+ projRefs [ex .String ()] = ex
242
+ return false
243
+ }
244
+ return true
245
+ })
246
+ }
188
247
189
- // don't let a sort happen on something unsortable right now
190
- switch projDataType .(type ) {
191
- case * parser.DataTypeStringSet , * parser.DataTypeIDSet :
192
- return nil , sql3 .NewErrExpectedSortableExpression (0 , 0 , projDataType .TypeDescription ())
248
+ // iterate the order by terms, make a list of the ones not projected
249
+ unprojectedRefs := make ([]* qualifiedRefPlanExpression , 0 )
250
+ for kobr , obr := range orderByRefs {
251
+ _ , found := projRefs [kobr ]
252
+ if ! found {
253
+ unprojectedRefs = append (unprojectedRefs , obr )
193
254
}
255
+ }
194
256
195
- f := & OrderByExpression {
196
- Index : index ,
197
- ExprType : projDataType ,
257
+ // sigh - ok. If we have unprojected refs, we need to insert a projection
258
+ if len (unprojectedRefs ) > 0 {
259
+ // create the final projection list - this will go before the order by
260
+ newProjections := make ([]types.PlanExpression , len (projections ))
261
+ for i , p := range projections {
262
+ switch pe := p .(type ) {
263
+ case * aliasPlanExpression :
264
+ newProjections [i ] = newQualifiedRefPlanExpression ("" , pe .aliasName , i , pe .Type ())
265
+ case * qualifiedRefPlanExpression :
266
+ newProjections [i ] = newQualifiedRefPlanExpression (pe .tableName , pe .columnName , i , pe .Type ())
267
+ default :
268
+ newProjections [i ] = newQualifiedRefPlanExpression ("" , p .String (), i , p .Type ())
269
+ }
198
270
}
199
- f .Order = orderByAsc
200
- if ot .Desc .IsValid () {
201
- f .Order = orderByDesc
271
+
272
+ // add the unprojected refs to the existing projection op
273
+ projectionOp , ok := compiledOp .(* PlanOpProjection )
274
+ if ! ok {
275
+ return nil , sql3 .NewErrInternalf ("unexpected compiledOp type '%T'" , compiledOp )
276
+ }
277
+ for _ , uref := range unprojectedRefs {
278
+ projectionOp .Projections = append (projectionOp .Projections , uref )
279
+ }
280
+
281
+ // add the order by on top of this
282
+ // rewrite all the order by expressions that are not qualified refs to be qualified
283
+ // refs referring to the expression
284
+ for i , oe := range orderByExprs {
285
+ _ , ok := oe .Expr .(* qualifiedRefPlanExpression )
286
+ if ! ok {
287
+ orderByExprs [i ].Expr = newQualifiedRefPlanExpression ("" , oe .Expr .String (), 0 , oe .Expr .Type ())
288
+ }
289
+ }
290
+ compiledOp = NewPlanOpOrderBy (orderByExprs , compiledOp )
291
+
292
+ // add the final projection on top of this
293
+ compiledOp = NewPlanOpProjection (newProjections , compiledOp )
294
+
295
+ } else {
296
+ // rewrite all the order by expressions that are not qualified refs to be qualified
297
+ // refs referring to the expression
298
+ for i , oe := range orderByExprs {
299
+ _ , ok := oe .Expr .(* qualifiedRefPlanExpression )
300
+ if ! ok {
301
+ orderByExprs [i ].Expr = newQualifiedRefPlanExpression ("" , oe .Expr .String (), 0 , oe .Expr .Type ())
302
+ }
202
303
}
203
- orderByFields = append ( orderByFields , f )
304
+ compiledOp = NewPlanOpOrderBy ( orderByExprs , compiledOp )
204
305
}
205
- compiledOp = NewPlanOpOrderBy (orderByFields , compiledOp )
206
306
}
207
307
208
308
// insert the top operator if it exists
@@ -583,11 +683,10 @@ func (p *ExecutionPlanner) analyzeSelectStatement(ctx context.Context, stmt *par
583
683
}
584
684
585
685
for _ , term := range stmt .OrderingTerms {
586
- expr , err := p .analyzeOrderingTermExpression (term .X , stmt )
686
+ err := p .analyzeOrderingTermExpression (term .X , stmt )
587
687
if err != nil {
588
688
return nil , err
589
689
}
590
- term .X = expr
591
690
}
592
691
593
692
return stmt , nil
0 commit comments