Skip to content

Commit fba2a54

Browse files
committed
Avoid projection alloc when possible
1 parent 06ed65f commit fba2a54

File tree

4 files changed

+89
-5
lines changed

4 files changed

+89
-5
lines changed

sql/memo/exec_builder.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ func (b *ExecBuilder) buildProject(r *Project, children ...sql.Node) (sql.Node,
353353
for i := range r.Projections {
354354
proj[i] = r.Projections[i]
355355
}
356-
return plan.NewProject(proj, children[0]), nil
356+
ret := plan.NewProject(proj, children[0])
357+
return ret, nil
357358
}
358359

359360
func (b *ExecBuilder) buildFilter(r *Filter, children ...sql.Node) (sql.Node, error) {

sql/plan/values.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import (
2323

2424
// Values represents a set of tuples of expressions.
2525
type Values struct {
26-
AliasName string
27-
ColumnNames map[string]string
28-
ExpressionTuples [][]sql.Expression
26+
AliasName string
27+
ColumnNames map[string]string
28+
ExpressionTuples [][]sql.Expression
29+
InPlaceProjection bool
2930
}
3031

3132
var _ sql.Node = (*Values)(nil)

sql/rowexec/rel.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package rowexec
1717
import (
1818
"errors"
1919
"fmt"
20+
"github.com/dolthub/go-mysql-server/sql/transform"
2021
"io"
2122
"os"
2223
"path/filepath"
@@ -97,8 +98,17 @@ func (b *BaseBuilder) buildValues(ctx *sql.Context, n *plan.Values, row sql.Row)
9798
} else {
9899
// For the values node, the relevant values to evaluate are the tuple itself. We may need to project
99100
// DEFAULT values onto them, which ProjectRow handles correctly (could require multiple passes)
101+
inPlaceOk, dirAsc := monotonicExprs(et)
100102
var err error
101-
vals, err = ProjectRow(ctx, et, vals)
103+
if inPlaceOk {
104+
if dirAsc {
105+
vals, err = ProjectRowInPlace(ctx, et, vals)
106+
} else {
107+
vals, err = ProjectRowInPlaceDesc(ctx, et, vals)
108+
}
109+
} else {
110+
vals, err = ProjectRow(ctx, et, vals)
111+
}
102112
if err != nil {
103113
return nil, err
104114
}
@@ -311,21 +321,52 @@ func (b *BaseBuilder) buildProject(ctx *sql.Context, n *plan.Project, row sql.Ro
311321
return nil, err
312322
}
313323

324+
inPlaceOk, dirAsc := monotonicExprs(n.Projections)
325+
314326
return sql.NewSpanIter(span, &ProjectIter{
315327
projs: n.Projections,
316328
canDefer: n.CanDefer,
329+
inPlace: inPlaceOk,
330+
asc: dirAsc,
317331
childIter: i,
318332
}), nil
319333
}
320334

335+
func monotonicExprs(exprs []sql.Expression) (bool, bool) {
336+
ascOk := true
337+
descOk := true
338+
for i, e := range exprs {
339+
if !ascOk && !ascOk {
340+
return false, false
341+
}
342+
transform.InspectExpr(e, func(e sql.Expression) bool {
343+
if gf, ok := e.(*expression.GetField); ok {
344+
if gf.Index() < i {
345+
// cant reference <i at position i
346+
ascOk = false
347+
} else if gf.Index() > i {
348+
// same but reverse iter order
349+
descOk = false
350+
}
351+
}
352+
return false
353+
})
354+
}
355+
return ascOk || descOk, ascOk
356+
}
357+
321358
func (b *BaseBuilder) buildVirtualColumnTable(ctx *sql.Context, n *plan.VirtualColumnTable, tableIter sql.RowIter, row sql.Row) (sql.RowIter, error) {
322359
span, ctx := ctx.Span("plan.VirtualColumnTable", trace.WithAttributes(
323360
attribute.Int("projections", len(n.Projections)),
324361
))
325362

363+
inPlaceOk, dirAsc := monotonicExprs(n.Projections)
364+
326365
return sql.NewSpanIter(span, &ProjectIter{
327366
projs: n.Projections,
328367
childIter: tableIter,
368+
inPlace: inPlaceOk,
369+
asc: dirAsc,
329370
}), nil
330371
}
331372

sql/rowexec/rel_iters.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,22 @@ type ProjectIter struct {
128128
projs []sql.Expression
129129
canDefer bool
130130
childIter sql.RowIter
131+
inPlace bool
132+
asc bool
131133
}
132134

133135
func (i *ProjectIter) Next(ctx *sql.Context) (sql.Row, error) {
134136
childRow, err := i.childIter.Next(ctx)
135137
if err != nil {
136138
return nil, err
137139
}
140+
if i.inPlace {
141+
if i.asc {
142+
return ProjectRowInPlace(ctx, i.projs, childRow)
143+
} else {
144+
return ProjectRowInPlaceDesc(ctx, i.projs, childRow)
145+
}
146+
}
138147
return ProjectRow(ctx, i.projs, childRow)
139148
}
140149

@@ -190,6 +199,38 @@ func ProjectRow(
190199
return fields, nil
191200
}
192201

202+
// ProjectRowInPlace evaluates a set of projections
203+
// for a row with no back-references.
204+
func ProjectRowInPlace(
205+
ctx *sql.Context,
206+
projections []sql.Expression,
207+
row sql.Row,
208+
) (sql.Row, error) {
209+
for i, expr := range projections {
210+
field, fErr := expr.Eval(ctx, row)
211+
if fErr != nil {
212+
return nil, fErr
213+
}
214+
row[i] = normalizeNegativeZeros(field)
215+
}
216+
return row, nil
217+
}
218+
219+
func ProjectRowInPlaceDesc(
220+
ctx *sql.Context,
221+
projections []sql.Expression,
222+
row sql.Row,
223+
) (sql.Row, error) {
224+
for i := len(projections); i >= 0; i-- {
225+
field, fErr := projections[i].Eval(ctx, row)
226+
if fErr != nil {
227+
return nil, fErr
228+
}
229+
row[i] = normalizeNegativeZeros(field)
230+
}
231+
return row, nil
232+
}
233+
193234
func defaultValFromProjectExpr(e sql.Expression) (*sql.ColumnDefaultValue, bool) {
194235
if defaultVal, ok := e.(*expression.Wrapper); ok {
195236
e = defaultVal.Unwrap()

0 commit comments

Comments
 (0)