Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: support create global binding #9846

Merged
merged 54 commits into from
Apr 17, 2019
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
0cebb75
plan binding:add global binding
iamzhoug37 Mar 21, 2019
4f5df56
fix tidy fmt lint
iamzhoug37 Mar 21, 2019
68ee4d8
remove blan
iamzhoug37 Mar 21, 2019
9747bff
fix fmt
iamzhoug37 Mar 21, 2019
44d4619
fix error-check
iamzhoug37 Mar 21, 2019
0d85387
fix createBindPlan originSql and bindSql unnormolize
iamzhoug37 Mar 22, 2019
150b574
fix import
iamzhoug37 Mar 22, 2019
e2dc348
add index for origin_sql and defaultDB
iamzhoug37 Mar 25, 2019
367c7cc
merge master
iamzhoug37 Mar 25, 2019
5aa25ef
fix comment
iamzhoug37 Mar 26, 2019
6c1bd81
add version update
iamzhoug37 Mar 28, 2019
ae25b95
fix go.sum
iamzhoug37 Mar 28, 2019
ea3940a
replace user session ctx with domain's session ctx
iamzhoug37 Mar 29, 2019
e9e9cb0
fix lint
iamzhoug37 Mar 29, 2019
9cadfff
fix comment
iamzhoug37 Apr 1, 2019
787564e
xx
iamzhoug37 Apr 1, 2019
2d7d6d3
fx comment
iamzhoug37 Apr 1, 2019
3416b81
fix ci
iamzhoug37 Apr 1, 2019
ecd00b4
fix version
iamzhoug37 Apr 1, 2019
1df98f0
replace originalSQL with rowId
iamzhoug37 Apr 2, 2019
91b7494
fix ci
iamzhoug37 Apr 2, 2019
8849d3d
fix comment
iamzhoug37 Apr 2, 2019
7e9d879
add privilage check
iamzhoug37 Apr 3, 2019
45c24f3
fix make lint
iamzhoug37 Apr 3, 2019
e89f6c9
create bind plan
iamzhoug37 Apr 3, 2019
d77a038
fix comment
iamzhoug37 Apr 3, 2019
520ea9e
fix comment
iamzhoug37 Apr 4, 2019
23e3b97
remove unused code
iamzhoug37 Apr 4, 2019
924145e
timestamp bugfix
iamzhoug37 Apr 4, 2019
9e539a4
Merge branch 'master' into plan-management-add-global-binding
alivxxx Apr 4, 2019
fca0e5d
Merge branch 'master' into plan-management-add-global-binding
iamzhoug37 Apr 4, 2019
debf0c3
fix undefined error
iamzhoug37 Apr 4, 2019
bd1ae07
cjrioe
iamzhoug37 Apr 4, 2019
9380e05
merge master
iamzhoug37 Apr 12, 2019
8c6e4ec
add create update cache
iamzhoug37 Apr 15, 2019
682e5af
fix comment
iamzhoug37 Apr 16, 2019
d22f5dc
fix comment
iamzhoug37 Apr 16, 2019
3527cbc
lock
iamzhoug37 Apr 16, 2019
f45010a
aa
iamzhoug37 Apr 16, 2019
fda7535
tiny refine
zz-jason Apr 16, 2019
927346a
tiny refine planner and executor
zz-jason Apr 16, 2019
d5ffa60
release lock
iamzhoug37 Apr 16, 2019
918d8d6
Merge branch 'plan-management-add-global-binding' of github.com:iamzh…
iamzhoug37 Apr 16, 2019
efc4ad3
refine mutex in handle
zz-jason Apr 16, 2019
1b59531
Merge branch 'plan-management-add-global-binding' of github.com:iamzh…
iamzhoug37 Apr 16, 2019
33a2af4
code refactor
iamzhoug37 Apr 17, 2019
8a6b7b0
fix vet
iamzhoug37 Apr 17, 2019
85fa209
remove db
iamzhoug37 Apr 17, 2019
07ccf7b
Merge branch 'master' into plan-management-add-global-binding
zz-jason Apr 17, 2019
2bbb6d4
update cache
iamzhoug37 Apr 17, 2019
3e4539a
Merge branch 'plan-management-add-global-binding' of github.com:iamzh…
iamzhoug37 Apr 17, 2019
4fac6c3
make fmt
iamzhoug37 Apr 17, 2019
603074c
add licensed
iamzhoug37 Apr 17, 2019
2f10bb5
move import
iamzhoug37 Apr 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions bindinfo/bind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2019 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 bindinfo

