226 lines
8.5 KiB
Go
226 lines
8.5 KiB
Go
// 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,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package executor_test
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/pingcap/tidb/parser/mysql"
|
|
plannercore "github.com/pingcap/tidb/planner/core"
|
|
"github.com/pingcap/tidb/session"
|
|
txninfo "github.com/pingcap/tidb/session/txninfo"
|
|
"github.com/pingcap/tidb/testkit"
|
|
"github.com/pingcap/tidb/util"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPreparedNameResolver(t *testing.T) {
|
|
t.Parallel()
|
|
store, clean := testkit.CreateMockStore(t)
|
|
defer clean()
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("create table t (id int, KEY id (id))")
|
|
tk.MustExec("prepare stmt from 'select * from t limit ? offset ?'")
|
|
_, err := tk.Exec("prepare stmt from 'select b from t'")
|
|
require.EqualError(t, err, "[planner:1054]Unknown column 'b' in 'field list'")
|
|
|
|
_, err = tk.Exec("prepare stmt from '(select * FROM t) union all (select * FROM t) order by a limit ?'")
|
|
require.EqualError(t, err, "[planner:1054]Unknown column 'a' in 'order clause'")
|
|
}
|
|
|
|
// a 'create table' DDL statement should be accepted if it has no parameters.
|
|
func TestPreparedDDL(t *testing.T) {
|
|
t.Parallel()
|
|
store, clean := testkit.CreateMockStore(t)
|
|
defer clean()
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("prepare stmt from 'create table t (id int, KEY id (id))'")
|
|
}
|
|
|
|
// TestUnsupportedStmtForPrepare is related to https://github.com/pingcap/tidb/issues/17412
|
|
func TestUnsupportedStmtForPrepare(t *testing.T) {
|
|
t.Parallel()
|
|
store, clean := testkit.CreateMockStore(t)
|
|
defer clean()
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec(`prepare stmt0 from "create table t0(a int primary key)"`)
|
|
tk.MustGetErrCode(`prepare stmt1 from "execute stmt0"`, mysql.ErrUnsupportedPs)
|
|
tk.MustGetErrCode(`prepare stmt2 from "deallocate prepare stmt0"`, mysql.ErrUnsupportedPs)
|
|
tk.MustGetErrCode(`prepare stmt4 from "prepare stmt3 from 'create table t1(a int, b int)'"`, mysql.ErrUnsupportedPs)
|
|
}
|
|
|
|
func TestIgnorePlanCache(t *testing.T) {
|
|
t.Parallel()
|
|
store, clean := testkit.CreateMockStore(t)
|
|
defer clean()
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
|
|
tk.MustExec("create table t (id int primary key, num int)")
|
|
tk.MustExec("insert into t values (1, 1)")
|
|
tk.MustExec("insert into t values (2, 2)")
|
|
tk.MustExec("insert into t values (3, 3)")
|
|
tk.MustExec("prepare stmt from 'select /*+ IGNORE_PLAN_CACHE() */ * from t where id=?'")
|
|
tk.MustExec("set @ignore_plan_doma = 1")
|
|
tk.MustExec("execute stmt using @ignore_plan_doma")
|
|
require.False(t, tk.Session().GetSessionVars().StmtCtx.UseCache)
|
|
}
|
|
|
|
type mockSessionManager2 struct {
|
|
se session.Session
|
|
killed int32
|
|
}
|
|
|
|
func (sm *mockSessionManager2) ShowTxnList() []*txninfo.TxnInfo {
|
|
panic("unimplemented!")
|
|
}
|
|
|
|
func (sm *mockSessionManager2) ShowProcessList() map[uint64]*util.ProcessInfo {
|
|
pl := make(map[uint64]*util.ProcessInfo)
|
|
if pi, ok := sm.GetProcessInfo(0); ok {
|
|
pl[pi.ID] = pi
|
|
}
|
|
return pl
|
|
}
|
|
|
|
func (sm *mockSessionManager2) GetProcessInfo(id uint64) (pi *util.ProcessInfo, notNil bool) {
|
|
pi = sm.se.ShowProcess()
|
|
if pi != nil {
|
|
notNil = true
|
|
}
|
|
return
|
|
}
|
|
|
|
func (sm *mockSessionManager2) Kill(connectionID uint64, query bool) {
|
|
atomic.StoreInt32(&sm.killed, 1)
|
|
atomic.StoreUint32(&sm.se.GetSessionVars().Killed, 1)
|
|
}
|
|
func (sm *mockSessionManager2) KillAllConnections() {}
|
|
func (sm *mockSessionManager2) UpdateTLSConfig(cfg *tls.Config) {}
|
|
func (sm *mockSessionManager2) ServerID() uint64 {
|
|
return 1
|
|
}
|
|
|
|
func TestPreparedStmtWithHint(t *testing.T) {
|
|
t.Parallel()
|
|
// see https://github.com/pingcap/tidb/issues/18535
|
|
store, dom, err := newStoreWithBootstrap()
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, store.Close())
|
|
dom.Close()
|
|
}()
|
|
|
|
se, err := session.CreateSession4Test(store)
|
|
require.NoError(t, err)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.SetSession(se)
|
|
|
|
sm := &mockSessionManager2{
|
|
se: se,
|
|
}
|
|
se.SetSessionManager(sm)
|
|
go dom.ExpensiveQueryHandle().SetSessionManager(sm).Run()
|
|
tk.MustExec("prepare stmt from \"select /*+ max_execution_time(100) */ sleep(10)\"")
|
|
tk.MustQuery("execute stmt").Check(testkit.Rows("1"))
|
|
require.Equal(t, int32(1), atomic.LoadInt32(&sm.killed))
|
|
}
|
|
|
|
func TestIssue29850(t *testing.T) {
|
|
store, dom, err := newStoreWithBootstrap()
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, store.Close())
|
|
dom.Close()
|
|
}()
|
|
|
|
orgEnable := plannercore.PreparedPlanCacheEnabled()
|
|
defer func() {
|
|
plannercore.SetPreparedPlanCache(orgEnable)
|
|
}()
|
|
plannercore.SetPreparedPlanCache(true)
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
tk.MustExec(`set tidb_enable_clustered_index=on`)
|
|
tk.MustExec("set @@tidb_enable_collect_execution_info=0")
|
|
tk.MustExec(`use test`)
|
|
tk.MustExec(`CREATE TABLE customer (
|
|
c_id int(11) NOT NULL,
|
|
c_d_id int(11) NOT NULL,
|
|
c_first varchar(16) DEFAULT NULL,
|
|
c_w_id int(11) NOT NULL,
|
|
c_last varchar(16) DEFAULT NULL,
|
|
c_credit char(2) DEFAULT NULL,
|
|
c_discount decimal(4,4) DEFAULT NULL,
|
|
PRIMARY KEY (c_w_id,c_d_id,c_id),
|
|
KEY idx_customer (c_w_id,c_d_id,c_last,c_first))`)
|
|
tk.MustExec(`CREATE TABLE warehouse (
|
|
w_id int(11) NOT NULL,
|
|
w_tax decimal(4,4) DEFAULT NULL,
|
|
PRIMARY KEY (w_id))`)
|
|
tk.MustExec(`prepare stmt from 'SELECT c_discount, c_last, c_credit, w_tax
|
|
FROM customer, warehouse
|
|
WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?'`)
|
|
tk.MustExec(`set @w_id=1262`)
|
|
tk.MustExec(`set @c_d_id=7`)
|
|
tk.MustExec(`set @c_id=1549`)
|
|
tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows())
|
|
tkProcess := tk.Session().ShowProcess()
|
|
ps := []*util.ProcessInfo{tkProcess}
|
|
tk.Session().SetSessionManager(&mockSessionManager1{PS: ps})
|
|
tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use PointGet
|
|
`Projection_7 0.00 root test.customer.c_discount, test.customer.c_last, test.customer.c_credit, test.warehouse.w_tax`,
|
|
`└─MergeJoin_8 0.00 root inner join, left key:test.customer.c_w_id, right key:test.warehouse.w_id`,
|
|
` ├─Point_Get_34(Build) 1.00 root table:warehouse handle:1262`,
|
|
` └─Point_Get_33(Probe) 1.00 root table:customer, clustered index:PRIMARY(c_w_id, c_d_id, c_id) `))
|
|
tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows())
|
|
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the cached plan
|
|
|
|
tk.MustExec(`create table t (a int primary key)`)
|
|
tk.MustExec(`insert into t values (1), (2)`)
|
|
tk.MustExec(`prepare stmt from 'select * from t where a>=? and a<=?'`)
|
|
tk.MustExec(`set @a1=1, @a2=2`)
|
|
tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1"))
|
|
tkProcess = tk.Session().ShowProcess()
|
|
ps = []*util.ProcessInfo{tkProcess}
|
|
tk.Session().SetSessionManager(&mockSessionManager1{PS: ps})
|
|
tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // cannot use PointGet since it contains a range condition
|
|
`Selection_7 1.00 root ge(test.t.a, 1), le(test.t.a, 1)`,
|
|
`└─TableReader_6 1.00 root data:TableRangeScan_5`,
|
|
` └─TableRangeScan_5 1.00 cop[tikv] table:t range:[1,1], keep order:false, stats:pseudo`))
|
|
tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2"))
|
|
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
|
|
|
|
tk.MustExec(`prepare stmt from 'select * from t where a=? or a=?'`)
|
|
tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1"))
|
|
tkProcess = tk.Session().ShowProcess()
|
|
ps = []*util.ProcessInfo{tkProcess}
|
|
tk.Session().SetSessionManager(&mockSessionManager1{PS: ps})
|
|
tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // cannot use PointGet since it contains a or condition
|
|
`Selection_7 1.00 root or(eq(test.t.a, 1), eq(test.t.a, 1))`,
|
|
`└─TableReader_6 1.00 root data:TableRangeScan_5`,
|
|
` └─TableRangeScan_5 1.00 cop[tikv] table:t range:[1,1], keep order:false, stats:pseudo`))
|
|
tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2"))
|
|
}
|