Files
tidb/plan/plans/from.go
qiuyesuifeng 4aeaeada1d *: clean up
2015-10-20 12:19:06 +08:00

329 lines
8.7 KiB
Go

// Copyright 2014 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.
// 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 (
"strings"
"github.com/juju/errors"
"github.com/pingcap/tidb/column"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/field"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/parser/opcode"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/table"
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/format"
"github.com/pingcap/tidb/util/types"
)
var (
_ plan.Plan = (*TableDefaultPlan)(nil)
_ plan.Plan = (*TableNilPlan)(nil)
)
// TableNilPlan iterates rows but does nothing, e.g. SELECT 1 FROM t;
type TableNilPlan struct {
T table.Table
iter kv.Iterator
}
// Explain implements the plan.Plan interface.
func (r *TableNilPlan) Explain(w format.Formatter) {
w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.T.TableName(), field.RFQNames(r.GetFields()))
}
// GetFields implements the plan.Plan interface.
func (r *TableNilPlan) GetFields() []*field.ResultField {
return []*field.ResultField{}
}
// Filter implements the plan.Plan Filter interface.
func (r *TableNilPlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
return r, false, nil
}
// Next implements plan.Plan Next interface.
func (r *TableNilPlan) Next(ctx context.Context) (row *plan.Row, err error) {
if r.iter == nil {
var txn kv.Transaction
txn, err = ctx.GetTxn(false)
if err != nil {
return nil, errors.Trace(err)
}
r.iter, err = txn.Seek([]byte(r.T.FirstKey()))
if err != nil {
return nil, errors.Trace(err)
}
}
if !r.iter.Valid() || !strings.HasPrefix(r.iter.Key(), r.T.KeyPrefix()) {
return
}
handle, err := util.DecodeHandleFromRowKey(r.iter.Key())
if err != nil {
return nil, errors.Trace(err)
}
rk := r.T.RecordKey(handle, nil)
// Even though the data is nil, we should return not nil row,
// or the iteration will stop.
row = &plan.Row{}
r.iter, err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
return
}
// Close implements plan.Plan Close interface.
func (r *TableNilPlan) Close() error {
if r.iter != nil {
r.iter.Close()
}
r.iter = nil
return nil
}
// TableDefaultPlan iterates rows from a table, in general case
// it performs a full table scan, but using Filter function,
// it will return a new IndexPlan if an index is found in Filter function.
type TableDefaultPlan struct {
T table.Table
Fields []*field.ResultField
iter kv.Iterator
}
// Explain implements the plan.Plan Explain interface.
func (r *TableDefaultPlan) Explain(w format.Formatter) {
w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.T.TableName(), field.RFQNames(r.Fields))
}
func (r *TableDefaultPlan) filterBinOp(ctx context.Context, x *expression.BinaryOperation) (plan.Plan, bool, error) {
ok, name, rval, err := x.IsIdentCompareVal()
if err != nil {
return r, false, err
}
if !ok {
return r, false, nil
}
if rval == nil {
// if nil, any <, <=, >, >=, =, != operator will do nothing
// any value compared null returns null
// TODO: if we support <=> later, we must handle null
return &NullPlan{r.GetFields()}, true, nil
}
_, tn, cn := field.SplitQualifiedName(name)
t := r.T
if tn != "" && tn != t.TableName().L {
return r, false, nil
}
c := column.FindCol(t.Cols(), cn)
if c == nil {
return nil, false, errors.Errorf("No such column: %s", cn)
}
ix := t.FindIndexByColName(cn)
if ix == nil { // Column cn has no index.
return r, false, nil
}
var seekVal interface{}
if seekVal, err = types.Convert(rval, &c.FieldType); err != nil {
return nil, false, err
}
return &indexPlan{
src: t,
col: c,
idxName: ix.Name.O,
idx: ix.X,
spans: toSpans(x.Op, rval, seekVal),
}, true, nil
}
func (r *TableDefaultPlan) filterIdent(ctx context.Context, x *expression.Ident, trueValue bool) (plan.Plan, bool, error) { //TODO !ident
t := r.T
for _, v := range t.Cols() {
if x.L != v.Name.L {
continue
}
xi := v.Offset
if xi >= len(t.Indices()) {
return r, false, nil
}
ix := t.Indices()[xi]
if ix == nil { // Column cn has no index.
return r, false, nil
}
var spans []*indexSpan
if trueValue {
spans = toSpans(opcode.NE, 0, 0)
} else {
spans = toSpans(opcode.EQ, 0, 0)
}
return &indexPlan{
src: t,
col: v,
idxName: ix.Name.L,
idx: ix.X,
spans: spans,
}, true, nil
}
return r, false, nil
}
func (r *TableDefaultPlan) filterIsNull(ctx context.Context, x *expression.IsNull) (plan.Plan, bool, error) {
if _, ok := x.Expr.(*expression.Ident); !ok {
// if expression is not Ident expression, we cannot use index
// e.g, "(x > null) is not null", (x > null) is a binary expression, we must evaluate it first
return r, false, nil
}
cns := expression.MentionedColumns(x.Expr)
if len(cns) == 0 {
return r, false, nil
}
cn := cns[0]
t := r.T
ix := t.FindIndexByColName(cn)
if ix == nil { // Column cn has no index.
return r, false, nil
}
col := column.FindCol(t.Cols(), cn)
var spans []*indexSpan
if x.Not {
spans = toSpans(opcode.GE, minNotNullVal, nil)
} else {
spans = toSpans(opcode.EQ, nil, nil)
}
return &indexPlan{
src: t,
col: col,
idxName: ix.Name.L,
idx: ix.X,
spans: spans,
}, true, nil
}
// FilterForUpdateAndDelete is for updating and deleting (without checking return
// columns), in order to check whether if we can use IndexPlan or not.
func (r *TableDefaultPlan) FilterForUpdateAndDelete(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
// disable column check
return r.filter(ctx, expr, false)
}
// Filter implements plan.Plan Filter interface.
func (r *TableDefaultPlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
return r.filter(ctx, expr, true)
}
func (r *TableDefaultPlan) filter(ctx context.Context, expr expression.Expression, checkColumns bool) (plan.Plan, bool, error) {
if checkColumns {
colNames := expression.MentionedColumns(expr)
// make sure all mentioned column names are in Fields
// if not, e.g. the expr has two table like t1.c1 = t2.c2, we can't use filter
if !field.ContainAllFieldNames(colNames, r.Fields) {
return r, false, nil
}
}
switch x := expr.(type) {
case *expression.BinaryOperation:
return r.filterBinOp(ctx, x)
case *expression.Ident:
return r.filterIdent(ctx, x, true)
case *expression.IsNull:
return r.filterIsNull(ctx, x)
case *expression.UnaryOperation:
if x.Op != '!' {
break
}
if operand, ok := x.V.(*expression.Ident); ok {
return r.filterIdent(ctx, operand, false)
}
}
return r, false, nil
}
// GetFields implements the plan.Plan GetFields interface.
func (r *TableDefaultPlan) GetFields() []*field.ResultField {
return r.Fields
}
// Next implements plan.Plan Next interface.
func (r *TableDefaultPlan) Next(ctx context.Context) (row *plan.Row, err error) {
if r.iter == nil {
var txn kv.Transaction
txn, err = ctx.GetTxn(false)
if err != nil {
return nil, errors.Trace(err)
}
r.iter, err = txn.Seek([]byte(r.T.FirstKey()))
if err != nil {
return nil, errors.Trace(err)
}
}
if !r.iter.Valid() || !strings.HasPrefix(r.iter.Key(), r.T.KeyPrefix()) {
return
}
// TODO: check if lock valid
// the record layout in storage (key -> value):
// r1 -> lock-version
// r1_col1 -> r1 col1 value
// r1_col2 -> r1 col2 value
// r2 -> lock-version
// r2_col1 -> r2 col1 value
// r2_col2 -> r2 col2 value
// ...
rowKey := r.iter.Key()
handle, err := util.DecodeHandleFromRowKey(rowKey)
if err != nil {
return nil, errors.Trace(err)
}
// TODO: we could just fetch mentioned columns' values
row = &plan.Row{}
row.Data, err = r.T.Row(ctx, handle)
if err != nil {
return nil, errors.Trace(err)
}
// Put rowKey to the tail of record row
rke := &plan.RowKeyEntry{
Tbl: r.T,
Key: rowKey,
}
row.RowKeys = append(row.RowKeys, rke)
rk := r.T.RecordKey(handle, nil)
r.iter, err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
if err != nil {
return nil, errors.Trace(err)
}
return
}
// Close implements plan.Plan Close interface.
func (r *TableDefaultPlan) Close() error {
if r.iter != nil {
r.iter.Close()
r.iter = nil
}
return nil
}