Skip to content

Commit 04bdddc

Browse files
authored
[binder] hoist projections in certain cases where we can combine with top-level projection (#2813)
* [binder] hoist projections in certain cases where we can combine with top-level projection * fix plans * doc comment * fix build * better comment
1 parent 09d646b commit 04bdddc

File tree

7 files changed

+2849
-2962
lines changed

7 files changed

+2849
-2962
lines changed

enginetest/queries/integration_plans.go

Lines changed: 1615 additions & 1696 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enginetest/queries/query_plans.go

Lines changed: 252 additions & 294 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enginetest/queries/tpch_plans.go

Lines changed: 926 additions & 968 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enginetest/scriptgen/setup/setup_data.sg.go

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sql/plan/project.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Project struct {
2828
UnaryNode
2929
Projections []sql.Expression
3030
CanDefer bool
31+
deps sql.ColSet
3132
}
3233

3334
var _ sql.Expressioner = (*Project)(nil)
@@ -94,6 +95,25 @@ func unwrapGetField(expr sql.Expression) *expression.GetField {
9495
}
9596
}
9697

98+
// ExprDeps returns a column set of the ids referenced
99+
// in this list of expressions.
100+
func ExprDeps(exprs ...sql.Expression) sql.ColSet {
101+
var deps sql.ColSet
102+
for _, e := range exprs {
103+
sql.Inspect(e, func(e sql.Expression) bool {
104+
switch e := e.(type) {
105+
case sql.IdExpression:
106+
deps.Add(e.Id())
107+
case *Subquery:
108+
deps.Union(e.Correlated())
109+
default:
110+
}
111+
return true
112+
})
113+
}
114+
return deps
115+
}
116+
97117
// Schema implements the Node interface.
98118
func (p *Project) Schema() sql.Schema {
99119
var s = make(sql.Schema, len(p.Projections))

sql/planbuilder/factory.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,33 @@ func (f *factory) buildDistinct(child sql.Node) sql.Node {
220220
}
221221
return plan.NewDistinct(child)
222222
}
223+
224+
func (f *factory) buildSort(child sql.Node, exprs []sql.SortField, deps sql.ColSet, subquery bool) (sql.Node, error) {
225+
{
226+
// The default binder behavior adds a projection before and after
227+
// sort nodes for alias dependency correctness. In many cases the sort
228+
// does not reference an alias, and it is beneficial to hoist the inner
229+
// projection which lets the optimizer (1) remove redundant projcetions
230+
// and usually (2) more aggressively prune table columns.
231+
// (proj ->) sort -> proj -> child
232+
// =>
233+
// (proj ->) proj -> sort -> child
234+
// =>
235+
// (proj ->) sort -> child
236+
if p, ok := child.(*plan.Project); ok {
237+
var aliases []sql.Expression
238+
for _, p := range p.Projections {
239+
if _, ok := p.(*expression.Alias); ok {
240+
aliases = append(aliases, p)
241+
}
242+
}
243+
aliasCols := plan.ExprDeps(aliases...)
244+
if !aliasCols.Intersects(deps) {
245+
newP := plan.NewProject(p.Projections, plan.NewSort(exprs, p.Child))
246+
return f.buildProject(newP, subquery)
247+
}
248+
}
249+
}
250+
251+
return plan.NewSort(exprs, child), nil
252+
}

sql/planbuilder/orderby.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
"github.com/dolthub/go-mysql-server/sql"
2424
"github.com/dolthub/go-mysql-server/sql/expression"
25-
"github.com/dolthub/go-mysql-server/sql/plan"
2625
"github.com/dolthub/go-mysql-server/sql/transform"
2726
"github.com/dolthub/go-mysql-server/sql/types"
2827
)
@@ -199,6 +198,7 @@ func (b *Builder) buildOrderBy(inScope, orderByScope *scope) {
199198
return
200199
}
201200
var sortFields sql.SortFields
201+
var deps sql.ColSet
202202
for _, c := range orderByScope.cols {
203203
so := sql.Ascending
204204
if c.descending {
@@ -213,8 +213,12 @@ func (b *Builder) buildOrderBy(inScope, orderByScope *scope) {
213213
Order: so,
214214
}
215215
sortFields = append(sortFields, sf)
216+
deps.Add(sql.ColumnId(c.id))
217+
}
218+
sort, err := b.f.buildSort(inScope.node, sortFields, deps, inScope.refsSubquery)
219+
if err != nil {
220+
b.handleErr(err)
216221
}
217-
sort := plan.NewSort(sortFields, inScope.node)
218222
inScope.node = sort
219223
return
220224
}

0 commit comments

Comments
 (0)