diff --git a/pkg/planner/core/indexmerge_path_test.go b/pkg/planner/core/indexmerge_path_test.go index 990717fcabcbe..1c5f9b3fc9c09 100644 --- a/pkg/planner/core/indexmerge_path_test.go +++ b/pkg/planner/core/indexmerge_path_test.go @@ -232,6 +232,110 @@ func TestMVIndexRandom(t *testing.T) { } } +func TestPlanCacheMVIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE ti ( + item_pk varbinary(255) NOT NULL, + item_id varchar(45) DEFAULT NULL, + item_set_id varchar(45) DEFAULT NULL, + m_id bigint(20) DEFAULT NULL, + m_item_id varchar(127) DEFAULT NULL, + m_item_set_id varchar(127) DEFAULT NULL, + country varchar(2) DEFAULT NULL, + domains json DEFAULT NULL, + signatures json DEFAULT NULL, + short_link json DEFAULT NULL, + long_link json DEFAULT NULL, + f_item_ids json DEFAULT NULL, + f_profile_ids json DEFAULT NULL, + product_sources json DEFAULT NULL, + PRIMARY KEY (item_pk) /*T![clustered_index] CLUSTERED */, + UNIQUE KEY item_id (item_id), + KEY m_item_id (m_item_id), + KEY m_item_set_id (m_item_set_id), + KEY m_id (m_id), + KEY item_set_id (item_set_id), + KEY m_item_and_m_id (m_item_id, m_id), + KEY domains ((cast(domains as char(253) array))), + KEY signatures ((cast(signatures as char(32) array))), + KEY f_profile_ids ((cast(f_profile_ids as unsigned array))), + KEY short_link_old ((cast(short_link as char(1000) array))), + KEY long_link ((cast(long_link as char(1000) array))), + KEY f_item_ids ((cast(f_item_ids as unsigned array))), + KEY short_link ((cast(short_link as char(1000) array)),country))`) + + for i := 0; i < 50; i++ { + var insertVals []string + insertVals = append(insertVals, fmt.Sprintf("'%v'", i)) // item_pk varbinary(255) NOT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", i)) // item_id varchar(45) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", rand.Intn(30))) // item_set_id varchar(45) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("%v", rand.Intn(30))) // m_id bigint(20) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", rand.Intn(30))) // m_item_id varchar(127) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", rand.Intn(30))) // m_item_set_id varchar(127) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", rand.Intn(3))) // country varchar(2) DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "string", maxStrLen: 5, distinct: 10}))) // domains json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "string", maxStrLen: 5, distinct: 10}))) // signatures json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "string", maxStrLen: 5, distinct: 10}))) // short_link json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "string", maxStrLen: 5, distinct: 10}))) // long_link json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "unsigned", distinct: 10}))) // f_item_ids json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "unsigned", distinct: 10}))) // f_profile_ids json DEFAULT NULL, + insertVals = append(insertVals, fmt.Sprintf("'%v'", randArray(randMVIndexValOpts{valType: "string", maxStrLen: 5, distinct: 10}))) // product_sources json DEFAULT NULL, + tk.MustExec(fmt.Sprintf("insert into ti values (%v)", strings.Join(insertVals, ","))) + } + + tk.MustExec(`set @@tidb_opt_fix_control = "45798:on"`) + + check := func(sql string, params ...string) { + sqlWithoutParam := sql + var setStmt, usingStmt string + for i, p := range params { + sqlWithoutParam = strings.Replace(sqlWithoutParam, "?", p, 1) + if i > 0 { + setStmt += ", " + usingStmt += ", " + } + setStmt += fmt.Sprintf("@a%v=%v", i, p) + usingStmt += fmt.Sprintf("@a%v", i) + } + result := tk.MustQuery(sqlWithoutParam).Sort() + tk.MustExec(fmt.Sprintf("set %v", setStmt)) + tk.MustExec(fmt.Sprintf("prepare stmt from '%v'", sql)) + result1 := tk.MustQuery(fmt.Sprintf("execute stmt using %v", usingStmt)).Sort() + result.Check(result1.Rows()) + result2 := tk.MustQuery(fmt.Sprintf("execute stmt using %v", usingStmt)).Sort() + result.Check(result2.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + } + randV := func(vs ...string) string { + return vs[rand.Intn(len(vs))] + } + + for i := 0; i < 50; i++ { + check(`select * from ti where (? member of (short_link)) and (ti.country = ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3))) + check(`select * from ti where (? member of (f_profile_ids) AND (ti.m_item_set_id = ?) AND (ti.country = ?))`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3))) + check(`select * from ti where (? member of (short_link))`, fmt.Sprintf("'%v'", rand.Intn(30))) + check(`select * from ti where (? member of (long_link)) AND (ti.country = ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3))) + check(`select * from ti where (? member of (long_link))`, fmt.Sprintf("'%v'", rand.Intn(30))) + check(`select * from ti where (? member of (f_profile_ids) AND (ti.m_item_set_id = ?) AND (ti.country = ?))`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3))) + check(`select * from ti where (m_id = ? and m_item_id = ? and country = ?) OR (? member of (short_link) and not json_overlaps(product_sources, ?) and country = ?)`, + fmt.Sprintf("%v", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(3)), randV(`'["0","1","2"]'`, `'["0"]'`, `'["1","2"]'`)) + check(`select * from ti where ? member of (domains) AND ? member of (signatures) AND ? member of (f_profile_ids) AND ? member of (short_link) AND ? member of (long_link) AND ? member of (f_item_ids)`, + fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("%v", rand.Intn(30))) + check(`select * from ti where ? member of (domains) AND ? member of (signatures) AND ? member of (f_profile_ids) AND ? member of (short_link) AND ? member of (long_link) AND ? member of (f_item_ids) AND m_item_id IS NULL AND m_id = ? AND country IS NOT NULL`, + fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("%v", rand.Intn(30)), fmt.Sprintf("%v", rand.Intn(30))) + check(`select * from ti where ? member of (f_profile_ids) AND ? member of (short_link) AND json_overlaps(product_sources, ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), randV(`'["0","1","2"]'`, `'["0"]'`, `'["1","2"]'`)) + check(`select * from ti where ? member of (short_link) AND ti.country = "0" AND NOT ? member of (long_link) AND ti.m_item_id = "0"`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30))) + check(`select * from ti WHERE ? member of (domains) AND ? member of (signatures) AND json_contains(f_profile_ids, ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), randV(`'["0","1","2"]'`, `'["0"]'`, `'["1","2"]'`)) + check(`select * from ti where ? member of (short_link) AND ? member of (long_link) OR ? member of (f_profile_ids) AND ti.m_item_id = "0" OR ti.m_item_set_id = ?`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30))) + check(`select * from ti where ? member of (domains) OR ? member of (signatures) OR ? member of (f_profile_ids) OR json_contains(f_profile_ids, ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("%v", rand.Intn(30)), randV(`"[0,1]"`, `"[0,1,2]"`, `"[0]"`)) + check(`select * from ti WHERE ? member of (domains) OR ? member of (signatures) OR ? member of (long_link) OR json_contains(f_profile_ids, ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), randV(`"[0,1]"`, `"[0,1,2]"`, `"[0]"`)) + check(`select * from ti where ? member of (domains) OR ? member of (signatures) OR (? member of (f_profile_ids))`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("%v", rand.Intn(30))) + check(`select * from ti where ? member of (domains) OR ? member of (signatures) OR json_overlaps(f_profile_ids, ?)`, fmt.Sprintf("'%v'", rand.Intn(30)), fmt.Sprintf("'%v'", rand.Intn(30)), randV(`"[0,1]"`, `"[0,1,2]"`, `"[0]"`)) + } +} + func randMVIndexCondsXNF4MemberOf(nConds int, valOpts randMVIndexValOpts, CNF bool, randJCol, randNCol func() string) (conds, conds4PlanCache string, params []string) { for i := 0; i < nConds; i++ { if i > 0 {