Files
tidb/planner/core/prepare_test.go

2809 lines
124 KiB
Go

// Copyright 2018 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 core_test
import (
"context"
"fmt"
"math"
"math/rand"
"strconv"
"strings"
"testing"
"time"
"github.com/pingcap/tidb/executor"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/infoschema"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/metrics"
"github.com/pingcap/tidb/parser"
"github.com/pingcap/tidb/parser/auth"
"github.com/pingcap/tidb/parser/terror"
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/util/hint"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/require"
)
func TestPointGetPreparedPlan4PlanCache(t *testing.T) {
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk1.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk1.MustExec("drop database if exists ps_text")
defer tk1.MustExec("drop database if exists ps_text")
tk1.MustExec("create database ps_text")
tk1.MustExec("use ps_text")
tk1.MustExec(`create table t (a int, b int, c int,
primary key k_a(a),
unique key k_b(b))`)
tk1.MustExec("insert into t values (1, 1, 1)")
tk1.MustExec("insert into t values (2, 2, 2)")
tk1.MustExec("insert into t values (3, 3, 3)")
pspk1Id, _, _, err := tk1.Session().PrepareStmt("select * from t where a = ?")
require.NoError(t, err)
tk1.Session().GetSessionVars().PreparedStmts[pspk1Id].(*core.PlanCacheStmt).StmtCacheable = false
ctx := context.Background()
// first time plan generated
_, err = tk1.Session().ExecutePreparedStmt(ctx, pspk1Id, expression.Args2Expressions4Test(0))
require.NoError(t, err)
// using the generated plan but with different params
_, err = tk1.Session().ExecutePreparedStmt(ctx, pspk1Id, expression.Args2Expressions4Test(nil))
require.NoError(t, err)
}
func TestPreparePointGetWithDML(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t(a int, unique key(a))`)
tk.MustExec(`insert into t values(1), (2)`)
// txn1 left a cached plan
tk.MustExec(`begin`)
tk.MustExec(`prepare stmt from 'update t set a = ? where a = ?'`)
tk.MustExec(`set @a=1`)
tk.MustExec(`execute stmt using @a, @a`)
tk.MustExec(`commit`)
// txn2 can reuse the cached plan generated by txn1 directly
tk.MustExec(`begin`)
tk.MustExec(`prepare stmt from 'update t set a = ? where a = ?'`)
tk.MustExec(`set @a=2`)
tk.MustExec(`execute stmt using @a, @a`) // can reuse the cached plan directly
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustExec(`rollback`)
}
func TestPrepareIgnoreCloseStmtCmd(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t(a int, unique key(a))`)
// disable the ignore-stmt-cmd
tk.MustExec(`set @@tidb_ignore_prepared_cache_close_stmt=0`)
tk.MustQuery(`select @@tidb_ignore_prepared_cache_close_stmt`).Check(testkit.Rows("0"))
tk.MustExec(`prepare stmt from 'select * from t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustExec(`deallocate prepare stmt`) // close/deallocate this stmt
tk.MustExec(`prepare stmt from 'select * from t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) // cannot reuse last plan since it is closed
// enable the ignore-stmt-cmd
tk.MustExec(`set tidb_ignore_prepared_cache_close_stmt=1`)
tk.MustQuery(`select @@tidb_ignore_prepared_cache_close_stmt`).Check(testkit.Rows("1"))
tk.MustExec(`prepare stmt from 'select * from t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustExec(`deallocate prepare stmt`) // close/deallocate this stmt
tk.MustExec(`prepare stmt from 'select * from t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can reuse last plan since last close-cmd was ignored
}
func TestRandomFlushPlanCache(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2 := testkit.NewTestKit(t, store)
var err error
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec("drop table if exists t2")
tk.MustExec("create table t1(id int, a int, b int, key(a))")
tk.MustExec("create table t2(id int, a int, b int, key(a))")
tk.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk.MustExec("prepare stmt4 from 'SELECT * from t2';")
tk.MustExec("prepare stmt5 from 'SELECT * from t2 where id = 1';")
tk2.MustExec("use test")
tk2.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk2.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk2.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk2.MustExec("prepare stmt4 from 'SELECT * from t2';")
tk2.MustExec("prepare stmt5 from 'SELECT * from t2 where id = 1';")
prepareNum := 5
execStmts := make([]string, 0, prepareNum)
for i := 1; i <= prepareNum; i++ {
execStmt := fmt.Sprintf("execute stmt%d", i)
execStmts = append(execStmts, execStmt)
}
rand.Seed(time.Now().Unix())
for i := 0; i < 10; i++ {
// Warm up to make sure all the plans are in the cache.
for _, execStmt := range execStmts {
tk.MustExec(execStmt)
tk.MustExec(execStmt)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec(execStmt)
tk2.MustExec(execStmt)
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
for j := 0; j < 10; j++ {
session1PC, session2PC := "1", "1"
// random to flush the plan cache
randNum := rand.Intn(10)
if randNum == 0 {
session1PC, session2PC = "0", "0"
if j%2 == 0 {
err = tk.ExecToErr("admin flush instance plan_cache;")
} else {
err = tk2.ExecToErr("admin flush instance plan_cache;")
}
require.NoError(t, err)
} else if randNum == 1 {
session1PC = "0"
err = tk.ExecToErr("admin flush session plan_cache;")
require.NoError(t, err)
} else if randNum == 2 {
session2PC = "0"
err = tk2.ExecToErr("admin flush session plan_cache;")
require.NoError(t, err)
}
for _, execStmt := range execStmts {
tk.MustExec(execStmt)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows(session1PC))
tk2.MustExec(execStmt)
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows(session2PC))
}
}
err = tk.ExecToErr("admin flush instance plan_cache;")
require.NoError(t, err)
}
err = tk.ExecToErr("admin flush global plan_cache;")
require.EqualError(t, err, "Do not support the 'admin flush global scope.'")
}
func TestFlushPlanCache(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2 := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec("drop table if exists t2")
tk.MustExec("create table t1(id int, a int, b int, key(a))")
tk.MustExec("create table t2(id int, a int, b int, key(a))")
tk.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk.MustExec("execute stmt1;")
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk.MustExec("execute stmt2;")
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk.MustExec("execute stmt3;")
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("use test")
tk2.MustExec("drop table if exists t1")
tk2.MustExec("drop table if exists t2")
tk2.MustExec("create table t1(id int, a int, b int, key(a))")
tk2.MustExec("create table t2(id int, a int, b int, key(a))")
tk2.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk2.MustExec("execute stmt1;")
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk2.MustExec("execute stmt2;")
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk2.MustExec("execute stmt3;")
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("admin flush session plan_cache;")
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("admin flush instance plan_cache;")
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
err := tk.ExecToErr("admin flush global plan_cache;")
require.EqualError(t, err, "Do not support the 'admin flush global scope.'")
}
func TestFlushPlanCacheWithoutPCEnable(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=0`)
tk2.MustExec(`set tidb_enable_prepared_plan_cache=0`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec("drop table if exists t2")
tk.MustExec("create table t1(id int, a int, b int, key(a))")
tk.MustExec("create table t2(id int, a int, b int, key(a))")
tk.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk.MustExec("execute stmt1;")
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk.MustExec("execute stmt2;")
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk.MustExec("execute stmt3;")
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("use test")
tk2.MustExec("drop table if exists t1")
tk2.MustExec("drop table if exists t2")
tk2.MustExec("create table t1(id int, a int, b int, key(a))")
tk2.MustExec("create table t2(id int, a int, b int, key(a))")
tk2.MustExec("prepare stmt1 from 'SELECT * from t1,t2 where t1.id = t2.id';")
tk2.MustExec("execute stmt1;")
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("prepare stmt2 from 'SELECT * from t1';")
tk2.MustExec("execute stmt2;")
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("prepare stmt3 from 'SELECT * from t1 where id = 1';")
tk2.MustExec("execute stmt3;")
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("admin flush session plan_cache;")
tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1105 The plan cache is disable. So there no need to flush the plan cache"))
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("admin flush instance plan_cache;")
tk2.MustQuery("show warnings;").Check(testkit.Rows("Warning 1105 The plan cache is disable. So there no need to flush the plan cache"))
tk2.MustExec("execute stmt1;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt2;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk2.MustExec("execute stmt3;")
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt1;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt2;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute stmt3;")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
err := tk.ExecToErr("admin flush global plan_cache;")
require.EqualError(t, err, "Do not support the 'admin flush global scope.'")
}
func TestPrepareCache(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int primary key, b int, c int, index idx1(b, a), index idx2(b))")
tk.MustExec("insert into t values(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 1, 2)")
tk.MustExec(`prepare stmt1 from "select * from t use index(idx1) where a = ? and b = ?"`)
tk.MustExec(`prepare stmt2 from "select a, b from t use index(idx2) where b = ?"`)
tk.MustExec(`prepare stmt3 from "select * from t where a = ?"`)
tk.MustExec("set @a=1, @b=1")
// When executing one statement at the first time, we don't use cache, so we need to execute it at least twice to test the cache.
tk.MustQuery("execute stmt1 using @a, @b").Check(testkit.Rows("1 1 1"))
tk.MustQuery("execute stmt1 using @a, @b").Check(testkit.Rows("1 1 1"))
tk.MustQuery("execute stmt2 using @b").Check(testkit.Rows("1 1", "6 1"))
tk.MustQuery("execute stmt2 using @b").Check(testkit.Rows("1 1", "6 1"))
tk.MustQuery("execute stmt3 using @a").Check(testkit.Rows("1 1 1"))
tk.MustQuery("execute stmt3 using @a").Check(testkit.Rows("1 1 1"))
tk.MustExec(`prepare stmt4 from "select * from t where a > ?"`)
tk.MustExec("set @a=3")
tk.MustQuery("execute stmt4 using @a").Check(testkit.Rows("4 4 4", "5 5 5", "6 1 2"))
tk.MustQuery("execute stmt4 using @a").Check(testkit.Rows("4 4 4", "5 5 5", "6 1 2"))
tk.MustExec(`prepare stmt5 from "select c from t order by c"`)
tk.MustQuery("execute stmt5").Check(testkit.Rows("1", "2", "2", "3", "4", "5"))
tk.MustQuery("execute stmt5").Check(testkit.Rows("1", "2", "2", "3", "4", "5"))
tk.MustExec(`prepare stmt6 from "select distinct a from t order by a"`)
tk.MustQuery("execute stmt6").Check(testkit.Rows("1", "2", "3", "4", "5", "6"))
tk.MustQuery("execute stmt6").Check(testkit.Rows("1", "2", "3", "4", "5", "6"))
// test privilege change
rootSe := tk.Session()
tk.MustExec("drop table if exists tp")
tk.MustExec(`create table tp(c1 int, c2 int, primary key (c1))`)
tk.MustExec(`insert into tp values(1, 1), (2, 2), (3, 3)`)
tk.MustExec(`create user 'u_tp'@'localhost'`)
tk.MustExec(`grant select on test.tp to u_tp@'localhost';`)
// user u_tp
userSess := newSession(t, store, "test")
require.NoError(t, userSess.Auth(&auth.UserIdentity{Username: "u_tp", Hostname: "localhost"}, nil, nil, nil))
mustExec(t, userSess, `prepare ps_stp_r from 'select * from tp where c1 > ?'`)
mustExec(t, userSess, `set @p2 = 2`)
tk.SetSession(userSess)
tk.MustQuery(`execute ps_stp_r using @p2`).Check(testkit.Rows("3 3"))
tk.MustQuery(`execute ps_stp_r using @p2`).Check(testkit.Rows("3 3"))
tk.MustQuery(`execute ps_stp_r using @p2`).Check(testkit.Rows("3 3"))
// root revoke
tk.SetSession(rootSe)
tk.MustExec(`revoke all on test.tp from 'u_tp'@'localhost';`)
// user u_tp
tk.SetSession(userSess)
_, err := tk.Exec(`execute ps_stp_r using @p2`)
require.Error(t, err)
// grant again
tk.SetSession(rootSe)
tk.MustExec(`grant select on test.tp to u_tp@'localhost';`)
// user u_tp
tk.SetSession(userSess)
tk.MustQuery(`execute ps_stp_r using @p2`).Check(testkit.Rows("3 3"))
tk.MustQuery(`execute ps_stp_r using @p2`).Check(testkit.Rows("3 3"))
// restore
tk.SetSession(rootSe)
tk.MustExec("drop table if exists tp")
tk.MustExec(`DROP USER 'u_tp'@'localhost';`)
}
func TestPrepareCacheIndexScan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, c int, primary key (a, b))")
tk.MustExec("insert into t values(1, 1, 2), (1, 2, 3), (1, 3, 3), (2, 1, 2), (2, 2, 3), (2, 3, 3)")
tk.MustExec(`prepare stmt1 from "select a, c from t where a = ? and c = ?"`)
tk.MustExec("set @a=1, @b=3")
// When executing one statement at the first time, we don't use cache, so we need to execute it at least twice to test the cache.
tk.MustQuery("execute stmt1 using @a, @b").Check(testkit.Rows("1 3", "1 3"))
tk.MustQuery("execute stmt1 using @a, @b").Check(testkit.Rows("1 3", "1 3"))
}
// dtype: tinyint, unsigned, float, decimal, year
// rtype: null, valid, out-of-range, invalid, str, exists
func randValue(tk *testkit.TestKit, tbl, col, dtype, rtype string) string {
if rtype == "" {
rtypes := []string{"null", "valid", "out-of-range", "invalid", "str", "exists"}
rtype = rtypes[rand.Intn(len(rtypes))]
}
if rtype == "null" {
return "null"
}
if rtype == "exists" {
res := tk.MustQuery(fmt.Sprintf("select %v from %v limit 1", col, tbl)).Rows()[0][0].(string)
if res == "<nil>" {
res = "null"
}
return res
}
switch dtype {
case "tinyint":
switch rtype {
case "valid":
return fmt.Sprintf("%v", -128+rand.Intn(256))
case "out-of-range":
return fmt.Sprintf("%v", 128+rand.Intn(1024))
case "invalid":
return "'invalid-tinyint'"
case "str":
return fmt.Sprintf("'%v'", -128+rand.Intn(256))
}
case "unsigned":
switch rtype {
case "valid":
return fmt.Sprintf("%v", rand.Intn(4294967295))
case "out-of-range":
return fmt.Sprintf("-%v", rand.Intn(4294967295))
case "invalid":
return "'invalid-unsigned-int'"
case "str":
return fmt.Sprintf("'%v'", rand.Intn(4294967295))
}
case "float":
switch rtype {
case "valid":
return fmt.Sprintf("%v%.4fE%v", []string{"+", "-"}[rand.Intn(2)], rand.Float32(), rand.Intn(38))
case "out-of-range":
return fmt.Sprintf("%v%.4fE%v", []string{"+", "-"}[rand.Intn(2)], rand.Float32(), rand.Intn(100)+38)
case "invalid":
return "'invalid-float'"
case "str":
return fmt.Sprintf("'%v%.4fE%v'", []string{"+", "-"}[rand.Intn(2)], rand.Float32(), rand.Intn(38))
}
case "decimal": // (10,2)
switch rtype {
case "valid":
return fmt.Sprintf("%v%v.%v", []string{"+", "-"}[rand.Intn(2)], rand.Intn(99999999), rand.Intn(100))
case "out-of-range":
switch rand.Intn(2) {
case 0:
return fmt.Sprintf("%v%v.%v", []string{"+", "-"}[rand.Intn(2)], rand.Intn(99999999), rand.Intn(100000)+100000)
case 1:
return fmt.Sprintf("%v%v.%v", []string{"+", "-"}[rand.Intn(2)], rand.Intn(99999999)+99999999+1, rand.Intn(100))
}
case "invalid":
return "'invalid-decimal'"
case "str":
return fmt.Sprintf("'%v%v.%v'", []string{"+", "-"}[rand.Intn(2)], rand.Intn(99999999), rand.Intn(100))
}
case "year":
switch rtype {
case "valid":
return fmt.Sprintf("%v", 1901+rand.Intn(2155-1901))
case "out-of-range":
return fmt.Sprintf("%v", 2156+rand.Intn(2155-1901))
case "invalid":
return "'invalid-year'"
case "str":
return fmt.Sprintf("'%v'", 1901+rand.Intn(2155-1901))
}
}
return "'invalid-type-" + dtype + "'"
}
func TestPrepareCacheChangingParamType(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t_tinyint, t_unsigned, t_float, t_decimal, t_year`)
tk.MustExec(`create table t_tinyint (a tinyint, b tinyint, key(a))`)
tk.MustExec(`create table t_unsigned (a int unsigned, b int unsigned, key(a))`)
tk.MustExec(`create table t_float(a float, b float, key(a))`)
tk.MustExec(`create table t_decimal(a decimal(10,2), b decimal(10,2), key(a))`)
tk.MustExec(`create table t_year(a year, b year, key(a))`)
for _, dtype := range []string{"tinyint", "unsigned", "float", "decimal", "year"} {
tbl := "t_" + dtype
for i := 0; i < 10; i++ {
tk.MustExec(fmt.Sprintf("insert into %v values (%v, %v)", tbl, randValue(nil, "", "", dtype, "valid"), randValue(nil, "", "", dtype, "valid")))
}
tk.MustExec(fmt.Sprintf("insert into %v values (null, null)", tbl))
tk.MustExec(fmt.Sprintf("insert into %v values (%v, null)", tbl, randValue(nil, "", "", dtype, "valid")))
tk.MustExec(fmt.Sprintf("insert into %v values (null, %v)", tbl, randValue(nil, "", "", dtype, "valid")))
for round := 0; round < 10; round++ {
tk.MustExec(fmt.Sprintf(`prepare s1 from 'select * from %v where a=?'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s2 from 'select * from %v where b=?'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s3 from 'select * from %v where a in (?, ?, ?)'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s4 from 'select * from %v where b in (?, ?, ?)'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s5 from 'select * from %v where a>?'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s6 from 'select * from %v where b>?'`, tbl))
tk.MustExec(fmt.Sprintf(`prepare s7 from 'select * from %v where a>? and b>?'`, tbl))
for query := 0; query < 10; query++ {
a1, a2, a3 := randValue(tk, tbl, "a", dtype, ""), randValue(tk, tbl, "a", dtype, ""), randValue(tk, tbl, "a", dtype, "")
b1, b2, b3 := randValue(tk, tbl, "b", dtype, ""), randValue(tk, tbl, "b", dtype, ""), randValue(tk, tbl, "b", dtype, "")
tk.MustExec(fmt.Sprintf(`set @a1=%v,@a2=%v,@a3=%v`, a1, a2, a3))
tk.MustExec(fmt.Sprintf(`set @b1=%v,@b2=%v,@b3=%v`, b1, b2, b3))
compareResult := func(sql1, sql2 string) {
raw, err := tk.Exec(sql1)
if err != nil {
require.Error(t, tk.ExecToErr(sql2))
return
}
rs := tk.ResultSetToResult(raw, fmt.Sprintf("sql1:%s, sql2:%v", sql1, sql2))
rs.Sort().Check(tk.MustQuery(sql2).Sort().Rows())
}
compareResult(`execute s1 using @a1`, fmt.Sprintf(`select * from %v where a=%v`, tbl, a1))
compareResult(`execute s2 using @b1`, fmt.Sprintf(`select * from %v where b=%v`, tbl, b1))
compareResult(`execute s3 using @a1,@a2,@a3`, fmt.Sprintf(`select * from %v where a in (%v,%v,%v)`, tbl, a1, a2, a3))
compareResult(`execute s4 using @b1,@b2,@b3`, fmt.Sprintf(`select * from %v where b in (%v,%v,%v)`, tbl, b1, b2, b3))
compareResult(`execute s5 using @a1`, fmt.Sprintf(`select * from %v where a>%v`, tbl, a1))
compareResult(`execute s6 using @b1`, fmt.Sprintf(`select * from %v where b>%v`, tbl, b1))
compareResult(`execute s7 using @a1,@b1`, fmt.Sprintf(`select * from %v where a>%v and b>%v`, tbl, a1, b1))
}
}
}
}
func TestPrepareCacheChangeCharsetCollation(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t (a varchar(64))`)
tk.MustExec(`set character_set_connection=utf8`)
tk.MustExec(`prepare s from 'select * from t where a=?'`)
tk.MustExec(`set @x='a'`)
tk.MustExec(`execute s using @x`)
tk.MustExec(`set @x='b'`)
tk.MustExec(`execute s using @x`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustExec(`set character_set_connection=latin1`)
tk.MustExec(`set @x='c'`)
tk.MustExec(`execute s using @x`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) // cannot reuse the previous plan since the charset is changed
tk.MustExec(`set @x='d'`)
tk.MustExec(`execute s using @x`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustExec(`set collation_connection=binary`)
tk.MustExec(`set @x='e'`)
tk.MustExec(`execute s using @x`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) // cannot reuse the previous plan since the collation is changed
tk.MustExec(`set @x='f'`)
tk.MustExec(`execute s using @x`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}
func TestPrepareCacheDeferredFunction(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec("create table t1 (id int PRIMARY KEY, c1 TIMESTAMP(3) NOT NULL DEFAULT '2019-01-14 10:43:20', KEY idx1 (c1))")
tk.MustExec("prepare sel1 from 'select id, c1 from t1 where c1 < now(3)'")
sql1 := "execute sel1"
expectedPattern := `IndexReader\(Index\(t1.idx1\)\[\[-inf,[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]\)\]\)`
var cnt [2]float64
var planStr [2]string
metrics.ResettablePlanCacheCounterFortTest = true
metrics.PlanCacheCounter.Reset()
counter := metrics.PlanCacheCounter.WithLabelValues("prepare")
ctx := context.TODO()
p := parser.New()
p.SetParserConfig(parser.ParserConfig{EnableWindowFunction: true, EnableStrictDoubleTypeCheck: true})
for i := 0; i < 2; i++ {
stmt, err := p.ParseOneStmt(sql1, "", "")
require.NoError(t, err)
is := tk.Session().GetInfoSchema().(infoschema.InfoSchema)
builder, _ := core.NewPlanBuilder().Init(tk.Session(), is, &hint.BlockHintProcessor{})
p, err := builder.Build(ctx, stmt)
require.NoError(t, err)
execPlan, ok := p.(*core.Execute)
require.True(t, ok)
err = executor.ResetContextOfStmt(tk.Session(), stmt)
require.NoError(t, err)
plan, _, err := core.GetPlanFromSessionPlanCache(ctx, tk.Session(), false, is, execPlan.PrepStmt, execPlan.Params)
require.NoError(t, err)
planStr[i] = core.ToString(plan)
require.Regexpf(t, expectedPattern, planStr[i], "for %dth %s", i, sql1)
pb := &dto.Metric{}
err = counter.Write(pb)
require.NoError(t, err)
cnt[i] = pb.GetCounter().GetValue()
require.Equal(t, float64(i), cnt[i])
time.Sleep(time.Millisecond * 10)
}
require.Lessf(t, planStr[0], planStr[1], "plan 1: %v, plan 2: %v", planStr[0], planStr[1])
}
func TestPrepareCacheNow(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec(`prepare stmt1 from "select now(), current_timestamp(), utc_timestamp(), unix_timestamp(), sleep(0.1), now(), current_timestamp(), utc_timestamp(), unix_timestamp()"`)
// When executing one statement at the first time, we don't usTestPrepareCacheDeferredFunctione cache, so we need to execute it at least twice to test the cache.
_ = tk.MustQuery("execute stmt1").Rows()
rs := tk.MustQuery("execute stmt1").Rows()
require.Equal(t, rs[0][5].(string), rs[0][0].(string))
require.Equal(t, rs[0][6].(string), rs[0][1].(string))
require.Equal(t, rs[0][7].(string), rs[0][2].(string))
require.Equal(t, rs[0][8].(string), rs[0][3].(string))
}
func TestPrepareOverMaxPreparedStmtCount(t *testing.T) {
t.Skip("unstable, skip it and fix it before 20210705")
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
// test prepare and deallocate.
prePrepared := readGaugeInt(metrics.PreparedStmtGauge)
tk.MustExec(`prepare stmt1 from "select 1"`)
onePrepared := readGaugeInt(metrics.PreparedStmtGauge)
require.Equal(t, onePrepared, prePrepared+1)
tk.MustExec(`deallocate prepare stmt1`)
deallocPrepared := readGaugeInt(metrics.PreparedStmtGauge)
require.Equal(t, deallocPrepared, prePrepared)
// test change global limit and make it affected in test session.
tk.MustQuery("select @@max_prepared_stmt_count").Check(testkit.Rows("-1"))
tk.MustExec("set @@global.max_prepared_stmt_count = 2")
tk.MustQuery("select @@global.max_prepared_stmt_count").Check(testkit.Rows("2"))
// test close session to give up all prepared stmt
tk.MustExec(`prepare stmt2 from "select 1"`)
prePrepared = readGaugeInt(metrics.PreparedStmtGauge)
tk.Session().Close()
drawPrepared := readGaugeInt(metrics.PreparedStmtGauge)
require.Equal(t, drawPrepared, prePrepared-1)
// test meet max limit.
tk.RefreshSession()
tk.MustQuery("select @@max_prepared_stmt_count").Check(testkit.Rows("2"))
for i := 1; ; i++ {
prePrepared = readGaugeInt(metrics.PreparedStmtGauge)
if prePrepared >= 2 {
_, err := tk.Exec(`prepare stmt` + strconv.Itoa(i) + ` from "select 1"`)
require.True(t, terror.ErrorEqual(err, variable.ErrMaxPreparedStmtCountReached))
break
}
_, err := tk.Exec(`prepare stmt` + strconv.Itoa(i) + ` from "select 1"`)
require.NoError(t, err)
}
}
// unit test for issue https://github.com/pingcap/tidb/issues/8518
func TestPrepareTableAsNameOnGroupByWithCache(t *testing.T) {
t.Skip("unstable, skip it and fix it before 20210702")
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec(`create table t1 (
id int(11) unsigned not null primary key auto_increment,
partner_id varchar(35) not null,
t1_status_id int(10) unsigned
);`)
tk.MustExec(`insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"), ("3", "partner3", "10"), ("4", "partner4", "10");`)
tk.MustExec("drop table if exists t3")
tk.MustExec(`create table t3 (
id int(11) not null default '0',
preceding_id int(11) not null default '0',
primary key (id,preceding_id)
);`)
tk.MustExec(`prepare stmt from 'SELECT DISTINCT t1.partner_id
FROM t1
LEFT JOIN t3 ON t1.id = t3.id
LEFT JOIN t1 pp ON pp.id = t3.preceding_id
GROUP BY t1.id ;'`)
tk.MustQuery("execute stmt").Sort().Check(testkit.Rows("partner1", "partner2", "partner3", "partner4"))
}
func TestPrepareCachePointGetInsert(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t1 (a int, b int, primary key(a))")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3)")
tk.MustExec("create table t2 (a int, b int, primary key(a))")
tk.MustExec(`prepare stmt1 from "insert into t2 select * from t1 where a=?"`)
tk.MustExec("set @a=1")
tk.MustExec("execute stmt1 using @a")
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery("select * from t2 order by a").Check(testkit.Rows("1 1"))
tk.MustExec("set @a=2")
tk.MustExec("execute stmt1 using @a")
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustQuery("select * from t2 order by a").Check(testkit.Rows("1 1", "2 2"))
tk.MustExec("set @a=3")
tk.MustExec("execute stmt1 using @a")
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustQuery("select * from t2 order by a").Check(testkit.Rows("1 1", "2 2", "3 3"))
}
func TestIssue31280(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists UK_MU15569;")
tk.MustExec("CREATE TABLE `UK_MU15569` (" +
"`COL1` varbinary(20) DEFAULT NULL," +
"`COL2` bit(16) DEFAULT NULL," +
"`COL3` time DEFAULT NULL," +
"`COL4` int(11) DEFAULT NULL," +
"UNIQUE KEY `U_M_COL4` (`COL1`(10),`COL2`)," +
"UNIQUE KEY `U_M_COL5` (`COL3`,`COL2`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into UK_MU15569 values(0x1C4FDBA09B42D999AC3019B6A9C0C787FBA08446, 0xCA74, '-836:46:08', 735655453);")
tk.MustExec(`prepare stmt from 'select * from UK_MU15569 where col2 >= ? and col1 is not null and col3 = ?;';`)
tk.MustExec("set @a=-32373, @b='545:50:46.85487';")
// The tableDual plan can not be cached.
res := tk.MustQuery("execute stmt using @a,@b;")
require.Len(t, res.Rows(), 0)
tk.MustExec("set @a=-27225, @b='-836:46:08';")
res = tk.MustQuery("execute stmt using @a,@b;")
require.Len(t, res.Rows(), 1)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @a,@b;")
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
res1 := tk.MustQuery("select * from UK_MU15569 where col2 >= -27225 and col1 is not null and col3 = '-836:46:08';")
require.Equal(t, res1.Rows(), res.Rows())
}
func TestIssue31375(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists IDT_MULTI15844STROBJSTROBJ;")
tk.MustExec("CREATE TABLE `IDT_MULTI15844STROBJSTROBJ` (" +
"`COL1` enum('bb','aa') DEFAULT NULL," +
"`COL2` smallint(41) DEFAULT NULL," +
"`COL3` year(4) DEFAULT NULL," +
"KEY `U_M_COL4` (`COL1`,`COL2`)," +
"KEY `U_M_COL5` (`COL3`,`COL2`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into IDT_MULTI15844STROBJSTROBJ values('bb', -16994, 1987);")
tk.MustExec(`prepare stmt from 'SELECT/*+ HASH_JOIN(t1, t2) */ t2.* FROM IDT_MULTI15844STROBJSTROBJ t1 LEFT JOIN IDT_MULTI15844STROBJSTROBJ t2 ON t1.col1 = t2.col1 WHERE t1.col2 BETWEEN ? AND ? AND t1.col1 >= ?;';`)
tk.MustExec("set @a=752400293960, @b=258241896853, @c='none';")
// The tableDual plan can not be cached.
tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows())
tk.MustExec("set @a=-170756280585, @b=3756, @c='aa';")
tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows("bb -16994 1987"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @a,@b,@c;")
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}
// nolint:unused
func readGaugeInt(g prometheus.Gauge) int {
ch := make(chan prometheus.Metric, 1)
g.Collect(ch)
m := <-ch
mm := &dto.Metric{}
err := m.Write(mm)
if err != nil {
panic(err)
}
return int(mm.GetGauge().GetValue())
}
// unit test for issue https://github.com/pingcap/tidb/issues/9478
func TestPrepareWithWindowFunction(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("set @@tidb_enable_window_function = 1")
defer tk.MustExec("set @@tidb_enable_window_function = 0")
tk.MustExec("use test")
tk.MustExec("create table window_prepare(a int, b double)")
tk.MustExec("insert into window_prepare values(1, 1.1), (2, 1.9)")
tk.MustExec("prepare stmt1 from 'select row_number() over() from window_prepare';")
// Test the unnamed window can be executed successfully.
tk.MustQuery("execute stmt1").Check(testkit.Rows("1", "2"))
// Test the stmt can be prepared successfully.
tk.MustExec("prepare stmt2 from 'select count(a) over (order by a rows between ? preceding and ? preceding) from window_prepare'")
tk.MustExec("set @a=0, @b=1;")
tk.MustQuery("execute stmt2 using @a, @b").Check(testkit.Rows("0", "0"))
}
func TestPrepareWindowFunctionWithoutParamsCheck(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=0`)
tk.MustExec("set @@tidb_enable_window_function = 1")
defer tk.MustExec("set @@tidb_enable_window_function = 0")
tk.MustExec("use test")
tk.MustExec("CREATE TABLE t1 (d DOUBLE, id INT, sex CHAR(1), n INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(n));")
tk.MustExec("insert into t1(d, id, sex) values (1.0, 1, 'M'),(2.0, 2, 'F'),(3.0, 3, 'F'),(4.0, 4, 'F'),(5.0, 5, 'M')")
// prepare phase, we don't check the window function args.
tk.MustExec("prepare p from \"select id, sex, lead(id, ?) over () from t1\"")
// execute phase, we do check the window function args (param is initialized).
err := tk.ExecToErr("execute p using @p1")
require.NotNil(t, err)
require.EqualError(t, err, "[planner:1210]Incorrect arguments to lead")
tk.MustExec("set @p1 = 3")
// execute phase, we do check the window function args (param is valid).
tk.MustQuery("execute p using @p1").Check(testkit.Rows("1 M 4", "2 F 5", "3 F <nil>", "4 F <nil>", "5 M <nil>"))
// execute phase, we do check the window function args (param is invalid).
tk.MustExec("PREPARE p FROM \"SELECT id, sex, LEAD(id, ?) OVER (), ntile(?) over() FROM t1\"")
tk.MustExec("set @p2 = 3")
tk.MustQuery("execute p using @p1, @p2").Check(testkit.Rows("1 M 4 1", "2 F 5 1", "3 F <nil> 2", "4 F <nil> 2", "5 M <nil> 3"))
tk.MustExec("set @p2 = 0") // ntile doesn't allow param to be 0
err = tk.ExecToErr("execute p using @p1, @p2")
require.NotNil(t, err)
require.EqualError(t, err, "[planner:1210]Incorrect arguments to ntile")
}
func TestPrepareWithSnapshot(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
safePointName := "tikv_gc_safe_point"
safePointValue := "20060102-15:04:05 -0700"
safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)"
updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s')
ON DUPLICATE KEY
UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment)
tk.MustExec(updateSafePoint)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(id int primary key, v int)")
tk.MustExec("insert into t select 1, 2")
tk.MustExec("begin")
ts := tk.MustQuery("select @@tidb_current_ts").Rows()[0][0].(string)
tk.MustExec("commit")
tk.MustExec("update t set v = 3 where id = 1")
tk.MustExec("prepare s1 from 'select * from t where id = 1';")
tk.MustExec("prepare s2 from 'select * from t';")
tk.MustExec("set @@tidb_snapshot = " + ts)
tk.MustQuery("execute s1").Check(testkit.Rows("1 2"))
tk.MustQuery("execute s2").Check(testkit.Rows("1 2"))
}
func TestPrepareForGroupByItems(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(id int, v int)")
tk.MustExec("insert into t(id, v) values(1, 2),(1, 2),(2, 3);")
tk.MustExec("prepare s1 from 'select max(v) from t group by floor(id/?)';")
tk.MustExec("set @a=2;")
tk.MustQuery("execute s1 using @a;").Sort().Check(testkit.Rows("2", "3"))
tk.MustExec("prepare s1 from 'select max(v) from t group by ?';")
tk.MustExec("set @a=2;")
err := tk.ExecToErr("execute s1 using @a;")
require.EqualError(t, err, "Unknown column '2' in 'group statement'")
tk.MustExec("set @a=2.0;")
tk.MustQuery("execute s1 using @a;").Check(testkit.Rows("3"))
}
func TestPrepareCacheForClusteredIndex(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1(k varchar(100) primary key clustered, v1 int, v2 int)")
tk.MustExec("insert into t1 (k, v1, v2) values('a', 1, 2), ('b', 1, 1)")
tk.MustExec("create table t2(k varchar(100) primary key clustered, v int)")
tk.MustExec("insert into t2 (k, v) values('c', 100)")
tk.MustExec(`prepare prepare_1 from " select v2, v from t1 left join t2 on v1 != v2 "`)
tk.MustQuery("execute prepare_1").Check(testkit.Rows("2 100", "1 <nil>"))
tk.MustQuery("execute prepare_1").Check(testkit.Rows("2 100", "1 <nil>"))
}
func TestPrepareCacheForPartition(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
for _, pruneMode := range []string{string(variable.Static), string(variable.Dynamic)} {
tk.MustExec("set @@tidb_partition_prune_mode = '" + pruneMode + "'")
// Test for PointGet and IndexRead.
tk.MustExec("drop table if exists t_index_read")
tk.MustExec("create table t_index_read (id int, k int, c varchar(10), primary key (id, k)) partition by hash(id+k) partitions 10")
tk.MustExec("insert into t_index_read values (1, 2, 'abc'), (3, 4, 'def'), (5, 6, 'xyz')")
tk.MustExec("prepare stmt1 from 'select c from t_index_read where id = ? and k = ?;'")
tk.MustExec("set @id=1, @k=2")
// When executing one statement at the first time, we don't use cache, so we need to execute it at least twice to test the cache.
tk.MustQuery("execute stmt1 using @id, @k").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt1 using @id, @k").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5, @k=6")
tk.MustQuery("execute stmt1 using @id, @k").Check(testkit.Rows("xyz"))
tk.MustExec("prepare stmt2 from 'select c from t_index_read where id = ? and k = ? and 1 = 1;'")
tk.MustExec("set @id=1, @k=2")
tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5, @k=6")
tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("xyz"))
// Test for TableScan.
tk.MustExec("drop table if exists t_table_read")
tk.MustExec("create table t_table_read (id int, k int, c varchar(10), primary key(id)) partition by hash(id) partitions 10")
tk.MustExec("insert into t_table_read values (1, 2, 'abc'), (3, 4, 'def'), (5, 6, 'xyz')")
tk.MustExec("prepare stmt3 from 'select c from t_index_read where id = ?;'")
tk.MustExec("set @id=1")
// When executing one statement at the first time, we don't use cache, so we need to execute it at least twice to test the cache.
tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5")
tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("xyz"))
tk.MustExec("prepare stmt4 from 'select c from t_index_read where id = ? and k = ?'")
tk.MustExec("set @id=1, @k=2")
tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5, @k=6")
tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("xyz"))
// Query on range partition tables should not raise error.
tk.MustExec("drop table if exists t_range_index")
tk.MustExec("create table t_range_index (id int, k int, c varchar(10), primary key(id)) partition by range(id) ( PARTITION p0 VALUES LESS THAN (4), PARTITION p1 VALUES LESS THAN (14),PARTITION p2 VALUES LESS THAN (20) )")
tk.MustExec("insert into t_range_index values (1, 2, 'abc'), (5, 4, 'def'), (13, 6, 'xyz'), (17, 6, 'hij')")
tk.MustExec("prepare stmt5 from 'select c from t_range_index where id = ?'")
tk.MustExec("set @id=1")
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5")
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("def"))
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("def"))
tk.MustExec("set @id=13")
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("xyz"))
tk.MustExec("set @id=17")
tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("hij"))
tk.MustExec("drop table if exists t_range_table")
tk.MustExec("create table t_range_table (id int, k int, c varchar(10)) partition by range(id) ( PARTITION p0 VALUES LESS THAN (4), PARTITION p1 VALUES LESS THAN (14),PARTITION p2 VALUES LESS THAN (20) )")
tk.MustExec("insert into t_range_table values (1, 2, 'abc'), (5, 4, 'def'), (13, 6, 'xyz'), (17, 6, 'hij')")
tk.MustExec("prepare stmt6 from 'select c from t_range_table where id = ?'")
tk.MustExec("set @id=1")
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5")
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("def"))
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("def"))
tk.MustExec("set @id=13")
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("xyz"))
tk.MustExec("set @id=17")
tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("hij"))
// Test for list partition
tk.MustExec("drop table if exists t_list_index")
tk.MustExec("create table t_list_index (id int, k int, c varchar(10), primary key(id)) partition by list (id*2-id) ( PARTITION p0 VALUES IN (1,2,3,4), PARTITION p1 VALUES IN (5,6,7,8),PARTITION p2 VALUES IN (9,10,11,12))")
tk.MustExec("insert into t_list_index values (1, 1, 'abc'), (5, 5, 'def'), (9, 9, 'xyz'), (12, 12, 'hij')")
tk.MustExec("prepare stmt7 from 'select c from t_list_index where id = ?'")
tk.MustExec("set @id=1")
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5")
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("def"))
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("def"))
tk.MustExec("set @id=9")
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("xyz"))
tk.MustExec("set @id=12")
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows("hij"))
tk.MustExec("set @id=100")
tk.MustQuery("execute stmt7 using @id").Check(testkit.Rows())
// Test for list columns partition
tk.MustExec("drop table if exists t_list_index")
tk.MustExec("create table t_list_index (id int, k int, c varchar(10), primary key(id)) partition by list columns (id) ( PARTITION p0 VALUES IN (1,2,3,4), PARTITION p1 VALUES IN (5,6,7,8),PARTITION p2 VALUES IN (9,10,11,12))")
tk.MustExec("insert into t_list_index values (1, 1, 'abc'), (5, 5, 'def'), (9, 9, 'xyz'), (12, 12, 'hij')")
tk.MustExec("prepare stmt8 from 'select c from t_list_index where id = ?'")
tk.MustExec("set @id=1")
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("abc"))
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("abc"))
tk.MustExec("set @id=5")
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("def"))
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("def"))
tk.MustExec("set @id=9")
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("xyz"))
tk.MustExec("set @id=12")
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows("hij"))
tk.MustExec("set @id=100")
tk.MustQuery("execute stmt8 using @id").Check(testkit.Rows())
// https://github.com/pingcap/tidb/issues/33031
tk.MustExec(`drop table if exists Issue33031`)
tk.MustExec(`CREATE TABLE Issue33031 (COL1 int(16) DEFAULT '29' COMMENT 'NUMERIC UNIQUE INDEX', COL2 bigint(20) DEFAULT NULL, UNIQUE KEY UK_COL1 (COL1)) PARTITION BY RANGE (COL1) (PARTITION P0 VALUES LESS THAN (0))`)
tk.MustExec(`insert into Issue33031 values(-5, 7)`)
tk.MustExec(`prepare stmt from 'select *,? from Issue33031 where col2 < ? and col1 in (?, ?)'`)
tk.MustExec(`set @a=111, @b=1, @c=2, @d=22`)
tk.MustQuery(`execute stmt using @d,@a,@b,@c`).Check(testkit.Rows())
tk.MustExec(`set @a=112, @b=-2, @c=-5, @d=33`)
tk.MustQuery(`execute stmt using @d,@a,@b,@c`).Check(testkit.Rows("-5 7 33"))
if pruneMode == string(variable.Dynamic) {
// When the temporary disabling of prepared plan cache for dynamic partition prune mode is disabled, change this to 1!
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
}
}
}
func newSession(t *testing.T, store kv.Storage, dbName string) session.Session {
se, err := session.CreateSession4Test(store)
require.NoError(t, err)
mustExec(t, se, "create database if not exists "+dbName)
mustExec(t, se, "use "+dbName)
return se
}
func mustExec(t *testing.T, se session.Session, sql string) {
_, err := se.Execute(context.Background(), sql)
require.NoError(t, err)
}
func TestConstPropAndPPDWithCache(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a varchar(8) not null, b varchar(8) not null)")
tk.MustExec("insert into t values('1','1')")
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and t2.b = ? and t2.b = ?"`)
tk.MustExec("set @p0 = '1', @p1 = '2';")
tk.MustQuery("execute stmt using @p0, @p1").Check(testkit.Rows(
"0",
))
tk.MustExec("set @p0 = '1', @p1 = '1'")
tk.MustQuery("execute stmt using @p0, @p1").Check(testkit.Rows(
"1",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and ?"`)
tk.MustExec("set @p0 = 0")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
tk.MustExec("set @p0 = 1")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"1",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where ?"`)
tk.MustExec("set @p0 = 0")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
tk.MustExec("set @p0 = 1")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"1",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and t2.b = '1' and t2.b = ?"`)
tk.MustExec("set @p0 = '1'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"1",
))
tk.MustExec("set @p0 = '2'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and t1.a > ?"`)
tk.MustExec("set @p0 = '1'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
tk.MustExec("set @p0 = '0'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"1",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and t1.b > ? and t1.b > ?"`)
tk.MustExec("set @p0 = '0', @p1 = '0'")
tk.MustQuery("execute stmt using @p0,@p1").Check(testkit.Rows(
"1",
))
tk.MustExec("set @p0 = '0', @p1 = '1'")
tk.MustQuery("execute stmt using @p0,@p1").Check(testkit.Rows(
"0",
))
tk.MustExec(`prepare stmt from "select count(1) from t t1, t t2 where t1.a = t2.a and t1.b > ? and t1.b > '1'"`)
tk.MustExec("set @p0 = '1'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
tk.MustExec("set @p0 = '0'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows(
"0",
))
// Need to check if contain mutable before RefineCompareConstant() in inToExpression().
// Otherwise may hit wrong plan.
tk.MustExec("use test;")
tk.MustExec("drop table if exists t1;")
tk.MustExec("create table t1(c1 tinyint unsigned);")
tk.MustExec("insert into t1 values(111);")
tk.MustExec("prepare stmt from 'select 1 from t1 where c1 in (?)';")
tk.MustExec("set @a = '1.1';")
tk.MustQuery("execute stmt using @a;").Check(testkit.Rows())
tk.MustExec("set @a = '111';")
tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1"))
}
func TestPlanCacheUnionScan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // insert-tmt can hit the cache and affect hit counter in this UT
pb := &dto.Metric{}
metrics.ResettablePlanCacheCounterFortTest = true
metrics.PlanCacheCounter.Reset()
counter := metrics.PlanCacheCounter.WithLabelValues("prepare")
tk.MustExec("use test")
tk.MustExec("drop table if exists t1")
tk.MustExec("drop table if exists t2")
tk.MustExec("create table t1(a int not null)")
tk.MustExec("create table t2(a int not null)")
tk.MustExec("prepare stmt1 from 'select * from t1 where a > ?'")
tk.MustExec("set @p0 = 0")
tk.MustQuery("execute stmt1 using @p0").Check(testkit.Rows())
tk.MustExec("begin")
tk.MustQuery("execute stmt1 using @p0").Check(testkit.Rows())
err := counter.Write(pb)
require.NoError(t, err)
cnt := pb.GetCounter().GetValue()
require.Equal(t, float64(1), cnt)
tk.MustExec("insert into t1 values(1)")
// Cached plan is invalid now, it is not chosen and removed.
tk.MustQuery("execute stmt1 using @p0").Check(testkit.Rows(
"1",
))
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(1), cnt)
tk.MustExec("insert into t2 values(1)")
// Cached plan is chosen, modification on t2 does not impact plan of t1.
tk.MustQuery("execute stmt1 using @p0").Check(testkit.Rows(
"1",
))
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(2), cnt)
tk.MustExec("rollback")
// Though cached plan contains UnionScan, it does not impact correctness, so it is reused.
tk.MustQuery("execute stmt1 using @p0").Check(testkit.Rows())
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(3), cnt)
tk.MustExec("prepare stmt2 from 'select * from t1 left join t2 on true where t1.a > ?'")
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows())
tk.MustExec("begin")
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows())
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(4), cnt)
tk.MustExec("insert into t1 values(1)")
// Cached plan is invalid now, it is not chosen and removed.
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows(
"1 <nil>",
))
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(4), cnt)
tk.MustExec("insert into t2 values(1)")
// Cached plan is invalid now, it is not chosen and removed.
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows(
"1 1",
))
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(4), cnt)
// Cached plan is reused.
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows(
"1 1",
))
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(5), cnt)
tk.MustExec("rollback")
// Though cached plan contains UnionScan, it does not impact correctness, so it is reused.
tk.MustQuery("execute stmt2 using @p0").Check(testkit.Rows())
err = counter.Write(pb)
require.NoError(t, err)
cnt = pb.GetCounter().GetValue()
require.Equal(t, float64(6), cnt)
}
func TestPlanCacheSwitchDB(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
// create a table in test
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t(a int)`)
tk.MustExec(`insert into t values (-1)`)
tk.MustExec(`prepare stmt from 'select * from t'`)
// DB is not specified
se2, err := session.CreateSession4TestWithOpt(store, &session.Opt{
PreparedPlanCache: core.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false),
})
require.NoError(t, err)
tk2 := testkit.NewTestKitWithSession(t, store, se2)
require.Equal(t, tk2.ExecToErr(`prepare stmt from 'select * from t'`).Error(), "[planner:1046]No database selected")
require.Equal(t, tk2.ExecToErr(`prepare stmt from 'select * from test.t'`), nil)
// switch to a new DB
tk.MustExec(`drop database if exists plan_cache`)
tk.MustExec(`create database plan_cache`)
tk.MustExec(`use plan_cache`)
tk.MustExec(`create table t(a int)`)
tk.MustExec(`insert into t values (1)`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows("-1")) // read test.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery(`execute stmt`).Check(testkit.Rows("-1")) // read test.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
// prepare again
tk.MustExec(`prepare stmt from 'select * from t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows("1")) // read plan_cache.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery(`execute stmt`).Check(testkit.Rows("1")) // read plan_cache.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
// specify DB in the query
tk.MustExec(`prepare stmt from 'select * from test.t'`)
tk.MustQuery(`execute stmt`).Check(testkit.Rows("-1")) // read test.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery(`execute stmt`).Check(testkit.Rows("-1")) // read test.t
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}
func TestPlanCacheHitInfo(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(id int)")
tk.MustExec("insert into t values (1),(2),(3),(4)")
tk.MustExec("prepare stmt from 'select * from t where id=?'")
tk.MustExec("prepare stmt2 from 'select /*+ ignore_plan_cache() */ * from t where id=?'")
tk.MustExec("set @doma = 1")
// Test if last_plan_from_cache is appropriately initialized.
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @doma").Check(testkit.Rows("1"))
// Test if last_plan_from_cache is updated after a plan cache hit.
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @doma").Check(testkit.Rows("1"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustQuery("execute stmt2 using @doma").Check(testkit.Rows("1"))
// Test if last_plan_from_cache is updated after a plan cache miss caused by a prepared statement.
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
// Test if last_plan_from_cache is updated after a plan cache miss caused by a usual statement.
tk.MustQuery("execute stmt using @doma").Check(testkit.Rows("1"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustQuery("select * from t where id=1").Check(testkit.Rows("1"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
}
func TestIssue29303(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`set tidb_enable_clustered_index=on`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists PK_MULTI_COL_360`)
tk.MustExec(`CREATE TABLE PK_MULTI_COL_360 (
COL1 blob NOT NULL,
COL2 char(1) NOT NULL,
PRIMARY KEY (COL1(5),COL2) /*T![clustered_index] CLUSTERED */)`)
tk.MustExec(`INSERT INTO PK_MULTI_COL_360 VALUES ('�', '龂')`)
tk.MustExec(`prepare stmt from 'SELECT/*+ INL_JOIN(t1, t2) */ * FROM PK_MULTI_COL_360 t1 JOIN PK_MULTI_COL_360 t2 ON t1.col1 = t2.col1 WHERE t2.col2 BETWEEN ? AND ? AND t1.col2 BETWEEN ? AND ?'`)
tk.MustExec(`set @a="捲", @b="颽", @c="睭", @d="詼"`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows())
tk.MustExec(`set @a="龂", @b="龂", @c="龂", @d="龂"`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows("� 龂 � 龂"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) // unsafe range
}
func TestIssue34725(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`CREATE TABLE t (
a int(11) DEFAULT NULL,
b int(11) GENERATED ALWAYS AS (a) STORED NOT NULL,
PRIMARY KEY (b))`)
tk.MustExec(`insert into t(a) values(102)`)
tk.MustExec(`prepare stmt from "select * from t where b in (?, ?, ?)"`)
tk.MustExec(`set @a=102, @b=102, @c=102`)
tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows(`102 102`))
tk.MustExec(`set @a=-97, @b=-97, @c=-97`)
tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows())
}
func TestIssue33628(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=0`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t (a int primary key, b int)`)
tk.MustExec(`prepare stmt from "select * from t where a=10"`) // point-get plan
tk.MustExec(`execute stmt`)
tk.MustExec(`execute stmt`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
}
func TestIssue28942(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists IDT_MULTI15853STROBJSTROBJ`)
tk.MustExec(`
CREATE TABLE IDT_MULTI15853STROBJSTROBJ (
COL1 enum('aa','bb','cc') DEFAULT NULL,
COL2 mediumint(41) DEFAULT NULL,
KEY U_M_COL4 (COL1,COL2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`)
tk.MustExec(`insert into IDT_MULTI15853STROBJSTROBJ values("aa", 1)`)
tk.MustExec(`prepare stmt from 'SELECT * FROM IDT_MULTI15853STROBJSTROBJ WHERE col1 = ? AND col1 != ?'`)
tk.MustExec(`set @a="mm", @b="aa"`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows()) // empty result
tk.MustExec(`set @a="aa", @b="aa"`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows()) // empty result
}
func TestPlanCacheUnsignedHandleOverflow(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a bigint unsigned primary key)")
tk.MustExec("insert into t values(18446744073709551615)")
tk.MustExec("prepare stmt from 'select a from t where a=?'")
tk.MustExec("set @p = 1")
tk.MustQuery("execute stmt using @p").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @p").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("set @p = 18446744073709551615")
tk.MustQuery("execute stmt using @p").Check(testkit.Rows("18446744073709551615"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
func TestIssue28254(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists PK_GCOL_STORED9816")
tk.MustExec("CREATE TABLE `PK_GCOL_STORED9816` (`COL102` decimal(55,0) DEFAULT NULL)")
tk.MustExec("insert into PK_GCOL_STORED9816 values(9710290195629059011)")
tk.MustExec("prepare stmt from 'select count(*) from PK_GCOL_STORED9816 where col102 > ?'")
tk.MustExec("set @a=9860178624005968368")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("0"))
tk.MustExec("set @a=-7235178122860450591")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1"))
tk.MustExec("set @a=9860178624005968368")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("0"))
tk.MustExec("set @a=-7235178122860450591")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1"))
}
func TestIssue33067(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("DROP TABLE IF EXISTS `t`")
tk.MustExec("CREATE TABLE `t` (`COL1` char(20) DEFAULT NULL, `COL2` bit(16),`COL3` date, KEY `U_M_COL5` (`COL3`,`COL2`))")
tk.MustExec("insert into t values ('','>d','9901-06-17')")
tk.MustExec("prepare stmt from 'select * from t where col1 is not null and col2 not in (?, ?, ?) and col3 in (?, ?, ?)'")
tk.MustExec(`set @a=-21188, @b=26824, @c=31855, @d="5597-1-4", @e="5755-12-6", @f="1253-7-12"`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d,@e,@f`).Check(testkit.Rows())
tk.MustExec(`set @a=-5360, @b=-11715, @c=9399, @d="9213-09-13", @e="4705-12-24", @f="9901-06-17"`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d,@e,@f`).Check(testkit.Rows(" >d 9901-06-17"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
func TestIssue42439(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec(`CREATE TABLE UK_MU16407 (COL3 timestamp NULL DEFAULT NULL, UNIQUE KEY U3(COL3))`)
tk.MustExec(`insert into UK_MU16407 values("1985-08-31 18:03:27")`)
tk.MustExec(`PREPARE st FROM 'SELECT COL3 FROM UK_MU16407 WHERE COL3>?'`)
tk.MustExec(`set @a='2039-1-19 3:14:40'`)
tk.MustExec(`execute st using @a`) // no error
tk.MustExec(`set @a='1950-1-19 3:14:40'`)
tk.MustQuery(`execute st using @a`).Check(testkit.Rows(`1985-08-31 18:03:27`))
}
func TestIssue29486(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists UK_MULTI_COL_11691`)
tk.MustExec(`CREATE TABLE UK_MULTI_COL_11691 (
COL1 binary(20) DEFAULT NULL,
COL2 tinyint(16) DEFAULT NULL,
COL3 time DEFAULT NULL,
UNIQUE KEY U_M_COL (COL1(10),COL2,COL3))`)
tk.MustExec(`insert into UK_MULTI_COL_11691 values(0x340C604874B52E8D30440E8DC2BB170621D8A088, 126, "-105:17:32"),
(0x28EC2EDBAC7DF99045BDD0FCEAADAFBAC2ACF76F, 126, "102:54:04"),
(0x11C38221B3B1E463C94EC39F0D481303A58A50DC, 118, "599:13:47"),
(0x03E2FC9E0C846FF1A926BF829FA9D7BAED3FD7B1, 118, "-257:45:13")`)
tk.MustExec(`prepare stmt from 'SELECT/*+ INL_JOIN(t1, t2) */ t2.COL2 FROM UK_MULTI_COL_11691 t1 JOIN UK_MULTI_COL_11691 t2 ON t1.col1 = t2.col1 WHERE t1.col2 BETWEEN ? AND ? AND t2.col2 BETWEEN ? AND ?'`)
tk.MustExec(`set @a=-29408, @b=-9254, @c=-1849, @d=-2346`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows())
tk.MustExec(`set @a=126, @b=126, @c=-125, @d=707`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows("126", "126"))
}
func TestIssue28867(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1, t2")
tk.MustExec(`CREATE TABLE t1 (c_int int, c_str varchar(40), PRIMARY KEY (c_int, c_str))`)
tk.MustExec(`CREATE TABLE t2 (c_str varchar(40), PRIMARY KEY (c_str))`)
tk.MustExec(`insert into t1 values (1, '1')`)
tk.MustExec(`insert into t2 values ('1')`)
tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1 join t2 on t1.c_str <= t2.c_str where t1.c_int in (?,?)'`)
tk.MustExec(`set @a=10, @b=20`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows())
tk.MustExec(`set @a=1, @b=2`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("1 1 1"))
// test case for IndexJoin + PlanCache
tk.MustExec(`drop table t1, t2`)
tk.MustExec(`create table t1 (a int, b int, c int, index idxab(a, b, c))`)
tk.MustExec(`create table t2 (a int, b int)`)
tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.b=?'`)
tk.MustExec(`set @a=1`)
tk.MustExec(`execute stmt using @a`)
tk.MustExec(`execute stmt using @a`)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.c=?'`)
tk.MustExec(`set @a=1`)
tk.MustExec(`execute stmt using @a`)
tk.MustExec(`execute stmt using @a`)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestParamMarker4FastPlan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
// test handle for point get
tk.MustExec(`use test`)
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(pk int primary key)")
tk.MustExec("insert into t values(1)")
tk.MustExec(`prepare stmt from 'select * from t where pk = ?'`)
tk.MustExec(`set @a0=1.1, @a1='1.1', @a2=1, @a3=1.0, @a4='1.0'`)
tk.MustQuery(`execute stmt using @a2`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a2`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a3`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a3`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a0`).Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery(`execute stmt using @a4`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a4`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a1`).Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// test indexValues for point get
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(pk int, unique index idx(pk))")
tk.MustExec("insert into t values(1)")
tk.MustExec(`prepare stmt from 'select * from t where pk = ?'`)
tk.MustExec(`set @a0=1.1, @a1='1.1', @a2=1, @a3=1.0, @a4='1.0'`)
tk.MustQuery(`execute stmt using @a2`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a2`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a3`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a3`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a0`).Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery(`execute stmt using @a4`).Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a4`).Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a1`).Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// test _tidb_rowid for point get
tk.MustExec(`use test`)
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int);")
tk.MustExec("insert t values (1, 7), (1, 8), (1, 9);")
tk.MustExec(`prepare stmt from 'select * from t where _tidb_rowid = ?'`)
tk.MustExec(`set @a=2`)
tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1 8"))
tk.MustExec(`set @a=1`)
tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1 7"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
// test handle for batch point get
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(pk int primary key)")
tk.MustExec("insert into t values (1), (2), (3), (4), (5)")
tk.MustExec(`prepare stmt from 'select * from t where pk in (1, ?, ?)'`)
tk.MustExec(`set @a0=0, @a1=1, @a2=2, @a3=3, @a1_1=1.1, @a4=4, @a5=5`)
tk.MustQuery(`execute stmt using @a2, @a3`).Sort().Check(testkit.Rows("1", "2", "3"))
tk.MustQuery(`execute stmt using @a2, @a3`).Sort().Check(testkit.Rows("1", "2", "3"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a0, @a4`).Sort().Check(testkit.Rows("1", "4"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a1_1, @a5`).Sort().Check(testkit.Rows("1", "5"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// test indexValues for batch point get
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(pk int, unique index idx(pk))")
tk.MustExec("insert into t values (1), (2), (3), (4), (5)")
tk.MustExec(`prepare stmt from 'select * from t where pk in (1, ?, ?)'`)
tk.MustExec(`set @a0=0, @a1=1, @a2=2, @a3=3, @a1_1=1.1, @a4=4, @a5=5`)
tk.MustQuery(`execute stmt using @a2, @a3`).Sort().Check(testkit.Rows("1", "2", "3"))
tk.MustQuery(`execute stmt using @a2, @a3`).Sort().Check(testkit.Rows("1", "2", "3"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a0, @a4`).Sort().Check(testkit.Rows("1", "4"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery(`execute stmt using @a1_1, @a5`).Sort().Check(testkit.Rows("1", "5"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// test _tidb_rowid for batch point get
tk.MustExec(`use test`)
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int);")
tk.MustExec("insert t values (1, 7), (1, 8), (1, 9), (1, 10);")
tk.MustExec(`prepare stmt from 'select * from t where _tidb_rowid in (1, ?, ?)'`)
tk.MustExec(`set @a2=2, @a3=3`)
tk.MustQuery("execute stmt using @a2, @a3;").Sort().Check(testkit.Rows("1 7", "1 8", "1 9"))
tk.MustExec(`set @a2=4, @a3=2`)
tk.MustQuery("execute stmt using @a2, @a3;").Sort().Check(testkit.Rows("1 10", "1 7", "1 8"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // unsafe range
}
func TestIssue29565(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists PK_SIGNED_10094`)
tk.MustExec(`CREATE TABLE PK_SIGNED_10094 (COL1 decimal(55,0) NOT NULL, PRIMARY KEY (COL1))`)
tk.MustExec(`insert into PK_SIGNED_10094 values(-9999999999999999999999999999999999999999999999999999999)`)
tk.MustExec(`prepare stmt from 'select * from PK_SIGNED_10094 where col1 != ? and col1 + 10 <=> ? + 10'`)
tk.MustExec(`set @a=7309027171262036496, @b=-9798213896406520625`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows())
tk.MustExec(`set @a=5408499810319315618, @b=-9999999999999999999999999999999999999999999999999999999`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows("-9999999999999999999999999999999999999999999999999999999"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustExec(`set @a=7309027171262036496, @b=-9798213896406520625`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}
func TestIssue31730(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists PK_S_MULTI_37;`)
tk.MustExec(`CREATE TABLE PK_S_MULTI_37 (COL1 decimal(55,0) NOT NULL, COL2 decimal(55,0) NOT NULL,PRIMARY KEY (COL1, COL2) /*T![clustered_index] NONCLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`)
tk.MustExec(`insert into PK_S_MULTI_37 values(-9999999999999999999999999999999999999999999999, 1);`)
tk.MustExec(`prepare stmt from 'SELECT SUM(COL1+?), col2 FROM PK_S_MULTI_37 GROUP BY col2';`)
tk.MustExec(`set @a=1;`)
tk.MustQuery(`execute stmt using @a`).Check(testkit.Rows("-9999999999999999999999999999999999999999999998 1"))
}
func TestIssue28828(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("set @@tidb_enable_collect_execution_info=0;")
tk.MustExec("CREATE TABLE t (" +
"id bigint(20) NOT NULL," +
"audit_id bigint(20) NOT NULL," +
"PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */," +
"KEY index_audit_id (audit_id)" +
");")
tk.MustExec("insert into t values(1,9941971237863475), (2,9941971237863476), (3, 0);")
tk.MustExec("prepare stmt from 'select * from t where audit_id=?';")
tk.MustExec("set @a='9941971237863475', @b=9941971237863475, @c='xayh7vrWVNqZtzlJmdJQUwAHnkI8Ec', @d='0.0', @e='0.1', @f = '9941971237863476';")
tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1 9941971237863475"))
tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("1 9941971237863475"))
// When the type of parameters have been changed, the plan cache can not be used.
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @c;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @e;").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @f;").Check(testkit.Rows("2 9941971237863476"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustExec("prepare stmt from 'select count(*) from t where audit_id in (?, ?, ?, ?, ?)';")
tk.MustQuery("execute stmt using @a, @b, @c, @d, @e;").Check(testkit.Rows("2"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @f, @b, @c, @d, @e;").Check(testkit.Rows("3"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
}
func TestIssue28920(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists UK_GCOL_VIRTUAL_18928`)
tk.MustExec(`
CREATE TABLE UK_GCOL_VIRTUAL_18928 (
COL102 bigint(20) DEFAULT NULL,
COL103 bigint(20) DEFAULT NULL,
COL1 bigint(20) GENERATED ALWAYS AS (COL102 & 10) VIRTUAL,
COL2 varchar(20) DEFAULT NULL,
COL4 datetime DEFAULT NULL,
COL3 bigint(20) DEFAULT NULL,
COL5 float DEFAULT NULL,
UNIQUE KEY UK_COL1 (COL1))`)
tk.MustExec(`insert into UK_GCOL_VIRTUAL_18928(col102,col2) values("-5175976006730879891", "屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖")`)
tk.MustExec(`prepare stmt from 'SELECT * FROM UK_GCOL_VIRTUAL_18928 WHERE col1 < ? AND col2 != ?'`)
tk.MustExec(`set @a=10, @b="aa"`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("-5175976006730879891 <nil> 8 屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖 <nil> <nil> <nil>"))
}
func TestIssue18066(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.RefreshConnectionID()
require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil))
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int)")
tk.MustExec("prepare stmt from 'select * from t'")
tk.MustQuery("execute stmt").Check(testkit.Rows())
tk.MustQuery("select EXEC_COUNT,plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check(
testkit.Rows("1 0 0"))
tk.MustQuery("execute stmt").Check(testkit.Rows())
tk.MustQuery("select EXEC_COUNT,plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check(
testkit.Rows("2 1 1"))
tk.MustExec("prepare stmt from 'select * from t'")
tk.MustQuery("execute stmt").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select EXEC_COUNT, plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check(
testkit.Rows("3 2 1"))
}
func TestPrepareForGroupByMultiItems(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, c int , index idx(a));")
tk.MustExec("insert into t values(1,2, -1), (1,2, 1), (1,2, -1), (4,4,3);")
tk.MustExec("set @a=1")
tk.MustExec("set @b=3")
tk.MustExec(`set sql_mode=""`)
tk.MustExec(`prepare stmt from "select a, sum(b), c from t group by ?, ? order by ?, ?"`)
tk.MustQuery("select a, sum(b), c from t group by 1,3 order by 1,3;").Check(testkit.Rows("1 4 -1", "1 2 1", "4 4 3"))
tk.MustQuery(`execute stmt using @a, @b, @a, @b`).Check(testkit.Rows("1 4 -1", "1 2 1", "4 4 3"))
tk.MustExec("set @c=10")
require.EqualError(t, tk.ExecToErr("execute stmt using @a, @c, @a, @c"), "Unknown column '10' in 'group statement'")
tk.MustExec("set @v1=1.0")
tk.MustExec("set @v2=3.0")
tk.MustExec(`prepare stmt2 from "select sum(b) from t group by ?, ?"`)
tk.MustQuery(`execute stmt2 using @v1, @v2`).Check(testkit.Rows("10"))
}
func TestInvisibleIndexPrepare(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, unique idx_a(a))")
tk.MustExec("insert into t values(1)")
tk.MustExec(`prepare stmt1 from "select a from t order by a"`)
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
require.Len(t, tk.Session().GetSessionVars().StmtCtx.IndexNames, 1)
require.Equal(t, "t:idx_a", tk.Session().GetSessionVars().StmtCtx.IndexNames[0])
tk.MustExec("alter table t alter index idx_a invisible")
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
require.Len(t, tk.Session().GetSessionVars().StmtCtx.IndexNames, 0)
tk.MustExec("alter table t alter index idx_a visible")
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
require.Len(t, tk.Session().GetSessionVars().StmtCtx.IndexNames, 1)
require.Equal(t, "t:idx_a", tk.Session().GetSessionVars().StmtCtx.IndexNames[0])
}
// Test for issue https://github.com/pingcap/tidb/issues/22167
func TestPrepareCacheWithJoinTable(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists ta, tb")
tk.MustExec("CREATE TABLE ta(k varchar(32) NOT NULL DEFAULT ' ')")
tk.MustExec("CREATE TABLE tb (k varchar(32) NOT NULL DEFAULT ' ', s varchar(1) NOT NULL DEFAULT ' ')")
tk.MustExec("insert into ta values ('a')")
tk.MustExec("set @a=2, @b=1")
tk.MustExec(`prepare stmt from "select * from ta a left join tb b on 1 where ? = 1 or b.s is not null"`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows())
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("a <nil> <nil>"))
}
func TestPlanCacheSnapshot(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(id int)")
tk.MustExec("insert into t values (1),(2),(3),(4)")
// For mocktikv, safe point is not initialized, we manually insert it for snapshot to use.
timeSafe := time.Now().Add(-48 * 60 * 60 * time.Second).Format("20060102-15:04:05 -0700 MST")
safePointSQL := `INSERT HIGH_PRIORITY INTO mysql.tidb VALUES ('tikv_gc_safe_point', '%[1]s', '')
ON DUPLICATE KEY
UPDATE variable_value = '%[1]s'`
tk.MustExec(fmt.Sprintf(safePointSQL, timeSafe))
tk.MustExec("prepare stmt from 'select * from t where id=?'")
tk.MustExec("set @p = 1")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @p").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @p").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
// Record the current tso.
tk.MustExec("begin")
tso := tk.Session().GetSessionVars().TxnCtx.StartTS
tk.MustExec("rollback")
require.True(t, tso > 0)
// Insert one more row with id = 1.
tk.MustExec("insert into t values (1)")
tk.MustExec(fmt.Sprintf("set @@tidb_snapshot = '%d'", tso))
tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @p").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestPlanCachePointGetAndTableDual(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t0, t1, t2, t3, t4")
tk.MustExec("create table t0(c1 varchar(20), c2 varchar(20), c3 bigint(20), primary key(c1, c2))")
tk.MustExec("insert into t0 values('0000','7777',1)")
tk.MustExec("prepare s0 from 'select * from t0 where c1=? and c2>=? and c2<=?'")
tk.MustExec("set @a0='0000', @b0='9999'")
// TableDual is forbidden for plan-cache, a TableReader be built and cached.
tk.MustQuery("execute s0 using @a0, @b0, @a0").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s0 using @a0, @a0, @b0").Check(testkit.Rows("0000 7777 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("create table t1(c1 varchar(20), c2 varchar(20), c3 bigint(20), primary key(c1, c2))")
tk.MustExec("insert into t1 values('0000','7777',1)")
tk.MustExec("prepare s1 from 'select * from t1 where c1=? and c2>=? and c2<=?'")
tk.MustExec("set @a1='0000', @b1='9999'")
tk.MustQuery("execute s1 using @a1, @b1, @b1").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s1 using @a1, @a1, @b1").Check(testkit.Rows("0000 7777 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // c2>=9999 and c2<=9999 --> c2=9999
tk.MustExec("create table t2(c1 bigint(20) primary key, c2 varchar(20))")
tk.MustExec("insert into t2 values(1,'7777')")
tk.MustExec("prepare s2 from 'select * from t2 where c1>=? and c1<=?'")
tk.MustExec("set @a2=0, @b2=9")
tk.MustQuery("execute s2 using @a2, @a2").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s2 using @a2, @b2").Check(testkit.Rows("1 7777"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // PointPlan
tk.MustExec("create table t3(c1 int, c2 int, c3 int, unique key(c1), key(c2))")
tk.MustExec("insert into t3 values(2,1,1)")
tk.MustExec("prepare s3 from 'select /*+ use_index_merge(t3) */ * from t3 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a3=1,@b3=3")
tk.MustQuery("execute s3 using @a3,@a3").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s3 using @a3,@b3").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // c1>=1 and c1<=1 --> c1==1
tk.MustExec("prepare s3 from 'select /*+ use_index_merge(t3) */ * from t3 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a3=1,@b3=3")
tk.MustQuery("execute s3 using @b3,@a3").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s3 using @a3,@b3").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("create table t4(c1 int primary key, c2 int, c3 int, key(c2))")
tk.MustExec("insert into t4 values(2,1,1)")
tk.MustExec("prepare s4 from 'select /*+ use_index_merge(t4) */ * from t4 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a4=1,@b4=3")
tk.MustQuery("execute s4 using @a4,@a4").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s4 using @a4,@b4").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // c1>=3 and c1<=3 --> c1=3
tk.MustExec("prepare s4 from 'select /*+ use_index_merge(t4) */ * from t4 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a4=1,@b4=3")
tk.MustQuery("execute s4 using @b4,@a4").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("execute s4 using @a4,@b4").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
func TestIssue26873(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int primary key, b int, c int)")
tk.MustExec("prepare stmt from 'select * from t where a = 2 or a = ?'")
tk.MustExec("set @p = 3")
tk.MustQuery("execute stmt using @p").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @p").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
func TestIssue29511(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("CREATE TABLE `t` (`COL1` bigint(20) DEFAULT NULL COMMENT 'WITH DEFAULT', UNIQUE KEY `UK_COL1` (`COL1`))")
tk.MustExec("insert into t values(-3865356285544170443),(9223372036854775807);")
tk.MustExec("prepare stmt from 'select/*+ hash_agg() */ max(col1) from t where col1 = ? and col1 > ?;';")
tk.MustExec("set @a=-3865356285544170443, @b=-4055949188488870713;")
tk.MustQuery("execute stmt using @a,@b;").Check(testkit.Rows("-3865356285544170443"))
}
func TestIssue23671(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int, index ab(a, b))")
tk.MustExec("insert into t values (1, 1), (2, 2)")
tk.MustExec("prepare s1 from 'select * from t use index(ab) where a>=? and b>=? and b<=?'")
tk.MustExec("set @a=1, @b=1, @c=1")
tk.MustQuery("execute s1 using @a, @b, @c").Check(testkit.Rows("1 1"))
tk.MustExec("set @a=1, @b=1, @c=10")
tk.MustQuery("execute s1 using @a, @b, @c").Check(testkit.Rows("1 1", "2 2"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // b>=1 and b<=1 --> b=1
}
func TestIssue29296(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists UK_MU14722`)
tk.MustExec(`CREATE TABLE UK_MU14722 (
COL1 tinytext DEFAULT NULL,
COL2 tinyint(16) DEFAULT NULL,
COL3 datetime DEFAULT NULL,
COL4 int(11) DEFAULT NULL,
UNIQUE KEY U_M_COL (COL1(10)),
UNIQUE KEY U_M_COL2 (COL2),
UNIQUE KEY U_M_COL3 (COL3))`)
tk.MustExec(`insert into UK_MU14722 values("輮睅麤敜溺她晁瀪襄頮鹛涓誗钷廔筪惌嶙鎢塴", -121, "3383-02-19 07:58:28" , -639457963),
("偧孇鱓鼂瘠钻篝醗時鷷聽箌磇砀玸眞扦鸇祈灇", 127, "7902-03-05 08:54:04", -1094128660),
("浀玡慃淛漉围甧鴎史嬙砊齄w章炢忲噑硓哈樘", -127, "5813-04-16 03:07:20", -333397107),
("鑝粼啎鸼贖桖弦簼赭蠅鏪鐥蕿捐榥疗耹岜鬓槊", -117, "7753-11-24 10:14:24", 654872077)`)
tk.MustExec(`prepare stmt from 'SELECT * FROM UK_MU14722 WHERE col2 > ? OR col2 BETWEEN ? AND ? ORDER BY COL2 + ? LIMIT 3'`)
tk.MustExec(`set @a=30410, @b=3937, @c=22045, @d=-4374`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows())
tk.MustExec(`set @a=127, @b=127, @c=127, @d=127`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows(`偧孇鱓鼂瘠钻篝醗時鷷聽箌磇砀玸眞扦鸇祈灇 127 7902-03-05 08:54:04 -1094128660`))
}
func TestIssue28246(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists PK_AUTO_RANDOM9111;")
tk.MustExec("CREATE TABLE `PK_AUTO_RANDOM9111` ( `COL1` bigint(45) NOT NULL , `COL2` varchar(20) DEFAULT NULL, `COL4` datetime DEFAULT NULL, `COL3` bigint(20) DEFAULT NULL, `COL5` float DEFAULT NULL, PRIMARY KEY (`COL1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into PK_AUTO_RANDOM9111(col1) values (-9223372036854775808), (9223372036854775807);")
tk.MustExec("set @a=9223372036854775807, @b=1")
tk.MustExec(`prepare stmt from 'select min(col1) from PK_AUTO_RANDOM9111 where col1 > ?;';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("<nil>"))
// The plan contains the tableDual, so it will not be cached.
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("<nil>"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("9223372036854775807"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("<nil>"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestIssue29805(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_clustered_index=on;")
tk.MustExec("drop table if exists PK_TCOLLATION10197;")
tk.MustExec("CREATE TABLE `PK_TCOLLATION10197` (`COL1` char(1) NOT NULL, PRIMARY KEY (`COL1`(1)) /*T![clustered_index] CLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into PK_TCOLLATION10197 values('龺');")
tk.MustExec("set @a='畻', @b='龺';")
tk.MustExec(`prepare stmt from 'select/*+ hash_agg() */ count(distinct col1) from PK_TCOLLATION10197 where col1 > ?;';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec(`prepare stmt from 'select/*+ hash_agg() */ count(distinct col1) from PK_TCOLLATION10197 where col1 > ?;';`)
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("0"))
tk.MustQuery("select/*+ hash_agg() */ count(distinct col1) from PK_TCOLLATION10197 where col1 > '龺';").Check(testkit.Rows("0"))
}
func TestIssue29993(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
// test PointGet + cluster index
tk.MustExec("set tidb_enable_clustered_index=on;")
tk.MustExec("drop table if exists t;")
tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL PRIMARY KEY, col2 int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into t values('a', 1), ('b', 2);")
tk.MustExec("set @a='a', @b='b', @z='z';")
tk.MustExec(`prepare stmt from 'select col1 from t where col1 = ? and col2 in (1, 2);';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("b"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
// The length of range have been changed, so the plan can not be cached.
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
// test batchPointGet + cluster index
tk.MustExec("drop table if exists t;")
tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL, col2 int, PRIMARY KEY(col1, col2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into t values('a', 1), ('b', 2);")
tk.MustExec("set @a='a', @b='b', @z='z';")
tk.MustExec(`prepare stmt from 'select col1 from t where (col1, col2) in ((?, 1));';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // invalid since 'z' is not in enum('a', 'b')
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
// test PointGet + non cluster index
tk.MustExec("set tidb_enable_clustered_index=off;")
tk.MustExec("drop table if exists t;")
tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL PRIMARY KEY, col2 int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into t values('a', 1), ('b', 2);")
tk.MustExec("set @a='a', @b='b', @z='z';")
tk.MustExec(`prepare stmt from 'select col1 from t where col1 = ? and col2 in (1, 2);';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("b"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
// The length of range have been changed, so the plan can not be cached.
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
// test batchPointGet + non cluster index
tk.MustExec("drop table if exists t;")
tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL, col2 int, PRIMARY KEY(col1, col2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("insert into t values('a', 1), ('b', 2);")
tk.MustExec("set @a='a', @b='b', @z='z';")
tk.MustExec(`prepare stmt from 'select col1 from t where (col1, col2) in ((?, 1));';`)
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // invalid since 'z' is not in enum('a', 'b')
tk.MustQuery("execute stmt using @z").Check(testkit.Rows())
}
func TestIssue30100(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t;")
tk.MustExec("create table t(col1 enum('aa', 'bb'), col2 int, index(col1, col2));")
tk.MustExec("insert into t values('aa', 333);")
tk.MustExec(`prepare stmt from 'SELECT * FROM t t1 JOIN t t2 ON t1.col1 = t2.col1 WHERE t1.col1 <=> NULL';`)
tk.MustQuery("execute stmt").Check(testkit.Rows())
tk.MustQuery("execute stmt").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec(`prepare stmt from 'SELECT * FROM t t1 JOIN t t2 ON t1.col1 = t2.col1 WHERE t1.col1 <=> NULL and t2.col2 > ?';`)
tk.MustExec("set @a=0;")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows())
tk.MustQuery("execute stmt using @a").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestPartitionTable(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
// enable partition table dynamic mode
tk.MustExec("create database test_plan_cache")
tk.MustExec("use test_plan_cache")
tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
tk.MustExec("set @@tidb_enable_list_partition = 1")
type testcase struct {
t1Create string
t2Create string
rowGener func() string
varGener func() string
query string
}
randDateTime := func() string {
return fmt.Sprintf("%v-%v-%v %v:%v:%v",
1950+rand.Intn(100), 1+rand.Intn(12), 1+rand.Intn(28), // date
rand.Intn(24), rand.Intn(60), rand.Intn(60)) // time
}
randDate := func() string {
return fmt.Sprintf("%v-%v-%v", 1950+rand.Intn(100), 1+rand.Intn(12), 1+rand.Intn(28))
}
testcases := []testcase{
{ // hash partition + int
"create table t1(a int, b int) partition by hash(a) partitions 20",
"create table t2(a int, b int)",
func() string { return fmt.Sprintf("(%v, %v)", rand.Intn(100000000), rand.Intn(100000000)) },
func() string { return fmt.Sprintf("%v", rand.Intn(100000000)) },
`select * from %v where a > ?`,
},
{ // range partition + int
`create table t1(a int, b int) partition by range(a) (
partition p0 values less than (20000000),
partition p1 values less than (40000000),
partition p2 values less than (60000000),
partition p3 values less than (80000000),
partition p4 values less than (100000000))`,
`create table t2(a int, b int)`,
func() string { return fmt.Sprintf("(%v, %v)", rand.Intn(100000000), rand.Intn(100000000)) },
func() string { return fmt.Sprintf("%v", rand.Intn(100000000)) },
`select * from %v where a > ?`,
},
{ // range partition + varchar
`create table t1(a varchar(10), b varchar(10)) partition by range columns(a) (
partition p0 values less than ('200'),
partition p1 values less than ('400'),
partition p2 values less than ('600'),
partition p3 values less than ('800'),
partition p4 values less than ('9999'))`,
`create table t2(a varchar(10), b varchar(10))`,
func() string { return fmt.Sprintf(`("%v", "%v")`, rand.Intn(1000), rand.Intn(1000)) },
func() string { return fmt.Sprintf(`"%v"`, rand.Intn(1000)) },
`select * from %v where a > ?`,
},
{ // range partition + datetime
`create table t1(a datetime, b datetime) partition by range columns(a) (
partition p0 values less than ('1970-01-01 00:00:00'),
partition p1 values less than ('1990-01-01 00:00:00'),
partition p2 values less than ('2010-01-01 00:00:00'),
partition p3 values less than ('2030-01-01 00:00:00'),
partition p4 values less than ('2060-01-01 00:00:00'))`,
`create table t2(a datetime, b datetime)`,
func() string { return fmt.Sprintf(`("%v", "%v")`, randDateTime(), randDateTime()) },
func() string { return fmt.Sprintf(`"%v"`, randDateTime()) },
`select * from %v where a > ?`,
},
{ // range partition + date
`create table t1(a date, b date) partition by range columns(a) (
partition p0 values less than ('1970-01-01'),
partition p1 values less than ('1990-01-01'),
partition p2 values less than ('2010-01-01'),
partition p3 values less than ('2030-01-01'),
partition p4 values less than ('2060-01-01'))`,
`create table t2(a date, b date)`,
func() string { return fmt.Sprintf(`("%v", "%v")`, randDate(), randDate()) },
func() string { return fmt.Sprintf(`"%v"`, randDate()) },
`select * from %v where a > ?`,
},
{ // list partition + int
`create table t1(a int, b int) partition by list(a) (
partition p0 values in (0, 1, 2, 3, 4),
partition p1 values in (5, 6, 7, 8, 9),
partition p2 values in (10, 11, 12, 13, 14),
partition p3 values in (15, 16, 17, 18, 19))`,
`create table t2(a int, b int)`,
func() string { return fmt.Sprintf("(%v, %v)", rand.Intn(20), rand.Intn(20)) },
func() string { return fmt.Sprintf("%v", rand.Intn(20)) },
`select * from %v where a > ?`,
},
}
for _, tc := range testcases {
// create tables and insert some records
tk.MustExec("drop table if exists t1")
tk.MustExec("drop table if exists t2")
tk.MustExec(tc.t1Create)
tk.MustExec(tc.t2Create)
vals := make([]string, 0, 2048)
for i := 0; i < 2048; i++ {
vals = append(vals, tc.rowGener())
}
tk.MustExec(fmt.Sprintf("insert into t1 values %s", strings.Join(vals, ",")))
tk.MustExec(fmt.Sprintf("insert into t2 values %s", strings.Join(vals, ",")))
// the first query, @last_plan_from_cache should be zero
tk.MustExec(fmt.Sprintf(`prepare stmt1 from "%s"`, fmt.Sprintf(tc.query, "t1")))
tk.MustExec(fmt.Sprintf(`prepare stmt2 from "%s"`, fmt.Sprintf(tc.query, "t2")))
tk.MustExec(fmt.Sprintf("set @a=%v", tc.varGener()))
result1 := tk.MustQuery("execute stmt1 using @a").Sort().Rows()
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt2 using @a").Sort().Check(result1)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
for i := 0; i < 100; i++ {
tk.MustExec(fmt.Sprintf("set @a=%v", tc.varGener()))
result1 := tk.MustQuery("execute stmt1 using @a").Sort().Rows()
// When https://github.com/pingcap/tidb/pull/33098 is reverted this should be 1 again
tk.MustQuery("select @@last_plan_from_cache /* i=" + strconv.Itoa(i) + " prepared statement: (t1) " + tc.query + "\n-- create table: " + tc.t1Create + "*/").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt2 using @a").Sort().Check(result1)
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
}
}
func helperCheckPlanCache(t *testing.T, tk *testkit.TestKit, sql, expected string, arr []string) []string {
res := tk.MustQuery(sql)
got := res.Rows()[0][0]
if expected == "0" {
require.Equal(t, expected, got, sql)
} else {
if got != expected {
return append(arr, sql)
}
}
return arr
}
func TestPartitionWithVariedDataSources(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
// enable partition table dynamic mode
tk.MustExec("create database test_plan_cache2")
tk.MustExec("use test_plan_cache2")
tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
// prepare tables
tk.MustExec(`create table trangePK (a int primary key, b int) partition by range (a) (
partition p0 values less than (10000),
partition p1 values less than (20000),
partition p2 values less than (30000),
partition p3 values less than (40000))`)
tk.MustExec(`create table thashPK (a int primary key, b int) partition by hash (a) partitions 4`)
tk.MustExec(`create table tnormalPK (a int primary key, b int)`)
tk.MustExec(`create table trangeIdx (a int unique key, b int) partition by range (a) (
partition p0 values less than (10000),
partition p1 values less than (20000),
partition p2 values less than (30000),
partition p3 values less than (40000))`)
tk.MustExec(`create table thashIdx (a int unique key, b int) partition by hash (a) partitions 4`)
tk.MustExec(`create table tnormalIdx (a int unique key, b int)`)
uniqueVals := make(map[int]struct{})
vals := make([]string, 0, 1000)
for len(vals) < 1000 {
a := rand.Intn(40000)
if _, ok := uniqueVals[a]; ok {
continue
}
uniqueVals[a] = struct{}{}
b := rand.Intn(40000)
vals = append(vals, fmt.Sprintf("(%v, %v)", a, b))
}
for _, tbl := range []string{"trangePK", "thashPK", "tnormalPK", "trangeIdx", "thashIdx", "tnormalIdx"} {
tk.MustExec(fmt.Sprintf(`insert into %v values %v`, tbl, strings.Join(vals, ", ")))
tk.MustExec(`analyze table ` + tbl)
}
// TableReader, PointGet on PK, BatchGet on PK
for _, tbl := range []string{`trangePK`, `thashPK`, `tnormalPK`} {
tk.MustExec(fmt.Sprintf(`prepare stmt%v_tablescan from 'select * from %v use index(primary) where a > ? and a < ?'`, tbl, tbl))
tk.MustExec(fmt.Sprintf(`prepare stmt%v_pointget from 'select * from %v use index(primary) where a = ?'`, tbl, tbl))
tk.MustExec(fmt.Sprintf(`prepare stmt%v_batchget from 'select * from %v use index(primary) where a in (?, ?, ?)'`, tbl, tbl))
}
for i := 0; i < 100; i++ {
mina, maxa := rand.Intn(40000), rand.Intn(40000)
if mina > maxa {
mina, maxa = maxa, mina
}
tk.MustExec(fmt.Sprintf(`set @mina=%v, @maxa=%v`, mina, maxa))
tk.MustExec(fmt.Sprintf(`set @pointa=%v`, rand.Intn(40000)))
tk.MustExec(fmt.Sprintf(`set @a0=%v, @a1=%v, @a2=%v`, rand.Intn(40000), rand.Intn(40000), rand.Intn(40000)))
var rscan, rpoint, rbatch [][]interface{}
for id, tbl := range []string{`trangePK`, `thashPK`, `tnormalPK`} {
scan := tk.MustQuery(fmt.Sprintf(`execute stmt%v_tablescan using @mina, @maxa`, tbl)).Sort()
if id == 0 {
rscan = scan.Rows()
} else {
scan.Check(rscan)
}
point := tk.MustQuery(fmt.Sprintf(`execute stmt%v_pointget using @pointa`, tbl)).Sort()
if id == 0 {
rpoint = point.Rows()
} else {
point.Check(rpoint)
}
batch := tk.MustQuery(fmt.Sprintf(`execute stmt%v_batchget using @a0, @a1, @a2`, tbl)).Sort()
if id == 0 {
rbatch = batch.Rows()
} else {
batch.Check(rbatch)
}
}
}
// IndexReader, IndexLookUp, PointGet on Idx, BatchGet on Idx
for _, tbl := range []string{"trangeIdx", "thashIdx", "tnormalIdx"} {
tk.MustExec(fmt.Sprintf(`prepare stmt%v_indexscan from 'select a from %v use index(a) where a > ? and a < ?'`, tbl, tbl))
tk.MustExec(fmt.Sprintf(`prepare stmt%v_indexlookup from 'select * from %v use index(a) where a > ? and a < ?'`, tbl, tbl))
tk.MustExec(fmt.Sprintf(`prepare stmt%v_pointget_idx from 'select * from %v use index(a) where a = ?'`, tbl, tbl))
tk.MustExec(fmt.Sprintf(`prepare stmt%v_batchget_idx from 'select * from %v use index(a) where a in (?, ?, ?)'`, tbl, tbl))
}
loops := 100
missedPlanCache := make([]string, 0, 4)
for i := 0; i < loops; i++ {
mina, maxa := rand.Intn(40000), rand.Intn(40000)
if mina > maxa {
mina, maxa = maxa, mina
}
tk.MustExec(fmt.Sprintf(`set @mina=%v, @maxa=%v`, mina, maxa))
tk.MustExec(fmt.Sprintf(`set @pointa=%v`, rand.Intn(40000)))
tk.MustExec(fmt.Sprintf(`set @a0=%v, @a1=%v, @a2=%v`, rand.Intn(40000), rand.Intn(40000), rand.Intn(40000)))
var rscan, rlookup, rpoint, rbatch [][]interface{}
var expectedFromPlanCache string
for id, tbl := range []string{"trangeIdx", "thashIdx", "tnormalIdx"} {
scan := tk.MustQuery(fmt.Sprintf(`execute stmt%v_indexscan using @mina, @maxa`, tbl)).Sort()
if id == 2 {
expectedFromPlanCache = "1"
} else {
expectedFromPlanCache = "0"
}
tblStr := ` table: ` + tbl + " i :" + strconv.FormatInt(int64(i), 10) + " */"
if i > 0 {
missedPlanCache = helperCheckPlanCache(t, tk, `select @@last_plan_from_cache /* indexscan table: `+tblStr, expectedFromPlanCache, missedPlanCache)
}
if id == 0 {
rscan = scan.Rows()
} else {
scan.Check(rscan)
}
lookup := tk.MustQuery(fmt.Sprintf(`execute stmt%v_indexlookup using @mina, @maxa`, tbl)).Sort()
if i > 0 {
missedPlanCache = helperCheckPlanCache(t, tk, `select @@last_plan_from_cache /* indexlookup table: `+tblStr, expectedFromPlanCache, missedPlanCache)
}
if id == 0 {
rlookup = lookup.Rows()
} else {
lookup.Check(rlookup)
}
point := tk.MustQuery(fmt.Sprintf(`execute stmt%v_pointget_idx using @pointa`, tbl)).Sort()
if tbl == `tnormalPK` && i > 0 {
// PlanCache cannot support PointGet now since we haven't relocated partition after rebuilding range.
// Please see Execute.rebuildRange for more details.
missedPlanCache = helperCheckPlanCache(t, tk, `select @@last_plan_from_cache /* pointget table: `+tblStr, expectedFromPlanCache, missedPlanCache)
}
if id == 0 {
rpoint = point.Rows()
} else {
point.Check(rpoint)
}
batch := tk.MustQuery(fmt.Sprintf(`execute stmt%v_batchget_idx using @a0, @a1, @a2`, tbl)).Sort()
if i > 0 {
missedPlanCache = helperCheckPlanCache(t, tk, `select @@last_plan_from_cache /* batchget table: `+tblStr, expectedFromPlanCache, missedPlanCache)
}
if id == 0 {
rbatch = batch.Rows()
} else {
batch.Check(rbatch)
}
}
}
// Allow ~1% non-cached queries, due to background changes etc.
// (Actually just 1/3 %, since there are 3 tables * 4 queries per loop :)
if len(missedPlanCache) > (loops * 4 / 100) {
require.Equal(t, []string{}, missedPlanCache)
}
}
func TestCachedTable(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int, index i_b(b))")
tk.MustExec("insert into t values (1, 1), (2, 2)")
tk.MustExec("alter table t cache")
tk.MustExec("prepare tableScan from 'select * from t where a>=?'")
tk.MustExec("prepare indexScan from 'select b from t use index(i_b) where b>?'")
tk.MustExec("prepare indexLookup from 'select a from t use index(i_b) where b>? and b<?'")
tk.MustExec("prepare pointGet from 'select b from t use index(i_b) where b=?'")
tk.MustExec("set @a=1, @b=3")
lastReadFromCache := func(tk *testkit.TestKit) bool {
return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache
}
var cacheLoaded bool
for i := 0; i < 50; i++ {
tk.MustQuery("select * from t").Check(testkit.Rows("1 1", "2 2"))
if lastReadFromCache(tk) {
cacheLoaded = true
break
}
}
require.True(t, cacheLoaded)
// Cache the plan.
tk.MustQuery("execute tableScan using @a").Check(testkit.Rows("1 1", "2 2"))
tk.MustQuery("execute indexScan using @a").Check(testkit.Rows("2"))
tk.MustQuery("execute indexLookup using @a, @b").Check(testkit.Rows("2"))
tk.MustQuery("execute pointGet using @a").Check(testkit.Rows("1"))
// Table Scan
tk.MustQuery("execute tableScan using @a").Check(testkit.Rows("1 1", "2 2"))
require.True(t, lastReadFromCache(tk))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
// Index Scan
tk.MustQuery("execute indexScan using @a").Check(testkit.Rows("2"))
require.True(t, lastReadFromCache(tk))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
// IndexLookup
tk.MustQuery("execute indexLookup using @a, @b").Check(testkit.Rows("2"))
require.True(t, lastReadFromCache(tk))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // b>1 and b<3 --> b=2
// PointGet
tk.MustQuery("execute pointGet using @a").Check(testkit.Rows("1"))
require.True(t, lastReadFromCache(tk))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestPlanCacheWithRCWhenInfoSchemaChange(t *testing.T) {
ctx := context.Background()
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec("use test")
tk1.MustExec("set global tidb_enable_metadata_lock=0")
tk2.MustExec("use test")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int primary key, c int, index ic (c))")
// prepare text protocol
tk1.MustExec("prepare s from 'select /*+use_index(t1, ic)*/ * from t1 where 1'")
// prepare binary protocol
stmtID, _, _, err := tk2.Session().PrepareStmt("select /*+use_index(t1, ic)*/ * from t1 where 1")
require.Nil(t, err)
tk1.MustExec("set tx_isolation='READ-COMMITTED'")
tk1.MustExec("begin pessimistic")
tk2.MustExec("set tx_isolation='READ-COMMITTED'")
tk2.MustExec("begin pessimistic")
tk1.MustQuery("execute s").Check(testkit.Rows())
rs, err := tk2.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk2.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows())
tk3 := testkit.NewTestKit(t, store)
tk3.MustExec("use test")
tk3.MustExec("alter table t1 drop index ic")
tk3.MustExec("insert into t1 values(1, 0)")
// The execution after schema changed should not hit plan cache.
// execute text protocol
tk1.MustQuery("execute s").Check(testkit.Rows("1 0"))
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// execute binary protocol
rs, err = tk2.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk2.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 0"))
tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
func TestConsistencyBetweenPrepareExecuteAndNormalSql(t *testing.T) {
ctx := context.Background()
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec("set global tidb_enable_metadata_lock=0")
tk1.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk1.MustExec("use test")
tk2.MustExec("use test")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int primary key, c int)")
tk1.MustExec("insert into t1 values(1, 1), (2, 2)")
// prepare text protocol
tk1.MustExec("prepare s from 'select * from t1'")
// prepare binary protocol
stmtID, _, _, err := tk1.Session().PrepareStmt("select * from t1")
require.Nil(t, err)
tk1.MustExec("set tx_isolation='READ-COMMITTED'")
tk1.MustExec("begin pessimistic")
tk2.MustExec("set tx_isolation='READ-COMMITTED'")
tk2.MustExec("begin pessimistic")
// Execute using sql
tk1.MustQuery("execute s").Check(testkit.Rows("1 1", "2 2"))
// Execute using binary
rs, err := tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 1", "2 2"))
// Normal sql
tk1.MustQuery("select * from t1").Check(testkit.Rows("1 1", "2 2"))
// Change infoSchema
tk2.MustExec("alter table t1 drop column c")
tk2.MustExec("insert into t1 values (3)")
// Execute using sql
tk1.MustQuery("execute s").Check(testkit.Rows("1 1", "2 2", "3 <nil>"))
// Execute using binary
rs, err = tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 1", "2 2", "3 <nil>"))
// Normal sql
tk1.MustQuery("select * from t1").Check(testkit.Rows("1 1", "2 2", "3 <nil>"))
tk1.MustExec("commit")
// After beginning a new txn, the infoSchema should be the latest
tk1.MustExec("begin pessimistic")
tk1.MustQuery("select * from t1").Check(testkit.Rows("1", "2", "3"))
}
func verifyCache(ctx context.Context, t *testing.T, tk1 *testkit.TestKit, tk2 *testkit.TestKit, stmtID uint32) {
// Cache miss in the firs time.
tk1.MustExec("execute s")
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// This time, the cache will be hit.
rs, err := tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.NoError(t, err)
require.NoError(t, rs.Close())
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk1.MustExec("execute s")
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
// Change infoSchema version which will make the plan cache invalid in the next execute
tk2.MustExec("alter table t1 drop column c")
tk1.MustExec("execute s")
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
// Now the plan cache will be valid
rs, err = tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.NoError(t, err)
require.NoError(t, rs.Close())
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestCacheHitInRc(t *testing.T) {
ctx := context.Background()
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec("set global tidb_enable_metadata_lock=0")
tk1.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk1.MustExec("use test")
tk2.MustExec("use test")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int primary key, c int)")
tk1.MustExec("insert into t1 values(1, 1), (2, 2)")
// prepare text protocol
tk1.MustExec("prepare s from 'select * from t1'")
// prepare binary protocol
stmtID, _, _, err := tk1.Session().PrepareStmt("select * from t1")
require.Nil(t, err)
// Test for RC
tk1.MustExec("set tx_isolation='READ-COMMITTED'")
tk1.MustExec("begin pessimistic")
// Verify for the RC isolation
verifyCache(ctx, t, tk1, tk2, stmtID)
tk1.MustExec("rollback")
}
func TestCacheHitInForUpdateRead(t *testing.T) {
ctx := context.Background()
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk1.MustExec("use test")
tk2.MustExec("use test")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int primary key, c int)")
tk1.MustExec("insert into t1 values(1, 1), (2, 2)")
tk1.MustExec("prepare s from 'select * from t1 where id = 1 for update'")
stmtID, _, _, err := tk1.Session().PrepareStmt("select * from t1 where id = 1 for update")
require.Nil(t, err)
tk1.MustExec("begin pessimistic")
// Verify for the for update read
verifyCache(ctx, t, tk1, tk2, stmtID)
tk1.MustExec("rollback")
}
func TestPointGetForUpdateAutoCommitCache(t *testing.T) {
ctx := context.Background()
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk2.MustExec(`set tidb_enable_prepared_plan_cache=1`)
tk1.MustExec("use test")
tk2.MustExec("use test")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int primary key, c int)")
tk1.MustExec("insert into t1 values(1, 1), (2, 2)")
tk1.MustExec("prepare s from 'select * from t1 where id = 1 for update'")
stmtID, _, _, err := tk1.Session().PrepareStmt("select * from t1 where id = 1 for update")
require.Nil(t, err)
rs, err := tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 1"))
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
rs, err = tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 1"))
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk2.MustExec("alter table t1 drop column c")
tk2.MustExec("update t1 set id = 10 where id = 1")
rs, err = tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows())
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
rs, err = tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test())
require.Nil(t, err)
tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows())
tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}
func TestPreparedShowStatements(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(`prepare p1 from 'show variables like "tidb_snapshot"';`)
tk.MustQuery(`execute p1;`).Check(testkit.Rows("tidb_snapshot "))
tk.MustExec("create table t (a int, b int);")
tk.MustExec(`prepare p2 from "show columns from t where field = 'a'";`) // Only column `a` is selected.
tk.MustQuery(`execute p2;`).Check(testkit.Rows("a int(11) YES <nil> "))
tk.MustExec("create table t1 (a int, b int);")
tk.MustExec(`prepare p3 from "show tables where tables_in_test = 't1'";`) // Only table `t1` is selected.
tk.MustQuery("execute p3;").Check(testkit.Rows("t1"))
}
func TestIssue37901(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t4`)
tk.MustExec(`create table t4 (a date)`)
tk.MustExec(`prepare st1 from "insert into t4(a) select dt from (select ? as dt from dual union all select sysdate() ) a";`)
tk.MustExec(`set @t='2022-01-01 00:00:00.000000'`)
tk.MustExec(`execute st1 using @t`)
tk.MustQuery(`select count(*) from t4`).Check(testkit.Rows("2"))
}