Skip to content
This repository was archived by the owner on Feb 21, 2024. It is now read-only.

Commit e755fec

Browse files
authored
ORDER BY ....what now!? (fb-1954) (#2257)
* can now order by columns not in the select list * added testing coverage
1 parent c749e07 commit e755fec

File tree

8 files changed

+431
-93
lines changed

8 files changed

+431
-93
lines changed

sql3/planner/compileselect.go

Lines changed: 132 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/featurebasedb/featurebase/v3/sql3"
1212
"github.com/featurebasedb/featurebase/v3/sql3/parser"
1313
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
14-
"github.com/pkg/errors"
1514
)
1615

1716
// compileSelectStatment compiles a parser.SelectStatment AST into a PlanOperator
@@ -20,12 +19,12 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
2019

2120
aggregates := make([]types.PlanExpression, 0)
2221

23-
// handle projections
22+
// compile select list and generate a list of projections
2423
projections := make([]types.PlanExpression, 0)
2524
for _, c := range stmt.Columns {
2625
planExpr, err := p.compileExpr(c.Expr)
2726
if err != nil {
28-
return nil, errors.Wrap(err, "planning select column expression")
27+
return nil, err
2928
}
3029
if c.Alias != nil {
3130
planExpr = newAliasPlanExpression(c.Alias.Name, planExpr)
@@ -34,7 +33,7 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
3433
aggregates = p.gatherExprAggregates(planExpr, aggregates)
3534
}
3635

37-
// group by clause.
36+
// compile group by clause and generate a list of group by expressions
3837
groupByExprs := make([]types.PlanExpression, 0)
3938
for _, expr := range stmt.GroupByExprs {
4039
switch expr := expr.(type) {
@@ -44,32 +43,32 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
4443
return nil, sql3.NewErrInternalf("unsupported expression type in GROUP BY clause: %T", expr)
4544
}
4645
}
47-
var err error
4846

49-
// handle the where clause
47+
// compile the where clause
5048
where, err := p.compileExpr(stmt.WhereExpr)
5149
if err != nil {
5250
return nil, err
5351
}
5452

55-
// source expression
53+
// compile source expression
5654
source, err := p.compileSource(query, stmt.Source)
5755
if err != nil {
5856
return nil, err
5957
}
6058

61-
// if we did have a where, insert the filter op
59+
// if we did have a where, insert the filter op after source
6260
if where != nil {
6361
aggregates = p.gatherExprAggregates(where, aggregates)
6462
source = NewPlanOpFilter(p, where, source)
6563
}
6664

67-
// handle the having clause
65+
// compile the having clause
6866
having, err := p.compileExpr(stmt.HavingExpr)
6967
if err != nil {
7068
return nil, err
7169
}
7270

71+
// if we have a having, check references
7372
if having != nil {
7473
// gather aggregates
7574
aggregates = p.gatherExprAggregates(having, aggregates)
@@ -129,9 +128,49 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
129128
}
130129
}
131130

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+
133168
var compiledOp types.PlanOperator
169+
170+
// do we have straight projection or a group by?
134171
if len(aggregates) > 0 {
172+
// we have a group by
173+
135174
//check that any projections that are not aggregates are in the group by list
136175
nonAggregateReferences := make([]*qualifiedRefPlanExpression, 0)
137176
for _, expr := range projections {
@@ -172,37 +211,98 @@ func (p *ExecutionPlanner) compileSelectStatement(stmt *parser.SelectStatement,
172211
}
173212
compiledOp = NewPlanOpProjection(projections, groupByOp)
174213
} else {
214+
// no group by, just a straight projection
175215
compiledOp = NewPlanOpProjection(projections, source)
176216
}
177217

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
185232
}
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+
}
188247

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)
193254
}
255+
}
194256

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+
}
198270
}
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+
}
202303
}
203-
orderByFields = append(orderByFields, f)
304+
compiledOp = NewPlanOpOrderBy(orderByExprs, compiledOp)
204305
}
205-
compiledOp = NewPlanOpOrderBy(orderByFields, compiledOp)
206306
}
207307