import (
"github.com/pingcap/tidb/sessionctx"
)

var _ Manager = (*BindManager)(nil)

// BindManager implements Manager inferface.
type BindManager struct {
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
GlobalAccessor GlobalBindAccessor
}

type keyType int

func (k keyType) String() string {
return "bind-key"
}

// Manager is the interface for providing bind related operations.
type Manager interface {
AddGlobalBind(originSQL, bindSQL, defaultDB, charset, collation string) error
}

const key keyType = 0

// BindBinderManager binds Manager to context.
func BindBinderManager(ctx sessionctx.Context, pc Manager) {
ctx.SetValue(key, pc)
}

// GetBindManager gets Checker from context.
func GetBindManager(ctx sessionctx.Context) Manager {
if v, ok := ctx.Value(key).(Manager); ok {
return v
}
return nil
}

//AddGlobalBind implements Manager's AddGlobalBind interface.
func (b *BindManager) AddGlobalBind(originSQL, bindSQL, defaultDB, charset, collation string) error {
return b.GlobalAccessor.AddGlobalBind(originSQL, bindSQL, defaultDB, charset, collation)
}

// GlobalBindAccessor is the interface for accessing global bind info.
type GlobalBindAccessor interface {
AddGlobalBind(originSQL string, bindSQL string, defaultDB string, charset string, collation string) error
}
43 changes: 42 additions & 1 deletion bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (s *testSuite) TestBindParse(c *C) {
sql := fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation) VALUES ('%s', '%s', '%s', '%s', NOW(), NOW(),'%s', '%s')`,
originSQL, bindSQL, defaultDb, status, charset, collation)
tk.MustExec(sql)
bindHandle := bindinfo.NewHandle()
bindHandle := bindinfo.NewHandle(tk.Se)
bindCacheUpdater := bindinfo.NewBindCacheUpdater(tk.Se, bindHandle, s.Parser)
err := bindCacheUpdater.Update(true)
c.Check(err, IsNil)
Expand All @@ -134,3 +134,44 @@ func (s *testSuite) TestBindParse(c *C) {
c.Check(bindData[0].CreateTime, NotNil)
c.Check(bindData[0].UpdateTime, NotNil)
}

func (s *testSuite) TestGlobalBinding(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("drop table if exists t1")
tk.MustExec("create table t(i int, s varchar(20))")
tk.MustExec("create table t1(i int, s varchar(20))")
tk.MustExec("create index index_t on t(i,s)")

_, err := tk.Exec("create global binding for select * from t where i>100 using select * from t use index(index_t) where i>100")
c.Assert(err, IsNil, Commentf("err %v", err))
_, err = tk.Exec("create global binding for select * from t where i>99 using select * from t use index(index_t) where i>99")
c.Assert(err, NotNil)

bindHandle := bindinfo.NewHandle(tk.Se)
bindCacheUpdater := bindinfo.NewBindCacheUpdater(tk.Se, bindHandle, s.Parser)
err = bindCacheUpdater.Update(true)
c.Check(err, IsNil)
c.Check(len(bindHandle.Get()), Equals, 1)

hash := parser.DigestHash("select * from t where i>100")
bindData := bindHandle.Get()[hash]
c.Check(bindData, NotNil)
c.Check(len(bindData), Equals, 1)
c.Check(bindData[0].OriginalSQL, Equals, "select * from t where i > ?")
c.Check(bindData[0].BindSQL, Equals, "select * from t use index(index_t) where i>100")
c.Check(bindData[0].Db, Equals, "test")
c.Check(bindData[0].Status, Equals, "using")
c.Check(bindData[0].Charset, NotNil)
c.Check(bindData[0].Collation, NotNil)
c.Check(bindData[0].CreateTime, NotNil)
c.Check(bindData[0].UpdateTime, NotNil)

_, err = tk.Exec("delete from mysql.bind_info")
c.Assert(err, IsNil)

_, err = tk.Exec("create global binding for select * from t using select * from t1 use index for join(index_t)")
c.Assert(err, NotNil, Commentf("err %v", err))
}
97 changes: 92 additions & 5 deletions bindinfo/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@
package bindinfo

import (
"bytes"
"context"
"fmt"
"sync"
"sync/atomic"

"github.com/pingcap/errors"
"github.com/pingcap/parser"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/store/tikv/oracle"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/sqlexec"
)

const (
using = "using"
deleted = "deleted"
// BindUsing is the bind info's in use status.
BindUsing = "using"
// BindDeleted is the bind info's deleted status.
BindDeleted = "deleted"
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
)

// bindMeta stores the basic bind info and bindSql astNode.
Expand All @@ -45,6 +50,9 @@ type cache map[string][]*bindMeta
// Handle holds an atomic cache.
type Handle struct {
atomic.Value
lock sync.Mutex
ctx sessionctx.Context
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
exec sqlexec.SQLExecutor
}

// BindCacheUpdater is used to update the global cache.
Expand Down Expand Up @@ -83,8 +91,13 @@ func NewBindCacheUpdater(ctx sessionctx.Context, handle *Handle, parser *parser.
}

// NewHandle creates a Handle with a cache.
func NewHandle() *Handle {
handle := &Handle{}
func NewHandle(ctx sessionctx.Context) *Handle {
exec, _ := ctx.(sqlexec.SQLExecutor)
alivxxx marked this conversation as resolved.
Show resolved Hide resolved
handle := &Handle{
ctx: ctx,
exec: exec,
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}

return handle
}

Expand Down Expand Up @@ -177,7 +190,7 @@ func (b cache) appendNode(newBindRecord *bindRecord, sparser *parser.Parser) err
}
}
}
if newBindRecord.Status == deleted {
if newBindRecord.Status == BindDeleted {
return nil
}
stmtNodes, _, err := sparser.Parse(newBindRecord.BindSQL, newBindRecord.Charset, newBindRecord.Collation)
Expand All @@ -191,3 +204,77 @@ func (b cache) appendNode(newBindRecord *bindRecord, sparser *parser.Parser) err
b[hash] = append(b[hash], newNode)
return nil
}

// AddGlobalBind implements GlobalBindAccessor.AddGlobalBind interface.
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
func (h *Handle) AddGlobalBind(originSQL, bindSQL, defaultDB, charset, collation string) error {
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
h.lock.Lock()
defer h.lock.Unlock()
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved

ctx := context.TODO()
_, err := h.exec.Execute(ctx, "BEGIN")
if err != nil {
return errors.Trace(err)
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}
defer func() {
if err == nil {
_, err = h.exec.Execute(ctx, "COMMIT")
} else {
_, rbErr := h.exec.Execute(ctx, "ROLLBACK")
terror.Log(errors.Trace(rbErr))
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}
}()

sql := fmt.Sprintf("SELECT status FROM mysql.bind_info WHERE original_sql='%s' AND default_db='%s'",
originSQL, defaultDB)
rs, err := h.exec.Execute(ctx, sql)
if err != nil {
return err
}
if len(rs) == 1 {
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
chkBatch := rs[0].NewRecordBatch()
for {
err = rs[0].Next(context.TODO(), chkBatch)
if err != nil {
return errors.Trace(err)
}
if chkBatch.NumRows() == 0 {
break
}
it := chunk.NewIterator4Chunk(chkBatch.Chunk)
for row := it.Begin(); row != it.End(); row = it.Next() {
status := row.GetString(0)
if status == BindUsing {
err = errors.New("origin sql already has binding sql")
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
return err
}
sql = fmt.Sprintf("DELETE FROM mysql.bind_info WHERE original_sql='%s' and default_db='%s'", originSQL, defaultDB)
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
_, err = h.exec.Execute(ctx, sql)
if err != nil {
return errors.Trace(err)
}
}
}
}

txn, err := h.ctx.Txn(true)
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return errors.Trace(err)
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}
ts := oracle.GetTimeFromTS(txn.StartTS())
bindSQL = getEscapeCharacter(bindSQL)
sql = fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation) VALUES ('%s', '%s', '%s', '%s', '%s', '%s','%s', '%s')`,
originSQL, bindSQL, defaultDB, BindUsing, ts, ts, charset, collation)
_, err = h.exec.Execute(ctx, sql)
return errors.Trace(err)
}

