1
0
Fork 0
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:
Thomas Boerger 2016-11-29 17:26:36 +01:00 committed by Lunny Xiao
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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

205
vendor/github.com/pingcap/tidb/executor/executor_ddl.go generated vendored Normal file
View 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, ".")
}

View 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)
}

View 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
View 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
View 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
View 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 := &paramMarkerSorter{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
View 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
View 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,
}
}