208308
// insert the top operator if it exists
@@ -583,11 +683,10 @@ func (p *ExecutionPlanner) analyzeSelectStatement(ctx context.Context, stmt *par
583683
}
584684

585685
for _, term := range stmt.OrderingTerms {
586-
expr, err := p.analyzeOrderingTermExpression(term.X, stmt)
686+
err := p.analyzeOrderingTermExpression(term.X, stmt)
587687
if err != nil {
588688
return nil, err
589689
}
590-
term.X = expr
591690
}
592691

593692
return stmt, nil

sql3/planner/expression.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2817,26 +2817,62 @@ func (p *ExecutionPlanner) compileCallExpr(expr *parser.Call) (_ types.PlanExpre
28172817
}
28182818
}
28192819

2820-
func (p *ExecutionPlanner) compileOrderingTermExpr(expr parser.Expr) (index int, err error) {
2820+
func (p *ExecutionPlanner) compileOrderingTermExpr(expr parser.Expr, projections []types.PlanExpression, source parser.Source) (types.PlanExpression, error) {
28212821
if expr == nil {
2822-
return 0, nil
2822+
return nil, nil
28232823
}
28242824

28252825
switch thisExpr := expr.(type) {
2826-
case *parser.QualifiedRef:
2827-
return thisExpr.ColumnIndex, nil
2826+
case *parser.Ident:
2827+
for _, proj := range projections {
2828+
switch p := proj.(type) {
2829+
case *qualifiedRefPlanExpression:
2830+
if strings.EqualFold(thisExpr.Name, p.columnName) {
2831+
if !typeCanBeSortedOn(p.Type()) {
2832+
return nil, sql3.NewErrExpectedSortableExpression(0, 0, p.Type().TypeDescription())
2833+
}
2834+
return p, nil
2835+
}
2836+
case *aliasPlanExpression:
2837+
if strings.EqualFold(thisExpr.Name, p.aliasName) {
2838+
if !typeCanBeSortedOn(p.expr.Type()) {
2839+
return nil, sql3.NewErrExpectedSortableExpression(0, 0, p.expr.Type().TypeDescription())
2840+
}
2841+
return p.expr, nil
2842+
}
2843+
2844+
}
2845+
}
2846+
2847+
// we didn't find in projection list so go look in the source columns
2848+
for _, col := range source.PossibleOutputColumns() {
2849+
if strings.EqualFold(thisExpr.Name, col.ColumnName) {
2850+
orderExpr := newQualifiedRefPlanExpression(col.TableName, col.ColumnName, col.ColumnIndex, col.Datatype)
2851+
if !typeCanBeSortedOn(orderExpr.Type()) {
2852+
return nil, sql3.NewErrExpectedSortableExpression(0, 0, orderExpr.Type().TypeDescription())
2853+
}
2854+
return orderExpr, nil
2855+
}
2856+
}
2857+
2858+
return nil, sql3.NewErrColumnNotFound(thisExpr.NamePos.Line, thisExpr.NamePos.Column, thisExpr.Name)
28282859

28292860
case *parser.IntegerLit:
28302861
val, err := strconv.ParseInt(thisExpr.Value, 10, 64)
28312862
if err != nil {
2832-
return 0, err
2863+
return nil, err
28332864
}
28342865
// subtract one because ordering terms are 1 based, not 0 based
2835-
return int(val - 1), nil
2866+
index := int(val - 1)
2867+
// get the expr from the projection
2868+
orderExpr := projections[index]
2869+
if !typeCanBeSortedOn(orderExpr.Type()) {
2870+
return nil, sql3.NewErrExpectedSortableExpression(0, 0, orderExpr.Type().TypeDescription())
2871+
}
2872+
return orderExpr, nil
28362873

28372874
default:
2838-
return 0, sql3.NewErrInternalf("unexpected ordering expression type: %T", expr)
2839-
2875+
return nil, sql3.NewErrInternalf("unexpected ordering expression type: %T", expr)
28402876
}
28412877
}
28422878

0 commit comments

Comments
 (0)