2809 lines
124 KiB
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"))
|
|
}
|