Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom row exec #2593

Merged
merged 10 commits into from
Jul 23, 2024
188 changes: 188 additions & 0 deletions enginetest/join_op_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,194 @@ var DefaultJoinOpTests = []joinOpTest{
},
},
},
{
name: "keyless lookup join indexes",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table xy (x int, y int, z int, index y_idx(y));",
"CREATE table ab (a int primary key, b int, c int);",
"insert into xy values (1,0,3), (1,0,3), (0,2,1),(0,2,1);",
"insert into ab values (0,1,1), (1,2,2), (2,3,3), (3,2,2);",
},
},
tests: []JoinOpTests{
// covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a from xy join ab on y = a",
Expected: []sql.Row{{0, 0}, {0, 0}, {2, 2}, {2, 2}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a from xy join ab on y = a",
Expected: []sql.Row{{0, 0}, {0, 0}, {2, 2}, {2, 2}},
},
// covering indexed source
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2}, {2, 2}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2}, {2, 2}},
},
// non-covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,z from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 3}, {0, 0, 3}, {2, 2, 1}, {2, 2, 1}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a,z from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 3}, {0, 0, 3}, {2, 2, 1}, {2, 2, 1}},
},
// non-covering indexed source
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,z from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2, 1}, {2, 2, 1}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a,z from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2, 1}, {2, 2, 1}},
},
},
},
{
name: "keyed null lookup join indexes",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table xy (x int, y int, z int primary key, index y_idx(y));",
"CREATE table ab (a int, b int primary key, c int);",
"insert into xy values (1,0,0), (1,null,1), (0,2,2),(0,2,3);",
"insert into ab values (0,1,0), (1,2,1), (2,3,2), (null,4,3);",
},
},
tests: []JoinOpTests{
// non-covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,x from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 1}, {2, 2, 0}, {2, 2, 0}},
},
// covering
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,z from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 0}, {2, 2, 2}, {2, 2, 3}},
},
},
},
{
name: "partial key null lookup join indexes",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table xy (x int, y int, z int primary key, index y_idx(y,x));",
"CREATE table ab (a int, b int primary key, c int);",
"insert into xy values (1,0,0), (1,null,1), (0,2,2),(0,2,3);",
"insert into ab values (0,1,0), (1,2,1), (2,3,2), (null,4,3);",
},
},
tests: []JoinOpTests{
// non-covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,x from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 1}, {2, 2, 0}, {2, 2, 0}},
},
// covering
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,z from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 0}, {2, 2, 2}, {2, 2, 3}},
},
},
},
{
name: "keyed lookup join indexes",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table xy (x int, y int, z int primary key, index y_idx(y));",
"CREATE table ab (a int, b int primary key, c int);",
"insert into xy values (1,0,0), (1,0,1), (0,2,2),(0,2,3);",
"insert into ab values (0,1,0), (1,2,1), (2,3,2), (3,4,3);",
},
},
tests: []JoinOpTests{
// covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a from xy join ab on y = a",
Expected: []sql.Row{{0, 0}, {0, 0}, {2, 2}, {2, 2}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a from xy join ab on y = a",
Expected: []sql.Row{{0, 0}, {0, 0}, {2, 2}, {2, 2}},
},
// covering indexed source
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2}, {2, 2}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2}, {2, 2}},
},
// non-covering tablescan
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,x from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 1}, {0, 0, 1}, {2, 2, 0}, {2, 2, 0}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a,x from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 1}, {0, 0, 1}, {2, 2, 0}, {2, 2, 0}},
},
// non-covering indexed source
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,x from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2, 0}, {2, 2, 0}},
},
{
Query: "select /*+ JOIN_ORDER(xy,ab) */ y,a,x from xy join ab on y = a where y = 2",
Expected: []sql.Row{{2, 2, 0}, {2, 2, 0}},
},
},
},
{
name: "multi pk lookup join indexes",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table wxyz (w int, x int, y int, z int, primary key (x,w), index yw_idx(y,w));",
"CREATE table abcd (a int, b int, c int, d int, primary key (a,b), index ca_idx(c,a));",
"insert into wxyz values (1,0,0,0), (1,1,1,1), (0,2,2,1),(0,1,3,1);",
"insert into abcd values (0,0,0,0), (0,1,1,1), (0,2,2,1),(2,1,3,1);",
},
},
tests: []JoinOpTests{
{
Query: "select /*+ JOIN_ORDER(abcd,wxyz) */ y,a,z from wxyz join abcd on y = a",
Expected: []sql.Row{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {2, 2, 1}},
},
{
Query: "select /*+ JOIN_ORDER(abcd,wxyz) */ y,a,w from wxyz join abcd on y = a",
Expected: []sql.Row{{0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {2, 2, 0}},
},
},
},
{
name: "redundant keyless index",
setup: [][]string{
setup.MydbData[0],
{
"CREATE table xy (x int, y int, z int, index y_idx(x,y,z));",
"CREATE table ab (a int, b int primary key, c int);",
"insert into xy values (1,0,0), (1,0,1), (0,2,2),(0,2,3);",
"insert into ab values (0,1,0), (1,2,1), (2,3,2), (3,4,3);",
},
},
tests: []JoinOpTests{
{
Query: "select /*+ JOIN_ORDER(ab,xy) */ y,a,z from xy join ab on y = a",
Expected: []sql.Row{{0, 0, 1}, {0, 0, 0}, {2, 2, 3}, {2, 2, 2}},
},
},
},
{
name: "issue 5633, nil comparison in merge join",
setup: [][]string{
Expand Down
25 changes: 25 additions & 0 deletions sql/plan/indexed_table_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,31 @@ func (i *IndexedTableAccess) CanBuildIndex(ctx *sql.Context) (bool, error) {
return err == nil && !lookup.IsEmpty(), nil
}

func (i *IndexedTableAccess) IsStrictLookup() bool {
if !i.lb.index.IsUnique() {
return false
}
for _, m := range i.lb.matchesNullMask {
if m {
return false
}
}
if len(i.lb.keyExprs) != len(i.lb.index.Expressions()) {
// only partial key
return false
}
if strings.EqualFold(i.lb.index.ID(), "primary") {
return true
}
for _, e := range i.lb.keyExprs {
if e.IsNullable() {
// nullable key may not be
return false
}
}
return true
}

func (i *IndexedTableAccess) GetLookup(ctx *sql.Context, row sql.Row) (sql.IndexLookup, error) {
// if the lookup was provided at analysis time (static evaluation), use it.
if !i.lookup.IsEmpty() {
Expand Down
9 changes: 8 additions & 1 deletion sql/rowexec/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ type ExecBuilderFunc func(ctx *sql.Context, n sql.Node, r sql.Row) (sql.RowIter,
// BaseBuilder converts a plan tree into a RowIter tree. All relational nodes
// have a build statement. Custom source nodes that provide rows that implement
// sql.ExecSourceRel are also built into the tree.
type BaseBuilder struct{}
type BaseBuilder struct {
// if override is provided, we try to build executor with this first
override sql.NodeExecBuilder
}

func (b *BaseBuilder) Build(ctx *sql.Context, n sql.Node, r sql.Row) (sql.RowIter, error) {
defer trace.StartRegion(ctx, "ExecBuilder.Build").End()
return b.buildNodeExec(ctx, n, r)
}

func NewOverrideBuilder(override sql.NodeExecBuilder) sql.NodeExecBuilder {
return &BaseBuilder{override: override}
}
2 changes: 2 additions & 0 deletions sql/rowexec/dml.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ func (b *BaseBuilder) buildTriggerBeginEndBlock(ctx *sql.Context, n *plan.Trigge
statements: n.Children(),
row: row,
once: &sync.Once{},
b: b,
}, nil
}

Expand All @@ -308,6 +309,7 @@ func (b *BaseBuilder) buildTriggerExecutor(ctx *sql.Context, n *plan.TriggerExec
triggerEvent: n.TriggerEvent,
executionLogic: n.Right(),
ctx: ctx,
b: b,
}, nil
}

Expand Down
12 changes: 11 additions & 1 deletion sql/rowexec/node_builder.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ import (
)

func (b *BaseBuilder) buildNodeExec(ctx *sql.Context, n sql.Node, row sql.Row) (sql.RowIter, error) {
iter, err := b.buildNodeExecNoAnalyze(ctx, n, row)
var iter sql.RowIter
var err error
if b.override != nil {
iter, err = b.override.Build(ctx, n, row)
}
if err != nil {
return nil, err
}
if iter == nil {
iter, err = b.buildNodeExecNoAnalyze(ctx, n, row)
}
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion sql/rowexec/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func (b *BaseBuilder) buildElseCaseError(ctx *sql.Context, n plan.ElseCaseError,
}

func (b *BaseBuilder) buildOpen(ctx *sql.Context, n *plan.Open, row sql.Row) (sql.RowIter, error) {
return &openIter{pRef: n.Pref, name: n.Name, row: row}, nil
return &openIter{pRef: n.Pref, name: n.Name, row: row, b: b}, nil
}

func (b *BaseBuilder) buildClose(ctx *sql.Context, n *plan.Close, row sql.Row) (sql.RowIter, error) {
Expand Down