From 7ef4f0292ccba348744fa9b6451b4b690c0b6471 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Wed, 6 Nov 2024 19:29:22 +0800 Subject: [PATCH] expression: label more expressions as thread-safe and add more test cases (#57171) ref pingcap/tidb#54057 --- pkg/expression/builtin_op.go | 24 ++ pkg/expression/builtin_other.go | 28 ++ .../builtin_threadsafe_generated.go | 60 ++++ .../builtin_threadunsafe_generated.go | 60 ---- .../generator/builtin_threadsafe.go | 22 ++ .../casetest/instanceplancache/BUILD.bazel | 3 +- .../instanceplancache/builtin_func_test.go | 325 ++++++++++++++++++ 7 files changed, 461 insertions(+), 61 deletions(-) create mode 100644 pkg/planner/core/casetest/instanceplancache/builtin_func_test.go diff --git a/pkg/expression/builtin_op.go b/pkg/expression/builtin_op.go index db093effb7a29..56790330b0517 100644 --- a/pkg/expression/builtin_op.go +++ b/pkg/expression/builtin_op.go @@ -568,6 +568,10 @@ func (c *isTrueOrFalseFunctionClass) getFunction(ctx BuildContext, args []Expres type builtinRealIsTrueSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinRealIsTrueSig) Clone() builtinFunc { @@ -593,6 +597,10 @@ func (b *builtinRealIsTrueSig) evalInt(ctx EvalContext, row chunk.Row) (int64, b type builtinDecimalIsTrueSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinDecimalIsTrueSig) Clone() builtinFunc { @@ -618,6 +626,10 @@ func (b *builtinDecimalIsTrueSig) evalInt(ctx EvalContext, row chunk.Row) (int64 type builtinIntIsTrueSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinIntIsTrueSig) Clone() builtinFunc { @@ -668,6 +680,10 @@ func (b *builtinVectorFloat32IsTrueSig) evalInt(ctx EvalContext, row chunk.Row) type builtinRealIsFalseSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinRealIsFalseSig) Clone() builtinFunc { @@ -693,6 +709,10 @@ func (b *builtinRealIsFalseSig) evalInt(ctx EvalContext, row chunk.Row) (int64, type builtinDecimalIsFalseSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinDecimalIsFalseSig) Clone() builtinFunc { @@ -718,6 +738,10 @@ func (b *builtinDecimalIsFalseSig) evalInt(ctx EvalContext, row chunk.Row) (int6 type builtinIntIsFalseSig struct { baseBuiltinFunc keepNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinIntIsFalseSig) Clone() builtinFunc { diff --git a/pkg/expression/builtin_other.go b/pkg/expression/builtin_other.go index 88691e56bdf2d..7feb143efd342 100644 --- a/pkg/expression/builtin_other.go +++ b/pkg/expression/builtin_other.go @@ -191,6 +191,10 @@ type baseInSig struct { // It works with builtinInXXXSig.hashset to accelerate 'eval'. nonConstArgsIdx []int hasNull bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } // builtinInIntSig see https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in @@ -198,6 +202,10 @@ type builtinInIntSig struct { baseInSig // the bool value in the map is used to identify whether the constant stored in key is signed or unsigned hashSet map[int64]bool + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInIntSig) buildHashMapForConstArgs(ctx BuildContext) error { @@ -290,6 +298,10 @@ func (b *builtinInIntSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, type builtinInStringSig struct { baseInSig hashSet set.StringSet + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInStringSig) buildHashMapForConstArgs(ctx BuildContext) error { @@ -364,6 +376,10 @@ func (b *builtinInStringSig) evalInt(ctx EvalContext, row chunk.Row) (int64, boo type builtinInRealSig struct { baseInSig hashSet set.Float64Set + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInRealSig) buildHashMapForConstArgs(ctx BuildContext) error { @@ -435,6 +451,10 @@ func (b *builtinInRealSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, type builtinInDecimalSig struct { baseInSig hashSet set.StringSet + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInDecimalSig) buildHashMapForConstArgs(ctx BuildContext) error { @@ -515,6 +535,10 @@ func (b *builtinInDecimalSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bo type builtinInTimeSig struct { baseInSig hashSet map[types.CoreTime]struct{} + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInTimeSig) buildHashMapForConstArgs(ctx BuildContext) error { @@ -586,6 +610,10 @@ func (b *builtinInTimeSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, type builtinInDurationSig struct { baseInSig hashSet map[time.Duration]struct{} + + // NOTE: Any new fields added here must be thread-safe or immutable during execution, + // as this expression may be shared across sessions. + // If a field does not meet these requirements, set SafeToShareAcrossSession to false. } func (b *builtinInDurationSig) buildHashMapForConstArgs(ctx BuildContext) error { diff --git a/pkg/expression/builtin_threadsafe_generated.go b/pkg/expression/builtin_threadsafe_generated.go index fd123a9c2dc3e..5e1392a80f181 100644 --- a/pkg/expression/builtin_threadsafe_generated.go +++ b/pkg/expression/builtin_threadsafe_generated.go @@ -784,11 +784,21 @@ func (s *builtinDecimalAnyValueSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinDecimalIsFalseSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinDecimalIsNullSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinDecimalIsTrueSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinDecodeSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) @@ -1224,11 +1234,41 @@ func (s *builtinIfVectorFloat32Sig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInDecimalSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInDurationSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInIntSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinInJSONSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInRealSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInStringSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinInTimeSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinInVectorFloat32Sig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) @@ -1269,11 +1309,21 @@ func (s *builtinIntAnyValueSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinIntIsFalseSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinIntIsNullSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinIntIsTrueSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinInternalToBinarySig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) @@ -1914,11 +1964,21 @@ func (s *builtinRealAnyValueSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinRealIsFalseSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinRealIsNullSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) } +// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. +func (s *builtinRealIsTrueSig) SafeToShareAcrossSession() bool { + return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) +} + // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinReplaceSig) SafeToShareAcrossSession() bool { return safeToShareAcrossSession(&s.safeToShareAcrossSessionFlag, s.args) diff --git a/pkg/expression/builtin_threadunsafe_generated.go b/pkg/expression/builtin_threadunsafe_generated.go index bbe20eeeb2330..abaae4eacc80b 100644 --- a/pkg/expression/builtin_threadunsafe_generated.go +++ b/pkg/expression/builtin_threadunsafe_generated.go @@ -226,41 +226,11 @@ func (s *builtinReleaseAllLocksSig) SafeToShareAcrossSession() bool { return false } -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinRealIsTrueSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinDecimalIsTrueSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinIntIsTrueSig) SafeToShareAcrossSession() bool { - return false -} - // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinVectorFloat32IsTrueSig) SafeToShareAcrossSession() bool { return false } -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinRealIsFalseSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinDecimalIsFalseSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinIntIsFalseSig) SafeToShareAcrossSession() bool { - return false -} - // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinVectorFloat32IsFalseSig) SafeToShareAcrossSession() bool { return false @@ -271,36 +241,6 @@ func (s *builtinUnaryMinusDecimalSig) SafeToShareAcrossSession() bool { return false } -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInIntSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInStringSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInRealSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInDecimalSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInTimeSig) SafeToShareAcrossSession() bool { - return false -} - -// SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. -func (s *builtinInDurationSig) SafeToShareAcrossSession() bool { - return false -} - // SafeToShareAcrossSession implements BuiltinFunc.SafeToShareAcrossSession. func (s *builtinSetStringVarSig) SafeToShareAcrossSession() bool { return false diff --git a/pkg/expression/generator/builtin_threadsafe.go b/pkg/expression/generator/builtin_threadsafe.go index 9dee88a7ed4fa..a17b05d7f3a63 100644 --- a/pkg/expression/generator/builtin_threadsafe.go +++ b/pkg/expression/generator/builtin_threadsafe.go @@ -28,6 +28,24 @@ import ( "strings" ) +var ( + specialSafeFuncs = map[string]struct{}{ + "builtinInIntSig": {}, + "builtinInStringSig": {}, + "builtinInRealSig": {}, + "builtinInDecimalSig": {}, + "builtinInTimeSig": {}, + "builtinInDurationSig": {}, + "builtinRealIsTrueSig": {}, + "builtinDecimalIsTrueSig": {}, + "builtinIntIsTrueSig": {}, + "builtinRealIsFalseSig": {}, + "builtinDecimalIsFalseSig": {}, + "builtinIntIsFalseSig": {}, + // NOTE: please make sure there are test cases for all functions here. + } +) + func collectThreadSafeBuiltinFuncs(file string) (safeFuncNames, unsafeFuncNames []string) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, file, nil, 0) @@ -54,6 +72,10 @@ func collectThreadSafeBuiltinFuncs(file string) (safeFuncNames, unsafeFuncNames return true } allFuncNames = append(allFuncNames, typeName) + if _, ok := specialSafeFuncs[typeName]; ok { + safeFuncNames = append(safeFuncNames, typeName) + return true + } if len(structType.Fields.List) != 1 { // this structure only has 1 field return true } diff --git a/pkg/planner/core/casetest/instanceplancache/BUILD.bazel b/pkg/planner/core/casetest/instanceplancache/BUILD.bazel index 8158822d2663b..0a36b03a618b8 100644 --- a/pkg/planner/core/casetest/instanceplancache/BUILD.bazel +++ b/pkg/planner/core/casetest/instanceplancache/BUILD.bazel @@ -4,6 +4,7 @@ go_test( name = "instanceplancache_test", timeout = "short", srcs = [ + "builtin_func_test.go", "concurrency_test.go", "concurrency_tpcc_test.go", "dml_test.go", @@ -11,7 +12,7 @@ go_test( "others_test.go", ], flaky = True, - shard_count = 20, + shard_count = 28, deps = [ "//pkg/parser/auth", "//pkg/testkit", diff --git a/pkg/planner/core/casetest/instanceplancache/builtin_func_test.go b/pkg/planner/core/casetest/instanceplancache/builtin_func_test.go new file mode 100644 index 0000000000000..1278c3ad39bb1 --- /dev/null +++ b/pkg/planner/core/casetest/instanceplancache/builtin_func_test.go @@ -0,0 +1,325 @@ +// Copyright 2024 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 instanceplancache + +import ( + "fmt" + "math/rand" + "sync" + "testing" + + "github.com/pingcap/tidb/pkg/testkit" +) + +func TestBuiltinInIntSig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t1 (a int)") + tk.MustExec("create table t2 (a int, key(a))") + tk.MustExec("create table t3 (a int, primary key(a))") + tk.MustExec("create table t4 (a int, unique key(a))") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values (%d)", i)) + tk.MustExec(fmt.Sprintf("insert into t2 values (%d)", i)) + tk.MustExec(fmt.Sprintf("insert into t3 values (%d)", i)) + tk.MustExec(fmt.Sprintf("insert into t4 values (%d)", i)) + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + v1, v2 := rand.Intn(50), 50+rand.Intn(50) + tName := fmt.Sprintf("t%d", rand.Intn(4)+1) + tk.MustExec(fmt.Sprintf("prepare st from 'select a from %v where a in (?, ?)'", tName)) + tk.MustExec(fmt.Sprintf("set @p1=%v, @p2=%v", v1, v2)) + v1Str, v2Str := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2) + if v1Str > v2Str { + v1Str, v2Str = v2Str, v1Str + } + tk.MustQuery("execute st using @p1, @p2").Sort().Check(testkit.Rows(v1Str, v2Str)) + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinInStringSig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t1 (a varchar(20))") + tk.MustExec("create table t2 (a varchar(20), key(a))") + tk.MustExec("create table t3 (a varchar(20), primary key(a))") + tk.MustExec("create table t4 (a varchar(20), unique key(a))") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values ('%d')", i)) + tk.MustExec(fmt.Sprintf("insert into t2 values ('%d')", i)) + tk.MustExec(fmt.Sprintf("insert into t3 values ('%d')", i)) + tk.MustExec(fmt.Sprintf("insert into t4 values ('%d')", i)) + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + v1, v2 := rand.Intn(50), 50+rand.Intn(50) + tName := fmt.Sprintf("t%d", rand.Intn(4)+1) + tk.MustExec(fmt.Sprintf("prepare st from 'select a from %v where a in (?, ?)'", tName)) + tk.MustExec(fmt.Sprintf("set @p1='%v', @p2='%v'", v1, v2)) + v1Str, v2Str := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2) + if v1Str > v2Str { + v1Str, v2Str = v2Str, v1Str + } + tk.MustQuery("execute st using @p1, @p2").Sort().Check(testkit.Rows(v1Str, v2Str)) + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinInRealSig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t1 (a real)") + tk.MustExec("create table t2 (a real, key(a))") + tk.MustExec("create table t3 (a real, primary key(a))") + tk.MustExec("create table t4 (a real, unique key(a))") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values ('%d.1')", i)) + tk.MustExec(fmt.Sprintf("insert into t2 values ('%d.1')", i)) + tk.MustExec(fmt.Sprintf("insert into t3 values ('%d.1')", i)) + tk.MustExec(fmt.Sprintf("insert into t4 values ('%d.1')", i)) + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + v1, v2 := fmt.Sprintf("%v.1", rand.Intn(50)), fmt.Sprintf("%v.1", 50+rand.Intn(50)) + tName := fmt.Sprintf("t%d", rand.Intn(4)+1) + tk.MustExec(fmt.Sprintf("prepare st from 'select a from %v where a in (?, ?)'", tName)) + tk.MustExec(fmt.Sprintf("set @p1='%v', @p2='%v'", v1, v2)) + v1Str, v2Str := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2) + if v1Str > v2Str { + v1Str, v2Str = v2Str, v1Str + } + tk.MustQuery("execute st using @p1, @p2").Sort().Check(testkit.Rows(v1Str, v2Str)) + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinInDecimalSig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t1 (a decimal(10, 2))") + tk.MustExec("create table t2 (a decimal(10, 2), key(a))") + tk.MustExec("create table t3 (a decimal(10, 2), primary key(a))") + tk.MustExec("create table t4 (a decimal(10, 2), unique key(a))") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values ('%d.10')", i)) + tk.MustExec(fmt.Sprintf("insert into t2 values ('%d.10')", i)) + tk.MustExec(fmt.Sprintf("insert into t3 values ('%d.10')", i)) + tk.MustExec(fmt.Sprintf("insert into t4 values ('%d.10')", i)) + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + v1, v2 := fmt.Sprintf("%v.10", rand.Intn(50)), fmt.Sprintf("%v.10", 50+rand.Intn(50)) + tName := fmt.Sprintf("t%d", rand.Intn(4)+1) + tk.MustExec(fmt.Sprintf("prepare st from 'select a from %v where a in (?, ?)'", tName)) + tk.MustExec(fmt.Sprintf("set @p1='%v', @p2='%v'", v1, v2)) + v1Str, v2Str := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2) + if v1Str > v2Str { + v1Str, v2Str = v2Str, v1Str + } + tk.MustQuery("execute st using @p1, @p2").Sort().Check(testkit.Rows(v1Str, v2Str)) + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinInTimeSig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t1 (a datetime)") + tk.MustExec("create table t2 (a datetime, key(a))") + tk.MustExec("create table t3 (a datetime, primary key(a))") + tk.MustExec("create table t4 (a datetime, unique key(a))") + for i := 0; i < 40; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values ('2000-01-01 00:%v:00')", 10+i)) + tk.MustExec(fmt.Sprintf("insert into t2 values ('2000-01-01 00:%v:00')", 10+i)) + tk.MustExec(fmt.Sprintf("insert into t3 values ('2000-01-01 00:%v:00')", 10+i)) + tk.MustExec(fmt.Sprintf("insert into t4 values ('2000-01-01 00:%v:00')", 10+i)) + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + v1 := fmt.Sprintf("2000-01-01 00:%v:00", 10+rand.Intn(20)) + v2 := fmt.Sprintf("2000-01-01 00:%v:00", 30+rand.Intn(20)) + tName := fmt.Sprintf("t%d", rand.Intn(4)+1) + tk.MustExec(fmt.Sprintf("prepare st from 'select a from %v where a in (?, ?)'", tName)) + tk.MustExec(fmt.Sprintf("set @p1='%v', @p2='%v'", v1, v2)) + v1Str, v2Str := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2) + if v1Str > v2Str { + v1Str, v2Str = v2Str, v1Str + } + tk.MustQuery("execute st using @p1, @p2").Sort().Check(testkit.Rows(v1Str, v2Str)) + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinRealIsTrueFalse(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t (a real)") + tk.MustExec("insert into t values (1.1)") + tk.MustExec("insert into t values (2.2)") + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + vs := []string{"1.1", "2.2"} + if rand.Intn(2) < 1 { // is true + tk.MustExec("prepare st from 'select a from t where (a-?) is true'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[1-vIdx])) + } else { // is false + tk.MustExec("prepare st from 'select a from t where (a-?) is false'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[vIdx])) + } + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinDecimalIsTrueFalse(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t (a decimal(10, 2))") + tk.MustExec("insert into t values (1.10)") + tk.MustExec("insert into t values (2.20)") + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + vs := []string{"1.10", "2.20"} + if rand.Intn(2) < 1 { // is true + tk.MustExec("prepare st from 'select a from t where (a-?) is true'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[1-vIdx])) + } else { // is false + tk.MustExec("prepare st from 'select a from t where (a-?) is false'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[vIdx])) + } + } + wg.Done() + }() + } + wg.Wait() +} + +func TestBuiltinIntIsTrueFalse(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set global tidb_enable_instance_plan_cache=1`) + tk.MustExec("create table t (a int)") + tk.MustExec("insert into t values (1)") + tk.MustExec("insert into t values (2)") + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for k := 0; k < 100; k++ { + vs := []string{"1", "2"} + if rand.Intn(2) < 1 { // is true + tk.MustExec("prepare st from 'select a from t where (a-?) is true'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[1-vIdx])) + } else { // is false + tk.MustExec("prepare st from 'select a from t where (a-?) is false'") + vIdx := rand.Intn(2) + tk.MustExec(fmt.Sprintf("set @p1=%v", vs[vIdx])) + tk.MustQuery("execute st using @p1").Check(testkit.Rows(vs[vIdx])) + } + } + wg.Done() + }() + } + wg.Wait() +}