func getEscapeCharacter(str string) string {
var buffer bytes.Buffer
for _, v := range str {
if v == '\'' || v == '"' || v == '\\' {
buffer.WriteString("\\")
}
buffer.WriteString(string(v))
}
return buffer.String()
}
2 changes: 1 addition & 1 deletion domain/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ func (do *Domain) BindHandle() *bindinfo.Handle {
// be called only once in BootstrapSession.
func (do *Domain) LoadBindInfoLoop(ctx sessionctx.Context, parser *parser.Parser) error {
ctx.GetSessionVars().InRestrictedSQL = true
do.bindHandle = bindinfo.NewHandle()
do.bindHandle = bindinfo.NewHandle(ctx)

bindCacheUpdater := bindinfo.NewBindCacheUpdater(ctx, do.BindHandle(), parser)
err := bindCacheUpdater.Update(true)
Expand Down
50 changes: 50 additions & 0 deletions executor/bind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2019 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 (
"context"

"github.com/pingcap/errors"
"github.com/pingcap/parser/ast"
"github.com/pingcap/tidb/bindinfo"
"github.com/pingcap/tidb/util/chunk"
)

// CreateBindExec represents a create bind executor.
type CreateBindExec struct {
baseExecutor

originSQL string
bindSQL string
defaultDB string
charset string
collation string
isGlobal bool
bindAst ast.StmtNode
}

// Next implements the Executor Next interface.
func (e *CreateBindExec) Next(ctx context.Context, req *chunk.RecordBatch) error {
req.Reset()
bm := bindinfo.GetBindManager(e.ctx)
if bm == nil {
return errors.New("bind manager is nil")
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}
var err error
if e.isGlobal {
err = bm.AddGlobalBind(e.originSQL, e.bindSQL, e.defaultDB, e.charset, e.collation)
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
}
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
return err
}
20 changes: 20 additions & 0 deletions executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor {
return b.buildIndexLookUpReader(v)
case *plannercore.PhysicalWindow:
return b.buildWindow(v)
case *plannercore.CreateBindPlan:
return b.buildCreateBind(v)
default:
if mp, ok := p.(MockPhysicalPlan); ok {
return mp.GetExecutor()
Expand Down Expand Up @@ -1974,6 +1976,24 @@ func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) *WindowExec
}
}

func (b *executorBuilder) buildCreateBind(v *plannercore.CreateBindPlan) Executor {
base := newBaseExecutor(b.ctx, v.Schema(), v.ExplainID())
base.initCap = chunk.ZeroCapacity

charset, collation := b.ctx.GetSessionVars().GetCharsetInfo()
iamzhoug37 marked this conversation as resolved.
Show resolved Hide resolved
e := &CreateBindExec{
baseExecutor: base,
originSQL: v.OriginSQL,
bindSQL: v.BindSQL,
defaultDB: v.DefaultDB,
charset: charset,
collation: collation,
isGlobal: v.IsGlobal,
bindAst: v.BindStmt,
}
return e
}

func getPhysicalTableID(t table.Table) int64 {
if p, ok := t.(table.PhysicalTable); ok {
return p.GetPhysicalID()
Expand Down
Loading