*: support outer query for correlated sub query.

This commit is contained in:
siddontang
2015-09-16 12:05:42 +08:00
parent 60862da3d1
commit bee6991a78
10 changed files with 344 additions and 10 deletions

View File

@ -57,7 +57,13 @@ type Planner interface {
// Row represents a record row.
type Row struct {
Data []interface{}
// Data is the output record data for current Plan.
Data []interface{}
DataFields []*field.ResultField
// FromData is the first origin record data, generated by From.
FromData []interface{}
FromDataFields []*field.ResultField
RowKeys []*RowKeyEntry
}

View File

@ -67,7 +67,12 @@ func (r *SelectFieldsDefaultPlan) Next(ctx context.Context) (row *plan.Row, err
return nil, errors.Trace(err)
}
r.evalArgs[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.Src.GetFields(), srcRow.Data, field.DefaultFieldFlag)
v, err0 := GetIdentValue(name, r.Src.GetFields(), srcRow.Data, field.DefaultFieldFlag)
if err0 == nil {
return v, nil
}
return getIdentValueFromOuterQuery(ctx, name)
}
row = &plan.Row{
Data: make([]interface{}, len(r.Fields)),

View File

@ -96,7 +96,13 @@ func (r *GroupByDefaultPlan) evalGroupKey(ctx context.Context, k []interface{},
return v, nil
}
return r.getFieldValueByName(name, outRow)
v, err = r.getFieldValueByName(name, outRow)
if err == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
m[expressions.ExprEvalPositionFunc] = func(position int) (interface{}, error) {
@ -128,7 +134,13 @@ func (r *GroupByDefaultPlan) getFieldValueByName(name string, out []interface{})
func (r *GroupByDefaultPlan) evalNoneAggFields(ctx context.Context, out []interface{},
m map[interface{}]interface{}, in []interface{}) error {
m[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.Src.GetFields(), in, field.DefaultFieldFlag)
v, err := GetIdentValue(name, r.Src.GetFields(), in, field.DefaultFieldFlag)
if err == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
var err error
@ -150,7 +162,13 @@ func (r *GroupByDefaultPlan) evalAggFields(ctx context.Context, out []interface{
for i := range r.AggFields {
if i < r.HiddenFieldOffset {
m[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.Src.GetFields(), in, field.DefaultFieldFlag)
v, err := GetIdentValue(name, r.Src.GetFields(), in, field.DefaultFieldFlag)
if err == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
} else {
// having may contain aggregate function and we will add it to hidden field,
@ -166,7 +184,13 @@ func (r *GroupByDefaultPlan) evalAggFields(ctx context.Context, out []interface{
// if we can not find in table, we will try to find in un-hidden select list
// only hidden field can use this
return r.getFieldValueByName(name, out)
v, err = r.getFieldValueByName(name, out)
if err == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
}
@ -313,7 +337,7 @@ func (r *GroupByDefaultPlan) fetchAll(ctx context.Context) error {
if err != nil || out == nil {
return errors.Trace(err)
}
row := &plan.Row{Data: out}
row := &plan.Row{Data: out, DataFields: r.ResultFields}
r.rows = append(r.rows, &groupRow{Row: row, Args: map[interface{}]interface{}{}})
} else {
for _, row := range r.rows {

View File

@ -63,7 +63,13 @@ func (r *HavingPlan) Next(ctx context.Context) (row *plan.Row, err error) {
return nil, errors.Trace(err)
}
r.evalArgs[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.Src.GetFields(), srcRow.Data, field.CheckFieldFlag)
v, err0 := GetIdentValue(name, r.Src.GetFields(), srcRow.Data, field.CheckFieldFlag)
if err0 == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
r.evalArgs[expressions.ExprEvalPositionFunc] = func(position int) (interface{}, error) {
// position is in [1, len(fields)], so we must decrease 1 to get correct index

View File

@ -158,7 +158,13 @@ func (r *OrderByDefaultPlan) fetchAll(ctx context.Context) error {
break
}
evalArgs[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.ResultFields, row.Data, field.CheckFieldFlag)
v, err := GetIdentValue(name, r.ResultFields, row.Data, field.CheckFieldFlag)
if err == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
evalArgs[expressions.ExprEvalPositionFunc] = func(position int) (interface{}, error) {

186
plan/plans/outer_query.go Normal file
View File

@ -0,0 +1,186 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package plans
import (
"github.com/juju/errors"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/field"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/util/format"
)
// Phase type for plan.
const (
FromPhase = iota + 1
WherePhase
LockPhase
GroupByPhase
HavingPhase
SelectFieldsPhase
DistinctPhase
OrderByPhase
LimitPhase
BeforeFinalPhase
FinalPhase
)
// OuterQueryPlan is for subquery fetching value from outer query.
// e.g, select c1 from t where c1 = (select c2 from t2 where t2.c2 = t.c2).
// You can see that the subquery uses outer table t's column in where condition.
type OuterQueryPlan struct {
Src plan.Plan
// SrcPhase is the phase for the src plan.
SrcPhase int
HiddenFieldOffset int
}
// Explain implements the plan.Plan Explain interface.
func (p *OuterQueryPlan) Explain(w format.Formatter) {
p.Src.Explain(w)
}
// GetFields implements the plan.Plan GetFields interface.
func (p *OuterQueryPlan) GetFields() []*field.ResultField {
return p.Src.GetFields()
}
// Filter implements the plan.Plan Filter interface.
func (p *OuterQueryPlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
r, b, err := p.Src.Filter(ctx, expr)
if !b {
return r, false, errors.Trace(err)
}
p.Src = r
return p, true, nil
}
// Next implements the plan.Plan Next interface.
func (p *OuterQueryPlan) Next(ctx context.Context) (*plan.Row, error) {
row, err := p.Src.Next(ctx)
if row == nil || err != nil {
return nil, errors.Trace(err)
}
// We can let subquery fetching outer table data only after the following phase.
switch p.SrcPhase {
case FromPhase:
// Here row.Data is the whole data from table.
// We set it to FromData so that following phase can access the table reference.
row.FromData = row.Data
row.FromDataFields = p.Src.GetFields()
pushOuterQuery(ctx, row)
case WherePhase, LockPhase:
updateTopOuterQuery(ctx, row)
case GroupByPhase, SelectFieldsPhase, HavingPhase, DistinctPhase:
row.DataFields = p.Src.GetFields()[0:p.HiddenFieldOffset]
updateTopOuterQuery(ctx, row)
case BeforeFinalPhase:
popOuterQuery(ctx)
}
return row, nil
}
// Close implements the plan.Plan Close interface.
func (p *OuterQueryPlan) Close() error {
return p.Src.Close()
}
// A dummy type to avoid naming collision in context.
type outerQueryKeyType int
// String defines a Stringer function for debugging and pretty printing.
func (k outerQueryKeyType) String() string {
return "outer query"
}
// outerQueryKey is used to retrive outer table references for sub query.
const outerQueryKey outerQueryKeyType = 0
// outerQuery saves the outer table references.
// For every select, we will push a outerQuery to a stack for inner sub query use,
// and the top outerQuery is for current select.
// e.g, select c1 from t1 where c2 = (select c1 from t2 where t2.c1 = t1.c2 limit 1),
// the "select c1 from t1" is the outer query for the sub query in where phase, we will
// first push a outerQuery to the stack saving the row data for "select c1 from t1", then
// push the second outerQuery to the stack saving the row data for "select c1 from t2".
type outerQuery struct {
// outer is the last outer table reference.
last *outerQuery
row *plan.Row
}
// We will push a outerQuery after the from phase and pop it before the final phase.
// So we can guarantee that there is at least one outerQuery certainly.
func pushOuterQuery(ctx context.Context, row *plan.Row) {
t := &outerQuery{
row: row,
}
last := ctx.Value(outerQueryKey)
if last != nil {
// must be outerQuery
t.last = last.(*outerQuery)
}
ctx.SetValue(outerQueryKey, t)
}
func getOuterQuery(ctx context.Context) *outerQuery {
v := ctx.Value(outerQueryKey)
// Cannot empty and must be outerQuery
t := v.(*outerQuery)
return t
}
func popOuterQuery(ctx context.Context) {
t := getOuterQuery(ctx)
if t.last == nil {
ctx.ClearValue(outerQueryKey)
return
}
ctx.SetValue(outerQueryKey, t.last)
}
func updateTopOuterQuery(ctx context.Context, row *plan.Row) {
t := getOuterQuery(ctx)
t.row = row
}
func getIdentValueFromOuterQuery(ctx context.Context, name string) (interface{}, error) {
t := getOuterQuery(ctx)
// The top is current outerQuery, use its last.
t = t.last
for ; t != nil; t = t.last {
// first try to get from outer table reference.
v, err := GetIdentValue(name, t.row.FromDataFields, t.row.FromData, field.DefaultFieldFlag)
if err == nil {
return v, nil
}
// then try to get from outer select list.
v, err = GetIdentValue(name, t.row.DataFields, t.row.Data, field.FieldNameFlag)
if err == nil {
return v, nil
}
}
return nil, errors.Errorf("can not find value from outer query for field %s", name)
}

View File

@ -58,7 +58,13 @@ func (r *FilterDefaultPlan) Next(ctx context.Context) (row *plan.Row, err error)
return nil, errors.Trace(err)
}
r.evalArgs[expressions.ExprEvalIdentFunc] = func(name string) (interface{}, error) {
return GetIdentValue(name, r.GetFields(), row.Data, field.DefaultFieldFlag)
v, err0 := GetIdentValue(name, r.GetFields(), row.Data, field.DefaultFieldFlag)
if err0 == nil {
return v, nil
}
// try to find in outer query
return getIdentValueFromOuterQuery(ctx, name)
}
var meet bool
meet, err = r.meetCondition(ctx)

37
rset/rsets/outer_query.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package rsets
import (
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/plan/plans"
)
var (
_ plan.Planner = (*OuterQueryRset)(nil)
)
// OuterQueryRset is to generate OuterQueryPlan,
// so that sub query can fetch value from the table reference from outer query.
type OuterQueryRset struct {
Src plan.Plan
SrcPhase int
HiddenFieldOffset int
}
// Plan gets OuterQueryPlan.
func (r *OuterQueryRset) Plan(ctx context.Context) (plan.Plan, error) {
return &plans.OuterQueryPlan{Src: r.Src, SrcPhase: r.SrcPhase, HiddenFieldOffset: r.HiddenFieldOffset}, nil
}

View File

@ -0,0 +1,40 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package rsets
import (
. "github.com/pingcap/check"
"github.com/pingcap/tidb/plan/plans"
)
var _ = Suite(&testOuterQueryRsetSuite{})
type testOuterQueryRsetSuite struct {
r *OuterQueryRset
}
func (s *testOuterQueryRsetSuite) SetUpSuite(c *C) {
names := []string{"id", "name"}
tblPlan := newTestTablePlan(testData, names)
s.r = &OuterQueryRset{Src: tblPlan, HiddenFieldOffset: len(tblPlan.GetFields()), SrcPhase: plans.FromPhase}
}
func (s *testOuterQueryRsetSuite) TestOuterQueryRsetPlan(c *C) {
p, err := s.r.Plan(nil)
c.Assert(err, IsNil)
_, ok := p.(*plans.OuterQueryPlan)
c.Assert(ok, IsTrue)
}

View File

@ -140,11 +140,17 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.FromPhase,
HiddenFieldOffset: 0}).Plan(ctx)
if w := s.Where; w != nil {
r, err = (&rsets.WhereRset{Expr: w.Expr, Src: r}).Plan(ctx)
if err != nil {
return nil, err
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.WherePhase,
HiddenFieldOffset: 0}).Plan(ctx)
}
lock := s.Lock
if variable.IsAutocommit(ctx) {
@ -195,12 +201,17 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
SelectList: selectList}).Plan(ctx); err != nil {
return nil, err
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.SelectFieldsPhase,
HiddenFieldOffset: selectList.HiddenFieldOffset}).Plan(ctx)
default:
if r, err = (&rsets.GroupByRset{By: groupBy,
Src: r,
SelectList: selectList}).Plan(ctx); err != nil {
return nil, err
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.GroupByPhase,
HiddenFieldOffset: selectList.HiddenFieldOffset}).Plan(ctx)
}
if s := s.Having; s != nil {
@ -209,6 +220,8 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
Expr: s.Expr}).Plan(ctx); err != nil {
return nil, err
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.HavingPhase,
HiddenFieldOffset: selectList.HiddenFieldOffset}).Plan(ctx)
}
if s.Distinct {
@ -216,6 +229,8 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
SelectList: selectList}).Plan(ctx); err != nil {
return nil, err
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.DistinctPhase,
HiddenFieldOffset: selectList.HiddenFieldOffset}).Plan(ctx)
}
if s := s.OrderBy; s != nil {
@ -237,6 +252,9 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
}
}
r, _ = (&rsets.OuterQueryRset{Src: r, SrcPhase: plans.BeforeFinalPhase,
HiddenFieldOffset: selectList.HiddenFieldOffset}).Plan(ctx)
if r, err = (&rsets.SelectFinalRset{Src: r,
SelectList: selectList}).Plan(ctx); err != nil {
return nil, err