diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index 44713331b43c1..c2a2e2dc7a76d 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -685,7 +685,7 @@ func TestRuntimeHintsInEvolveTasks(t *testing.T) { tk.MustExec("admin flush bindings") rows := tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 2) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), max_execution_time(5000)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), no_order_index(@`sel_1` `test`.`t` `idx_c`), max_execution_time(5000)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0", rows[0][1]) } func TestDefaultSessionVars(t *testing.T) { diff --git a/bindinfo/capture_test.go b/bindinfo/capture_test.go index dc0215203b255..dfb9e35f47181 100644 --- a/bindinfo/capture_test.go +++ b/bindinfo/capture_test.go @@ -81,13 +81,13 @@ func TestDMLCapturePlanBaseline(t *testing.T) { rows = tk.MustQuery("show global bindings").Sort().Rows() require.Len(t, rows, 4) require.Equal(t, "delete from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) - require.Equal(t, "DELETE /*+ use_index(@`del_1` `test`.`t` `idx_b`)*/ FROM `test`.`t` WHERE `b` = 1 AND `c` > 1", rows[0][1]) + require.Equal(t, "DELETE /*+ use_index(@`del_1` `test`.`t` `idx_b`), no_order_index(@`del_1` `test`.`t` `idx_b`)*/ FROM `test`.`t` WHERE `b` = 1 AND `c` > 1", rows[0][1]) require.Equal(t, "insert into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?", rows[1][0]) - require.Equal(t, "INSERT INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[1][1]) + require.Equal(t, "INSERT INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`), no_order_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[1][1]) require.Equal(t, "replace into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?", rows[2][0]) - require.Equal(t, "REPLACE INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[2][1]) + require.Equal(t, "REPLACE INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`), no_order_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[2][1]) require.Equal(t, "update `test` . `t` set `a` = ? where `b` = ? and `c` > ?", rows[3][0]) - require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx_b`)*/ `test`.`t` SET `a`=1 WHERE `b` = 1 AND `c` > 1", rows[3][1]) + require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx_b`), no_order_index(@`upd_1` `test`.`t` `idx_b`)*/ `test`.`t` SET `a`=1 WHERE `b` = 1 AND `c` > 1", rows[3][1]) } func TestCapturePlanBaseline(t *testing.T) { @@ -259,7 +259,7 @@ func TestCapturePreparedStmt(t *testing.T) { rows := tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 1) require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), no_order_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) require.True(t, tk.MustUseIndex("select /*+ use_index(t,idx_b) */ * from t where b = 1 and c > 1", "idx_c(c)")) tk.MustExec("admin flush bindings") @@ -267,7 +267,7 @@ func TestCapturePreparedStmt(t *testing.T) { rows = tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 1) require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), no_order_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) } func TestCapturePlanBaselineIgnoreTiFlash(t *testing.T) { @@ -386,7 +386,7 @@ func TestCapturedBindingCharset(t *testing.T) { rows := tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 1) require.Equal(t, "update `test` . `t` set `name` = ? where `name` <= ?", rows[0][0]) - require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx`)*/ `test`.`t` SET `name`='hello' WHERE `name` <= 'abc'", rows[0][1]) + require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx`), no_order_index(@`upd_1` `test`.`t` `idx`)*/ `test`.`t` SET `name`='hello' WHERE `name` <= 'abc'", rows[0][1]) require.Equal(t, "utf8mb4", rows[0][6]) require.Equal(t, "utf8mb4_bin", rows[0][7]) } @@ -433,7 +433,7 @@ func TestUpdateSubqueryCapture(t *testing.T) { tk.MustExec("admin capture bindings") rows := tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 1) - bindSQL := "UPDATE /*+ hash_join(@`upd_1` `test`.`t1`), use_index(@`upd_1` `test`.`t1` `idx_b`), use_index(@`sel_1` `test`.`t2` ), use_index(@`sel_2` `test`.`t2` )*/ `test`.`t1` SET `b`=1 WHERE `b` = 2 AND (`a` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1) OR `c` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1))" + bindSQL := "UPDATE /*+ hash_join(@`upd_1` `test`.`t1`), use_index(@`upd_1` `test`.`t1` `idx_b`), no_order_index(@`upd_1` `test`.`t1` `idx_b`), use_index(@`sel_1` `test`.`t2` ), use_index(@`sel_2` `test`.`t2` )*/ `test`.`t1` SET `b`=1 WHERE `b` = 2 AND (`a` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1) OR `c` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1))" originSQL := "UPDATE `test`.`t1` SET `b`=1 WHERE `b` = 2 AND (`a` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1) OR `c` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1))" require.Equal(t, bindSQL, rows[0][1]) tk.MustExec(originSQL) @@ -488,7 +488,7 @@ func TestIssue20417(t *testing.T) { rows = tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 1) require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` = ?", rows[0][0]) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `b` = 2 AND `c` = 213124", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`), no_order_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `b` = 2 AND `c` = 213124", rows[0][1]) tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") // Test for evolve baseline @@ -505,13 +505,13 @@ func TestIssue20417(t *testing.T) { rows = tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 2) require.Equal(t, "select * from `test` . `t` where `c` = ?", rows[0][0]) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), no_order_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) require.Equal(t, "pending verify", rows[0][3]) tk.MustExec("admin evolve bindings") rows = tk.MustQuery("show global bindings").Rows() require.Len(t, rows, 2) require.Equal(t, "select * from `test` . `t` where `c` = ?", rows[0][0]) - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), no_order_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) status := rows[0][3].(string) require.True(t, status == bindinfo.Enabled || status == bindinfo.Rejected) tk.MustExec("set @@tidb_evolve_plan_baselines=0") @@ -556,7 +556,7 @@ func TestIssue25505(t *testing.T) { spmMap := map[string]string{} spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` < ? ) select * from `cte`"] = - "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` < 5) SELECT /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_ab`)*/ * FROM `cte`" + "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` < 5) SELECT /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_ab`), no_order_index(@`sel_3` `test`.`t1` `idx_ab`)*/ * FROM `cte`" spmMap["with recursive `cte1` ( `a` , `b` ) as ( select * from `test` . `t` where `b` = ? union select `a` + ? , `b` + ? from `cte1` where `a` < ? ) select * from `test` . `t`"] = "WITH RECURSIVE `cte1` (`a`, `b`) AS (SELECT * FROM `test`.`t` WHERE `b` = 1 UNION SELECT `a` + 1,`b` + 1 FROM `cte1` WHERE `a` < 2) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" spmMap["with `cte1` as ( select * from `test` . `t` ) , `cte2` as ( select ? ) select * from `test` . `t`"] = @@ -564,11 +564,11 @@ func TestIssue25505(t *testing.T) { spmMap["with `cte` as ( select * from `test` . `t` where `b` = ? ) select * from `test` . `t`"] = "WITH `cte` AS (SELECT * FROM `test`.`t` WHERE `b` = 6) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` > ? ) select * from `cte`"] = - "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` > 5) SELECT /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_b`)*/ * FROM `cte`" + "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` > 5) SELECT /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_b`), no_order_index(@`sel_3` `test`.`t1` `idx_b`)*/ * FROM `cte`" spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` > ? and `b` > ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = - "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` > 1 AND `b` > 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_ab`), use_index(@`sel_1` `test`.`t1` `idx_ab`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" + "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` > 1 AND `b` > 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_ab`), order_index(@`sel_3` `test`.`t2` `idx_ab`), use_index(@`sel_1` `test`.`t1` `idx_ab`), order_index(@`sel_1` `test`.`t1` `idx_ab`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` = ? and `b` = ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = - "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` = 1 AND `b` = 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_a`), use_index(@`sel_1` `test`.`t1` `idx_a`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" + "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` = 1 AND `b` = 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_a`), no_order_index(@`sel_3` `test`.`t2` `idx_a`), use_index(@`sel_1` `test`.`t1` `idx_a`), no_order_index(@`sel_1` `test`.`t1` `idx_a`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") @@ -992,8 +992,8 @@ func TestCaptureHints(t *testing.T) { {"select /*+ use_index_merge(t, a, b) */ a, b from t where a=1 or b=1", "use_index_merge(@`sel_1` `t` `a`, `b`)"}, {"select /*+ ignore_index(t, a) */ * from t where a=1", "ignore_index(`t` `a`)"}, // push-down hints - {"select /*+ limit_to_cop() */ * from t limit 10", "limit_to_cop()"}, - {"select /*+ agg_to_cop() */ a, count(*) from t group by a", "agg_to_cop()"}, + {"select /*+ limit_to_cop() */ * from t limit 10", "limit_to_cop(@`sel_1`)"}, + {"select /*+ agg_to_cop() */ a, count(*) from t group by a", "agg_to_cop(@`sel_1`)"}, // index-merge hints {"select /*+ no_index_merge() */ a, b from t where a>1 or b>1", "no_index_merge()"}, {"select /*+ use_index_merge(t, a, b) */ a, b from t where a>1 or b>1", "use_index_merge(@`sel_1` `t` `a`, `b`)"}, @@ -1012,8 +1012,8 @@ func TestCaptureHints(t *testing.T) { tk.MustExec(capCase.query) tk.MustExec("admin capture bindings") res := tk.MustQuery(`show global bindings`).Rows() - require.Equal(t, len(res), 1) // this query is captured, and - require.True(t, strings.Contains(res[0][1].(string), capCase.hint)) // the binding contains the expected hint + require.Equal(t, len(res), 1) // this query is captured, and + require.True(t, strings.Contains(res[0][1].(string), capCase.hint), fmt.Sprintf("%v:%v", capCase.query, res[0][1])) // the binding contains the expected hint // test sql digest parser4binding := parser.New() originNode, err := parser4binding.ParseOneStmt(capCase.query, "utf8mb4", "utf8mb4_general_ci") diff --git a/executor/adapter.go b/executor/adapter.go index c1d68f9b33379..93c48d8d24f45 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -1756,8 +1756,8 @@ func getEncodedPlan(stmtCtx *stmtctx.StatementContext, genHint bool) (encodedPla // so we have to iterate all hints from the customer and keep some other necessary hints. switch tableHint.HintName.L { case "memory_quota", "use_toja", "no_index_merge", "max_execution_time", - plannercore.HintAggToCop, plannercore.HintIgnoreIndex, - plannercore.HintReadFromStorage, plannercore.HintLimitToCop: + plannercore.HintIgnoreIndex, plannercore.HintReadFromStorage, + plannercore.HintSemiJoinRewrite, plannercore.HintNoDecorrelate: hints = append(hints, tableHint) } } diff --git a/infoschema/cluster_tables_test.go b/infoschema/cluster_tables_test.go index 60bb77da4267a..f9f1f0c6c1f75 100644 --- a/infoschema/cluster_tables_test.go +++ b/infoschema/cluster_tables_test.go @@ -898,29 +898,29 @@ func TestQuickBinding(t *testing.T) { expectedHint string dmlAndSubqueryTemplates []string } - defaultDMLAndSubqueryTemplates := []string{ - //"select a from (%v) tx where tx.a<1", // TODO: support sub query - "insert into t1 %v", - // TODO: more templates - } + //defaultDMLAndSubqueryTemplates := []string{ + // //"select a from (%v) tx where tx.a<1", // TODO: support sub query + // "insert into t1 %v", + // // TODO: more templates + //} testCases := []testCase{ // access path selection with use_index / ignore_index - {`select /*+ use_index(t1, k_a) */ * from t1 where b=?`, "use_index(@`sel_1` `test`.`t1` `k_a`)", defaultDMLAndSubqueryTemplates}, - {`select /*+ use_index(t1, k_bc) */ * from t1 where a=?`, "use_index(@`sel_1` `test`.`t1` `k_bc`)", defaultDMLAndSubqueryTemplates}, - {`select /*+ use_index(t1, primary) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` )", defaultDMLAndSubqueryTemplates}, - {`select /*+ ignore_index(t1, k_a, k_bc) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` ), ignore_index(`t1` `k_a`, `k_bc`)", defaultDMLAndSubqueryTemplates}, - {`select /*+ use_index(t1) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` )", defaultDMLAndSubqueryTemplates}, + {`select /*+ use_index(t1, k_a) */ * from t1 where b=?`, "use_index(@`sel_1` `test`.`t1` `k_a`), no_order_index(@`sel_1` `test`.`t1` `k_a`)", nil}, + {`select /*+ use_index(t1, k_bc) */ * from t1 where a=?`, "use_index(@`sel_1` `test`.`t1` `k_bc`), no_order_index(@`sel_1` `test`.`t1` `k_bc`)", nil}, + {`select /*+ use_index(t1, primary) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`)", nil}, + {`select /*+ ignore_index(t1, k_a, k_bc) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), ignore_index(`t1` `k_a`, `k_bc`)", nil}, + {`select /*+ use_index(t1) */ * from t1 where a=? and b=?`, "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`)", nil}, {`select /*+ use_index(t2) */ * from t2 where a=? and b=?`, "use_index(@`sel_1` `test`.`t2` )", nil}, // aggregation - {`select /*+ hash_agg(), use_index(t1, primary) */ count(*) from t1 where aSort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ IGNORE_INDEX(test.t, c_d_e) */ c from t order by c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e) */ * from t", "Best": "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`), no_order_index(@`sel_1` `test`.`t` `c_d_e`)" }, { "SQL": "select /*+ FORCE_INDEX(test.t, c_d_e) */ * from t", "Best": "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`), no_order_index(@`sel_1` `test`.`t` `c_d_e`)" }, { "SQL": "select /*+ USE_INDEX(t, c_d_e) */ * from t t1", "Best": "TableReader(Table(t))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t1` )" + "Hints": "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`)" }, { "SQL": "select /*+ IGNORE_INDEX(t, c_d_e) */ t1.c from t t1 order by t1.c", "Best": "IndexReader(Index(t.c_d_e)[[NULL,+inf]])", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`), order_index(@`sel_1` `test`.`t1` `c_d_e`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e) */ * from t t1", "Best": "TableReader(Table(t))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t1` )" + "Hints": "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`)" }, { "SQL": "select /*+ USE_INDEX(t1, c_d_e) */ * from t t1", "Best": "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`), no_order_index(@`sel_1` `test`.`t1` `c_d_e`)" }, { "SQL": "select /*+ IGNORE_INDEX(t1, c_d_e) */ t1.c from t t1 order by t1.c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` )" + "Hints": "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t1, c_d_e) */ * from t t1", "Best": "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`), no_order_index(@`sel_1` `test`.`t1` `c_d_e`)" }, { "SQL": "select /*+ USE_INDEX(t1, c_d_e), USE_INDEX(t2, f) */ * from t t1, t t2 where t1.a = t2.b", "Best": "LeftHashJoin{IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))->IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))}(test.t.a,test.t.b)", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`), use_index(@`sel_1` `test`.`t2` `f`), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `c_d_e`), no_order_index(@`sel_1` `test`.`t1` `c_d_e`), use_index(@`sel_1` `test`.`t2` `f`), no_order_index(@`sel_1` `test`.`t2` `f`)" }, { "SQL": "select /*+ IGNORE_INDEX(t1, c_d_e), IGNORE_INDEX(t2, f), HASH_JOIN(t1) */ * from t t1, t t2 where t1.a = t2.b", "Best": "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.b)", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_1` `test`.`t2` ), no_order_index(@`sel_1` `test`.`t2` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t1, c_d_e), FORCE_INDEX(t2, f) */ * from t t1, t t2 where t1.a = t2.b", "Best": "LeftHashJoin{IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))->IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))}(test.t.a,test.t.b)", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t1` `c_d_e`), use_index(@`sel_1` `test`.`t2` `f`), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `c_d_e`), no_order_index(@`sel_1` `test`.`t1` `c_d_e`), use_index(@`sel_1` `test`.`t2` `f`), no_order_index(@`sel_1` `test`.`t2` `f`)" }, { "SQL": "select /*+ USE_INDEX(t, c_d_e, f, g) */ * from t order by f", "Best": "IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `f`)" + "Hints": "use_index(@`sel_1` `test`.`t` `f`), order_index(@`sel_1` `test`.`t` `f`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e, f, g) */ * from t order by f", "Best": "IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `f`)" + "Hints": "use_index(@`sel_1` `test`.`t` `f`), order_index(@`sel_1` `test`.`t` `f`)" }, { "SQL": "select /*+ USE_INDEX(t) */ f from t where f > 10", "Best": "TableReader(Table(t)->Sel([gt(test.t.f, 10)]))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t) */ f from t where f > 10", "Best": "TableReader(Table(t)->Sel([gt(test.t.f, 10)]))", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX(t, no_such_index) */ * from t", "Best": "TableReader(Table(t))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ IGNORE_INDEX(t, no_such_index) */ * from t", "Best": "TableReader(Table(t))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t, no_such_index) */ * from t", "Best": "TableReader(Table(t))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX(t, c_d_e), IGNORE_INDEX(t, f) */ c from t order by c", "Best": "IndexReader(Index(t.c_d_e)[[NULL,+inf]])", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`), order_index(@`sel_1` `test`.`t` `c_d_e`)" }, { "SQL": "select /*+ USE_INDEX(t, f), IGNORE_INDEX(t, f) */ c from t order by c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX(t, c_d_e), IGNORE_INDEX(t, c_d_e) */ c from t order by c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX(t, c_d_e, f), IGNORE_INDEX(t, c_d_e) */ c from t order by c", "Best": "IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `f`)" + "Hints": "use_index(@`sel_1` `test`.`t` `f`), no_order_index(@`sel_1` `test`.`t` `f`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e), IGNORE_INDEX(t, f) */ c from t order by c", "Best": "IndexReader(Index(t.c_d_e)[[NULL,+inf]])", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t` `c_d_e`), order_index(@`sel_1` `test`.`t` `c_d_e`)" }, { "SQL": "select /*+ FORCE_INDEX(t, f), IGNORE_INDEX(t, f) */ c from t order by c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e), IGNORE_INDEX(t, c_d_e) */ c from t order by c", "Best": "TableReader(Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ FORCE_INDEX(t, c_d_e, f), IGNORE_INDEX(t, c_d_e) */ c from t order by c", "Best": "IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))->Sort", "HasWarn": false, - "Hints": "use_index(@`sel_1` `test`.`t` `f`)" + "Hints": "use_index(@`sel_1` `test`.`t` `f`), no_order_index(@`sel_1` `test`.`t` `f`)" } ] }, @@ -1969,19 +1969,19 @@ "SQL": "select /*+ NO_INDEX_MERGE(), USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2", "Best": "TableReader(Table(t)->Sel([or(lt(test.t.a, 1), gt(test.t.f, 2))]))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX_MERGE(t1, c_d_e, f_g) */ * from t where c < 1 or f > 2", "Best": "TableReader(Table(t)->Sel([or(lt(test.t.c, 1), gt(test.t.f, 2))]))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ NO_INDEX_MERGE(), USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2", "Best": "TableReader(Table(t)->Sel([or(lt(test.t.a, 1), gt(test.t.f, 2))]))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX_MERGE(t) USE_INDEX_MERGE(t) */ * from t where c < 1 or f > 2", @@ -1993,13 +1993,13 @@ "SQL": "select /*+ USE_INDEX_MERGE(db2.t) */ * from t where c < 1 or f > 2", "Best": "TableReader(Table(t)->Sel([or(lt(test.t.c, 1), gt(test.t.f, 2))]))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" }, { "SQL": "select /*+ USE_INDEX_MERGE(db2.t, c_d_e, f_g) */ * from t where c < 1 or f > 2", "Best": "TableReader(Table(t)->Sel([or(lt(test.t.c, 1), gt(test.t.f, 2))]))", "HasWarn": true, - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`)" } ] }, @@ -2421,57 +2421,57 @@ { "SQL": "select * from t order by b limit 1 for update", "Best": "TableReader(Table(t)->TopN([test.t.b],0,1))->TopN([test.t.b],0,1)->Lock", - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`), limit_to_cop(@`sel_1`)" }, { "SQL": "update t set a = 5 where b < 1 order by d limit 1", "Best": "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Update", - "Hints": "use_index(@`upd_1` `test`.`t` )" + "Hints": "use_index(@`upd_1` `test`.`t` ), no_order_index(@`upd_1` `test`.`t` `primary`), limit_to_cop(@`upd_1`)" }, { "SQL": "update t set a = 5", "Best": "TableReader(Table(t))->Update", - "Hints": "use_index(@`upd_1` `test`.`t` )" + "Hints": "use_index(@`upd_1` `test`.`t` ), no_order_index(@`upd_1` `test`.`t` `primary`)" }, { "SQL": "delete /*+ TIDB_INLJ(t1, t2) */ t1 from t t1, t t2 where t1.c=t2.c", "Best": "IndexJoin{TableReader(Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,NULL]], Table(t))}(test.t.c,test.t.c)->Delete", - "Hints": "use_index(@`del_1` `test`.`t1` ), use_index(@`del_1` `test`.`t2` `c_d_e`), inl_join(@`del_1` `test`.`t2`)" + "Hints": "inl_join(@`del_1` `test`.`t2`), use_index(@`del_1` `test`.`t1` ), no_order_index(@`del_1` `test`.`t1` `primary`), use_index(@`del_1` `test`.`t2` `c_d_e`), no_order_index(@`del_1` `test`.`t2` `c_d_e`)" }, { "SQL": "delete /*+ TIDB_SMJ(t1, t2) */ from t1 using t t1, t t2 where t1.c=t2.c", "Best": "MergeInnerJoin{IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(test.t.c,test.t.c)->Delete", - "Hints": "use_index(@`del_1` `test`.`t1` `c_d_e`), use_index(@`del_1` `test`.`t2` `c_d_e`), merge_join(@`del_1` `test`.`t1`)" + "Hints": "merge_join(@`del_1` `test`.`t1`), use_index(@`del_1` `test`.`t1` `c_d_e`), order_index(@`del_1` `test`.`t1` `c_d_e`), use_index(@`del_1` `test`.`t2` `c_d_e`), order_index(@`del_1` `test`.`t2` `c_d_e`)" }, { "SQL": "update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.c=1 where t1.a=t2.a", "Best": "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Update", - "Hints": "use_index(@`upd_1` `test`.`t1` ), use_index(@`upd_1` `test`.`t2` ), merge_join(@`upd_1` `test`.`t1`)" + "Hints": "merge_join(@`upd_1` `test`.`t1`), use_index(@`upd_1` `test`.`t1` ), order_index(@`upd_1` `test`.`t1` `primary`), use_index(@`upd_1` `test`.`t2` ), order_index(@`upd_1` `test`.`t2` `primary`)" }, { "SQL": "update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.c=1 where t1.a=t2.a", "Best": "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Update", - "Hints": "use_index(@`upd_1` `test`.`t1` ), use_index(@`upd_1` `test`.`t2` ), hash_join(@`upd_1` `test`.`t1`)" + "Hints": "hash_join(@`upd_1` `test`.`t1`), use_index(@`upd_1` `test`.`t1` ), no_order_index(@`upd_1` `test`.`t1` `primary`), use_index(@`upd_1` `test`.`t2` ), no_order_index(@`upd_1` `test`.`t2` `primary`)" }, { "SQL": "delete from t where b < 1 order by d limit 1", "Best": "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Delete", - "Hints": "use_index(@`del_1` `test`.`t` )" + "Hints": "use_index(@`del_1` `test`.`t` ), no_order_index(@`del_1` `test`.`t` `primary`), limit_to_cop(@`del_1`)" }, { "SQL": "delete from t", "Best": "TableReader(Table(t))->Delete", - "Hints": "use_index(@`del_1` `test`.`t` )" + "Hints": "use_index(@`del_1` `test`.`t` ), no_order_index(@`del_1` `test`.`t` `primary`)" }, { "SQL": "delete from t use index(c_d_e) where b = 1", "Best": "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t)->Sel([eq(test.t.b, 1)]))->Delete", - "Hints": "use_index(@`del_1` `test`.`t` `c_d_e`)" + "Hints": "use_index(@`del_1` `test`.`t` `c_d_e`), no_order_index(@`del_1` `test`.`t` `c_d_e`)" }, { "SQL": "insert into t select * from t where b < 1 order by d limit 1", "Best": "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Insert", - "Hints": "use_index(@`sel_1` `test`.`t` )" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`), limit_to_cop(@`sel_1`)" }, { "SQL": "insert into t (a, b, c, e, f, g) values(0,0,0,0,0,0)", @@ -2968,25 +2968,25 @@ "SQL": "select /*+ TIDB_INLJ(t1) */ t1.a, t2.a, t3.a from t t1, t t2, t t3 where t1.a = t2.a and t2.a = t3.a;", "Best": "MergeInnerJoin{IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->TableReader(Table(t))}(test.t.a,test.t.a)", "Warning": "", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t3` ), merge_join(@`sel_1` `test`.`t3`)" + "Hints": "merge_join(@`sel_1` `test`.`t3`), inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_1` `test`.`t2` ), order_index(@`sel_1` `test`.`t2` `primary`), use_index(@`sel_1` `test`.`t3` ), order_index(@`sel_1` `test`.`t3` `primary`)" }, { "SQL": "select /*+ TIDB_INLJ(test.t1) */ t1.a, t2.a, t3.a from t t1, t t2, t t3 where t1.a = t2.a and t2.a = t3.a;", "Best": "MergeInnerJoin{IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->TableReader(Table(t))}(test.t.a,test.t.a)", "Warning": "", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t3` ), merge_join(@`sel_1` `test`.`t3`)" + "Hints": "merge_join(@`sel_1` `test`.`t3`), inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_1` `test`.`t2` ), order_index(@`sel_1` `test`.`t2` `primary`), use_index(@`sel_1` `test`.`t3` ), order_index(@`sel_1` `test`.`t3` `primary`)" }, { "SQL": "select /*+ TIDB_INLJ(t1) */ t1.b, t2.a from t t1, t t2 where t1.b = t2.a;", "Best": "LeftHashJoin{TableReader(Table(t))->IndexReader(Index(t.f)[[NULL,+inf]])}(test.t.b,test.t.a)", "Warning": "[planner:1815]Optimizer Hint /*+ INL_JOIN(t1) */ or /*+ TIDB_INLJ(t1) */ is inapplicable", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` `f`), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_1` `test`.`t2` `f`), no_order_index(@`sel_1` `test`.`t2` `f`)" }, { "SQL": "select /*+ TIDB_INLJ(t2) */ t1.b, t2.a from t2 t1, t2 t2 where t1.b=t2.b and t2.c=-1;", "Best": "IndexJoin{TableReader(Table(t2)->Sel([eq(test.t2.c, -1)]))->IndexReader(Index(t2.b)[[NULL,NULL]])}(test.t2.b,test.t2.b)->Projection", "Warning": "[planner:1815]Optimizer Hint /*+ INL_JOIN(t2) */ or /*+ TIDB_INLJ(t2) */ is inapplicable", - "Hints": "use_index(@`sel_1` `test`.`t2` ), use_index(@`sel_1` `test`.`t1` `b`), inl_join(@`sel_1` `test`.`t1`)" + "Hints": "inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t2` ), no_order_index(@`sel_1` `test`.`t2` `primary`), use_index(@`sel_1` `test`.`t1` `b`), no_order_index(@`sel_1` `test`.`t1` `b`)" } ] }, @@ -3056,57 +3056,57 @@ { "SQL": "select /*+ MERGE_JOIN(@sel_1 t1), INL_JOIN(@sel_2 t3) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "IndexJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,NULL]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), no_order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ MERGE_JOIN(@sel_1 t1), INL_JOIN(@qb t3) */ t1.a, t1.b from t t1, (select /*+ QB_NAME(qb) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "IndexJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,NULL]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), no_order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ HASH_JOIN(@sel_1 t1), MERGE_JOIN(@sel_2 t2) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ HASH_JOIN(@sel_1 t1), MERGE_JOIN(@qb t2) */ t1.a, t1.b from t t1, (select /*+ QB_NAME(qb) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ INL_JOIN(@sel_1 t1), HASH_JOIN(@sel_2 t2) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ INL_JOIN(@sel_1 t1), HASH_JOIN(@qb t2) */ t1.a, t1.b from t t1, (select /*+ QB_NAME(qb) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Plan": "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(test.t.a,test.t.c)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` ), use_index(@`sel_2` `test`.`t3` `c_d_e`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` ), order_index(@`sel_2` `test`.`t2` `primary`), use_index(@`sel_2` `test`.`t3` `c_d_e`), order_index(@`sel_2` `test`.`t3` `c_d_e`)" }, { "SQL": "select /*+ HASH_AGG(@sel_1), STREAM_AGG(@sel_2) */ count(*) from t t1 where t1.a < (select count(*) from t t2 where t1.a > t2.a)", "Plan": "Apply{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.f)[[NULL,+inf]]->Sel([gt(test.t.a, test.t.a)])->StreamAgg)->StreamAgg}->HashAgg", - "Hints": "use_index(@`sel_1` `test`.`t1` `f`), use_index(@`sel_2` `test`.`t2` `f`), stream_agg(@`sel_2`), hash_agg(@`sel_1`)" + "Hints": "hash_agg(@`sel_1`), use_index(@`sel_1` `test`.`t1` `f`), no_order_index(@`sel_1` `test`.`t1` `f`), stream_agg(@`sel_2`), use_index(@`sel_2` `test`.`t2` `f`), no_order_index(@`sel_2` `test`.`t2` `f`), agg_to_cop(@`sel_2`)" }, { "SQL": "select /*+ STREAM_AGG(@sel_1), HASH_AGG(@qb) */ count(*) from t t1 where t1.a < (select /*+ QB_NAME(qb) */ count(*) from t t2 where t1.a > t2.a)", "Plan": "Apply{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.f)[[NULL,+inf]]->Sel([gt(test.t.a, test.t.a)])->HashAgg)->HashAgg}->StreamAgg", - "Hints": "use_index(@`sel_1` `test`.`t1` `f`), use_index(@`sel_2` `test`.`t2` `f`), hash_agg(@`sel_2`), stream_agg(@`sel_1`)" + "Hints": "stream_agg(@`sel_1`), use_index(@`sel_1` `test`.`t1` `f`), no_order_index(@`sel_1` `test`.`t1` `f`), hash_agg(@`sel_2`), use_index(@`sel_2` `test`.`t2` `f`), no_order_index(@`sel_2` `test`.`t2` `f`), agg_to_cop(@`sel_2`)" }, { "SQL": "select /*+ HASH_AGG(@sel_2) */ a, (select count(*) from t t1 where t1.b > t.a) from t where b > (select b from t t2 where t2.b = t.a limit 1)", "Plan": "Apply{Apply{TableReader(Table(t))->TableReader(Table(t)->Sel([eq(test.t.b, test.t.a)])->Limit)->Limit}->TableReader(Table(t)->Sel([gt(test.t.b, test.t.a)])->HashAgg)->HashAgg}->Projection", - "Hints": "use_index(@`sel_1` `test`.`t` ), use_index(@`sel_3` `test`.`t2` ), use_index(@`sel_2` `test`.`t1` ), hash_agg(@`sel_2`)" + "Hints": "use_index(@`sel_1` `test`.`t` ), no_order_index(@`sel_1` `test`.`t` `primary`), use_index(@`sel_3` `test`.`t2` ), no_order_index(@`sel_3` `test`.`t2` `primary`), limit_to_cop(@`sel_3`), hash_agg(@`sel_2`), use_index(@`sel_2` `test`.`t1` ), no_order_index(@`sel_2` `test`.`t1` `primary`), agg_to_cop(@`sel_2`)" }, { "SQL": "select /*+ HASH_JOIN(@sel_1 t1), HASH_JOIN(@sel_2 t1) */ t1.b, t2.a, t2.aa from t t1, (select t1.a as a, t2.a as aa from t t1, t t2) t2 where t1.a = t2.aa;", "Plan": "LeftHashJoin{LeftHashJoin{TableReader(Table(t))->IndexReader(Index(t.f)[[NULL,+inf]])}(test.t.a,test.t.a)->IndexReader(Index(t.f)[[NULL,+inf]])}->Projection", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` `f`), use_index(@`sel_2` `test`.`t1` `f`)" + "Hints": "use_index(@`sel_1` `test`.`t1` ), no_order_index(@`sel_1` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` `f`), no_order_index(@`sel_2` `test`.`t2` `f`), use_index(@`sel_2` `test`.`t1` `f`), no_order_index(@`sel_2` `test`.`t1` `f`)" }, { "SQL": "select /*+ HASH_JOIN(@sel_2 t1@sel_2, t2@sel_2), MERGE_JOIN(@sel_1 t1@sel_1, t2@sel_1) */ * from (select t1.a, t1.b from t t1, t t2 where t1.a = t2.a) t1, t t2 where t1.b = t2.b", "Plan": "MergeInnerJoin{TableReader(Table(t))->Sort->LeftHashJoin{TableReader(Table(t))->IndexReader(Index(t.f)[[NULL,+inf]])}(test.t.a,test.t.a)->Sort}(test.t.b,test.t.b)->Projection", - "Hints": "use_index(@`sel_1` `test`.`t2` ), use_index(@`sel_2` `test`.`t1` ), use_index(@`sel_2` `test`.`t2` `f`), hash_join(@`sel_2` `test`.`t1`)" + "Hints": "use_index(@`sel_1` `test`.`t2` ), no_order_index(@`sel_1` `test`.`t2` `primary`), hash_join(@`sel_2` `test`.`t1`), use_index(@`sel_2` `test`.`t1` ), no_order_index(@`sel_2` `test`.`t1` `primary`), use_index(@`sel_2` `test`.`t2` `f`), no_order_index(@`sel_2` `test`.`t2` `f`)" } ] }, @@ -4506,52 +4506,52 @@ { "SQL": "select /*+ HASH_JOIN(t1) */ t1.b, t2.b from t1, t2 where t1.a = t2.a;", "Plan": "LeftHashJoin{TableReader(Table(t1)->Sel([not(isnull(test.t1.a))]))->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t1.a,test.t2.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ HASH_JOIN(t1) */ t1.b, t2.b from t1 inner join t2 on t1.a = t2.a;", "Plan": "LeftHashJoin{TableReader(Table(t1)->Sel([not(isnull(test.t1.a))]))->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t1.a,test.t2.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ HASH_JOIN(t1) */ t1.b, t2.b from t1 left outer join t2 on t1.a = t2.a;", "Plan": "LeftHashJoin{TableReader(Table(t1))->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t1.a,test.t2.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ HASH_JOIN(t1) */ t1.b, t2.b from t1 right outer join t2 on t1.a = t2.a;", "Plan": "RightHashJoin{TableReader(Table(t1)->Sel([not(isnull(test.t1.a))]))->TableReader(Table(t2))}(test.t1.a,test.t2.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` ), hash_join(@`sel_1` `test`.`t1`)" + "Hints": "hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` ), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select 1 from (select /*+ HASH_JOIN(t1) */ t1.a in (select t2.a from t2) from t1) x;", "Plan": "LeftHashJoin{IndexReader(Index(t1.idx_a)[[NULL,+inf]])->IndexReader(Index(t2.idx_a)[[NULL,+inf]])}->Projection", - "Hints": "use_index(@`sel_2` `test`.`t1` `idx_a`), use_index(@`sel_3` `test`.`t2` `idx_a`), hash_join(@`sel_2` `test`.`t1`)" + "Hints": "hash_join(@`sel_2` `test`.`t1`), use_index(@`sel_2` `test`.`t1` `idx_a`), no_order_index(@`sel_2` `test`.`t1` `idx_a`), use_index(@`sel_3` `test`.`t2` `idx_a`), no_order_index(@`sel_3` `test`.`t2` `idx_a`)" }, { "SQL": "select 1 from (select /*+ HASH_JOIN(t1) */ t1.a not in (select t2.a from t2) from t1) x;", "Plan": "LeftHashJoin{IndexReader(Index(t1.idx_a)[[NULL,+inf]])->IndexReader(Index(t2.idx_a)[[NULL,+inf]])}->Projection", - "Hints": "use_index(@`sel_2` `test`.`t1` `idx_a`), use_index(@`sel_3` `test`.`t2` `idx_a`), hash_join(@`sel_2` `test`.`t1`)" + "Hints": "hash_join(@`sel_2` `test`.`t1`), use_index(@`sel_2` `test`.`t1` `idx_a`), no_order_index(@`sel_2` `test`.`t1` `idx_a`), use_index(@`sel_3` `test`.`t2` `idx_a`), no_order_index(@`sel_3` `test`.`t2` `idx_a`)" }, { "SQL": "select /*+ INL_JOIN(t1) */ t1.b, t2.b from t1 inner join t2 on t1.a = t2.a;", "Plan": "IndexJoin{IndexLookUp(Index(t1.idx_a)[[NULL,NULL]]->Sel([not(isnull(test.t1.a))]), Table(t1))->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t2.a,test.t1.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` ), inl_join(@`sel_1` `test`.`t1`)" + "Hints": "inl_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `idx_a`), no_order_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ INL_HASH_JOIN(t1) */ t1.b, t2.b from t1 inner join t2 on t1.a = t2.a;", "Plan": "IndexHashJoin{IndexLookUp(Index(t1.idx_a)[[NULL,NULL]]->Sel([not(isnull(test.t1.a))]), Table(t1))->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t2.a,test.t1.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` ), inl_hash_join(@`sel_1` `test`.`t1`)" + "Hints": "inl_hash_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `idx_a`), no_order_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ INL_MERGE_JOIN(t1) */ t1.b, t2.b from t1 inner join t2 on t1.a = t2.a;", "Plan": "IndexMergeJoin{IndexLookUp(Index(t1.idx_a)[[NULL,NULL]]->Sel([not(isnull(test.t1.a))]), Table(t1))->Projection->TableReader(Table(t2)->Sel([not(isnull(test.t2.a))]))}(test.t2.a,test.t1.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` ), inl_merge_join(@`sel_1` `test`.`t1`)" + "Hints": "inl_merge_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `idx_a`), order_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` )" }, { "SQL": "select /*+ MERGE_JOIN(t1) */ t1.b, t2.b from t1 inner join t2 on t1.a = t2.a;", "Plan": "MergeInnerJoin{IndexLookUp(Index(t1.idx_a)[[-inf,+inf]], Table(t1))->Projection->IndexLookUp(Index(t2.idx_a)[[-inf,+inf]], Table(t2))->Projection}(test.t1.a,test.t2.a)", - "Hints": "use_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` `idx_a`), merge_join(@`sel_1` `test`.`t1`)" + "Hints": "merge_join(@`sel_1` `test`.`t1`), use_index(@`sel_1` `test`.`t1` `idx_a`), order_index(@`sel_1` `test`.`t1` `idx_a`), use_index(@`sel_1` `test`.`t2` `idx_a`), order_index(@`sel_1` `test`.`t2` `idx_a`)" } ] }, diff --git a/planner/core/hints.go b/planner/core/hints.go index 2d67f6800b77f..0d45e341ce314 100644 --- a/planner/core/hints.go +++ b/planner/core/hints.go @@ -40,11 +40,8 @@ func GenHintsFromFlatPlan(flat *FlatPhysicalPlan) []*ast.TableOptimizerHint { return nil } for _, op := range selectPlan { - if !op.IsRoot { - continue - } p := op.Origin.(PhysicalPlan) - hints = genHintsFromSingle(p, nodeTp, hints) + hints = genHintsFromSingle(p, nodeTp, op.StoreType, hints) } for _, cte := range flat.CTEs { for i, op := range cte { @@ -52,30 +49,16 @@ func GenHintsFromFlatPlan(flat *FlatPhysicalPlan) []*ast.TableOptimizerHint { continue } p := op.Origin.(PhysicalPlan) - hints = genHintsFromSingle(p, nodeTp, hints) + hints = genHintsFromSingle(p, nodeTp, op.StoreType, hints) } } - return hints + return removeDuplicatedHints(hints) } // GenHintsFromPhysicalPlan generates hints from physical plan. -// Deprecated: FlattenPhysicalPlan() + GenHintsFromFlatPlan() is preferred. func GenHintsFromPhysicalPlan(p Plan) []*ast.TableOptimizerHint { - var hints []*ast.TableOptimizerHint - switch pp := p.(type) { - case *Explain: - return GenHintsFromPhysicalPlan(pp.TargetPlan) - case *Update: - hints = genHintsFromPhysicalPlan(pp.SelectPlan, utilhint.TypeUpdate) - case *Delete: - hints = genHintsFromPhysicalPlan(pp.SelectPlan, utilhint.TypeDelete) - // For Insert, we only generate hints that would be used in select query block. - case *Insert: - hints = genHintsFromPhysicalPlan(pp.SelectPlan, utilhint.TypeSelect) - case PhysicalPlan: - hints = genHintsFromPhysicalPlan(pp, utilhint.TypeSelect) - } - return hints + flat := FlattenPhysicalPlan(p, false) + return GenHintsFromFlatPlan(flat) } func getTableName(tblName model.CIStr, asName *model.CIStr) model.CIStr { @@ -152,27 +135,19 @@ func getJoinHints(sctx sessionctx.Context, joinType string, parentOffset int, no return res } -func genHintsFromPhysicalPlan(p PhysicalPlan, nodeType utilhint.NodeType) (res []*ast.TableOptimizerHint) { - if p == nil { - return res - } - for _, child := range p.Children() { - res = append(res, genHintsFromPhysicalPlan(child, nodeType)...) - } - if phCte, ok := p.(*PhysicalCTE); ok { - res = append(res, genHintsFromPhysicalPlan(phCte.CTE.seedPartPhysicalPlan, nodeType)...) - res = append(res, genHintsFromPhysicalPlan(phCte.CTE.recursivePartPhysicalPlan, nodeType)...) - } - - return genHintsFromSingle(p, nodeType, res) -} - -func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.TableOptimizerHint) []*ast.TableOptimizerHint { +func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, storeType kv.StoreType, res []*ast.TableOptimizerHint) []*ast.TableOptimizerHint { qbName, err := utilhint.GenerateQBName(nodeType, p.SelectBlockOffset()) if err != nil { return res } switch pp := p.(type) { + case *PhysicalLimit, *PhysicalTopN: + if storeType == kv.TiKV { + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(HintLimitToCop), + }) + } case *PhysicalTableReader: tbl := pp.TablePlans[0].(*PhysicalTableScan) if tbl.StoreType == kv.TiFlash { @@ -188,6 +163,18 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T HintName: model.NewCIStr(HintUseIndex), Tables: []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}}, }) + if tbl.Table.PKIsHandle || tbl.Table.IsCommonHandle { // it's a primary key + orderHint := HintOrderIndex + if !tbl.KeepOrder { + orderHint = HintNoOrderIndex + } + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(orderHint), + Tables: []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}}, + Indexes: []model.CIStr{model.NewCIStr("primary")}, + }) + } } case *PhysicalIndexLookUpReader: index := pp.IndexPlans[0].(*PhysicalIndexScan) @@ -197,6 +184,16 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}}, Indexes: []model.CIStr{index.Index.Name}, }) + orderHint := HintOrderIndex + if !index.KeepOrder { + orderHint = HintNoOrderIndex + } + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(orderHint), + Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}}, + Indexes: []model.CIStr{index.Index.Name}, + }) case *PhysicalIndexReader: index := pp.IndexPlans[0].(*PhysicalIndexScan) res = append(res, &ast.TableOptimizerHint{ @@ -205,6 +202,16 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}}, Indexes: []model.CIStr{index.Index.Name}, }) + orderHint := HintOrderIndex + if !index.KeepOrder { + orderHint = HintNoOrderIndex + } + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(orderHint), + Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}}, + Indexes: []model.CIStr{index.Index.Name}, + }) case *PhysicalIndexMergeReader: indexs := make([]model.CIStr, 0, 2) var tableName model.CIStr @@ -230,11 +237,23 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T QBName: qbName, HintName: model.NewCIStr(HintHashAgg), }) + if storeType == kv.TiKV { + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(HintAggToCop), + }) + } case *PhysicalStreamAgg: res = append(res, &ast.TableOptimizerHint{ QBName: qbName, HintName: model.NewCIStr(HintStreamAgg), }) + if storeType == kv.TiKV { + res = append(res, &ast.TableOptimizerHint{ + QBName: qbName, + HintName: model.NewCIStr(HintAggToCop), + }) + } case *PhysicalMergeJoin: res = append(res, getJoinHints(p.SCtx(), HintSMJ, p.SelectBlockOffset(), nodeType, pp.children...)...) case *PhysicalHashJoin: @@ -249,3 +268,20 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T } return res } + +func removeDuplicatedHints(hints []*ast.TableOptimizerHint) []*ast.TableOptimizerHint { + if len(hints) < 2 { + return hints + } + m := make(map[string]struct{}, len(hints)) + res := make([]*ast.TableOptimizerHint, 0, len(hints)) + for _, hint := range hints { + key := utilhint.RestoreTableOptimizerHint(hint) + if _, ok := m[key]; ok { + continue + } + m[key] = struct{}{} + res = append(res, hint) + } + return res +} diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index c93e0d7d6d6ab..61537898655c9 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -319,10 +319,10 @@ func TestExplainJoinHints(t *testing.T) { tk.MustExec("drop table if exists t") tk.MustExec("create table t(a int, b int, c int, key(b), key(c))") tk.MustQuery("explain format='hint' select /*+ inl_merge_join(t2) */ * from t t1 inner join t t2 on t1.b = t2.b and t1.c = 1").Check(testkit.Rows( - "inl_merge_join(@`sel_1` `test`.`t2`), use_index(@`sel_1` `test`.`t1` `c`), use_index(@`sel_1` `test`.`t2` `b`), inl_merge_join(`t2`)", + "inl_merge_join(@`sel_1` `test`.`t2`), use_index(@`sel_1` `test`.`t1` `c`), no_order_index(@`sel_1` `test`.`t1` `c`), use_index(@`sel_1` `test`.`t2` `b`), order_index(@`sel_1` `test`.`t2` `b`), inl_merge_join(`t2`)", )) tk.MustQuery("explain format='hint' select /*+ inl_hash_join(t2) */ * from t t1 inner join t t2 on t1.b = t2.b and t1.c = 1").Check(testkit.Rows( - "inl_hash_join(@`sel_1` `test`.`t2`), use_index(@`sel_1` `test`.`t1` `c`), use_index(@`sel_1` `test`.`t2` `b`), inl_hash_join(`t2`)", + "inl_hash_join(@`sel_1` `test`.`t2`), use_index(@`sel_1` `test`.`t1` `c`), no_order_index(@`sel_1` `test`.`t1` `c`), use_index(@`sel_1` `test`.`t2` `b`), no_order_index(@`sel_1` `test`.`t2` `b`), inl_hash_join(`t2`)", )) } diff --git a/planner/core/plan_test.go b/planner/core/plan_test.go index c71b831e0d8fa..1d293d704aeb4 100644 --- a/planner/core/plan_test.go +++ b/planner/core/plan_test.go @@ -371,7 +371,7 @@ func TestExplainFormatHint(t *testing.T) { "/*+ use_index(@`sel_2` `test`.`t2` `idx_c2`), hash_agg(@`sel_2`), use_index(@`sel_1` `test`.`t1` `idx_c2`), hash_agg(@`sel_1`) */ " + "count(1) from t t1 " + "where c2 in (select c2 from t t2 where t2.c2 < 15 and t2.c2 > 12)").Check(testkit.Rows( - "hash_agg(@`sel_1`), hash_agg(@`sel_2`), use_index(@`sel_2` `test`.`t2` `idx_c2`), use_index(@`sel_1` `test`.`t1` `idx_c2`)")) + "hash_agg(@`sel_1`), hash_agg(@`sel_2`), use_index(@`sel_2` `test`.`t2` `idx_c2`), no_order_index(@`sel_2` `test`.`t2` `idx_c2`), agg_to_cop(@`sel_2`), use_index(@`sel_1` `test`.`t1` `idx_c2`), no_order_index(@`sel_1` `test`.`t1` `idx_c2`)")) } func TestExplainFormatHintRecoverableForTiFlashReplica(t *testing.T) { @@ -420,11 +420,11 @@ func TestNthPlanHint(t *testing.T) { tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int, b int, c int, index(a), index(b), index(a,b))") tk.MustQuery("explain format='hint' select * from t where a=1 and b=1").Check(testkit.Rows( - "use_index(@`sel_1` `test`.`t` `a_2`)")) + "use_index(@`sel_1` `test`.`t` `a_2`), no_order_index(@`sel_1` `test`.`t` `a_2`)")) tk.MustQuery("explain format='hint' select /*+ nth_plan(1) */ * from t where a=1 and b=1").Check(testkit.Rows( "use_index(@`sel_1` `test`.`t` ), nth_plan(1)")) tk.MustQuery("explain format='hint' select /*+ nth_plan(2) */ * from t where a=1 and b=1").Check(testkit.Rows( - "use_index(@`sel_1` `test`.`t` `a_2`), nth_plan(2)")) + "use_index(@`sel_1` `test`.`t` `a_2`), no_order_index(@`sel_1` `test`.`t` `a_2`), nth_plan(2)")) tk.MustExec("explain format='hint' select /*+ nth_plan(3) */ * from t where a=1 and b=1") tk.MustQuery("show warnings").Check(testkit.Rows( @@ -436,7 +436,7 @@ func TestNthPlanHint(t *testing.T) { // Test warning for multiply hints. tk.MustQuery("explain format='hint' select /*+ nth_plan(1) nth_plan(2) */ * from t where a=1 and b=1").Check(testkit.Rows( - "use_index(@`sel_1` `test`.`t` `a_2`), nth_plan(1), nth_plan(2)")) + "use_index(@`sel_1` `test`.`t` `a_2`), no_order_index(@`sel_1` `test`.`t` `a_2`), nth_plan(1), nth_plan(2)")) tk.MustQuery("show warnings").Check(testkit.Rows( "Warning 1105 NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(2)", "Warning 1105 NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(2)"))