|
| 1 | +// Copyright 2024 PingCAP, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package core_test |
| 16 | + |
| 17 | +import ( |
| 18 | + "context" |
| 19 | + "encoding/json" |
| 20 | + "testing" |
| 21 | + |
| 22 | + "github.com/pingcap/tidb/pkg/planner/core" |
| 23 | + "github.com/pingcap/tidb/pkg/planner/core/base" |
| 24 | + "github.com/pingcap/tidb/pkg/testkit" |
| 25 | + "github.com/stretchr/testify/require" |
| 26 | +) |
| 27 | + |
| 28 | +func TestPlanCacheClone(t *testing.T) { |
| 29 | + store := testkit.CreateMockStore(t) |
| 30 | + tk := testkit.NewTestKit(t, store) |
| 31 | + tk.MustExec(`use test`) |
| 32 | + tk.MustExec(`create table t (a int, b int, c int, primary key(a), key(b))`) |
| 33 | + |
| 34 | + // TableScan |
| 35 | + testCachedPlanClone(t, tk, `prepare st from 'select * from t where a<?'`, |
| 36 | + `set @a1=1, @a2=2`, `execute st using @a1`, `execute st using @a2`) |
| 37 | + testCachedPlanClone(t, tk, `prepare st from 'select * from t where a>=?'`, |
| 38 | + `set @a1=1, @a2=2`, `execute st using @a1`, `execute st using @a2`) |
| 39 | + |
| 40 | + //// IndexScan |
| 41 | + //testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(b) where b<=?'`, |
| 42 | + // `set @a1=1, @a2=2`, `execute st using @a1`, `execute st using @a2`) |
| 43 | + //testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(b) where b>?'`, |
| 44 | + // `set @a1=1, @a2=2`, `execute st using @a1`, `execute st using @a2`) |
| 45 | + |
| 46 | + // TableScan + Selection |
| 47 | + testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(primary) where a<? and b<?'`, |
| 48 | + `set @a1=1, @b1=1, @a2=2, @b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) |
| 49 | + testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(primary) where a<? and b+?=10'`, |
| 50 | + `set @a1=1, @b1=1, @a2=2, @b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) |
| 51 | + |
| 52 | + //// IndexScan + Selection |
| 53 | + //testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(b) where a<? and b<?'`, |
| 54 | + // `set @a1=1, @b1=1, @a2=2, @b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) |
| 55 | + //testCachedPlanClone(t, tk, `prepare st from 'select * from t use index(b) where a<? and b+?=10'`, |
| 56 | + // `set @a1=1, @b1=1, @a2=2, @b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) |
| 57 | +} |
| 58 | + |
| 59 | +func testCachedPlanClone(t *testing.T, tk *testkit.TestKit, prep, set, exec1, exec2 string) { |
| 60 | + tk.MustExec(prep) |
| 61 | + tk.MustExec(set) |
| 62 | + tk.MustQuery(exec1) // generate the first cached plan |
| 63 | + |
| 64 | + var original base.Plan |
| 65 | + var originalFingerprint string |
| 66 | + before := func(cachedVal *core.PlanCacheValue) { |
| 67 | + // get the current cached plan and its fingerprint |
| 68 | + original, originalFingerprint = cachedVal.Plan, planFingerprint(t, cachedVal.Plan) |
| 69 | + // replace the cached plan with a cloned one |
| 70 | + cloned, ok := original.CloneForPlanCache() |
| 71 | + require.True(t, ok) |
| 72 | + cachedVal.Plan = cloned |
| 73 | + } |
| 74 | + after := func(cachedVal *core.PlanCacheValue) { |
| 75 | + cloned := cachedVal.Plan |
| 76 | + require.True(t, originalFingerprint != planFingerprint(t, cloned)) // this cloned one have been adjusted by the optimizer |
| 77 | + require.True(t, originalFingerprint == planFingerprint(t, original)) // the prior one should keep unchanged |
| 78 | + } |
| 79 | + ctx := context.WithValue(context.Background(), core.PlanCacheKeyTestBeforeAdjust{}, before) |
| 80 | + ctx = context.WithValue(ctx, core.PlanCacheKeyTestAfterAdjust{}, after) |
| 81 | + tk.MustQueryWithContext(ctx, exec2) |
| 82 | +} |
| 83 | + |
| 84 | +func planFingerprint(t *testing.T, p base.Plan) string { |
| 85 | + v, err := json.Marshal(p) |
| 86 | + require.NoError(t, err) |
| 87 | + return string(v) |
| 88 | +} |
0 commit comments