diff --git a/pkg/planner/core/casetest/hint/testdata/integration_suite_in.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_in.json index 7aafa5beeb073..58865059e9805 100644 --- a/pkg/planner/core/casetest/hint/testdata/integration_suite_in.json +++ b/pkg/planner/core/casetest/hint/testdata/integration_suite_in.json @@ -89,6 +89,8 @@ // cte hint "explain format = 'plan_tree' select /*+ qb_name(qb_v7, v7), merge(@qb_v7) */ * from v7;", "explain format = 'plan_tree' select /*+ qb_name(qb_v8, v8), merge(@qb_v8) */ * from v8;", + "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", + "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select_i_am_unused, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", // agg to cop hint "explain format = 'plan_tree' select /*+ qb_name(qb_v9, v9), AGG_TO_COP(@qb_v9) */ * from v9;", diff --git a/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json index 0a13ee06b97b3..8bdcfcc5a2010 100644 --- a/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json +++ b/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json @@ -817,6 +817,32 @@ ], "Warn": null }, + { + "SQL": "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", + "Plan": [ + "IndexLookUp root limit embedded(offset:0, count:100)", + "├─Limit(Build) cop[tikv] offset:0, count:100", + "│ └─IndexRangeScan cop[tikv] table:t1, index:idx_a(a) range:[2,2], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warn": [ + "[planner:1815]Hint ignore_index(`t1`@`my_select` `idx_a`) is ignored due to unknown query block name", + "[planner:1815]The qb_name hint my_select is unused, please check whether the table list in the qb_name hint my_select is correct" + ] + }, + { + "SQL": "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select_i_am_unused, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", + "Plan": [ + "IndexLookUp root limit embedded(offset:0, count:100)", + "├─Limit(Build) cop[tikv] offset:0, count:100", + "│ └─IndexRangeScan cop[tikv] table:t1, index:idx_a(a) range:[2,2], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warn": [ + "[planner:1815]Hint ignore_index(`t1`@`my_select` `idx_a`) is ignored due to unknown query block name", + "[planner:1815]The qb_name hint my_select_i_am_unused is unused, please check whether the table list in the qb_name hint my_select_i_am_unused is correct" + ] + }, { "SQL": "explain format = 'plan_tree' select /*+ qb_name(qb_v9, v9), AGG_TO_COP(@qb_v9) */ * from v9;", "Plan": [ diff --git a/pkg/planner/core/casetest/hint/testdata/integration_suite_xut.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_xut.json index 0a13ee06b97b3..8bdcfcc5a2010 100644 --- a/pkg/planner/core/casetest/hint/testdata/integration_suite_xut.json +++ b/pkg/planner/core/casetest/hint/testdata/integration_suite_xut.json @@ -817,6 +817,32 @@ ], "Warn": null }, + { + "SQL": "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", + "Plan": [ + "IndexLookUp root limit embedded(offset:0, count:100)", + "├─Limit(Build) cop[tikv] offset:0, count:100", + "│ └─IndexRangeScan cop[tikv] table:t1, index:idx_a(a) range:[2,2], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warn": [ + "[planner:1815]Hint ignore_index(`t1`@`my_select` `idx_a`) is ignored due to unknown query block name", + "[planner:1815]The qb_name hint my_select is unused, please check whether the table list in the qb_name hint my_select is correct" + ] + }, + { + "SQL": "explain format = 'plan_tree' with tt as (select * from t1 where a=2 limit 100) select /*+ qb_name(my_select_i_am_unused, tt@sel_1), ignore_index(t1@my_select, idx_a) */ * from tt;", + "Plan": [ + "IndexLookUp root limit embedded(offset:0, count:100)", + "├─Limit(Build) cop[tikv] offset:0, count:100", + "│ └─IndexRangeScan cop[tikv] table:t1, index:idx_a(a) range:[2,2], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warn": [ + "[planner:1815]Hint ignore_index(`t1`@`my_select` `idx_a`) is ignored due to unknown query block name", + "[planner:1815]The qb_name hint my_select_i_am_unused is unused, please check whether the table list in the qb_name hint my_select_i_am_unused is correct" + ] + }, { "SQL": "explain format = 'plan_tree' select /*+ qb_name(qb_v9, v9), AGG_TO_COP(@qb_v9) */ * from v9;", "Plan": [ diff --git a/pkg/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go index 439634b8b77a3..200e09aacf729 100644 --- a/pkg/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -4759,6 +4759,7 @@ func (b *PlanBuilder) tryBuildCTE(ctx context.Context, tn *ast.TableName, asName return p, nil } + b.warnViewStyleHintsForCTE(tn, asName) b.handleHelper.pushMap(nil) hasLimit := false @@ -4856,6 +4857,37 @@ func (b *PlanBuilder) tryBuildCTE(ctx context.Context, tn *ast.TableName, asName return nil, nil } +func (b *PlanBuilder) warnViewStyleHintsForCTE(tn *ast.TableName, asName *ast.CIStr) { + if b.hintProcessor == nil || len(b.hintProcessor.ViewQBNameToTable) == 0 { + return + } + + // View-style QB_NAME hints are collected before name resolution. If they + // target a CTE reference, the CTE path cannot consume the forwarded hints, + // so report them as ignored instead of silently dropping them. + cteRefName := tn.Name + if asName != nil && asName.L != "" { + cteRefName = *asName + } + currentOffset := b.getSelectOffset() + for qbName, hintedTables := range b.hintProcessor.ViewQBNameToTable { + if len(hintedTables) == 0 || hintedTables[0].TableName.L != cteRefName.L { + continue + } + hintOffset := 1 + if hintedTables[0].QBName.L != "" { + hintOffset = b.hintProcessor.GetHintOffset(hintedTables[0].QBName, currentOffset) + } + if hintOffset != currentOffset { + continue + } + for _, tableHint := range b.hintProcessor.ViewQBNameToHints[qbName] { + hintStr := h.RestoreTableOptimizerHint(tableHint) + b.ctx.GetSessionVars().StmtCtx.SetHintWarning(fmt.Sprintf("Hint %s is ignored due to unknown query block name", hintStr)) + } + } +} + // computeCTEInlineFlag, Combine the declaration of CTE and the use of CTE to jointly determine **whether a CTE can be inlined** /* There are some cases that CTE must be not inlined.