Files
tidb/expression/subquery/subquery.go
2015-09-23 10:18:56 +08:00

247 lines
5.9 KiB
Go

// 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 subquery
import (
"fmt"
"strings"
"github.com/juju/errors"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/stmt"
)
// Statement implements stmt.Statement and plan.Planner interface.
type Statement interface {
stmt.Statement
plan.Planner
}
var _ expression.Expression = (*SubQuery)(nil)
// SubQuery expresion holds a select statement.
// TODO: complete according to https://dev.mysql.com/doc/refman/5.7/en/subquery-restrictions.html
type SubQuery struct {
// Stmt is the sub select statement.
Stmt Statement
// Value holds the sub select result.
Val interface{}
// UseOuterQuery represents that whether subquery uses reference to a table for the outer query.
// If use, we cannot cache the sub query result.
UseOuter bool
p plan.Plan
}
// SetValue implements expression.SubQuery interface.
func (sq *SubQuery) SetValue(val interface{}) {
sq.Val = val
}
// Value implements expression.SubQuery interface.
func (sq *SubQuery) Value() interface{} {
return sq.Val
}
// UseOuterQuery implements expression.SubQuery interface.
func (sq *SubQuery) UseOuterQuery() bool {
return sq.UseOuter
}
// Clone implements the Expression Clone interface.
func (sq *SubQuery) Clone() expression.Expression {
nsq := &SubQuery{Stmt: sq.Stmt, Val: sq.Val, p: sq.p, UseOuter: sq.UseOuter}
return nsq
}
// Eval implements the Expression Eval interface.
// Eval doesn't support multi rows return, so we can only get a scalar or a row result.
// If you want to get multi rows, use EvalRows instead.
func (sq *SubQuery) Eval(ctx context.Context, args map[interface{}]interface{}) (v interface{}, err error) {
if !sq.UseOuter && sq.Val != nil {
return sq.Val, nil
}
rows, err := sq.EvalRows(ctx, args, 2)
if err != nil {
return nil, errors.Trace(err)
}
switch len(rows) {
case 0:
return nil, nil
case 1:
sq.Val = rows[0]
return sq.Val, nil
default:
return nil, errors.Errorf("Subquery returns more than 1 row")
}
}
// Accept implements Expression Accept interface.
func (sq *SubQuery) Accept(v expression.Visitor) (expression.Expression, error) {
return v.VisitSubQuery(sq)
}
// IsStatic implements the Expression IsStatic interface, always returns false.
func (sq *SubQuery) IsStatic() bool {
return false
}
// String implements the Expression String interface.
func (sq *SubQuery) String() string {
if sq.Stmt != nil {
stmtStr := strings.TrimSuffix(sq.Stmt.OriginText(), ";")
return fmt.Sprintf("(%s)", stmtStr)
}
return ""
}
// ColumnCount returns column count for the sub query.
func (sq *SubQuery) ColumnCount(ctx context.Context) (int, error) {
p, err := sq.Plan(ctx)
if err != nil {
return 0, errors.Trace(err)
}
return len(p.GetFields()), nil
}
// Plan implements plan.Planner interface.
func (sq *SubQuery) Plan(ctx context.Context) (plan.Plan, error) {
if sq.p != nil {
return sq.p, nil
}
var err error
sq.p, err = sq.Stmt.Plan(ctx)
return sq.p, errors.Trace(err)
}
// EvalRows executes the subquery and returns the multi rows with rowCount.
// rowCount < 0 means no limit.
// If the ColumnCount is 1, we will return a column result like {1, 2, 3},
// otherwise, we will return a table result like {{1, 1}, {2, 2}}.
func (sq *SubQuery) EvalRows(ctx context.Context, args map[interface{}]interface{}, rowCount int) ([]interface{}, error) {
p, err := sq.Plan(ctx)
if err != nil {
return nil, errors.Trace(err)
}
defer p.Close()
sq.Push(ctx)
var (
row *plan.Row
res = []interface{}{}
)
for rowCount != 0 {
row, err = p.Next(ctx)
if err != nil {
break
}
if row == nil {
break
}
if len(row.Data) == 1 {
res = append(res, row.Data[0])
} else {
res = append(res, row.Data)
}
if rowCount > 0 {
rowCount--
}
}
err0 := sq.Pop(ctx)
if err0 != nil {
return res, errors.Wrap(err, err0)
}
return res, errors.Trace(err)
}
// A dummy type to avoid naming collision in context.
type subQueryStackKeyType int
// String defines a Stringer function for debugging and pretty printing.
func (k subQueryStackKeyType) String() string {
return "sub query stack"
}
// SubQueryStackKey holds the running sub query's stack.
const SubQueryStackKey subQueryStackKeyType = 0
// Push pushes this SubQuery to the query stack.
func (sq *SubQuery) Push(ctx context.Context) {
var st []*SubQuery
v := ctx.Value(SubQueryStackKey)
if v == nil {
st = []*SubQuery{}
} else {
// must ok
st = v.([]*SubQuery)
}
st = append(st, sq)
ctx.SetValue(SubQueryStackKey, st)
}
// Pop pops this SubQuery off the query stack.
func (sq *SubQuery) Pop(ctx context.Context) error {
v := ctx.Value(SubQueryStackKey)
if v == nil {
return errors.Errorf("pop empty sub query stack")
}
st := v.([]*SubQuery)
// can not empty
n := len(st) - 1
if st[n] != sq {
return errors.Errorf("pop invalid top sub query in stack, want %v, but top is %v", sq, st[n])
}
st[n] = nil
st = st[0:n]
if len(st) == 0 {
ctx.ClearValue(SubQueryStackKey)
return nil
}
ctx.SetValue(SubQueryStackKey, st)
return nil
}
// SetOuterQueryUsed is called when current running subquery uses outer query.
func SetOuterQueryUsed(ctx context.Context) {
v := ctx.Value(SubQueryStackKey)
if v == nil {
return
}
st := v.([]*SubQuery)
// if current sub query uses outer query, the select result can not be cached,
// at the same time, all the upper sub query must not cache the result too.
for i := len(st) - 1; i >= 0; i-- {
st[i].UseOuter = true
}
}