forked from forgejo/forgejo
Integrate public as bindata optionally (#293)
* Dropped unused codekit config * Integrated dynamic and static bindata for public * Ignore public bindata * Add a general generate make task * Integrated flexible public assets into web command * Updated vendoring, added all missiong govendor deps * Made the linter happy with the bindata and dynamic code * Moved public bindata definition to modules directory * Ignoring the new bindata path now * Updated to the new public modules import path * Updated public bindata command and drop the new prefix
This commit is contained in:
parent
4680c349dd
commit
b6a95a8cb3
691 changed files with 305318 additions and 1272 deletions
120
vendor/github.com/pingcap/tidb/executor/adapter.go
generated
vendored
Normal file
120
vendor/github.com/pingcap/tidb/executor/adapter.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
// 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 executor
|
||||
|
||||
import (
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
)
|
||||
|
||||
// recordSet wraps an executor, implements ast.RecordSet interface
|
||||
type recordSet struct {
|
||||
fields []*ast.ResultField
|
||||
executor Executor
|
||||
}
|
||||
|
||||
func (a *recordSet) Fields() ([]*ast.ResultField, error) {
|
||||
return a.fields, nil
|
||||
}
|
||||
|
||||
func (a *recordSet) Next() (*ast.Row, error) {
|
||||
row, err := a.executor.Next()
|
||||
if err != nil || row == nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
return &ast.Row{Data: row.Data}, nil
|
||||
}
|
||||
|
||||
func (a *recordSet) Close() error {
|
||||
return a.executor.Close()
|
||||
}
|
||||
|
||||
type statement struct {
|
||||
is infoschema.InfoSchema
|
||||
plan plan.Plan
|
||||
}
|
||||
|
||||
func (a *statement) OriginText() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *statement) SetText(text string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (a *statement) IsDDL() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *statement) Exec(ctx context.Context) (ast.RecordSet, error) {
|
||||
b := newExecutorBuilder(ctx, a.is)
|
||||
e := b.build(a.plan)
|
||||
if b.err != nil {
|
||||
return nil, errors.Trace(b.err)
|
||||
}
|
||||
|
||||
if executorExec, ok := e.(*ExecuteExec); ok {
|
||||
err := executorExec.Build()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
e = executorExec.StmtExec
|
||||
}
|
||||
|
||||
if len(e.Fields()) == 0 {
|
||||
// No result fields means no Recordset.
|
||||
defer e.Close()
|
||||
for {
|
||||
row, err := e.Next()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if row == nil {
|
||||
// It's used to insert retry.
|
||||
changeInsertValueForRetry(a.plan, e)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs := e.Fields()
|
||||
for _, f := range fs {
|
||||
if len(f.ColumnAsName.O) == 0 {
|
||||
f.ColumnAsName = f.Column.Name
|
||||
}
|
||||
}
|
||||
return &recordSet{
|
||||
executor: e,
|
||||
fields: fs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func changeInsertValueForRetry(p plan.Plan, e Executor) {
|
||||
if v, ok := p.(*plan.Insert); ok {
|
||||
var insertValue *InsertValues
|
||||
if !v.IsReplace {
|
||||
insertValue = e.(*InsertExec).InsertValues
|
||||
} else {
|
||||
insertValue = e.(*ReplaceExec).InsertValues
|
||||
}
|
||||
v.Columns = insertValue.Columns
|
||||
v.Setlist = insertValue.Setlist
|
||||
if len(v.Setlist) == 0 {
|
||||
v.Lists = insertValue.Lists
|
||||
}
|
||||
}
|
||||
}
|
438
vendor/github.com/pingcap/tidb/executor/builder.go
generated
vendored
Normal file
438
vendor/github.com/pingcap/tidb/executor/builder.go
generated
vendored
Normal file
|
@ -0,0 +1,438 @@
|
|||
// 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 executor
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/column"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
"github.com/pingcap/tidb/parser/opcode"
|
||||
"github.com/pingcap/tidb/sessionctx/autocommit"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
// executorBuilder builds an Executor from a Plan.
|
||||
// The InfoSchema must be the same one used in InfoBinder.
|
||||
type executorBuilder struct {
|
||||
ctx context.Context
|
||||
is infoschema.InfoSchema
|
||||
err error
|
||||
}
|
||||
|
||||
func newExecutorBuilder(ctx context.Context, is infoschema.InfoSchema) *executorBuilder {
|
||||
return &executorBuilder{
|
||||
ctx: ctx,
|
||||
is: is,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) build(p plan.Plan) Executor {
|
||||
switch v := p.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *plan.Aggregate:
|
||||
return b.buildAggregate(v)
|
||||
case *plan.CheckTable:
|
||||
return b.buildCheckTable(v)
|
||||
case *plan.DDL:
|
||||
return b.buildDDL(v)
|
||||
case *plan.Deallocate:
|
||||
return b.buildDeallocate(v)
|
||||
case *plan.Delete:
|
||||
return b.buildDelete(v)
|
||||
case *plan.Distinct:
|
||||
return b.buildDistinct(v)
|
||||
case *plan.Execute:
|
||||
return b.buildExecute(v)
|
||||
case *plan.Explain:
|
||||
return b.buildExplain(v)
|
||||
case *plan.Filter:
|
||||
src := b.build(v.Src())
|
||||
return b.buildFilter(src, v.Conditions)
|
||||
case *plan.Having:
|
||||
return b.buildHaving(v)
|
||||
case *plan.IndexScan:
|
||||
return b.buildIndexScan(v)
|
||||
case *plan.Insert:
|
||||
return b.buildInsert(v)
|
||||
case *plan.JoinInner:
|
||||
return b.buildJoinInner(v)
|
||||
case *plan.JoinOuter:
|
||||
return b.buildJoinOuter(v)
|
||||
case *plan.Limit:
|
||||
return b.buildLimit(v)
|
||||
case *plan.Prepare:
|
||||
return b.buildPrepare(v)
|
||||
case *plan.SelectFields:
|
||||
return b.buildSelectFields(v)
|
||||
case *plan.SelectLock:
|
||||
return b.buildSelectLock(v)
|
||||
case *plan.ShowDDL:
|
||||
return b.buildShowDDL(v)
|
||||
case *plan.Show:
|
||||
return b.buildShow(v)
|
||||
case *plan.Simple:
|
||||
return b.buildSimple(v)
|
||||
case *plan.Sort:
|
||||
return b.buildSort(v)
|
||||
case *plan.TableScan:
|
||||
return b.buildTableScan(v)
|
||||
case *plan.Union:
|
||||
return b.buildUnion(v)
|
||||
case *plan.Update:
|
||||
return b.buildUpdate(v)
|
||||
default:
|
||||
b.err = ErrUnknownPlan.Gen("Unknown Plan %T", p)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildFilter(src Executor, conditions []ast.ExprNode) Executor {
|
||||
if len(conditions) == 0 {
|
||||
return src
|
||||
}
|
||||
return &FilterExec{
|
||||
Src: src,
|
||||
Condition: b.joinConditions(conditions),
|
||||
ctx: b.ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildTableScan(v *plan.TableScan) Executor {
|
||||
table, _ := b.is.TableByID(v.Table.ID)
|
||||
e := &TableScanExec{
|
||||
t: table,
|
||||
fields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
ranges: v.Ranges,
|
||||
seekHandle: math.MinInt64,
|
||||
}
|
||||
return b.buildFilter(e, v.FilterConditions)
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildShowDDL(v *plan.ShowDDL) Executor {
|
||||
return &ShowDDLExec{
|
||||
fields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildCheckTable(v *plan.CheckTable) Executor {
|
||||
return &CheckTableExec{
|
||||
tables: v.Tables,
|
||||
ctx: b.ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildDeallocate(v *plan.Deallocate) Executor {
|
||||
return &DeallocateExec{
|
||||
ctx: b.ctx,
|
||||
Name: v.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildIndexScan(v *plan.IndexScan) Executor {
|
||||
tbl, _ := b.is.TableByID(v.Table.ID)
|
||||
var idx *column.IndexedCol
|
||||
for _, val := range tbl.Indices() {
|
||||
if val.IndexInfo.Name.L == v.Index.Name.L {
|
||||
idx = val
|
||||
break
|
||||
}
|
||||
}
|
||||
e := &IndexScanExec{
|
||||
tbl: tbl,
|
||||
idx: idx,
|
||||
fields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
Desc: v.Desc,
|
||||
valueTypes: make([]*types.FieldType, len(idx.Columns)),
|
||||
}
|
||||
|
||||
for i, ic := range idx.Columns {
|
||||
col := tbl.Cols()[ic.Offset]
|
||||
e.valueTypes[i] = &col.FieldType
|
||||
}
|
||||
|
||||
e.Ranges = make([]*IndexRangeExec, len(v.Ranges))
|
||||
for i, val := range v.Ranges {
|
||||
e.Ranges[i] = b.buildIndexRange(e, val)
|
||||
}
|
||||
return b.buildFilter(e, v.FilterConditions)
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildIndexRange(scan *IndexScanExec, v *plan.IndexRange) *IndexRangeExec {
|
||||
ran := &IndexRangeExec{
|
||||
scan: scan,
|
||||
lowVals: v.LowVal,
|
||||
lowExclude: v.LowExclude,
|
||||
highVals: v.HighVal,
|
||||
highExclude: v.HighExclude,
|
||||
}
|
||||
return ran
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildJoinOuter(v *plan.JoinOuter) *JoinOuterExec {
|
||||
e := &JoinOuterExec{
|
||||
OuterExec: b.build(v.Outer),
|
||||
InnerPlan: v.Inner,
|
||||
fields: v.Fields(),
|
||||
builder: b,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildJoinInner(v *plan.JoinInner) *JoinInnerExec {
|
||||
e := &JoinInnerExec{
|
||||
InnerPlans: v.Inners,
|
||||
innerExecs: make([]Executor, len(v.Inners)),
|
||||
Condition: b.joinConditions(v.Conditions),
|
||||
fields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
builder: b,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) joinConditions(conditions []ast.ExprNode) ast.ExprNode {
|
||||
if len(conditions) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(conditions) == 1 {
|
||||
return conditions[0]
|
||||
}
|
||||
condition := &ast.BinaryOperationExpr{
|
||||
Op: opcode.AndAnd,
|
||||
L: conditions[0],
|
||||
R: b.joinConditions(conditions[1:]),
|
||||
}
|
||||
return condition
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildSelectLock(v *plan.SelectLock) Executor {
|
||||
src := b.build(v.Src())
|
||||
if autocommit.ShouldAutocommit(b.ctx) {
|
||||
// Locking of rows for update using SELECT FOR UPDATE only applies when autocommit
|
||||
// is disabled (either by beginning transaction with START TRANSACTION or by setting
|
||||
// autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
|
||||
return src
|
||||
}
|
||||
e := &SelectLockExec{
|
||||
Src: src,
|
||||
Lock: v.Lock,
|
||||
ctx: b.ctx,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildSelectFields(v *plan.SelectFields) Executor {
|
||||
src := b.build(v.Src())
|
||||
e := &SelectFieldsExec{
|
||||
Src: src,
|
||||
ResultFields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildAggregate(v *plan.Aggregate) Executor {
|
||||
src := b.build(v.Src())
|
||||
e := &AggregateExec{
|
||||
Src: src,
|
||||
ResultFields: v.Fields(),
|
||||
ctx: b.ctx,
|
||||
AggFuncs: v.AggFuncs,
|
||||
GroupByItems: v.GroupByItems,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildHaving(v *plan.Having) Executor {
|
||||
src := b.build(v.Src())
|
||||
return b.buildFilter(src, v.Conditions)
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildSort(v *plan.Sort) Executor {
|
||||
src := b.build(v.Src())
|
||||
e := &SortExec{
|
||||
Src: src,
|
||||
ByItems: v.ByItems,
|
||||
ctx: b.ctx,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildLimit(v *plan.Limit) Executor {
|
||||
src := b.build(v.Src())
|
||||
e := &LimitExec{
|
||||
Src: src,
|
||||
Offset: v.Offset,
|
||||
Count: v.Count,
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildUnion(v *plan.Union) Executor {
|
||||
e := &UnionExec{
|
||||
fields: v.Fields(),
|
||||
Sels: make([]Executor, len(v.Selects)),
|
||||
}
|
||||
for i, sel := range v.Selects {
|
||||
selExec := b.build(sel)
|
||||
e.Sels[i] = selExec
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildDistinct(v *plan.Distinct) Executor {
|
||||
return &DistinctExec{Src: b.build(v.Src())}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildPrepare(v *plan.Prepare) Executor {
|
||||
return &PrepareExec{
|
||||
Ctx: b.ctx,
|
||||
IS: b.is,
|
||||
Name: v.Name,
|
||||
SQLText: v.SQLText,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildExecute(v *plan.Execute) Executor {
|
||||
return &ExecuteExec{
|
||||
Ctx: b.ctx,
|
||||
IS: b.is,
|
||||
Name: v.Name,
|
||||
UsingVars: v.UsingVars,
|
||||
ID: v.ID,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildUpdate(v *plan.Update) Executor {
|
||||
selExec := b.build(v.SelectPlan)
|
||||
return &UpdateExec{ctx: b.ctx, SelectExec: selExec, OrderedList: v.OrderedList}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildDelete(v *plan.Delete) Executor {
|
||||
selExec := b.build(v.SelectPlan)
|
||||
return &DeleteExec{
|
||||
ctx: b.ctx,
|
||||
SelectExec: selExec,
|
||||
Tables: v.Tables,
|
||||
IsMultiTable: v.IsMultiTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildShow(v *plan.Show) Executor {
|
||||
e := &ShowExec{
|
||||
Tp: v.Tp,
|
||||
DBName: model.NewCIStr(v.DBName),
|
||||
Table: v.Table,
|
||||
Column: v.Column,
|
||||
User: v.User,
|
||||
Flag: v.Flag,
|
||||
Full: v.Full,
|
||||
GlobalScope: v.GlobalScope,
|
||||
ctx: b.ctx,
|
||||
is: b.is,
|
||||
fields: v.Fields(),
|
||||
}
|
||||
if e.Tp == ast.ShowGrants && len(e.User) == 0 {
|
||||
e.User = variable.GetSessionVars(e.ctx).User
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildSimple(v *plan.Simple) Executor {
|
||||
switch s := v.Statement.(type) {
|
||||
case *ast.GrantStmt:
|
||||
return b.buildGrant(s)
|
||||
}
|
||||
return &SimpleExec{Statement: v.Statement, ctx: b.ctx}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildInsert(v *plan.Insert) Executor {
|
||||
ivs := &InsertValues{
|
||||
ctx: b.ctx,
|
||||
Columns: v.Columns,
|
||||
Lists: v.Lists,
|
||||
Setlist: v.Setlist,
|
||||
}
|
||||
if v.SelectPlan != nil {
|
||||
ivs.SelectExec = b.build(v.SelectPlan)
|
||||
}
|
||||
// Get Table
|
||||
ts, ok := v.Table.TableRefs.Left.(*ast.TableSource)
|
||||
if !ok {
|
||||
b.err = errors.New("Can not get table")
|
||||
return nil
|
||||
}
|
||||
tn, ok := ts.Source.(*ast.TableName)
|
||||
if !ok {
|
||||
b.err = errors.New("Can not get table")
|
||||
return nil
|
||||
}
|
||||
tableInfo := tn.TableInfo
|
||||
tbl, ok := b.is.TableByID(tableInfo.ID)
|
||||
if !ok {
|
||||
b.err = errors.Errorf("Can not get table %d", tableInfo.ID)
|
||||
return nil
|
||||
}
|
||||
ivs.Table = tbl
|
||||
if v.IsReplace {
|
||||
return b.buildReplace(ivs)
|
||||
}
|
||||
insert := &InsertExec{
|
||||
InsertValues: ivs,
|
||||
OnDuplicate: v.OnDuplicate,
|
||||
Priority: v.Priority,
|
||||
}
|
||||
// fields is used to evaluate values expr.
|
||||
insert.fields = ts.GetResultFields()
|
||||
return insert
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildReplace(vals *InsertValues) Executor {
|
||||
return &ReplaceExec{
|
||||
InsertValues: vals,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildGrant(grant *ast.GrantStmt) Executor {
|
||||
return &GrantExec{
|
||||
ctx: b.ctx,
|
||||
Privs: grant.Privs,
|
||||
ObjectType: grant.ObjectType,
|
||||
Level: grant.Level,
|
||||
Users: grant.Users,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildDDL(v *plan.DDL) Executor {
|
||||
return &DDLExec{Statement: v.Statement, ctx: b.ctx, is: b.is}
|
||||
}
|
||||
|
||||
func (b *executorBuilder) buildExplain(v *plan.Explain) Executor {
|
||||
return &ExplainExec{
|
||||
StmtPlan: v.StmtPlan,
|
||||
fields: v.Fields(),
|
||||
}
|
||||
}
|
60
vendor/github.com/pingcap/tidb/executor/compiler.go
generated
vendored
Normal file
60
vendor/github.com/pingcap/tidb/executor/compiler.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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 executor
|
||||
|
||||
import (
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/optimizer"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
)
|
||||
|
||||
// Compiler compiles an ast.StmtNode to a stmt.Statement.
|
||||
type Compiler struct {
|
||||
}
|
||||
|
||||
// Compile compiles an ast.StmtNode to a stmt.Statement.
|
||||
// If it is supported to use new plan and executer, it optimizes the node to
|
||||
// a plan, and we wrap the plan in an adapter as stmt.Statement.
|
||||
// If it is not supported, the node will be converted to old statement.
|
||||
func (c *Compiler) Compile(ctx context.Context, node ast.StmtNode) (ast.Statement, error) {
|
||||
ast.SetFlag(node)
|
||||
|
||||
is := sessionctx.GetDomain(ctx).InfoSchema()
|
||||
if err := optimizer.Preprocess(node, is, ctx); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
// Validate should be after NameResolve.
|
||||
if err := optimizer.Validate(node, false); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
sb := NewSubQueryBuilder(is)
|
||||
p, err := optimizer.Optimize(ctx, node, sb)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
sa := &statement{
|
||||
is: is,
|
||||
plan: p,
|
||||
}
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
// NewSubQueryBuilder builds and returns a new SubQuery builder.
|
||||
func NewSubQueryBuilder(is infoschema.InfoSchema) plan.SubQueryBuilder {
|
||||
return &subqueryBuilder{is: is}
|
||||
}
|
1157
vendor/github.com/pingcap/tidb/executor/executor.go
generated
vendored
Normal file
1157
vendor/github.com/pingcap/tidb/executor/executor.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
205
vendor/github.com/pingcap/tidb/executor/executor_ddl.go
generated
vendored
Normal file
205
vendor/github.com/pingcap/tidb/executor/executor_ddl.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/privilege"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
"github.com/pingcap/tidb/terror"
|
||||
)
|
||||
|
||||
// DDLExec represents a DDL executor.
|
||||
type DDLExec struct {
|
||||
Statement ast.StmtNode
|
||||
ctx context.Context
|
||||
is infoschema.InfoSchema
|
||||
done bool
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *DDLExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Execution Next interface.
|
||||
func (e *DDLExec) Next() (*Row, error) {
|
||||
if e.done {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
switch x := e.Statement.(type) {
|
||||
case *ast.TruncateTableStmt:
|
||||
err = e.executeTruncateTable(x)
|
||||
case *ast.CreateDatabaseStmt:
|
||||
err = e.executeCreateDatabase(x)
|
||||
case *ast.CreateTableStmt:
|
||||
err = e.executeCreateTable(x)
|
||||
case *ast.CreateIndexStmt:
|
||||
err = e.executeCreateIndex(x)
|
||||
case *ast.DropDatabaseStmt:
|
||||
err = e.executeDropDatabase(x)
|
||||
case *ast.DropTableStmt:
|
||||
err = e.executeDropTable(x)
|
||||
case *ast.DropIndexStmt:
|
||||
err = e.executeDropIndex(x)
|
||||
case *ast.AlterTableStmt:
|
||||
err = e.executeAlterTable(x)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
e.done = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *DDLExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeTruncateTable(s *ast.TruncateTableStmt) error {
|
||||
table, ok := e.is.TableByID(s.Table.TableInfo.ID)
|
||||
if !ok {
|
||||
return errors.New("table not found, should never happen")
|
||||
}
|
||||
return table.Truncate(e.ctx)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeCreateDatabase(s *ast.CreateDatabaseStmt) error {
|
||||
var opt *ast.CharsetOpt
|
||||
if len(s.Options) != 0 {
|
||||
opt = &ast.CharsetOpt{}
|
||||
for _, val := range s.Options {
|
||||
switch val.Tp {
|
||||
case ast.DatabaseOptionCharset:
|
||||
opt.Chs = val.Value
|
||||
case ast.DatabaseOptionCollate:
|
||||
opt.Col = val.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().CreateSchema(e.ctx, model.NewCIStr(s.Name), opt)
|
||||
if err != nil {
|
||||
if terror.ErrorEqual(err, infoschema.DatabaseExists) && s.IfNotExists {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeCreateTable(s *ast.CreateTableStmt) error {
|
||||
ident := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().CreateTable(e.ctx, ident, s.Cols, s.Constraints, s.Options)
|
||||
if terror.ErrorEqual(err, infoschema.TableExists) {
|
||||
if s.IfNotExists {
|
||||
return nil
|
||||
}
|
||||
return infoschema.TableExists.Gen("CREATE TABLE: table exists %s", ident)
|
||||
}
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeCreateIndex(s *ast.CreateIndexStmt) error {
|
||||
ident := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().CreateIndex(e.ctx, ident, s.Unique, model.NewCIStr(s.IndexName), s.IndexColNames)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeDropDatabase(s *ast.DropDatabaseStmt) error {
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().DropSchema(e.ctx, model.NewCIStr(s.Name))
|
||||
if terror.ErrorEqual(err, infoschema.DatabaseNotExists) {
|
||||
if s.IfExists {
|
||||
err = nil
|
||||
} else {
|
||||
err = infoschema.DatabaseDropExists.Gen("Can't drop database '%s'; database doesn't exist", s.Name)
|
||||
}
|
||||
}
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeDropTable(s *ast.DropTableStmt) error {
|
||||
var notExistTables []string
|
||||
for _, tn := range s.Tables {
|
||||
fullti := ast.Ident{Schema: tn.Schema, Name: tn.Name}
|
||||
schema, ok := e.is.SchemaByName(tn.Schema)
|
||||
if !ok {
|
||||
// TODO: we should return special error for table not exist, checking "not exist" is not enough,
|
||||
// because some other errors may contain this error string too.
|
||||
notExistTables = append(notExistTables, fullti.String())
|
||||
continue
|
||||
}
|
||||
tb, err := e.is.TableByName(tn.Schema, tn.Name)
|
||||
if err != nil && strings.HasSuffix(err.Error(), "not exist") {
|
||||
notExistTables = append(notExistTables, fullti.String())
|
||||
continue
|
||||
} else if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
// Check Privilege
|
||||
privChecker := privilege.GetPrivilegeChecker(e.ctx)
|
||||
hasPriv, err := privChecker.Check(e.ctx, schema, tb.Meta(), mysql.DropPriv)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if !hasPriv {
|
||||
return errors.Errorf("You do not have the privilege to drop table %s.%s.", tn.Schema, tn.Name)
|
||||
}
|
||||
|
||||
err = sessionctx.GetDomain(e.ctx).DDL().DropTable(e.ctx, fullti)
|
||||
if infoschema.DatabaseNotExists.Equal(err) || infoschema.TableNotExists.Equal(err) {
|
||||
notExistTables = append(notExistTables, fullti.String())
|
||||
} else if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
if len(notExistTables) > 0 && !s.IfExists {
|
||||
return infoschema.TableDropExists.Gen("DROP TABLE: table %s does not exist", strings.Join(notExistTables, ","))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeDropIndex(s *ast.DropIndexStmt) error {
|
||||
ti := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().DropIndex(e.ctx, ti, model.NewCIStr(s.IndexName))
|
||||
if (infoschema.DatabaseNotExists.Equal(err) || infoschema.TableNotExists.Equal(err)) && s.IfExists {
|
||||
err = nil
|
||||
}
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *DDLExec) executeAlterTable(s *ast.AlterTableStmt) error {
|
||||
ti := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}
|
||||
err := sessionctx.GetDomain(e.ctx).DDL().AlterTable(e.ctx, ti, s.Specs)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func joinColumnName(columnName *ast.ColumnName) string {
|
||||
var originStrs []string
|
||||
if columnName.Schema.O != "" {
|
||||
originStrs = append(originStrs, columnName.Schema.O)
|
||||
}
|
||||
if columnName.Table.O != "" {
|
||||
originStrs = append(originStrs, columnName.Table.O)
|
||||
}
|
||||
originStrs = append(originStrs, columnName.Name.O)
|
||||
return strings.Join(originStrs, ".")
|
||||
}
|
279
vendor/github.com/pingcap/tidb/executor/executor_simple.go
generated
vendored
Normal file
279
vendor/github.com/pingcap/tidb/executor/executor_simple.go
generated
vendored
Normal file
|
@ -0,0 +1,279 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/evaluator"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
"github.com/pingcap/tidb/sessionctx/db"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
"github.com/pingcap/tidb/util"
|
||||
"github.com/pingcap/tidb/util/charset"
|
||||
"github.com/pingcap/tidb/util/sqlexec"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
// SimpleExec represents simple statement executor.
|
||||
// For statements do simple execution.
|
||||
// includes `UseStmt`, 'SetStmt`, `SetCharsetStmt`.
|
||||
// `DoStmt`, `BeginStmt`, `CommitStmt`, `RollbackStmt`.
|
||||
// TODO: list all simple statements.
|
||||
type SimpleExec struct {
|
||||
Statement ast.StmtNode
|
||||
ctx context.Context
|
||||
done bool
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *SimpleExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Execution Next interface.
|
||||
func (e *SimpleExec) Next() (*Row, error) {
|
||||
if e.done {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
switch x := e.Statement.(type) {
|
||||
case *ast.UseStmt:
|
||||
err = e.executeUse(x)
|
||||
case *ast.SetStmt:
|
||||
err = e.executeSet(x)
|
||||
case *ast.SetCharsetStmt:
|
||||
err = e.executeSetCharset(x)
|
||||
case *ast.DoStmt:
|
||||
err = e.executeDo(x)
|
||||
case *ast.BeginStmt:
|
||||
err = e.executeBegin(x)
|
||||
case *ast.CommitStmt:
|
||||
err = e.executeCommit(x)
|
||||
case *ast.RollbackStmt:
|
||||
err = e.executeRollback(x)
|
||||
case *ast.CreateUserStmt:
|
||||
err = e.executeCreateUser(x)
|
||||
case *ast.SetPwdStmt:
|
||||
err = e.executeSetPwd(x)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
e.done = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *SimpleExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeUse(s *ast.UseStmt) error {
|
||||
dbname := model.NewCIStr(s.DBName)
|
||||
dbinfo, exists := sessionctx.GetDomain(e.ctx).InfoSchema().SchemaByName(dbname)
|
||||
if !exists {
|
||||
return infoschema.DatabaseNotExists.Gen("database %s not exists", dbname)
|
||||
}
|
||||
db.BindCurrentSchema(e.ctx, dbname.O)
|
||||
// character_set_database is the character set used by the default database.
|
||||
// The server sets this variable whenever the default database changes.
|
||||
// See: http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_character_set_database
|
||||
sessionVars := variable.GetSessionVars(e.ctx)
|
||||
sessionVars.Systems[variable.CharsetDatabase] = dbinfo.Charset
|
||||
sessionVars.Systems[variable.CollationDatabase] = dbinfo.Collate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeSet(s *ast.SetStmt) error {
|
||||
sessionVars := variable.GetSessionVars(e.ctx)
|
||||
globalVars := variable.GetGlobalVarAccessor(e.ctx)
|
||||
for _, v := range s.Variables {
|
||||
// Variable is case insensitive, we use lower case.
|
||||
name := strings.ToLower(v.Name)
|
||||
if !v.IsSystem {
|
||||
// User variable.
|
||||
value, err := evaluator.Eval(e.ctx, v.Value)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
delete(sessionVars.Users, name)
|
||||
} else {
|
||||
sessionVars.Users[name] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
sysVar := variable.GetSysVar(name)
|
||||
if sysVar == nil {
|
||||
return variable.UnknownSystemVar.Gen("Unknown system variable '%s'", name)
|
||||
}
|
||||
if sysVar.Scope == variable.ScopeNone {
|
||||
return errors.Errorf("Variable '%s' is a read only variable", name)
|
||||
}
|
||||
if v.IsGlobal {
|
||||
if sysVar.Scope&variable.ScopeGlobal > 0 {
|
||||
value, err := evaluator.Eval(e.ctx, v.Value)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if value == nil {
|
||||
value = ""
|
||||
}
|
||||
svalue, err := types.ToString(value)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
err = globalVars.SetGlobalSysVar(e.ctx, name, svalue)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
return errors.Errorf("Variable '%s' is a SESSION variable and can't be used with SET GLOBAL", name)
|
||||
}
|
||||
if sysVar.Scope&variable.ScopeSession > 0 {
|
||||
if value, err := evaluator.Eval(e.ctx, v.Value); err != nil {
|
||||
return errors.Trace(err)
|
||||
} else if value == nil {
|
||||
sessionVars.Systems[name] = ""
|
||||
} else {
|
||||
sessionVars.Systems[name] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("Variable '%s' is a GLOBAL variable and should be set with SET GLOBAL", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeSetCharset(s *ast.SetCharsetStmt) error {
|
||||
collation := s.Collate
|
||||
if len(collation) == 0 {
|
||||
var err error
|
||||
collation, err = charset.GetDefaultCollation(s.Charset)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
sessionVars := variable.GetSessionVars(e.ctx)
|
||||
for _, v := range variable.SetNamesVariables {
|
||||
sessionVars.Systems[v] = s.Charset
|
||||
}
|
||||
sessionVars.Systems[variable.CollationConnection] = collation
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeDo(s *ast.DoStmt) error {
|
||||
for _, expr := range s.Exprs {
|
||||
_, err := evaluator.Eval(e.ctx, expr)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeBegin(s *ast.BeginStmt) error {
|
||||
_, err := e.ctx.GetTxn(true)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
// With START TRANSACTION, autocommit remains disabled until you end
|
||||
// the transaction with COMMIT or ROLLBACK. The autocommit mode then
|
||||
// reverts to its previous state.
|
||||
variable.GetSessionVars(e.ctx).SetStatusFlag(mysql.ServerStatusInTrans, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeCommit(s *ast.CommitStmt) error {
|
||||
err := e.ctx.FinishTxn(false)
|
||||
variable.GetSessionVars(e.ctx).SetStatusFlag(mysql.ServerStatusInTrans, false)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeRollback(s *ast.RollbackStmt) error {
|
||||
err := e.ctx.FinishTxn(true)
|
||||
variable.GetSessionVars(e.ctx).SetStatusFlag(mysql.ServerStatusInTrans, false)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeCreateUser(s *ast.CreateUserStmt) error {
|
||||
users := make([]string, 0, len(s.Specs))
|
||||
for _, spec := range s.Specs {
|
||||
userName, host := parseUser(spec.User)
|
||||
exists, err1 := userExists(e.ctx, userName, host)
|
||||
if err1 != nil {
|
||||
return errors.Trace(err1)
|
||||
}
|
||||
if exists {
|
||||
if !s.IfNotExists {
|
||||
return errors.New("Duplicate user")
|
||||
}
|
||||
continue
|
||||
}
|
||||
pwd := ""
|
||||
if spec.AuthOpt.ByAuthString {
|
||||
pwd = util.EncodePassword(spec.AuthOpt.AuthString)
|
||||
} else {
|
||||
pwd = util.EncodePassword(spec.AuthOpt.HashString)
|
||||
}
|
||||
user := fmt.Sprintf(`("%s", "%s", "%s")`, host, userName, pwd)
|
||||
users = append(users, user)
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return nil
|
||||
}
|
||||
sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, Password) VALUES %s;`, mysql.SystemDB, mysql.UserTable, strings.Join(users, ", "))
|
||||
_, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse user string into username and host
|
||||
// root@localhost -> roor, localhost
|
||||
func parseUser(user string) (string, string) {
|
||||
strs := strings.Split(user, "@")
|
||||
return strs[0], strs[1]
|
||||
}
|
||||
|
||||
func userExists(ctx context.Context, name string, host string) (bool, error) {
|
||||
sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s";`, mysql.SystemDB, mysql.UserTable, name, host)
|
||||
rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
defer rs.Close()
|
||||
row, err := rs.Next()
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
return row != nil, nil
|
||||
}
|
||||
|
||||
func (e *SimpleExec) executeSetPwd(s *ast.SetPwdStmt) error {
|
||||
// TODO: If len(s.User) == 0, use CURRENT_USER()
|
||||
userName, host := parseUser(s.User)
|
||||
// Update mysql.user
|
||||
sql := fmt.Sprintf(`UPDATE %s.%s SET password="%s" WHERE User="%s" AND Host="%s";`, mysql.SystemDB, mysql.UserTable, util.EncodePassword(s.Password), userName, host)
|
||||
_, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
932
vendor/github.com/pingcap/tidb/executor/executor_write.go
generated
vendored
Normal file
932
vendor/github.com/pingcap/tidb/executor/executor_write.go
generated
vendored
Normal file
|
@ -0,0 +1,932 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/column"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/evaluator"
|
||||
"github.com/pingcap/tidb/kv"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
"github.com/pingcap/tidb/table"
|
||||
"github.com/pingcap/tidb/terror"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Executor = &UpdateExec{}
|
||||
_ Executor = &DeleteExec{}
|
||||
_ Executor = &InsertExec{}
|
||||
)
|
||||
|
||||
// UpdateExec represents an update executor.
|
||||
type UpdateExec struct {
|
||||
SelectExec Executor
|
||||
OrderedList []*ast.Assignment
|
||||
|
||||
// Map for unique (Table, handle) pair.
|
||||
updatedRowKeys map[table.Table]map[int64]struct{}
|
||||
ctx context.Context
|
||||
|
||||
rows []*Row // The rows fetched from TableExec.
|
||||
newRowsData [][]types.Datum // The new values to be set.
|
||||
fetched bool
|
||||
cursor int
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *UpdateExec) Next() (*Row, error) {
|
||||
if !e.fetched {
|
||||
err := e.fetchRows()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
e.fetched = true
|
||||
}
|
||||
|
||||
columns, err := getUpdateColumns(e.OrderedList)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if e.cursor >= len(e.rows) {
|
||||
return nil, nil
|
||||
}
|
||||
if e.updatedRowKeys == nil {
|
||||
e.updatedRowKeys = make(map[table.Table]map[int64]struct{})
|
||||
}
|
||||
row := e.rows[e.cursor]
|
||||
newData := e.newRowsData[e.cursor]
|
||||
for _, entry := range row.RowKeys {
|
||||
tbl := entry.Tbl
|
||||
if e.updatedRowKeys[tbl] == nil {
|
||||
e.updatedRowKeys[tbl] = make(map[int64]struct{})
|
||||
}
|
||||
offset := e.getTableOffset(tbl)
|
||||
handle := entry.Handle
|
||||
oldData := row.Data[offset : offset+len(tbl.Cols())]
|
||||
newTableData := newData[offset : offset+len(tbl.Cols())]
|
||||
|
||||
_, ok := e.updatedRowKeys[tbl][handle]
|
||||
if ok {
|
||||
// Each matching row is updated once, even if it matches the conditions multiple times.
|
||||
continue
|
||||
}
|
||||
// Update row
|
||||
err1 := updateRecord(e.ctx, handle, oldData, newTableData, columns, tbl, offset, false)
|
||||
if err1 != nil {
|
||||
return nil, errors.Trace(err1)
|
||||
}
|
||||
e.updatedRowKeys[tbl][handle] = struct{}{}
|
||||
}
|
||||
e.cursor++
|
||||
return &Row{}, nil
|
||||
}
|
||||
|
||||
func getUpdateColumns(assignList []*ast.Assignment) (map[int]*ast.Assignment, error) {
|
||||
m := make(map[int]*ast.Assignment, len(assignList))
|
||||
for i, v := range assignList {
|
||||
m[i] = v
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (e *UpdateExec) fetchRows() error {
|
||||
for {
|
||||
row, err := e.SelectExec.Next()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
data := make([]types.Datum, len(e.SelectExec.Fields()))
|
||||
newData := make([]types.Datum, len(e.SelectExec.Fields()))
|
||||
for i, f := range e.SelectExec.Fields() {
|
||||
data[i] = types.NewDatum(f.Expr.GetValue())
|
||||
newData[i] = data[i]
|
||||
if e.OrderedList[i] != nil {
|
||||
val, err := evaluator.Eval(e.ctx, e.OrderedList[i].Expr)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
newData[i] = types.NewDatum(val)
|
||||
}
|
||||
}
|
||||
row.Data = data
|
||||
e.rows = append(e.rows, row)
|
||||
e.newRowsData = append(e.newRowsData, newData)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UpdateExec) getTableOffset(t table.Table) int {
|
||||
fields := e.SelectExec.Fields()
|
||||
i := 0
|
||||
for i < len(fields) {
|
||||
field := fields[i]
|
||||
if field.Table.Name.L == t.Meta().Name.L {
|
||||
return i
|
||||
}
|
||||
i += len(field.Table.Columns)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func updateRecord(ctx context.Context, h int64, oldData, newData []types.Datum, updateColumns map[int]*ast.Assignment, t table.Table, offset int, onDuplicateUpdate bool) error {
|
||||
if err := t.LockRow(ctx, h, false); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
cols := t.Cols()
|
||||
touched := make(map[int]bool, len(cols))
|
||||
|
||||
assignExists := false
|
||||
var newHandle types.Datum
|
||||
for i, asgn := range updateColumns {
|
||||
if asgn == nil {
|
||||
continue
|
||||
}
|
||||
if i < offset || i >= offset+len(cols) {
|
||||
// The assign expression is for another table, not this.
|
||||
continue
|
||||
}
|
||||
|
||||
colIndex := i - offset
|
||||
col := cols[colIndex]
|
||||
if col.IsPKHandleColumn(t.Meta()) {
|
||||
newHandle = newData[i]
|
||||
}
|
||||
if mysql.HasAutoIncrementFlag(col.Flag) {
|
||||
if newData[i].Kind() == types.KindNull {
|
||||
return errors.Errorf("Column '%v' cannot be null", col.Name.O)
|
||||
}
|
||||
val, err := newData[i].ToInt64()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
t.RebaseAutoID(val, true)
|
||||
}
|
||||
|
||||
touched[colIndex] = true
|
||||
assignExists = true
|
||||
}
|
||||
|
||||
// If no assign list for this table, no need to update.
|
||||
if !assignExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check whether new value is valid.
|
||||
if err := column.CastValues(ctx, newData, cols); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
if err := column.CheckNotNull(cols, newData); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// If row is not changed, we should do nothing.
|
||||
rowChanged := false
|
||||
for i := range oldData {
|
||||
if !touched[i] {
|
||||
continue
|
||||
}
|
||||
|
||||
n, err := newData[i].CompareDatum(oldData[i])
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if n != 0 {
|
||||
rowChanged = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !rowChanged {
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS
|
||||
if variable.GetSessionVars(ctx).ClientCapability&mysql.ClientFoundRows > 0 {
|
||||
variable.GetSessionVars(ctx).AddAffectedRows(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if newHandle.Kind() != types.KindNull {
|
||||
err = t.RemoveRecord(ctx, h, oldData)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
_, err = t.AddRecord(ctx, newData)
|
||||
} else {
|
||||
// Update record to new value and update index.
|
||||
err = t.UpdateRecord(ctx, h, oldData, newData, touched)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Record affected rows.
|
||||
if !onDuplicateUpdate {
|
||||
variable.GetSessionVars(ctx).AddAffectedRows(1)
|
||||
} else {
|
||||
variable.GetSessionVars(ctx).AddAffectedRows(2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
// Returns nil to indicate there is no output.
|
||||
func (e *UpdateExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *UpdateExec) Close() error {
|
||||
return e.SelectExec.Close()
|
||||
}
|
||||
|
||||
// DeleteExec represents a delete executor.
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/delete.html
|
||||
type DeleteExec struct {
|
||||
SelectExec Executor
|
||||
|
||||
ctx context.Context
|
||||
Tables []*ast.TableName
|
||||
IsMultiTable bool
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *DeleteExec) Next() (*Row, error) {
|
||||
if e.finished {
|
||||
return nil, nil
|
||||
}
|
||||
defer func() {
|
||||
e.finished = true
|
||||
}()
|
||||
if e.IsMultiTable && len(e.Tables) == 0 {
|
||||
return &Row{}, nil
|
||||
}
|
||||
tblIDMap := make(map[int64]bool, len(e.Tables))
|
||||
// Get table alias map.
|
||||
tblNames := make(map[string]string)
|
||||
|
||||
// Map for unique (Table, handle) pair.
|
||||
rowKeyMap := make(map[table.Table]map[int64]struct{})
|
||||
if e.IsMultiTable {
|
||||
// Delete from multiple tables should consider table ident list.
|
||||
fs := e.SelectExec.Fields()
|
||||
for _, f := range fs {
|
||||
if len(f.TableAsName.L) > 0 {
|
||||
tblNames[f.TableAsName.L] = f.TableName.Name.L
|
||||
} else {
|
||||
tblNames[f.TableName.Name.L] = f.TableName.Name.L
|
||||
}
|
||||
}
|
||||
for _, t := range e.Tables {
|
||||
// Consider DBName.
|
||||
_, ok := tblNames[t.Name.L]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Unknown table '%s' in MULTI DELETE", t.Name.O)
|
||||
}
|
||||
tblIDMap[t.TableInfo.ID] = true
|
||||
}
|
||||
}
|
||||
for {
|
||||
row, err := e.SelectExec.Next()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if row == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, entry := range row.RowKeys {
|
||||
if e.IsMultiTable {
|
||||
tid := entry.Tbl.Meta().ID
|
||||
if _, ok := tblIDMap[tid]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if rowKeyMap[entry.Tbl] == nil {
|
||||
rowKeyMap[entry.Tbl] = make(map[int64]struct{})
|
||||
}
|
||||
rowKeyMap[entry.Tbl][entry.Handle] = struct{}{}
|
||||
}
|
||||
}
|
||||
for t, handleMap := range rowKeyMap {
|
||||
for handle := range handleMap {
|
||||
data, err := t.Row(e.ctx, handle)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
err = e.removeRow(e.ctx, t, handle, data)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (e *DeleteExec) getTable(ctx context.Context, tableName *ast.TableName) (table.Table, error) {
|
||||
return sessionctx.GetDomain(ctx).InfoSchema().TableByName(tableName.Schema, tableName.Name)
|
||||
}
|
||||
|
||||
func (e *DeleteExec) removeRow(ctx context.Context, t table.Table, h int64, data []types.Datum) error {
|
||||
err := t.RemoveRecord(ctx, h, data)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
variable.GetSessionVars(ctx).AddAffectedRows(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
// Returns nil to indicate there is no output.
|
||||
func (e *DeleteExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *DeleteExec) Close() error {
|
||||
return e.SelectExec.Close()
|
||||
}
|
||||
|
||||
// InsertValues is the data to insert.
|
||||
type InsertValues struct {
|
||||
currRow int
|
||||
ctx context.Context
|
||||
SelectExec Executor
|
||||
|
||||
Table table.Table
|
||||
Columns []*ast.ColumnName
|
||||
Lists [][]ast.ExprNode
|
||||
Setlist []*ast.Assignment
|
||||
}
|
||||
|
||||
// InsertExec represents an insert executor.
|
||||
type InsertExec struct {
|
||||
*InsertValues
|
||||
|
||||
OnDuplicate []*ast.Assignment
|
||||
fields []*ast.ResultField
|
||||
|
||||
Priority int
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *InsertExec) Next() (*Row, error) {
|
||||
if e.finished {
|
||||
return nil, nil
|
||||
}
|
||||
cols, err := e.getColumns(e.Table.Cols())
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
txn, err := e.ctx.GetTxn(false)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
toUpdateColumns, err := getOnDuplicateUpdateColumns(e.OnDuplicate, e.Table)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
var rows [][]types.Datum
|
||||
if e.SelectExec != nil {
|
||||
rows, err = e.getRowsSelect(cols)
|
||||
} else {
|
||||
rows, err = e.getRows(cols)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
if len(e.OnDuplicate) == 0 {
|
||||
txn.SetOption(kv.PresumeKeyNotExists, nil)
|
||||
}
|
||||
h, err := e.Table.AddRecord(e.ctx, row)
|
||||
txn.DelOption(kv.PresumeKeyNotExists)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(e.OnDuplicate) == 0 || !terror.ErrorEqual(err, kv.ErrKeyExists) {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if err = e.onDuplicateUpdate(row, h, toUpdateColumns); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
e.finished = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
// Returns nil to indicate there is no output.
|
||||
func (e *InsertExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *InsertExec) Close() error {
|
||||
if e.SelectExec != nil {
|
||||
return e.SelectExec.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// There are three types of insert statements:
|
||||
// 1 insert ... values(...) --> name type column
|
||||
// 2 insert ... set x=y... --> set type column
|
||||
// 3 insert ... (select ..) --> name type column
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/insert.html
|
||||
func (e *InsertValues) getColumns(tableCols []*column.Col) ([]*column.Col, error) {
|
||||
var cols []*column.Col
|
||||
var err error
|
||||
|
||||
if len(e.Setlist) > 0 {
|
||||
// Process `set` type column.
|
||||
columns := make([]string, 0, len(e.Setlist))
|
||||
for _, v := range e.Setlist {
|
||||
columns = append(columns, v.Column.Name.O)
|
||||
}
|
||||
|
||||
cols, err = column.FindCols(tableCols, columns)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("INSERT INTO %s: %s", e.Table.Meta().Name.O, err)
|
||||
}
|
||||
|
||||
if len(cols) == 0 {
|
||||
return nil, errors.Errorf("INSERT INTO %s: empty column", e.Table.Meta().Name.O)
|
||||
}
|
||||
} else {
|
||||
// Process `name` type column.
|
||||
columns := make([]string, 0, len(e.Columns))
|
||||
for _, v := range e.Columns {
|
||||
columns = append(columns, v.Name.O)
|
||||
}
|
||||
cols, err = column.FindCols(tableCols, columns)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("INSERT INTO %s: %s", e.Table.Meta().Name.O, err)
|
||||
}
|
||||
|
||||
// If cols are empty, use all columns instead.
|
||||
if len(cols) == 0 {
|
||||
cols = tableCols
|
||||
}
|
||||
}
|
||||
|
||||
// Check column whether is specified only once.
|
||||
err = column.CheckOnce(cols)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
return cols, nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) fillValueList() error {
|
||||
if len(e.Setlist) > 0 {
|
||||
if len(e.Lists) > 0 {
|
||||
return errors.Errorf("INSERT INTO %s: set type should not use values", e.Table)
|
||||
}
|
||||
var l []ast.ExprNode
|
||||
for _, v := range e.Setlist {
|
||||
l = append(l, v.Expr)
|
||||
}
|
||||
e.Lists = append(e.Lists, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) checkValueCount(insertValueCount, valueCount, num int, cols []*column.Col) error {
|
||||
if insertValueCount != valueCount {
|
||||
// "insert into t values (), ()" is valid.
|
||||
// "insert into t values (), (1)" is not valid.
|
||||
// "insert into t values (1), ()" is not valid.
|
||||
// "insert into t values (1,2), (1)" is not valid.
|
||||
// So the value count must be same for all insert list.
|
||||
return errors.Errorf("Column count doesn't match value count at row %d", num+1)
|
||||
}
|
||||
if valueCount == 0 && len(e.Columns) > 0 {
|
||||
// "insert into t (c1) values ()" is not valid.
|
||||
return errors.Errorf("INSERT INTO %s: expected %d value(s), have %d", e.Table.Meta().Name.O, len(e.Columns), 0)
|
||||
} else if valueCount > 0 && valueCount != len(cols) {
|
||||
return errors.Errorf("INSERT INTO %s: expected %d value(s), have %d", e.Table.Meta().Name.O, len(cols), valueCount)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) getColumnDefaultValues(cols []*column.Col) (map[string]types.Datum, error) {
|
||||
defaultValMap := map[string]types.Datum{}
|
||||
for _, col := range cols {
|
||||
if value, ok, err := table.GetColDefaultValue(e.ctx, &col.ColumnInfo); ok {
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
defaultValMap[col.Name.L] = value
|
||||
}
|
||||
}
|
||||
return defaultValMap, nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) getRows(cols []*column.Col) (rows [][]types.Datum, err error) {
|
||||
// process `insert|replace ... set x=y...`
|
||||
if err = e.fillValueList(); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
defaultVals, err := e.getColumnDefaultValues(e.Table.Cols())
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
rows = make([][]types.Datum, len(e.Lists))
|
||||
length := len(e.Lists[0])
|
||||
for i, list := range e.Lists {
|
||||
if err = e.checkValueCount(length, len(list), i, cols); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
e.currRow = i
|
||||
rows[i], err = e.getRow(cols, list, defaultVals)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *InsertValues) getRow(cols []*column.Col, list []ast.ExprNode, defaultVals map[string]types.Datum) ([]types.Datum, error) {
|
||||
vals := make([]types.Datum, len(list))
|
||||
var err error
|
||||
for i, expr := range list {
|
||||
if d, ok := expr.(*ast.DefaultExpr); ok {
|
||||
cn := d.Name
|
||||
if cn != nil {
|
||||
var found bool
|
||||
vals[i], found = defaultVals[cn.Name.L]
|
||||
if !found {
|
||||
return nil, errors.Errorf("default column not found - %s", cn.Name.O)
|
||||
}
|
||||
} else {
|
||||
vals[i] = defaultVals[cols[i].Name.L]
|
||||
}
|
||||
} else {
|
||||
var val interface{}
|
||||
val, err = evaluator.Eval(e.ctx, expr)
|
||||
vals[i].SetValue(val)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return e.fillRowData(cols, vals)
|
||||
}
|
||||
|
||||
func (e *InsertValues) getRowsSelect(cols []*column.Col) ([][]types.Datum, error) {
|
||||
// process `insert|replace into ... select ... from ...`
|
||||
if len(e.SelectExec.Fields()) != len(cols) {
|
||||
return nil, errors.Errorf("Column count %d doesn't match value count %d", len(cols), len(e.SelectExec.Fields()))
|
||||
}
|
||||
var rows [][]types.Datum
|
||||
for {
|
||||
innerRow, err := e.SelectExec.Next()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if innerRow == nil {
|
||||
break
|
||||
}
|
||||
e.currRow = len(rows)
|
||||
row, err := e.fillRowData(cols, innerRow.Data)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) fillRowData(cols []*column.Col, vals []types.Datum) ([]types.Datum, error) {
|
||||
row := make([]types.Datum, len(e.Table.Cols()))
|
||||
marked := make(map[int]struct{}, len(vals))
|
||||
for i, v := range vals {
|
||||
offset := cols[i].Offset
|
||||
row[offset] = v
|
||||
marked[offset] = struct{}{}
|
||||
}
|
||||
err := e.initDefaultValues(row, marked)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if err = column.CastValues(e.ctx, row, cols); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if err = column.CheckNotNull(e.Table.Cols(), row); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (e *InsertValues) initDefaultValues(row []types.Datum, marked map[int]struct{}) error {
|
||||
var rewriteValueCol *column.Col
|
||||
var defaultValueCols []*column.Col
|
||||
for i, c := range e.Table.Cols() {
|
||||
if row[i].Kind() != types.KindNull {
|
||||
// Column value isn't nil and column isn't auto-increment, continue.
|
||||
if !mysql.HasAutoIncrementFlag(c.Flag) {
|
||||
continue
|
||||
}
|
||||
val, err := row[i].ToInt64()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if val != 0 {
|
||||
e.Table.RebaseAutoID(val, true)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If the nil value is evaluated in insert list, we will use nil except auto increment column.
|
||||
if _, ok := marked[i]; ok && !mysql.HasAutoIncrementFlag(c.Flag) && !mysql.HasTimestampFlag(c.Flag) {
|
||||
continue
|
||||
}
|
||||
|
||||
if mysql.HasAutoIncrementFlag(c.Flag) {
|
||||
recordID, err := e.Table.AllocAutoID()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
row[i].SetInt64(recordID)
|
||||
// Notes: incompatible with mysql
|
||||
// MySQL will set last insert id to the first row, as follows:
|
||||
// `t(id int AUTO_INCREMENT, c1 int, PRIMARY KEY (id))`
|
||||
// `insert t (c1) values(1),(2),(3);`
|
||||
// Last insert id will be 1, not 3.
|
||||
variable.GetSessionVars(e.ctx).SetLastInsertID(uint64(recordID))
|
||||
// It's used for retry.
|
||||
rewriteValueCol = c
|
||||
} else {
|
||||
var err error
|
||||
row[i], _, err = table.GetColDefaultValue(e.ctx, &c.ColumnInfo)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
|
||||
defaultValueCols = append(defaultValueCols, c)
|
||||
}
|
||||
if err := column.CastValues(e.ctx, row, defaultValueCols); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// It's used for retry.
|
||||
if rewriteValueCol == nil {
|
||||
return nil
|
||||
}
|
||||
if len(e.Setlist) > 0 {
|
||||
val := &ast.Assignment{
|
||||
Column: &ast.ColumnName{Name: rewriteValueCol.Name},
|
||||
Expr: ast.NewValueExpr(row[rewriteValueCol.Offset].GetValue())}
|
||||
if len(e.Setlist) < rewriteValueCol.Offset+1 {
|
||||
e.Setlist = append(e.Setlist, val)
|
||||
return nil
|
||||
}
|
||||
setlist := make([]*ast.Assignment, 0, len(e.Setlist)+1)
|
||||
setlist = append(setlist, e.Setlist[:rewriteValueCol.Offset]...)
|
||||
setlist = append(setlist, val)
|
||||
e.Setlist = append(setlist, e.Setlist[rewriteValueCol.Offset:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// records the values of each row.
|
||||
vals := make([]ast.ExprNode, len(row))
|
||||
for i, col := range row {
|
||||
vals[i] = ast.NewValueExpr(col.GetValue())
|
||||
}
|
||||
if len(e.Lists) <= e.currRow {
|
||||
e.Lists = append(e.Lists, vals)
|
||||
} else {
|
||||
e.Lists[e.currRow] = vals
|
||||
}
|
||||
|
||||
// records the column name only once.
|
||||
if e.currRow != len(e.Lists)-1 {
|
||||
return nil
|
||||
}
|
||||
if len(e.Columns) < rewriteValueCol.Offset+1 {
|
||||
e.Columns = append(e.Columns, &ast.ColumnName{Name: rewriteValueCol.Name})
|
||||
return nil
|
||||
}
|
||||
cols := make([]*ast.ColumnName, 0, len(e.Columns)+1)
|
||||
cols = append(cols, e.Columns[:rewriteValueCol.Offset]...)
|
||||
cols = append(cols, &ast.ColumnName{Name: rewriteValueCol.Name})
|
||||
e.Columns = append(cols, e.Columns[rewriteValueCol.Offset:]...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InsertExec) onDuplicateUpdate(row []types.Datum, h int64, cols map[int]*ast.Assignment) error {
|
||||
// On duplicate key update the duplicate row.
|
||||
// Evaluate the updated value.
|
||||
// TODO: report rows affected and last insert id.
|
||||
data, err := e.Table.Row(e.ctx, h)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
// For evaluate ValuesExpr
|
||||
// http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values
|
||||
for i, rf := range e.fields {
|
||||
rf.Expr.SetValue(row[i].GetValue())
|
||||
}
|
||||
// Evaluate assignment
|
||||
newData := make([]types.Datum, len(data))
|
||||
for i, c := range row {
|
||||
asgn, ok := cols[i]
|
||||
if !ok {
|
||||
newData[i] = c
|
||||
continue
|
||||
}
|
||||
var val interface{}
|
||||
val, err = evaluator.Eval(e.ctx, asgn.Expr)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
newData[i].SetValue(val)
|
||||
}
|
||||
if err = updateRecord(e.ctx, h, data, newData, cols, e.Table, 0, true); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findColumnByName(t table.Table, name string) (*column.Col, error) {
|
||||
_, tableName, colName := splitQualifiedName(name)
|
||||
if len(tableName) > 0 && tableName != t.Meta().Name.O {
|
||||
return nil, errors.Errorf("unknown field %s.%s", tableName, colName)
|
||||
}
|
||||
|
||||
c := column.FindCol(t.Cols(), colName)
|
||||
if c == nil {
|
||||
return nil, errors.Errorf("unknown field %s", colName)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func getOnDuplicateUpdateColumns(assignList []*ast.Assignment, t table.Table) (map[int]*ast.Assignment, error) {
|
||||
m := make(map[int]*ast.Assignment, len(assignList))
|
||||
|
||||
for _, v := range assignList {
|
||||
col := v.Column
|
||||
c, err := findColumnByName(t, joinQualifiedName("", col.Table.L, col.Name.L))
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
m[c.Offset] = v
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ReplaceExec represents a replace executor.
|
||||
type ReplaceExec struct {
|
||||
*InsertValues
|
||||
Priority int
|
||||
finished bool
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
// Returns nil to indicate there is no output.
|
||||
func (e *ReplaceExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *ReplaceExec) Close() error {
|
||||
if e.SelectExec != nil {
|
||||
return e.SelectExec.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *ReplaceExec) Next() (*Row, error) {
|
||||
if e.finished {
|
||||
return nil, nil
|
||||
}
|
||||
cols, err := e.getColumns(e.Table.Cols())
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
var rows [][]types.Datum
|
||||
if e.SelectExec != nil {
|
||||
rows, err = e.getRowsSelect(cols)
|
||||
} else {
|
||||
rows, err = e.getRows(cols)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
h, err := e.Table.AddRecord(e.ctx, row)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if err != nil && !terror.ErrorEqual(err, kv.ErrKeyExists) {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
|
||||
// While the insertion fails because a duplicate-key error occurs for a primary key or unique index,
|
||||
// a storage engine may perform the REPLACE as an update rather than a delete plus insert.
|
||||
// See: http://dev.mysql.com/doc/refman/5.7/en/replace.html.
|
||||
if err = e.replaceRow(h, row); err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
variable.GetSessionVars(e.ctx).AddAffectedRows(1)
|
||||
}
|
||||
e.finished = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (e *ReplaceExec) replaceRow(handle int64, replaceRow []types.Datum) error {
|
||||
row, err := e.Table.Row(e.ctx, handle)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
isReplace := false
|
||||
touched := make(map[int]bool, len(row))
|
||||
for i, val := range row {
|
||||
v, err1 := val.CompareDatum(replaceRow[i])
|
||||
if err1 != nil {
|
||||
return errors.Trace(err1)
|
||||
}
|
||||
if v != 0 {
|
||||
touched[i] = true
|
||||
isReplace = true
|
||||
}
|
||||
}
|
||||
if isReplace {
|
||||
variable.GetSessionVars(e.ctx).AddAffectedRows(1)
|
||||
if err = e.Table.UpdateRecord(e.ctx, handle, row, replaceRow, touched); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SplitQualifiedName splits an identifier name to db, table and field name.
|
||||
func splitQualifiedName(name string) (db string, table string, field string) {
|
||||
seps := strings.Split(name, ".")
|
||||
|
||||
l := len(seps)
|
||||
switch l {
|
||||
case 1:
|
||||
// `name` is field.
|
||||
field = seps[0]
|
||||
case 2:
|
||||
// `name` is `table.field`.
|
||||
table, field = seps[0], seps[1]
|
||||
case 3:
|
||||
// `name` is `db.table.field`.
|
||||
db, table, field = seps[0], seps[1], seps[2]
|
||||
default:
|
||||
// `name` is `db.table.field`.
|
||||
db, table, field = seps[l-3], seps[l-2], seps[l-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// JoinQualifiedName converts db, table, field to a qualified name.
|
||||
func joinQualifiedName(db string, table string, field string) string {
|
||||
if len(db) > 0 {
|
||||
return fmt.Sprintf("%s.%s.%s", db, table, field)
|
||||
} else if len(table) > 0 {
|
||||
return fmt.Sprintf("%s.%s", table, field)
|
||||
} else {
|
||||
return field
|
||||
}
|
||||
}
|
217
vendor/github.com/pingcap/tidb/executor/explain.go
generated
vendored
Normal file
217
vendor/github.com/pingcap/tidb/executor/explain.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
"github.com/pingcap/tidb/parser/opcode"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
type explainEntry struct {
|
||||
ID int64
|
||||
selectType string
|
||||
table string
|
||||
joinType string
|
||||
possibleKeys string
|
||||
key string
|
||||
keyLen string
|
||||
ref string
|
||||
rows int64
|
||||
extra []string
|
||||
}
|
||||
|
||||
func (e *explainEntry) setJoinTypeForTableScan(p *plan.TableScan) {
|
||||
if len(p.AccessConditions) == 0 {
|
||||
e.joinType = "ALL"
|
||||
return
|
||||
}
|
||||
if p.RefAccess {
|
||||
e.joinType = "eq_ref"
|
||||
return
|
||||
}
|
||||
for _, con := range p.AccessConditions {
|
||||
if x, ok := con.(*ast.BinaryOperationExpr); ok {
|
||||
if x.Op == opcode.EQ {
|
||||
e.joinType = "const"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
e.joinType = "range"
|
||||
}
|
||||
|
||||
func (e *explainEntry) setJoinTypeForIndexScan(p *plan.IndexScan) {
|
||||
if len(p.AccessConditions) == 0 {
|
||||
e.joinType = "index"
|
||||
return
|
||||
}
|
||||
if len(p.AccessConditions) == p.AccessEqualCount {
|
||||
if p.RefAccess {
|
||||
if p.Index.Unique {
|
||||
e.joinType = "eq_ref"
|
||||
} else {
|
||||
e.joinType = "ref"
|
||||
}
|
||||
} else {
|
||||
if p.Index.Unique {
|
||||
e.joinType = "const"
|
||||
} else {
|
||||
e.joinType = "range"
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
e.joinType = "range"
|
||||
}
|
||||
|
||||
// ExplainExec represents an explain executor.
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
|
||||
type ExplainExec struct {
|
||||
StmtPlan plan.Plan
|
||||
fields []*ast.ResultField
|
||||
rows []*Row
|
||||
cursor int
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *ExplainExec) Fields() []*ast.ResultField {
|
||||
return e.fields
|
||||
}
|
||||
|
||||
// Next implements Execution Next interface.
|
||||
func (e *ExplainExec) Next() (*Row, error) {
|
||||
if e.rows == nil {
|
||||
e.fetchRows()
|
||||
}
|
||||
if e.cursor >= len(e.rows) {
|
||||
return nil, nil
|
||||
}
|
||||
row := e.rows[e.cursor]
|
||||
e.cursor++
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (e *ExplainExec) fetchRows() {
|
||||
visitor := &explainVisitor{id: 1}
|
||||
e.StmtPlan.Accept(visitor)
|
||||
for _, entry := range visitor.entries {
|
||||
row := &Row{}
|
||||
row.Data = types.MakeDatums(
|
||||
entry.ID,
|
||||
entry.selectType,
|
||||
entry.table,
|
||||
entry.joinType,
|
||||
entry.key,
|
||||
entry.key,
|
||||
entry.keyLen,
|
||||
entry.ref,
|
||||
entry.rows,
|
||||
strings.Join(entry.extra, "; "),
|
||||
)
|
||||
for i := range row.Data {
|
||||
if row.Data[i].Kind() == types.KindString && row.Data[i].GetString() == "" {
|
||||
row.Data[i].SetNull()
|
||||
}
|
||||
}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *ExplainExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type explainVisitor struct {
|
||||
id int64
|
||||
|
||||
// Sort extra should be appended in the first table in a join.
|
||||
sort bool
|
||||
entries []*explainEntry
|
||||
}
|
||||
|
||||
func (v *explainVisitor) Enter(p plan.Plan) (plan.Plan, bool) {
|
||||
switch x := p.(type) {
|
||||
case *plan.TableScan:
|
||||
v.entries = append(v.entries, v.newEntryForTableScan(x))
|
||||
case *plan.IndexScan:
|
||||
v.entries = append(v.entries, v.newEntryForIndexScan(x))
|
||||
case *plan.Sort:
|
||||
v.sort = true
|
||||
}
|
||||
return p, false
|
||||
}
|
||||
|
||||
func (v *explainVisitor) Leave(p plan.Plan) (plan.Plan, bool) {
|
||||
return p, true
|
||||
}
|
||||
|
||||
func (v *explainVisitor) newEntryForTableScan(p *plan.TableScan) *explainEntry {
|
||||
entry := &explainEntry{
|
||||
ID: v.id,
|
||||
selectType: "SIMPLE",
|
||||
table: p.Table.Name.O,
|
||||
}
|
||||
entry.setJoinTypeForTableScan(p)
|
||||
if entry.joinType != "ALL" {
|
||||
entry.key = "PRIMARY"
|
||||
entry.keyLen = "8"
|
||||
}
|
||||
if len(p.AccessConditions)+len(p.FilterConditions) > 0 {
|
||||
entry.extra = append(entry.extra, "Using where")
|
||||
}
|
||||
|
||||
v.setSortExtra(entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
func (v *explainVisitor) newEntryForIndexScan(p *plan.IndexScan) *explainEntry {
|
||||
entry := &explainEntry{
|
||||
ID: v.id,
|
||||
selectType: "SIMPLE",
|
||||
table: p.Table.Name.O,
|
||||
key: p.Index.Name.O,
|
||||
}
|
||||
if len(p.AccessConditions) != 0 {
|
||||
keyLen := 0
|
||||
for i := 0; i < len(p.Index.Columns); i++ {
|
||||
if i < p.AccessEqualCount {
|
||||
keyLen += p.Index.Columns[i].Length
|
||||
} else if i < len(p.AccessConditions) {
|
||||
keyLen += p.Index.Columns[i].Length
|
||||
break
|
||||
}
|
||||
}
|
||||
entry.keyLen = strconv.Itoa(keyLen)
|
||||
}
|
||||
entry.setJoinTypeForIndexScan(p)
|
||||
if len(p.AccessConditions)+len(p.FilterConditions) > 0 {
|
||||
entry.extra = append(entry.extra, "Using where")
|
||||
}
|
||||
|
||||
v.setSortExtra(entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
func (v *explainVisitor) setSortExtra(entry *explainEntry) {
|
||||
if v.sort {
|
||||
entry.extra = append(entry.extra, "Using filesort")
|
||||
v.sort = false
|
||||
}
|
||||
}
|
520
vendor/github.com/pingcap/tidb/executor/grant.go
generated
vendored
Normal file
520
vendor/github.com/pingcap/tidb/executor/grant.go
generated
vendored
Normal file
|
@ -0,0 +1,520 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/column"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
"github.com/pingcap/tidb/sessionctx/db"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
"github.com/pingcap/tidb/table"
|
||||
"github.com/pingcap/tidb/util/sqlexec"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
/***
|
||||
* Grant Statement
|
||||
* See: https://dev.mysql.com/doc/refman/5.7/en/grant.html
|
||||
************************************************************************************/
|
||||
var (
|
||||
_ Executor = (*GrantExec)(nil)
|
||||
)
|
||||
|
||||
// GrantExec executes GrantStmt.
|
||||
type GrantExec struct {
|
||||
Privs []*ast.PrivElem
|
||||
ObjectType ast.ObjectTypeType
|
||||
Level *ast.GrantLevel
|
||||
Users []*ast.UserSpec
|
||||
|
||||
ctx context.Context
|
||||
done bool
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *GrantExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Execution Next interface.
|
||||
func (e *GrantExec) Next() (*Row, error) {
|
||||
if e.done {
|
||||
return nil, nil
|
||||
}
|
||||
// Grant for each user
|
||||
for _, user := range e.Users {
|
||||
// Check if user exists.
|
||||
userName, host := parseUser(user.User)
|
||||
exists, err := userExists(e.ctx, userName, host)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if !exists {
|
||||
return nil, errors.Errorf("Unknown user: %s", user.User)
|
||||
}
|
||||
|
||||
// If there is no privilege entry in corresponding table, insert a new one.
|
||||
// DB scope: mysql.DB
|
||||
// Table scope: mysql.Tables_priv
|
||||
// Column scope: mysql.Columns_priv
|
||||
switch e.Level.Level {
|
||||
case ast.GrantLevelDB:
|
||||
err := e.checkAndInitDBPriv(userName, host)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
case ast.GrantLevelTable:
|
||||
err := e.checkAndInitTablePriv(userName, host)
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
// Grant each priv to the user.
|
||||
for _, priv := range e.Privs {
|
||||
if len(priv.Cols) > 0 {
|
||||
// Check column scope privilege entry.
|
||||
// TODO: Check validity before insert new entry.
|
||||
err1 := e.checkAndInitColumnPriv(userName, host, priv.Cols)
|
||||
if err1 != nil {
|
||||
return nil, errors.Trace(err1)
|
||||
}
|
||||
}
|
||||
err2 := e.grantPriv(priv, user)
|
||||
if err2 != nil {
|
||||
return nil, errors.Trace(err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
e.done = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *GrantExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if DB scope privilege entry exists in mysql.DB.
|
||||
// If unexists, insert a new one.
|
||||
func (e *GrantExec) checkAndInitDBPriv(user string, host string) error {
|
||||
db, err := e.getTargetSchema()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
ok, err := dbUserExists(e.ctx, user, host, db.Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
// Entry does not exist for user-host-db. Insert a new entry.
|
||||
return initDBPrivEntry(e.ctx, user, host, db.Name.O)
|
||||
}
|
||||
|
||||
// Check if table scope privilege entry exists in mysql.Tables_priv.
|
||||
// If unexists, insert a new one.
|
||||
func (e *GrantExec) checkAndInitTablePriv(user string, host string) error {
|
||||
db, tbl, err := e.getTargetSchemaAndTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
ok, err := tableUserExists(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
// Entry does not exist for user-host-db-tbl. Insert a new entry.
|
||||
return initTablePrivEntry(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O)
|
||||
}
|
||||
|
||||
// Check if column scope privilege entry exists in mysql.Columns_priv.
|
||||
// If unexists, insert a new one.
|
||||
func (e *GrantExec) checkAndInitColumnPriv(user string, host string, cols []*ast.ColumnName) error {
|
||||
db, tbl, err := e.getTargetSchemaAndTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
for _, c := range cols {
|
||||
col := column.FindCol(tbl.Cols(), c.Name.L)
|
||||
if col == nil {
|
||||
return errors.Errorf("Unknown column: %s", c.Name.O)
|
||||
}
|
||||
ok, err := columnPrivEntryExists(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
// Entry does not exist for user-host-db-tbl-col. Insert a new entry.
|
||||
err = initColumnPrivEntry(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert a new row into mysql.DB with empty privilege.
|
||||
func initDBPrivEntry(ctx context.Context, user string, host string, db string) error {
|
||||
sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB) VALUES ("%s", "%s", "%s")`, mysql.SystemDB, mysql.DBTable, host, user, db)
|
||||
_, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Insert a new row into mysql.Tables_priv with empty privilege.
|
||||
func initTablePrivEntry(ctx context.Context, user string, host string, db string, tbl string) error {
|
||||
sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB, Table_name, Table_priv, Column_priv) VALUES ("%s", "%s", "%s", "%s", "", "")`, mysql.SystemDB, mysql.TablePrivTable, host, user, db, tbl)
|
||||
_, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Insert a new row into mysql.Columns_priv with empty privilege.
|
||||
func initColumnPrivEntry(ctx context.Context, user string, host string, db string, tbl string, col string) error {
|
||||
sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB, Table_name, Column_name, Column_priv) VALUES ("%s", "%s", "%s", "%s", "%s", "")`, mysql.SystemDB, mysql.ColumnPrivTable, host, user, db, tbl, col)
|
||||
_, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Grant priv to user in s.Level scope.
|
||||
func (e *GrantExec) grantPriv(priv *ast.PrivElem, user *ast.UserSpec) error {
|
||||
switch e.Level.Level {
|
||||
case ast.GrantLevelGlobal:
|
||||
return e.grantGlobalPriv(priv, user)
|
||||
case ast.GrantLevelDB:
|
||||
return e.grantDBPriv(priv, user)
|
||||
case ast.GrantLevelTable:
|
||||
if len(priv.Cols) == 0 {
|
||||
return e.grantTablePriv(priv, user)
|
||||
}
|
||||
return e.grantColumnPriv(priv, user)
|
||||
default:
|
||||
return errors.Errorf("Unknown grant level: %#v", e.Level)
|
||||
}
|
||||
}
|
||||
|
||||
// Manipulate mysql.user table.
|
||||
func (e *GrantExec) grantGlobalPriv(priv *ast.PrivElem, user *ast.UserSpec) error {
|
||||
asgns, err := composeGlobalPrivUpdate(priv.Priv)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
userName, host := parseUser(user.User)
|
||||
sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s"`, mysql.SystemDB, mysql.UserTable, asgns, userName, host)
|
||||
_, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Manipulate mysql.db table.
|
||||
func (e *GrantExec) grantDBPriv(priv *ast.PrivElem, user *ast.UserSpec) error {
|
||||
db, err := e.getTargetSchema()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
asgns, err := composeDBPrivUpdate(priv.Priv)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
userName, host := parseUser(user.User)
|
||||
sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s";`, mysql.SystemDB, mysql.DBTable, asgns, userName, host, db.Name.O)
|
||||
_, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Manipulate mysql.tables_priv table.
|
||||
func (e *GrantExec) grantTablePriv(priv *ast.PrivElem, user *ast.UserSpec) error {
|
||||
db, tbl, err := e.getTargetSchemaAndTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
userName, host := parseUser(user.User)
|
||||
asgns, err := composeTablePrivUpdate(e.ctx, priv.Priv, userName, host, db.Name.O, tbl.Meta().Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, asgns, userName, host, db.Name.O, tbl.Meta().Name.O)
|
||||
_, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Manipulate mysql.tables_priv table.
|
||||
func (e *GrantExec) grantColumnPriv(priv *ast.PrivElem, user *ast.UserSpec) error {
|
||||
db, tbl, err := e.getTargetSchemaAndTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
userName, host := parseUser(user.User)
|
||||
for _, c := range priv.Cols {
|
||||
col := column.FindCol(tbl.Cols(), c.Name.L)
|
||||
if col == nil {
|
||||
return errors.Errorf("Unknown column: %s", c)
|
||||
}
|
||||
asgns, err := composeColumnPrivUpdate(e.ctx, priv.Priv, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, asgns, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O)
|
||||
_, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compose update stmt assignment list string for global scope privilege update.
|
||||
func composeGlobalPrivUpdate(priv mysql.PrivilegeType) (string, error) {
|
||||
if priv == mysql.AllPriv {
|
||||
strs := make([]string, 0, len(mysql.Priv2UserCol))
|
||||
for _, v := range mysql.Priv2UserCol {
|
||||
strs = append(strs, fmt.Sprintf(`%s="Y"`, v))
|
||||
}
|
||||
return strings.Join(strs, ", "), nil
|
||||
}
|
||||
col, ok := mysql.Priv2UserCol[priv]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown priv: %v", priv)
|
||||
}
|
||||
return fmt.Sprintf(`%s="Y"`, col), nil
|
||||
}
|
||||
|
||||
// Compose update stmt assignment list for db scope privilege update.
|
||||
func composeDBPrivUpdate(priv mysql.PrivilegeType) (string, error) {
|
||||
if priv == mysql.AllPriv {
|
||||
strs := make([]string, 0, len(mysql.AllDBPrivs))
|
||||
for _, p := range mysql.AllDBPrivs {
|
||||
v, ok := mysql.Priv2UserCol[p]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown db privilege %v", priv)
|
||||
}
|
||||
strs = append(strs, fmt.Sprintf(`%s="Y"`, v))
|
||||
}
|
||||
return strings.Join(strs, ", "), nil
|
||||
}
|
||||
col, ok := mysql.Priv2UserCol[priv]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown priv: %v", priv)
|
||||
}
|
||||
return fmt.Sprintf(`%s="Y"`, col), nil
|
||||
}
|
||||
|
||||
// Compose update stmt assignment list for table scope privilege update.
|
||||
func composeTablePrivUpdate(ctx context.Context, priv mysql.PrivilegeType, name string, host string, db string, tbl string) (string, error) {
|
||||
var newTablePriv, newColumnPriv string
|
||||
if priv == mysql.AllPriv {
|
||||
for _, p := range mysql.AllTablePrivs {
|
||||
v, ok := mysql.Priv2SetStr[p]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown table privilege %v", p)
|
||||
}
|
||||
if len(newTablePriv) == 0 {
|
||||
newTablePriv = v
|
||||
} else {
|
||||
newTablePriv = fmt.Sprintf("%s,%s", newTablePriv, v)
|
||||
}
|
||||
}
|
||||
for _, p := range mysql.AllColumnPrivs {
|
||||
v, ok := mysql.Priv2SetStr[p]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown column privilege %v", p)
|
||||
}
|
||||
if len(newColumnPriv) == 0 {
|
||||
newColumnPriv = v
|
||||
} else {
|
||||
newColumnPriv = fmt.Sprintf("%s,%s", newColumnPriv, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currTablePriv, currColumnPriv, err := getTablePriv(ctx, name, host, db, tbl)
|
||||
if err != nil {
|
||||
return "", errors.Trace(err)
|
||||
}
|
||||
p, ok := mysql.Priv2SetStr[priv]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown priv: %v", priv)
|
||||
}
|
||||
if len(currTablePriv) == 0 {
|
||||
newTablePriv = p
|
||||
} else {
|
||||
newTablePriv = fmt.Sprintf("%s,%s", currTablePriv, p)
|
||||
}
|
||||
for _, cp := range mysql.AllColumnPrivs {
|
||||
if priv == cp {
|
||||
if len(currColumnPriv) == 0 {
|
||||
newColumnPriv = p
|
||||
} else {
|
||||
newColumnPriv = fmt.Sprintf("%s,%s", currColumnPriv, p)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(`Table_priv="%s", Column_priv="%s", Grantor="%s"`, newTablePriv, newColumnPriv, variable.GetSessionVars(ctx).User), nil
|
||||
}
|
||||
|
||||
// Compose update stmt assignment list for column scope privilege update.
|
||||
func composeColumnPrivUpdate(ctx context.Context, priv mysql.PrivilegeType, name string, host string, db string, tbl string, col string) (string, error) {
|
||||
newColumnPriv := ""
|
||||
if priv == mysql.AllPriv {
|
||||
for _, p := range mysql.AllColumnPrivs {
|
||||
v, ok := mysql.Priv2SetStr[p]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown column privilege %v", p)
|
||||
}
|
||||
if len(newColumnPriv) == 0 {
|
||||
newColumnPriv = v
|
||||
} else {
|
||||
newColumnPriv = fmt.Sprintf("%s,%s", newColumnPriv, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currColumnPriv, err := getColumnPriv(ctx, name, host, db, tbl, col)
|
||||
if err != nil {
|
||||
return "", errors.Trace(err)
|
||||
}
|
||||
p, ok := mysql.Priv2SetStr[priv]
|
||||
if !ok {
|
||||
return "", errors.Errorf("Unknown priv: %v", priv)
|
||||
}
|
||||
if len(currColumnPriv) == 0 {
|
||||
newColumnPriv = p
|
||||
} else {
|
||||
newColumnPriv = fmt.Sprintf("%s,%s", currColumnPriv, p)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(`Column_priv="%s"`, newColumnPriv), nil
|
||||
}
|
||||
|
||||
// Helper function to check if the sql returns any row.
|
||||
func recordExists(ctx context.Context, sql string) (bool, error) {
|
||||
rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
defer rs.Close()
|
||||
row, err := rs.Next()
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
return row != nil, nil
|
||||
}
|
||||
|
||||
// Check if there is an entry with key user-host-db in mysql.DB.
|
||||
func dbUserExists(ctx context.Context, name string, host string, db string) (bool, error) {
|
||||
sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s";`, mysql.SystemDB, mysql.DBTable, name, host, db)
|
||||
return recordExists(ctx, sql)
|
||||
}
|
||||
|
||||
// Check if there is an entry with key user-host-db-tbl in mysql.Tables_priv.
|
||||
func tableUserExists(ctx context.Context, name string, host string, db string, tbl string) (bool, error) {
|
||||
sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, name, host, db, tbl)
|
||||
return recordExists(ctx, sql)
|
||||
}
|
||||
|
||||
// Check if there is an entry with key user-host-db-tbl-col in mysql.Columns_priv.
|
||||
func columnPrivEntryExists(ctx context.Context, name string, host string, db string, tbl string, col string) (bool, error) {
|
||||
sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, name, host, db, tbl, col)
|
||||
return recordExists(ctx, sql)
|
||||
}
|
||||
|
||||
// Get current table scope privilege set from mysql.Tables_priv.
|
||||
// Return Table_priv and Column_priv.
|
||||
func getTablePriv(ctx context.Context, name string, host string, db string, tbl string) (string, string, error) {
|
||||
sql := fmt.Sprintf(`SELECT Table_priv, Column_priv FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, name, host, db, tbl)
|
||||
rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
if err != nil {
|
||||
return "", "", errors.Trace(err)
|
||||
}
|
||||
defer rs.Close()
|
||||
row, err := rs.Next()
|
||||
if err != nil {
|
||||
return "", "", errors.Trace(err)
|
||||
}
|
||||
var tPriv, cPriv string
|
||||
if row.Data[0].Kind() == types.KindMysqlSet {
|
||||
tablePriv := row.Data[0].GetMysqlSet()
|
||||
tPriv = tablePriv.Name
|
||||
}
|
||||
if row.Data[1].Kind() == types.KindMysqlSet {
|
||||
columnPriv := row.Data[1].GetMysqlSet()
|
||||
cPriv = columnPriv.Name
|
||||
}
|
||||
return tPriv, cPriv, nil
|
||||
}
|
||||
|
||||
// Get current column scope privilege set from mysql.Columns_priv.
|
||||
// Return Column_priv.
|
||||
func getColumnPriv(ctx context.Context, name string, host string, db string, tbl string, col string) (string, error) {
|
||||
sql := fmt.Sprintf(`SELECT Column_priv FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, name, host, db, tbl, col)
|
||||
rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql)
|
||||
if err != nil {
|
||||
return "", errors.Trace(err)
|
||||
}
|
||||
defer rs.Close()
|
||||
row, err := rs.Next()
|
||||
if err != nil {
|
||||
return "", errors.Trace(err)
|
||||
}
|
||||
cPriv := ""
|
||||
if row.Data[0].Kind() == types.KindMysqlSet {
|
||||
cPriv = row.Data[0].GetMysqlSet().Name
|
||||
}
|
||||
return cPriv, nil
|
||||
}
|
||||
|
||||
// Find the schema by dbName.
|
||||
func (e *GrantExec) getTargetSchema() (*model.DBInfo, error) {
|
||||
dbName := e.Level.DBName
|
||||
if len(dbName) == 0 {
|
||||
// Grant *, use current schema
|
||||
dbName = db.GetCurrentSchema(e.ctx)
|
||||
if len(dbName) == 0 {
|
||||
return nil, errors.New("Miss DB name for grant privilege.")
|
||||
}
|
||||
}
|
||||
//check if db exists
|
||||
schema := model.NewCIStr(dbName)
|
||||
is := sessionctx.GetDomain(e.ctx).InfoSchema()
|
||||
db, ok := is.SchemaByName(schema)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Unknown schema name: %s", dbName)
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Find the schema and table by dbName and tableName.
|
||||
func (e *GrantExec) getTargetSchemaAndTable() (*model.DBInfo, table.Table, error) {
|
||||
db, err := e.getTargetSchema()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Trace(err)
|
||||
}
|
||||
name := model.NewCIStr(e.Level.TableName)
|
||||
is := sessionctx.GetDomain(e.ctx).InfoSchema()
|
||||
tbl, err := is.TableByName(db.Name, name)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Trace(err)
|
||||
}
|
||||
return db, tbl, nil
|
||||
}
|
277
vendor/github.com/pingcap/tidb/executor/prepared.go
generated
vendored
Normal file
277
vendor/github.com/pingcap/tidb/executor/prepared.go
generated
vendored
Normal file
|
@ -0,0 +1,277 @@
|
|||
// 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 executor
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/evaluator"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/optimizer"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
"github.com/pingcap/tidb/parser"
|
||||
"github.com/pingcap/tidb/sessionctx"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Executor = &DeallocateExec{}
|
||||
_ Executor = &ExecuteExec{}
|
||||
_ Executor = &PrepareExec{}
|
||||
)
|
||||
|
||||
type paramMarkerSorter struct {
|
||||
markers []*ast.ParamMarkerExpr
|
||||
}
|
||||
|
||||
func (p *paramMarkerSorter) Len() int {
|
||||
return len(p.markers)
|
||||
}
|
||||
|
||||
func (p *paramMarkerSorter) Less(i, j int) bool {
|
||||
return p.markers[i].Offset < p.markers[j].Offset
|
||||
}
|
||||
|
||||
func (p *paramMarkerSorter) Swap(i, j int) {
|
||||
p.markers[i], p.markers[j] = p.markers[j], p.markers[i]
|
||||
}
|
||||
|
||||
type paramMarkerExtractor struct {
|
||||
markers []*ast.ParamMarkerExpr
|
||||
}
|
||||
|
||||
func (e *paramMarkerExtractor) Enter(in ast.Node) (ast.Node, bool) {
|
||||
return in, false
|
||||
}
|
||||
|
||||
func (e *paramMarkerExtractor) Leave(in ast.Node) (ast.Node, bool) {
|
||||
if x, ok := in.(*ast.ParamMarkerExpr); ok {
|
||||
e.markers = append(e.markers, x)
|
||||
}
|
||||
return in, true
|
||||
}
|
||||
|
||||
// Prepared represents a prepared statement.
|
||||
type Prepared struct {
|
||||
Stmt ast.StmtNode
|
||||
Params []*ast.ParamMarkerExpr
|
||||
SchemaVersion int64
|
||||
}
|
||||
|
||||
// PrepareExec represents a PREPARE executor.
|
||||
type PrepareExec struct {
|
||||
IS infoschema.InfoSchema
|
||||
Ctx context.Context
|
||||
Name string
|
||||
SQLText string
|
||||
|
||||
ID uint32
|
||||
ResultFields []*ast.ResultField
|
||||
ParamCount int
|
||||
Err error
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *PrepareExec) Fields() []*ast.ResultField {
|
||||
// returns nil to indicate prepare will not return Recordset.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *PrepareExec) Next() (*Row, error) {
|
||||
e.DoPrepare()
|
||||
return nil, e.Err
|
||||
}
|
||||
|
||||
// Close implements plan.Plan Close interface.
|
||||
func (e *PrepareExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoPrepare prepares the statement, it can be called multiple times without
|
||||
// side effect.
|
||||
func (e *PrepareExec) DoPrepare() {
|
||||
vars := variable.GetSessionVars(e.Ctx)
|
||||
if e.ID != 0 {
|
||||
// Must be the case when we retry a prepare.
|
||||
// Make sure it is idempotent.
|
||||
_, ok := vars.PreparedStmts[e.ID]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
charset, collation := variable.GetCharsetInfo(e.Ctx)
|
||||
stmts, err := parser.Parse(e.SQLText, charset, collation)
|
||||
if err != nil {
|
||||
e.Err = errors.Trace(err)
|
||||
return
|
||||
}
|
||||
if len(stmts) != 1 {
|
||||
e.Err = ErrPrepareMulti
|
||||
return
|
||||
}
|
||||
stmt := stmts[0]
|
||||
var extractor paramMarkerExtractor
|
||||
stmt.Accept(&extractor)
|
||||
|
||||
// The parameter markers are appended in visiting order, which may not
|
||||
// be the same as the position order in the query string. We need to
|
||||
// sort it by position.
|
||||
sorter := ¶mMarkerSorter{markers: extractor.markers}
|
||||
sort.Sort(sorter)
|
||||
e.ParamCount = len(sorter.markers)
|
||||
prepared := &Prepared{
|
||||
Stmt: stmt,
|
||||
Params: sorter.markers,
|
||||
SchemaVersion: e.IS.SchemaMetaVersion(),
|
||||
}
|
||||
|
||||
err = optimizer.Prepare(e.IS, e.Ctx, stmt)
|
||||
if err != nil {
|
||||
e.Err = errors.Trace(err)
|
||||
return
|
||||
}
|
||||
if resultSetNode, ok := stmt.(ast.ResultSetNode); ok {
|
||||
e.ResultFields = resultSetNode.GetResultFields()
|
||||
}
|
||||
|
||||
if e.ID == 0 {
|
||||
e.ID = vars.GetNextPreparedStmtID()
|
||||
}
|
||||
if e.Name != "" {
|
||||
vars.PreparedStmtNameToID[e.Name] = e.ID
|
||||
}
|
||||
vars.PreparedStmts[e.ID] = prepared
|
||||
}
|
||||
|
||||
// ExecuteExec represents an EXECUTE executor.
|
||||
// It executes a prepared statement.
|
||||
type ExecuteExec struct {
|
||||
IS infoschema.InfoSchema
|
||||
Ctx context.Context
|
||||
Name string
|
||||
UsingVars []ast.ExprNode
|
||||
ID uint32
|
||||
StmtExec Executor
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *ExecuteExec) Fields() []*ast.ResultField {
|
||||
// Will never be called.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *ExecuteExec) Next() (*Row, error) {
|
||||
// Will never be called.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Close implements plan.Plan Close interface.
|
||||
func (e *ExecuteExec) Close() error {
|
||||
// Will never be called.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a prepared statement into an executor.
|
||||
func (e *ExecuteExec) Build() error {
|
||||
vars := variable.GetSessionVars(e.Ctx)
|
||||
if e.Name != "" {
|
||||
e.ID = vars.PreparedStmtNameToID[e.Name]
|
||||
}
|
||||
v := vars.PreparedStmts[e.ID]
|
||||
if v == nil {
|
||||
return ErrStmtNotFound
|
||||
}
|
||||
prepared := v.(*Prepared)
|
||||
|
||||
if len(prepared.Params) != len(e.UsingVars) {
|
||||
return ErrWrongParamCount
|
||||
}
|
||||
|
||||
for i, usingVar := range e.UsingVars {
|
||||
val, err := evaluator.Eval(e.Ctx, usingVar)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
prepared.Params[i].SetValue(val)
|
||||
}
|
||||
|
||||
if prepared.SchemaVersion != e.IS.SchemaMetaVersion() {
|
||||
// If the schema version has changed we need to prepare it again,
|
||||
// if this time it failed, the real reason for the error is schema changed.
|
||||
err := optimizer.Prepare(e.IS, e.Ctx, prepared.Stmt)
|
||||
if err != nil {
|
||||
return ErrSchemaChanged.Gen("Schema change casued error: %s", err.Error())
|
||||
}
|
||||
prepared.SchemaVersion = e.IS.SchemaMetaVersion()
|
||||
}
|
||||
sb := &subqueryBuilder{is: e.IS}
|
||||
plan, err := optimizer.Optimize(e.Ctx, prepared.Stmt, sb)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
b := newExecutorBuilder(e.Ctx, e.IS)
|
||||
stmtExec := b.build(plan)
|
||||
if b.err != nil {
|
||||
return errors.Trace(b.err)
|
||||
}
|
||||
e.StmtExec = stmtExec
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeallocateExec represent a DEALLOCATE executor.
|
||||
type DeallocateExec struct {
|
||||
Name string
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *DeallocateExec) Fields() []*ast.ResultField {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next implements Executor Next interface.
|
||||
func (e *DeallocateExec) Next() (*Row, error) {
|
||||
vars := variable.GetSessionVars(e.ctx)
|
||||
id, ok := vars.PreparedStmtNameToID[e.Name]
|
||||
if !ok {
|
||||
return nil, ErrStmtNotFound
|
||||
}
|
||||
delete(vars.PreparedStmtNameToID, e.Name)
|
||||
delete(vars.PreparedStmts, id)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Close implements plan.Plan Close interface.
|
||||
func (e *DeallocateExec) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompileExecutePreparedStmt compiles a session Execute command to a stmt.Statement.
|
||||
func CompileExecutePreparedStmt(ctx context.Context, ID uint32, args ...interface{}) ast.Statement {
|
||||
execPlan := &plan.Execute{ID: ID}
|
||||
execPlan.UsingVars = make([]ast.ExprNode, len(args))
|
||||
for i, val := range args {
|
||||
execPlan.UsingVars[i] = ast.NewValueExpr(val)
|
||||
}
|
||||
sa := &statement{
|
||||
is: sessionctx.GetDomain(ctx).InfoSchema(),
|
||||
plan: execPlan,
|
||||
}
|
||||
return sa
|
||||
}
|
461
vendor/github.com/pingcap/tidb/executor/show.go
generated
vendored
Normal file
461
vendor/github.com/pingcap/tidb/executor/show.go
generated
vendored
Normal file
|
@ -0,0 +1,461 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/column"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/privilege"
|
||||
"github.com/pingcap/tidb/sessionctx/variable"
|
||||
"github.com/pingcap/tidb/table"
|
||||
"github.com/pingcap/tidb/util/charset"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
// ShowExec represents a show executor.
|
||||
type ShowExec struct {
|
||||
Tp ast.ShowStmtType // Databases/Tables/Columns/....
|
||||
DBName model.CIStr
|
||||
Table *ast.TableName // Used for showing columns.
|
||||
Column *ast.ColumnName // Used for `desc table column`.
|
||||
Flag int // Some flag parsed from sql, such as FULL.
|
||||
Full bool
|
||||
User string // Used for show grants.
|
||||
|
||||
// Used by show variables
|
||||
GlobalScope bool
|
||||
|
||||
fields []*ast.ResultField
|
||||
ctx context.Context
|
||||
is infoschema.InfoSchema
|
||||
|
||||
fetched bool
|
||||
rows []*Row
|
||||
cursor int
|
||||
}
|
||||
|
||||
// Fields implements Executor Fields interface.
|
||||
func (e *ShowExec) Fields() []*ast.ResultField {
|
||||
return e.fields
|
||||
}
|
||||
|
||||
// Next implements Execution Next interface.
|
||||
func (e *ShowExec) Next() (*Row, error) {
|
||||
if e.rows == nil {
|
||||
err := e.fetchAll()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
if e.cursor >= len(e.rows) {
|
||||
return nil, nil
|
||||
}
|
||||
row := e.rows[e.cursor]
|
||||
for i, field := range e.fields {
|
||||
field.Expr.SetValue(row.Data[i].GetValue())
|
||||
}
|
||||
e.cursor++
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchAll() error {
|
||||
switch e.Tp {
|
||||
case ast.ShowCharset:
|
||||
return e.fetchShowCharset()
|
||||
case ast.ShowCollation:
|
||||
return e.fetchShowCollation()
|
||||
case ast.ShowColumns:
|
||||
return e.fetchShowColumns()
|
||||
case ast.ShowCreateTable:
|
||||
return e.fetchShowCreateTable()
|
||||
case ast.ShowDatabases:
|
||||
return e.fetchShowDatabases()
|
||||
case ast.ShowEngines:
|
||||
return e.fetchShowEngines()
|
||||
case ast.ShowGrants:
|
||||
return e.fetchShowGrants()
|
||||
case ast.ShowIndex:
|
||||
return e.fetchShowIndex()
|
||||
case ast.ShowProcedureStatus:
|
||||
return e.fetchShowProcedureStatus()
|
||||
case ast.ShowStatus:
|
||||
return e.fetchShowStatus()
|
||||
case ast.ShowTables:
|
||||
return e.fetchShowTables()
|
||||
case ast.ShowTableStatus:
|
||||
return e.fetchShowTableStatus()
|
||||
case ast.ShowTriggers:
|
||||
return e.fetchShowTriggers()
|
||||
case ast.ShowVariables:
|
||||
return e.fetchShowVariables()
|
||||
case ast.ShowWarnings:
|
||||
// empty result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowEngines() error {
|
||||
row := &Row{
|
||||
Data: types.MakeDatums(
|
||||
"InnoDB",
|
||||
"DEFAULT",
|
||||
"Supports transactions, row-level locking, and foreign keys",
|
||||
"YES",
|
||||
"YES",
|
||||
"YES",
|
||||
),
|
||||
}
|
||||
e.rows = append(e.rows, row)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowDatabases() error {
|
||||
dbs := e.is.AllSchemaNames()
|
||||
// TODO: let information_schema be the first database
|
||||
sort.Strings(dbs)
|
||||
for _, d := range dbs {
|
||||
e.rows = append(e.rows, &Row{Data: types.MakeDatums(d)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowTables() error {
|
||||
if !e.is.SchemaExists(e.DBName) {
|
||||
return errors.Errorf("Can not find DB: %s", e.DBName)
|
||||
}
|
||||
// sort for tables
|
||||
var tableNames []string
|
||||
for _, v := range e.is.SchemaTables(e.DBName) {
|
||||
tableNames = append(tableNames, v.Meta().Name.L)
|
||||
}
|
||||
sort.Strings(tableNames)
|
||||
for _, v := range tableNames {
|
||||
data := types.MakeDatums(v)
|
||||
if e.Full {
|
||||
// TODO: support "VIEW" later if we have supported view feature.
|
||||
// now, just use "BASE TABLE".
|
||||
data = append(data, types.NewDatum("BASE TABLE"))
|
||||
}
|
||||
e.rows = append(e.rows, &Row{Data: data})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowTableStatus() error {
|
||||
if !e.is.SchemaExists(e.DBName) {
|
||||
return errors.Errorf("Can not find DB: %s", e.DBName)
|
||||
}
|
||||
|
||||
// sort for tables
|
||||
var tableNames []string
|
||||
for _, v := range e.is.SchemaTables(e.DBName) {
|
||||
tableNames = append(tableNames, v.Meta().Name.L)
|
||||
}
|
||||
sort.Strings(tableNames)
|
||||
|
||||
for _, v := range tableNames {
|
||||
now := mysql.CurrentTime(mysql.TypeDatetime)
|
||||
data := types.MakeDatums(v, "InnoDB", "10", "Compact", 100, 100, 100, 100, 100, 100, 100,
|
||||
now, now, now, "utf8_general_ci", "", "", "")
|
||||
e.rows = append(e.rows, &Row{Data: data})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowColumns() error {
|
||||
tb, err := e.getTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
cols := tb.Cols()
|
||||
for _, col := range cols {
|
||||
if e.Column != nil && e.Column.Name.L != col.Name.L {
|
||||
continue
|
||||
}
|
||||
|
||||
desc := column.NewColDesc(col)
|
||||
|
||||
// The FULL keyword causes the output to include the column collation and comments,
|
||||
// as well as the privileges you have for each column.
|
||||
row := &Row{}
|
||||
if e.Full {
|
||||
row.Data = types.MakeDatums(
|
||||
desc.Field,
|
||||
desc.Type,
|
||||
desc.Collation,
|
||||
desc.Null,
|
||||
desc.Key,
|
||||
desc.DefaultValue,
|
||||
desc.Extra,
|
||||
desc.Privileges,
|
||||
desc.Comment,
|
||||
)
|
||||
} else {
|
||||
row.Data = types.MakeDatums(
|
||||
desc.Field,
|
||||
desc.Type,
|
||||
desc.Null,
|
||||
desc.Key,
|
||||
desc.DefaultValue,
|
||||
desc.Extra,
|
||||
)
|
||||
}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowIndex() error {
|
||||
tb, err := e.getTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
for _, idx := range tb.Indices() {
|
||||
for i, col := range idx.Columns {
|
||||
nonUniq := 1
|
||||
if idx.Unique {
|
||||
nonUniq = 0
|
||||
}
|
||||
var subPart interface{}
|
||||
if col.Length != types.UnspecifiedLength {
|
||||
subPart = col.Length
|
||||
}
|
||||
data := types.MakeDatums(
|
||||
tb.Meta().Name.O, // Table
|
||||
nonUniq, // Non_unique
|
||||
idx.Name.O, // Key_name
|
||||
i+1, // Seq_in_index
|
||||
col.Name.O, // Column_name
|
||||
"utf8_bin", // Colation
|
||||
0, // Cardinality
|
||||
subPart, // Sub_part
|
||||
nil, // Packed
|
||||
"YES", // Null
|
||||
idx.Tp.String(), // Index_type
|
||||
"", // Comment
|
||||
idx.Comment, // Index_comment
|
||||
)
|
||||
e.rows = append(e.rows, &Row{Data: data})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowCharset() error {
|
||||
// See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html
|
||||
descs := charset.GetAllCharsets()
|
||||
for _, desc := range descs {
|
||||
row := &Row{
|
||||
Data: types.MakeDatums(
|
||||
desc.Name,
|
||||
desc.Desc,
|
||||
desc.DefaultCollation,
|
||||
desc.Maxlen,
|
||||
),
|
||||
}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowVariables() error {
|
||||
sessionVars := variable.GetSessionVars(e.ctx)
|
||||
globalVars := variable.GetGlobalVarAccessor(e.ctx)
|
||||
for _, v := range variable.SysVars {
|
||||
var err error
|
||||
var value string
|
||||
if !e.GlobalScope {
|
||||
// Try to get Session Scope variable value first.
|
||||
sv, ok := sessionVars.Systems[v.Name]
|
||||
if ok {
|
||||
value = sv
|
||||
} else {
|
||||
// If session scope variable is not set, get the global scope value.
|
||||
value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
row := &Row{Data: types.MakeDatums(v.Name, value)}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowStatus() error {
|
||||
statusVars, err := variable.GetStatusVars()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
for status, v := range statusVars {
|
||||
if e.GlobalScope && v.Scope == variable.ScopeSession {
|
||||
continue
|
||||
}
|
||||
value, err := types.ToString(v.Value)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
row := &Row{Data: types.MakeDatums(status, value)}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowCreateTable() error {
|
||||
tb, err := e.getTable()
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// TODO: let the result more like MySQL.
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(fmt.Sprintf("CREATE TABLE `%s` (\n", tb.Meta().Name.O))
|
||||
for i, col := range tb.Cols() {
|
||||
buf.WriteString(fmt.Sprintf(" `%s` %s", col.Name.O, col.GetTypeDesc()))
|
||||
if mysql.HasAutoIncrementFlag(col.Flag) {
|
||||
buf.WriteString(" NOT NULL AUTO_INCREMENT")
|
||||
} else {
|
||||
if mysql.HasNotNullFlag(col.Flag) {
|
||||
buf.WriteString(" NOT NULL")
|
||||
}
|
||||
switch col.DefaultValue {
|
||||
case nil:
|
||||
buf.WriteString(" DEFAULT NULL")
|
||||
case "CURRENT_TIMESTAMP":
|
||||
buf.WriteString(" DEFAULT CURRENT_TIMESTAMP")
|
||||
default:
|
||||
buf.WriteString(fmt.Sprintf(" DEFAULT '%v'", col.DefaultValue))
|
||||
}
|
||||
|
||||
if mysql.HasOnUpdateNowFlag(col.Flag) {
|
||||
buf.WriteString(" ON UPDATE CURRENT_TIMESTAMP")
|
||||
}
|
||||
}
|
||||
if i != len(tb.Cols())-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(tb.Indices()) > 0 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
|
||||
for i, idx := range tb.Indices() {
|
||||
if idx.Primary {
|
||||
buf.WriteString(" PRIMARY KEY ")
|
||||
} else if idx.Unique {
|
||||
buf.WriteString(fmt.Sprintf(" UNIQUE KEY `%s` ", idx.Name.O))
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf(" KEY `%s` ", idx.Name.O))
|
||||
}
|
||||
|
||||
cols := make([]string, 0, len(idx.Columns))
|
||||
for _, c := range idx.Columns {
|
||||
cols = append(cols, c.Name.O)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("(`%s`)", strings.Join(cols, "`,`")))
|
||||
if i != len(tb.Indices())-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
|
||||
buf.WriteString(") ENGINE=InnoDB")
|
||||
if s := tb.Meta().Charset; len(s) > 0 {
|
||||
buf.WriteString(fmt.Sprintf(" DEFAULT CHARSET=%s", s))
|
||||
} else {
|
||||
buf.WriteString(" DEFAULT CHARSET=latin1")
|
||||
}
|
||||
|
||||
data := types.MakeDatums(tb.Meta().Name.O, buf.String())
|
||||
e.rows = append(e.rows, &Row{Data: data})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowCollation() error {
|
||||
collations := charset.GetCollations()
|
||||
for _, v := range collations {
|
||||
isDefault := ""
|
||||
if v.IsDefault {
|
||||
isDefault = "Yes"
|
||||
}
|
||||
row := &Row{Data: types.MakeDatums(
|
||||
v.Name,
|
||||
v.CharsetName,
|
||||
v.ID,
|
||||
isDefault,
|
||||
"Yes",
|
||||
1,
|
||||
)}
|
||||
e.rows = append(e.rows, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowGrants() error {
|
||||
// Get checker
|
||||
checker := privilege.GetPrivilegeChecker(e.ctx)
|
||||
if checker == nil {
|
||||
return errors.New("Miss privilege checker!")
|
||||
}
|
||||
gs, err := checker.ShowGrants(e.ctx, e.User)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
for _, g := range gs {
|
||||
data := types.MakeDatums(g)
|
||||
e.rows = append(e.rows, &Row{Data: data})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowTriggers() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) fetchShowProcedureStatus() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ShowExec) getTable() (table.Table, error) {
|
||||
if e.Table == nil {
|
||||
return nil, errors.New("table not found")
|
||||
}
|
||||
tb, ok := e.is.TableByID(e.Table.TableInfo.ID)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("table %s not found", e.Table.Name)
|
||||
}
|
||||
return tb, nil
|
||||
}
|
||||
|
||||
// Close implements Executor Close interface.
|
||||
func (e *ShowExec) Close() error {
|
||||
return nil
|
||||
}
|
145
vendor/github.com/pingcap/tidb/executor/subquery.go
generated
vendored
Normal file
145
vendor/github.com/pingcap/tidb/executor/subquery.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2016 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 executor
|
||||
|
||||
import (
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/context"
|
||||
"github.com/pingcap/tidb/infoschema"
|
||||
"github.com/pingcap/tidb/optimizer/plan"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
var _ ast.SubqueryExec = &subquery{}
|
||||
|
||||
// subquery is an exprNode with a plan.
|
||||
type subquery struct {
|
||||
types.Datum
|
||||
Type *types.FieldType
|
||||
flag uint64
|
||||
text string
|
||||
plan plan.Plan
|
||||
is infoschema.InfoSchema
|
||||
}
|
||||
|
||||
// SetDatum implements Expression interface.
|
||||
func (sq *subquery) SetDatum(datum types.Datum) {
|
||||
sq.Datum = datum
|
||||
}
|
||||
|
||||
// GetDatum implements Expression interface.
|
||||
func (sq *subquery) GetDatum() *types.Datum {
|
||||
return &sq.Datum
|
||||
}
|
||||
|
||||
// SetFlag implements Expression interface.
|
||||
func (sq *subquery) SetFlag(flag uint64) {
|
||||
sq.flag = flag
|
||||
}
|
||||
|
||||
// GetFlag implements Expression interface.
|
||||
func (sq *subquery) GetFlag() uint64 {
|
||||
return sq.flag
|
||||
}
|
||||
|
||||
// SetText implements Node interface.
|
||||
func (sq *subquery) SetText(text string) {
|
||||
sq.text = text
|
||||
}
|
||||
|
||||
// Text implements Node interface.
|
||||
func (sq *subquery) Text() string {
|
||||
return sq.text
|
||||
}
|
||||
|
||||
// SetType implements Expression interface.
|
||||
func (sq *subquery) SetType(tp *types.FieldType) {
|
||||
sq.Type = tp
|
||||
}
|
||||
|
||||
// GetType implements Expression interface.
|
||||
func (sq *subquery) GetType() *types.FieldType {
|
||||
return sq.Type
|
||||
}
|
||||
|
||||
func (sq *subquery) Accept(v ast.Visitor) (ast.Node, bool) {
|
||||
// SubQuery is not a normal ExprNode.
|
||||
newNode, skipChildren := v.Enter(sq)
|
||||
if skipChildren {
|
||||
return v.Leave(newNode)
|
||||
}
|
||||
sq = newNode.(*subquery)
|
||||
return v.Leave(sq)
|
||||
}
|
||||
|
||||
func (sq *subquery) EvalRows(ctx context.Context, rowCount int) ([]interface{}, error) {
|
||||
b := newExecutorBuilder(ctx, sq.is)
|
||||
plan.Refine(sq.plan)
|
||||
e := b.build(sq.plan)
|
||||
if b.err != nil {
|
||||
return nil, errors.Trace(b.err)
|
||||
}
|
||||
defer e.Close()
|
||||
if len(e.Fields()) == 0 {
|
||||
// No result fields means no Recordset.
|
||||
for {
|
||||
row, err := e.Next()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
}
|
||||
if row == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
var (
|
||||
err error
|
||||
row *Row
|
||||
rows = []interface{}{}
|
||||
)
|
||||
for rowCount != 0 {
|
||||
row, err = e.Next()
|
||||
if err != nil {
|
||||
return rows, errors.Trace(err)
|
||||
}
|
||||
if row == nil {
|
||||
break
|
||||
}
|
||||
if len(row.Data) == 1 {
|
||||
rows = append(rows, row.Data[0].GetValue())
|
||||
} else {
|
||||
rows = append(rows, types.DatumsToInterfaces(row.Data))
|
||||
}
|
||||
if rowCount > 0 {
|
||||
rowCount--
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (sq *subquery) ColumnCount() (int, error) {
|
||||
return len(sq.plan.Fields()), nil
|
||||
}
|
||||
|
||||
type subqueryBuilder struct {
|
||||
is infoschema.InfoSchema
|
||||
}
|
||||
|
||||
func (sb *subqueryBuilder) Build(p plan.Plan) ast.SubqueryExec {
|
||||
return &subquery{
|
||||
is: sb.is,
|
||||
plan: p,
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue