From 24a92655c4c664f39d3dedf06e9e2ac1a3c56dc8 Mon Sep 17 00:00:00 2001 From: Yiding Cui Date: Thu, 14 Jul 2022 15:49:05 +0800 Subject: [PATCH] cherry pick #35325 to release-6.1 Signed-off-by: ti-srebot --- bindinfo/bind_test.go | 23 + parser/hintparser.go | 687 ++++++++++++++++++ parser/hintparser.y | 3 + parser/hintparser_test.go | 5 +- parser/misc.go | 1 + planner/core/expression_rewriter.go | 36 +- planner/core/logical_plan_builder.go | 18 +- planner/core/logical_plan_test.go | 2 +- planner/core/logical_plans.go | 1 + planner/core/optimizer.go | 2 + planner/core/physical_plan_test.go | 51 ++ planner/core/physical_plan_trace_test.go | 4 +- planner/core/planbuilder.go | 11 + planner/core/rule_build_key_info.go | 4 + planner/core/rule_semi_join_rewrite.go | 115 +++ .../core/testdata/integration_suite_in.json | 6 +- .../core/testdata/integration_suite_out.json | 62 ++ planner/core/testdata/plan_suite_in.json | 11 + planner/core/testdata/plan_suite_out.json | 81 +++ .../testdata/plan_suite_unexported_in.json | 11 + .../testdata/plan_suite_unexported_out.json | 11 + testkit/testkit.go | 22 + 22 files changed, 1147 insertions(+), 20 deletions(-) create mode 100644 planner/core/rule_semi_join_rewrite.go diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index 896c56471edb3..98947fc0abf8e 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -331,6 +331,29 @@ func TestExplain(t *testing.T) { tk.MustExec("drop global binding for SELECT * from t1 union SELECT * from t1") } +func TestBindSemiJoinRewrite(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t1(id int)") + tk.MustExec("create table t2(id int)") + require.True(t, tk.HasKeywordInOperatorInfo("select * from t1 where exists(select 1 from t2 where t1.id=t2.id)", "semi join")) + require.True(t, tk.NotHasKeywordInOperatorInfo("select * from t1 where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t2 where t1.id=t2.id)", "semi join")) + + tk.MustExec(` +create global binding for + select * from t1 where exists(select 1 from t2 where t1.id=t2.id) +using + select * from t1 where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t2 where t1.id=t2.id) +`) + + require.True(t, tk.NotHasKeywordInOperatorInfo("select * from t1 where exists(select 1 from t2 where t1.id=t2.id)", "semi join")) +} + // TestBindingSymbolList tests sql with "?, ?, ?, ?", fixes #13871 func TestBindingSymbolList(t *testing.T) { store, dom, clean := testkit.CreateMockStoreAndDomain(t) diff --git a/parser/hintparser.go b/parser/hintparser.go index b86638e44b682..d4d424daf4f79 100644 --- a/parser/hintparser.go +++ b/parser/hintparser.go @@ -41,19 +41,32 @@ type yyhintXError struct { } const ( +<<<<<<< HEAD yyhintDefault = 57417 +======= + yyhintDefault = 57419 +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) yyhintEOFCode = 57344 yyhintErrCode = 57345 hintAggToCop = 57377 hintBCJoin = 57390 hintBKA = 57355 hintBNL = 57357 +<<<<<<< HEAD hintDupsWeedOut = 57413 hintFalse = 57409 hintFirstMatch = 57414 hintForceIndex = 57401 hintGB = 57412 hintHashAgg = 57379 +======= + hintDupsWeedOut = 57415 + hintFalse = 57411 + hintFirstMatch = 57416 + hintForceIndex = 57402 + hintGB = 57414 + hintHashAgg = 57380 +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) hintHashJoin = 57359 hintIdentifier = 57347 hintIgnoreIndex = 57380 @@ -68,6 +81,7 @@ const ( hintJoinOrder = 57352 hintJoinPrefix = 57353 hintJoinSuffix = 57354 +<<<<<<< HEAD hintLeading = 57403 hintLimitToCop = 57400 hintLooseScan = 57415 @@ -100,11 +114,48 @@ const ( hintSMJoin = 57389 hintSemijoin = 57371 hintSetVar = 57374 +======= + hintLeading = 57404 + hintLimitToCop = 57401 + hintLooseScan = 57417 + hintMB = 57413 + hintMRR = 57366 + hintMaterialization = 57418 + hintMaxExecutionTime = 57374 + hintMemoryQuota = 57385 + hintMerge = 57362 + hintNoBKA = 57356 + hintNoBNL = 57358 + hintNoHashJoin = 57361 + hintNoICP = 57368 + hintNoIndexMerge = 57365 + hintNoMRR = 57367 + hintNoMerge = 57363 + hintNoRangeOptimization = 57369 + hintNoSemijoin = 57373 + hintNoSkipScan = 57371 + hintNoSwapJoinInputs = 57386 + hintNthPlan = 57400 + hintOLAP = 57406 + hintOLTP = 57407 + hintOrderedHashJoin = 57360 + hintPartition = 57408 + hintQBName = 57377 + hintQueryType = 57387 + hintReadConsistentReplica = 57388 + hintReadFromStorage = 57389 + hintResourceGroup = 57376 + hintSMJoin = 57390 + hintSemiJoinRewrite = 57405 + hintSemijoin = 57372 + hintSetVar = 57375 +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) hintSingleAtIdentifier = 57349 hintSkipScan = 57369 hintStraightJoin = 57402 hintStreamAgg = 57391 hintStringLit = 57350 +<<<<<<< HEAD hintSwapJoinInputs = 57392 hintTiFlash = 57408 hintTiKV = 57407 @@ -118,10 +169,26 @@ const ( yyhintMaxDepth = 200 yyhintTabOfs = -174 +======= + hintSwapJoinInputs = 57393 + hintTiFlash = 57410 + hintTiKV = 57409 + hintTimeRange = 57398 + hintTrue = 57412 + hintUseCascades = 57399 + hintUseIndex = 57395 + hintUseIndexMerge = 57394 + hintUsePlanCache = 57396 + hintUseToja = 57397 + + yyhintMaxDepth = 200 + yyhintTabOfs = -178 +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) ) var ( yyhintXLAT = map[int]int{ +<<<<<<< HEAD 41: 0, // ')' (131x) 57377: 1, // hintAggToCop (123x) 57390: 2, // hintBCJoin (123x) @@ -236,6 +303,124 @@ var ( 57417: 111, // $default (0x) 57345: 112, // error (0x) 57348: 113, // hintInvalid (0x) +======= + 41: 0, // ')' (133x) + 57378: 1, // hintAggToCop (125x) + 57391: 2, // hintBCJoin (125x) + 57355: 3, // hintBKA (125x) + 57357: 4, // hintBNL (125x) + 57402: 5, // hintForceIndex (125x) + 57380: 6, // hintHashAgg (125x) + 57359: 7, // hintHashJoin (125x) + 57381: 8, // hintIgnoreIndex (125x) + 57379: 9, // hintIgnorePlanCache (125x) + 57364: 10, // hintIndexMerge (125x) + 57382: 11, // hintInlHashJoin (125x) + 57383: 12, // hintInlJoin (125x) + 57384: 13, // hintInlMergeJoin (125x) + 57351: 14, // hintJoinFixedOrder (125x) + 57352: 15, // hintJoinOrder (125x) + 57353: 16, // hintJoinPrefix (125x) + 57354: 17, // hintJoinSuffix (125x) + 57404: 18, // hintLeading (125x) + 57401: 19, // hintLimitToCop (125x) + 57374: 20, // hintMaxExecutionTime (125x) + 57385: 21, // hintMemoryQuota (125x) + 57362: 22, // hintMerge (125x) + 57366: 23, // hintMRR (125x) + 57356: 24, // hintNoBKA (125x) + 57358: 25, // hintNoBNL (125x) + 57361: 26, // hintNoHashJoin (125x) + 57368: 27, // hintNoICP (125x) + 57365: 28, // hintNoIndexMerge (125x) + 57363: 29, // hintNoMerge (125x) + 57367: 30, // hintNoMRR (125x) + 57369: 31, // hintNoRangeOptimization (125x) + 57373: 32, // hintNoSemijoin (125x) + 57371: 33, // hintNoSkipScan (125x) + 57386: 34, // hintNoSwapJoinInputs (125x) + 57400: 35, // hintNthPlan (125x) + 57360: 36, // hintOrderedHashJoin (125x) + 57377: 37, // hintQBName (125x) + 57387: 38, // hintQueryType (125x) + 57388: 39, // hintReadConsistentReplica (125x) + 57389: 40, // hintReadFromStorage (125x) + 57376: 41, // hintResourceGroup (125x) + 57372: 42, // hintSemijoin (125x) + 57405: 43, // hintSemiJoinRewrite (125x) + 57375: 44, // hintSetVar (125x) + 57370: 45, // hintSkipScan (125x) + 57390: 46, // hintSMJoin (125x) + 57403: 47, // hintStraightJoin (125x) + 57392: 48, // hintStreamAgg (125x) + 57393: 49, // hintSwapJoinInputs (125x) + 57398: 50, // hintTimeRange (125x) + 57399: 51, // hintUseCascades (125x) + 57395: 52, // hintUseIndex (125x) + 57394: 53, // hintUseIndexMerge (125x) + 57396: 54, // hintUsePlanCache (125x) + 57397: 55, // hintUseToja (125x) + 44: 56, // ',' (123x) + 57415: 57, // hintDupsWeedOut (103x) + 57416: 58, // hintFirstMatch (103x) + 57417: 59, // hintLooseScan (103x) + 57418: 60, // hintMaterialization (103x) + 57410: 61, // hintTiFlash (103x) + 57409: 62, // hintTiKV (103x) + 57411: 63, // hintFalse (102x) + 57406: 64, // hintOLAP (102x) + 57407: 65, // hintOLTP (102x) + 57412: 66, // hintTrue (102x) + 57414: 67, // hintGB (101x) + 57413: 68, // hintMB (101x) + 57347: 69, // hintIdentifier (100x) + 57349: 70, // hintSingleAtIdentifier (85x) + 93: 71, // ']' (79x) + 57408: 72, // hintPartition (73x) + 46: 73, // '.' (69x) + 61: 74, // '=' (69x) + 40: 75, // '(' (64x) + 57344: 76, // $end (24x) + 57439: 77, // QueryBlockOpt (17x) + 57431: 78, // Identifier (13x) + 57346: 79, // hintIntLit (8x) + 57350: 80, // hintStringLit (5x) + 57421: 81, // CommaOpt (4x) + 57427: 82, // HintTable (4x) + 57428: 83, // HintTableList (4x) + 91: 84, // '[' (3x) + 57420: 85, // BooleanHintName (2x) + 57422: 86, // HintIndexList (2x) + 57424: 87, // HintStorageType (2x) + 57425: 88, // HintStorageTypeAndTable (2x) + 57429: 89, // HintTableListOpt (2x) + 57434: 90, // JoinOrderOptimizerHintName (2x) + 57435: 91, // NullaryHintName (2x) + 57438: 92, // PartitionListOpt (2x) + 57441: 93, // StorageOptimizerHintOpt (2x) + 57442: 94, // SubqueryOptimizerHintName (2x) + 57445: 95, // SubqueryStrategy (2x) + 57446: 96, // SupportedIndexLevelOptimizerHintName (2x) + 57447: 97, // SupportedTableLevelOptimizerHintName (2x) + 57448: 98, // TableOptimizerHintOpt (2x) + 57450: 99, // UnsupportedIndexLevelOptimizerHintName (2x) + 57451: 100, // UnsupportedTableLevelOptimizerHintName (2x) + 57423: 101, // HintQueryType (1x) + 57426: 102, // HintStorageTypeAndTableList (1x) + 57430: 103, // HintTrueOrFalse (1x) + 57432: 104, // IndexNameList (1x) + 57433: 105, // IndexNameListOpt (1x) + 57436: 106, // OptimizerHintList (1x) + 57437: 107, // PartitionList (1x) + 57440: 108, // Start (1x) + 57443: 109, // SubqueryStrategies (1x) + 57444: 110, // SubqueryStrategiesOpt (1x) + 57449: 111, // UnitOfBytes (1x) + 57452: 112, // Value (1x) + 57419: 113, // $default (0x) + 57345: 114, // error (0x) + 57348: 115, // hintInvalid (0x) +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) } yyhintSymNames = []string{ @@ -281,6 +466,7 @@ var ( "hintReadFromStorage", "hintResourceGroup", "hintSemijoin", + "hintSemiJoinRewrite", "hintSetVar", "hintSkipScan", "hintSMJoin", @@ -357,6 +543,7 @@ var ( yyhintReductions = []struct{ xsym, components int }{ {0, 1}, +<<<<<<< HEAD {106, 1}, {104, 1}, {104, 3}, @@ -423,10 +610,102 @@ var ( {98, 1}, {98, 1}, {98, 1}, +======= + {108, 1}, + {106, 1}, + {106, 3}, + {106, 1}, + {106, 3}, + {98, 4}, + {98, 4}, + {98, 4}, + {98, 4}, + {98, 4}, + {98, 4}, + {98, 5}, + {98, 5}, + {98, 5}, + {98, 6}, + {98, 4}, + {98, 4}, + {98, 6}, + {98, 6}, + {98, 5}, + {98, 4}, + {98, 5}, + {93, 5}, + {102, 1}, + {102, 3}, + {88, 4}, + {77, 0}, + {77, 1}, + {81, 0}, + {81, 1}, + {92, 0}, + {92, 4}, + {107, 1}, + {107, 3}, + {89, 1}, + {89, 1}, + {83, 2}, + {83, 3}, + {82, 3}, + {82, 5}, + {86, 4}, + {105, 0}, + {105, 1}, + {104, 1}, + {104, 3}, + {110, 0}, + {110, 1}, + {109, 1}, + {109, 3}, + {112, 1}, + {112, 1}, + {112, 1}, + {111, 1}, + {111, 1}, + {103, 1}, + {103, 1}, + {90, 1}, + {90, 1}, + {90, 1}, + {100, 1}, + {100, 1}, + {100, 1}, + {100, 1}, + {100, 1}, + {100, 1}, + {100, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {97, 1}, + {99, 1}, + {99, 1}, + {99, 1}, + {99, 1}, + {99, 1}, + {99, 1}, + {99, 1}, + {96, 1}, + {96, 1}, + {96, 1}, + {96, 1}, + {94, 1}, + {94, 1}, +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) {95, 1}, {95, 1}, {95, 1}, {95, 1}, +<<<<<<< HEAD {95, 1}, {95, 1}, {95, 1}, @@ -530,10 +809,97 @@ var ( {76, 1}, {76, 1}, {76, 1}, +======= + {85, 1}, + {85, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {91, 1}, + {101, 1}, + {101, 1}, + {87, 1}, + {87, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, + {78, 1}, +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) } yyhintXErrors = map[yyhintXError]string{} +<<<<<<< HEAD yyhintParseTab = [257][]uint16{ // 0 {1: 234, 208, 200, 202, 226, 232, 214, 224, 238, 216, 210, 209, 213, 179, 197, 198, 199, 215, 235, 186, 191, 205, 217, 201, 203, 204, 219, 236, 206, 218, 220, 228, 222, 212, 187, 190, 195, 237, 196, 189, 227, 188, 221, 207, 239, 233, 211, 192, 230, 223, 225, 231, 229, 83: 193, 88: 180, 194, 91: 178, 185, 94: 184, 182, 177, 183, 181, 104: 176, 106: 175}, @@ -844,6 +1210,323 @@ var ( // 255 {1: 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 74: 171}, {1: 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 74: 169}, +======= + yyhintParseTab = [261][]uint16{ + // 0 + {1: 239, 212, 204, 206, 231, 237, 218, 229, 243, 221, 214, 213, 217, 183, 201, 202, 203, 220, 240, 190, 195, 209, 222, 205, 207, 208, 224, 241, 210, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 211, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 182, 189, 96: 188, 186, 181, 187, 185, 106: 180, 108: 179}, + {76: 178}, + {1: 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 336, 76: 177, 81: 436}, + {1: 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 76: 176}, + {1: 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 76: 174}, + // 5 + {75: 433}, + {75: 430}, + {75: 427}, + {75: 422}, + {75: 419}, + // 10 + {75: 408}, + {75: 396}, + {75: 392}, + {75: 388}, + {75: 380}, + // 15 + {75: 377}, + {75: 374}, + {75: 367}, + {75: 362}, + {75: 356}, + // 20 + {75: 353}, + {75: 347}, + {75: 246}, + {75: 121}, + {75: 120}, + // 25 + {75: 119}, + {75: 118}, + {75: 117}, + {75: 116}, + {75: 115}, + // 30 + {75: 114}, + {75: 113}, + {75: 112}, + {75: 111}, + {75: 110}, + // 35 + {75: 109}, + {75: 108}, + {75: 107}, + {75: 106}, + {75: 105}, + // 40 + {75: 104}, + {75: 103}, + {75: 102}, + {75: 101}, + {75: 100}, + // 45 + {75: 99}, + {75: 98}, + {75: 97}, + {75: 96}, + {75: 95}, + // 50 + {75: 94}, + {75: 93}, + {75: 92}, + {75: 91}, + {75: 90}, + // 55 + {75: 89}, + {75: 84}, + {75: 83}, + {75: 82}, + {75: 81}, + // 60 + {75: 80}, + {75: 79}, + {75: 78}, + {75: 77}, + {75: 76}, + // 65 + {75: 75}, + {75: 74}, + {75: 73}, + {61: 151, 151, 70: 248, 77: 247}, + {61: 253, 252, 87: 251, 250, 102: 249}, + // 70 + {150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 71: 150, 150, 79: 150}, + {344, 56: 345}, + {154, 56: 154}, + {84: 254}, + {84: 70}, + // 75 + {84: 69}, + {1: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 256, 83: 255}, + {56: 342, 71: 341}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 258, 82: 257}, + {141, 56: 141, 71: 141}, + // 80 + {151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 151, 151, 328, 77: 327}, + {68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68}, + {67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67}, + {66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}, + {65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65}, + // 85 + {64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}, + {63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63}, + {62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62}, + {61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61}, + {60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60}, + // 90 + {59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, + {58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58}, + {57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57}, + {56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56}, + {55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55}, + // 95 + {54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54}, + {53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53}, + {52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52}, + {51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51}, + {50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50}, + // 100 + {49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49}, + {48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48}, + {47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47}, + {46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46}, + {45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45}, + // 105 + {44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44}, + {43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43}, + {42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42}, + {41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41}, + {40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, + // 110 + {39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39}, + {38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38}, + {37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37}, + {36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36}, + {35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35}, + // 115 + {34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34}, + {33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33}, + {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}, + {31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + // 120 + {29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27}, + {26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26}, + {25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25}, + // 125 + {24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24}, + {23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23}, + {22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22}, + {21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + // 130 + {19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19}, + {18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18}, + {17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}, + {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + {15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + // 135 + {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + // 140 + {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, + // 145 + {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 71: 147, 331, 92: 340}, + // 150 + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 329}, + {151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 151, 151, 77: 330}, + {147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 71: 147, 331, 92: 332}, + {75: 333}, + {138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 71: 138}, + // 155 + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 335, 107: 334}, + {337, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 336, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 81: 338}, + {145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145}, + {148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 57: 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 80: 148}, + {146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 71: 146}, + // 160 + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 339}, + {144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144}, + {139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 71: 139}, + {152, 56: 152}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 258, 82: 343}, + // 165 + {140, 56: 140, 71: 140}, + {1: 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 76: 155}, + {61: 253, 252, 87: 251, 346}, + {153, 56: 153}, + {64: 151, 151, 70: 248, 77: 348}, + // 170 + {64: 350, 351, 101: 349}, + {352}, + {72}, + {71}, + {1: 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 76: 156}, + // 175 + {151, 70: 248, 77: 354}, + {355}, + {1: 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 76: 157}, + {63: 151, 66: 151, 70: 248, 77: 357}, + {63: 360, 66: 359, 103: 358}, + // 180 + {361}, + {123}, + {122}, + {1: 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 76: 158}, + {80: 363}, + // 185 + {56: 336, 80: 149, 364}, + {80: 365}, + {366}, + {1: 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 76: 159}, + {70: 248, 77: 368, 79: 151}, + // 190 + {79: 369}, + {67: 372, 371, 111: 370}, + {373}, + {125}, + {124}, + // 195 + {1: 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 76: 160}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 375}, + {376}, + {1: 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 76: 161}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 378}, + // 200 + {379}, + {1: 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 76: 162}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 381}, + {74: 382}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 385, 386, 384, 112: 383}, + // 205 + {387}, + {128}, + {127}, + {126}, + {1: 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 76: 163}, + // 210 + {70: 248, 77: 389, 79: 151}, + {79: 390}, + {391}, + {1: 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 76: 164}, + {70: 248, 77: 393, 79: 151}, + // 215 + {79: 394}, + {395}, + {1: 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 76: 165}, + {151, 57: 151, 151, 151, 151, 70: 248, 77: 397}, + {132, 57: 401, 402, 403, 404, 95: 400, 109: 399, 398}, + // 220 + {407}, + {131, 56: 405}, + {130, 56: 130}, + {88, 56: 88}, + {87, 56: 87}, + // 225 + {86, 56: 86}, + {85, 56: 85}, + {57: 401, 402, 403, 404, 95: 406}, + {129, 56: 129}, + {1: 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 76: 166}, + // 230 + {1: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 410, 86: 409}, + {418}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 258, 82: 411}, + {149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 336, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 81: 412}, + {136, 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 415, 104: 414, 413}, + // 235 + {137}, + {135, 56: 416}, + {134, 56: 134}, + {1: 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 417}, + {133, 56: 133}, + // 240 + {1: 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 76: 167}, + {1: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 410, 86: 420}, + {421}, + {1: 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 76: 168}, + {151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 425, 83: 424, 89: 423}, + // 245 + {426}, + {143, 56: 342}, + {142, 287, 301, 264, 266, 311, 290, 268, 291, 289, 273, 292, 293, 294, 260, 261, 262, 263, 313, 288, 283, 295, 271, 275, 265, 267, 270, 277, 274, 272, 276, 278, 282, 280, 296, 310, 269, 286, 297, 298, 299, 285, 281, 314, 284, 279, 300, 312, 302, 303, 308, 309, 305, 304, 306, 307, 57: 323, 324, 325, 326, 318, 317, 319, 315, 316, 320, 322, 321, 259, 78: 258, 82: 257}, + {1: 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 76: 169}, + {151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 425, 83: 424, 89: 428}, + // 250 + {429}, + {1: 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 76: 170}, + {1: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 57: 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 248, 77: 256, 83: 431}, + {432, 56: 342}, + {1: 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 76: 171}, + // 255 + {151, 70: 248, 77: 434}, + {435}, + {1: 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 76: 172}, + {1: 239, 212, 204, 206, 231, 237, 218, 229, 243, 221, 214, 213, 217, 183, 201, 202, 203, 220, 240, 190, 195, 209, 222, 205, 207, 208, 224, 241, 210, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 211, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 438, 189, 96: 188, 186, 437, 187, 185}, + {1: 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 76: 175}, + // 260 + {1: 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 76: 173}, +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) } ) @@ -883,7 +1566,11 @@ func yyhintlex1(yylex yyhintLexer, lval *yyhintSymType) (n int) { } func yyhintParse(yylex yyhintLexer, parser *hintParser) int { +<<<<<<< HEAD const yyError = 112 +======= + const yyError = 114 +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) yyEx, _ := yylex.(yyhintLexerEx) var yyn int diff --git a/parser/hintparser.y b/parser/hintparser.y index e3c3dacf4faff..7f0eadfa17685 100644 --- a/parser/hintparser.y +++ b/parser/hintparser.y @@ -107,6 +107,7 @@ import ( hintForceIndex "FORCE_INDEX" hintStraightJoin "STRAIGHT_JOIN" hintLeading "LEADING" + hintSemiJoinRewrite "SEMI_JOIN_REWRITE" /* Other keywords */ hintOLAP "OLAP" @@ -583,6 +584,7 @@ NullaryHintName: | "READ_CONSISTENT_REPLICA" | "IGNORE_PLAN_CACHE" | "STRAIGHT_JOIN" +| "SEMI_JOIN_REWRITE" HintQueryType: "OLAP" @@ -649,6 +651,7 @@ Identifier: | "FORCE_INDEX" | "STRAIGHT_JOIN" | "LEADING" +| "SEMI_JOIN_REWRITE" /* other keywords */ | "OLAP" | "OLTP" diff --git a/parser/hintparser_test.go b/parser/hintparser_test.go index 5c252b2d4af78..e8df92de37e00 100644 --- a/parser/hintparser_test.go +++ b/parser/hintparser_test.go @@ -262,7 +262,7 @@ func TestParseHint(t *testing.T) { }, }, { - input: "READ_FROM_STORAGE(@foo TIKV[a, b], TIFLASH[c, d]) HASH_AGG() READ_FROM_STORAGE(TIKV[e])", + input: "READ_FROM_STORAGE(@foo TIKV[a, b], TIFLASH[c, d]) HASH_AGG() SEMI_JOIN_REWRITE() READ_FROM_STORAGE(TIKV[e])", output: []*ast.TableOptimizerHint{ { HintName: model.NewCIStr("READ_FROM_STORAGE"), @@ -285,6 +285,9 @@ func TestParseHint(t *testing.T) { { HintName: model.NewCIStr("HASH_AGG"), }, + { + HintName: model.NewCIStr("SEMI_JOIN_REWRITE"), + }, { HintName: model.NewCIStr("READ_FROM_STORAGE"), HintData: model.NewCIStr("TIKV"), diff --git a/parser/misc.go b/parser/misc.go index 1a4539ec4506a..e2c8e3cbf6427 100644 --- a/parser/misc.go +++ b/parser/misc.go @@ -937,6 +937,7 @@ var hintTokenMap = map[string]int{ "FORCE_INDEX": hintForceIndex, "STRAIGHT_JOIN": hintStraightJoin, "LEADING": hintLeading, + "SEMI_JOIN_REWRITE": hintSemiJoinRewrite, // TiDB hint aliases "TIDB_HJ": hintHashJoin, diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index dd452eb5d238b..61054225304c1 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -315,7 +315,9 @@ func (er *expressionRewriter) constructBinaryOpFunction(l expression.Expression, } } -func (er *expressionRewriter) buildSubquery(ctx context.Context, subq *ast.SubqueryExpr) (LogicalPlan, error) { +// buildSubquery translates the subquery ast to plan. +// Currently, only the EXIST can apply the rewrite hint(rewrite the semi join to inner join with aggregation). +func (er *expressionRewriter) buildSubquery(ctx context.Context, subq *ast.SubqueryExpr, rewriteHintCanTakeEffect bool) (np LogicalPlan, hasSemiJoinRewriteHint bool, err error) { if er.schema != nil { outerSchema := er.schema.Clone() er.b.outerSchemas = append(er.b.outerSchemas, outerSchema) @@ -325,18 +327,25 @@ func (er *expressionRewriter) buildSubquery(ctx context.Context, subq *ast.Subqu er.b.outerNames = er.b.outerNames[0 : len(er.b.outerNames)-1] }() } + // Store the old value before we enter the subquery and reset they to default value. + oldRewriteHintCanTakeEffect := er.b.checkSemiJoinHint + er.b.checkSemiJoinHint = rewriteHintCanTakeEffect + oldHasHint := er.b.hasValidSemiJoinHint + er.b.hasValidSemiJoinHint = false outerWindowSpecs := er.b.windowSpecs defer func() { er.b.windowSpecs = outerWindowSpecs + er.b.checkSemiJoinHint = oldRewriteHintCanTakeEffect + er.b.hasValidSemiJoinHint = oldHasHint }() - np, err := er.b.buildResultSetNode(ctx, subq.Query) + np, err = er.b.buildResultSetNode(ctx, subq.Query) if err != nil { - return nil, err + return nil, false, err } // Pop the handle map generated by the subquery. er.b.handleHelper.popMap() - return np, nil + return np, er.b.hasValidSemiJoinHint, nil } // Enter implements Visitor interface. @@ -500,7 +509,7 @@ func (er *expressionRewriter) buildSemiApplyFromEqualSubq(np LogicalPlan, l, r e if er.err != nil { return } - er.p, er.err = er.b.buildSemiApply(er.p, np, []expression.Expression{condition}, er.asScalar, not) + er.p, er.err = er.b.buildSemiApply(er.p, np, []expression.Expression{condition}, er.asScalar, not, false) } func (er *expressionRewriter) handleCompareSubquery(ctx context.Context, v *ast.CompareSubqueryExpr) (ast.Node, bool) { @@ -516,7 +525,7 @@ func (er *expressionRewriter) handleCompareSubquery(ctx context.Context, v *ast. er.err = errors.Errorf("Unknown compare type %T", v.R) return v, true } - np, err := er.buildSubquery(ctx, subq) + np, _, err := er.buildSubquery(ctx, subq, false) if err != nil { er.err = err return v, true @@ -690,7 +699,7 @@ func (er *expressionRewriter) buildQuantifierPlan(plan4Agg *LogicalAggregation, // plan4Agg.buildProjectionIfNecessary() if !er.asScalar { // For Semi LogicalApply without aux column, the result is no matter false or null. So we can add it to join predicate. - er.p, er.err = er.b.buildSemiApply(er.p, plan4Agg, []expression.Expression{cond}, false, false) + er.p, er.err = er.b.buildSemiApply(er.p, plan4Agg, []expression.Expression{cond}, false, false, false) return } // If we treat the result as a scalar value, we will add a projection with a extra column to output true, false or null. @@ -810,14 +819,19 @@ func (er *expressionRewriter) handleExistSubquery(ctx context.Context, v *ast.Ex er.err = errors.Errorf("Unknown exists type %T", v.Sel) return v, true } - np, err := er.buildSubquery(ctx, subq) + np, hasRewriteHint, err := er.buildSubquery(ctx, subq, true) if err != nil { er.err = err return v, true } np = er.popExistsSubPlan(np) +<<<<<<< HEAD if er.b.disableSubQueryPreprocessing || len(ExtractCorrelatedCols4LogicalPlan(np)) > 0 { er.p, er.err = er.b.buildSemiApply(er.p, np, nil, er.asScalar, v.Not) +======= + if len(ExtractCorrelatedCols4LogicalPlan(np)) > 0 { + er.p, er.err = er.b.buildSemiApply(er.p, np, nil, er.asScalar, v.Not, hasRewriteHint) +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) if er.err != nil || !er.asScalar { return v, true } @@ -884,7 +898,7 @@ func (er *expressionRewriter) handleInSubquery(ctx context.Context, v *ast.Patte er.err = errors.Errorf("Unknown compare type %T", v.Sel) return v, true } - np, err := er.buildSubquery(ctx, subq) + np, _, err := er.buildSubquery(ctx, subq, false) if err != nil { er.err = err return v, true @@ -968,7 +982,7 @@ func (er *expressionRewriter) handleInSubquery(ctx context.Context, v *ast.Patte } er.p = join } else { - er.p, er.err = er.b.buildSemiApply(er.p, np, expression.SplitCNFItems(checkCondition), asScalar, v.Not) + er.p, er.err = er.b.buildSemiApply(er.p, np, expression.SplitCNFItems(checkCondition), asScalar, v.Not, false) if er.err != nil { return v, true } @@ -985,7 +999,7 @@ func (er *expressionRewriter) handleInSubquery(ctx context.Context, v *ast.Patte func (er *expressionRewriter) handleScalarSubquery(ctx context.Context, v *ast.SubqueryExpr) (ast.Node, bool) { ci := er.b.prepareCTECheckForSubQuery() defer resetCTECheckForSubQuery(ci) - np, err := er.buildSubquery(ctx, v) + np, _, err := er.buildSubquery(ctx, v, false) if err != nil { er.err = err return v, true diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 5a76bd9d34385..d6e2fa9d92b0c 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -117,6 +117,8 @@ const ( HintIgnorePlanCache = "ignore_plan_cache" // HintLimitToCop is a hint enforce pushing limit or topn to coprocessor. HintLimitToCop = "limit_to_cop" + // HintSemiJoinRewrite is a hint to force we rewrite the semi join operator as much as possible. + HintSemiJoinRewrite = "semi_join_rewrite" ) const ( @@ -3626,6 +3628,12 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev leadingJoinOrder = append(leadingJoinOrder, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) } leadingHintCnt++ + case HintSemiJoinRewrite: + if !b.checkSemiJoinHint { + b.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("The SEMI_JOIN_REWRITE hint is not used correctly, maybe it's not in a subquery or the subquery is not EXISTS clause.")) + continue + } + b.hasValidSemiJoinHint = true default: // ignore hints that not implemented } @@ -4866,10 +4874,10 @@ func (b *PlanBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, t } // buildSemiApply builds apply plan with outerPlan and innerPlan, which apply semi-join for every row from outerPlan and the whole innerPlan. -func (b *PlanBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition []expression.Expression, asScalar, not bool) (LogicalPlan, error) { +func (b *PlanBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition []expression.Expression, asScalar, not, considerRewrite bool) (LogicalPlan, error) { b.optFlag = b.optFlag | flagPredicatePushDown | flagBuildKeyInfo | flagDecorrelate - join, err := b.buildSemiJoin(outerPlan, innerPlan, condition, asScalar, not) + join, err := b.buildSemiJoin(outerPlan, innerPlan, condition, asScalar, not, considerRewrite) if err != nil { return nil, err } @@ -4905,7 +4913,7 @@ func (b *PlanBuilder) buildMaxOneRow(p LogicalPlan) LogicalPlan { return maxOneRow } -func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onCondition []expression.Expression, asScalar bool, not bool) (*LogicalJoin, error) { +func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onCondition []expression.Expression, asScalar, not, forceRewrite bool) (*LogicalJoin, error) { joinPlan := LogicalJoin{}.Init(b.ctx, b.getSelectOffset()) for i, expr := range onCondition { onCondition[i] = expr.Decorrelate(outerPlan.Schema()) @@ -4959,6 +4967,10 @@ func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio return nil, errors.New("Join hints are conflict, you can only specify one type of join") } } + if forceRewrite { + joinPlan.preferJoinType |= preferRewriteSemiJoin + b.optFlag |= flagSemiJoinRewrite + } return joinPlan, nil } diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index b6f279f81c4e5..4d67356260908 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -505,7 +505,7 @@ func TestSubquery(t *testing.T) { p, _, err := BuildLogicalPlanForTest(ctx, s.ctx, stmt, s.is) require.NoError(t, err) if lp, ok := p.(LogicalPlan); ok { - p, err = logicalOptimize(context.TODO(), flagBuildKeyInfo|flagDecorrelate|flagPrunColumns|flagPrunColumnsAgain, lp) + p, err = logicalOptimize(context.TODO(), flagBuildKeyInfo|flagDecorrelate|flagPrunColumns|flagPrunColumnsAgain|flagSemiJoinRewrite, lp) require.NoError(t, err) } testdata.OnRecord(func() { diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 00f6ccc372137..8a5d75244b23d 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -117,6 +117,7 @@ const ( preferHashJoin preferMergeJoin preferBCJoin + preferRewriteSemiJoin preferHashAgg preferStreamAgg ) diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index b1d8dcd04ced4..f816ec0e8dc24 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -57,6 +57,7 @@ const ( flagStabilizeResults flagBuildKeyInfo flagDecorrelate + flagSemiJoinRewrite flagEliminateAgg flagEliminateProjection flagMaxMinEliminate @@ -77,6 +78,7 @@ var optRuleList = []logicalOptRule{ &resultReorder{}, &buildKeySolver{}, &decorrelateSolver{}, + &semiJoinRewriter{}, &aggregationEliminator{}, &projectionEliminator{}, &maxMinEliminator{}, diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index fe5c5cba7da00..ec9c71d6fa245 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -911,6 +911,57 @@ func TestAggregationHints(t *testing.T) { } } +func TestSemiJoinRewriteHints(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int)") + + sessionVars := tk.Session().GetSessionVars() + sessionVars.SetHashAggFinalConcurrency(1) + sessionVars.SetHashAggPartialConcurrency(1) + + var input []string + var output []struct { + SQL string + Plan []string + Warning string + } + planSuiteData := core.GetPlanSuiteData() + planSuiteData.GetTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, test := range input { + comment := fmt.Sprintf("case: %v sql: %v", i, test) + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + _, _, err = planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief'" + test).Rows()) + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + }) + tk.MustQuery("explain format = 'brief'" + test).Check(testkit.Rows(output[i].Plan...)) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + } +} + func TestExplainJoinHints(t *testing.T) { store, clean := testkit.CreateMockStore(t) defer clean() diff --git a/planner/core/physical_plan_trace_test.go b/planner/core/physical_plan_trace_test.go index 9988fb6cacfa9..30cb5d6d1a976 100644 --- a/planner/core/physical_plan_trace_test.go +++ b/planner/core/physical_plan_trace_test.go @@ -83,9 +83,7 @@ func TestPhysicalOptimizeWithTraceEnabled(t *testing.T) { domain.GetDomain(sctx).MockInfoCacheAndLoadInfoSchema(dom.InfoSchema()) plan, err := builder.Build(context.TODO(), stmt) require.NoError(t, err) - flag := uint64(0) - flag = flag | 1<<3 | 1<<8 - _, _, err = core.DoOptimize(context.TODO(), sctx, flag, plan.(core.LogicalPlan)) + _, _, err = core.DoOptimize(context.TODO(), sctx, builder.GetOptFlag(), plan.(core.LogicalPlan)) require.NoError(t, err) otrace := sctx.GetSessionVars().StmtCtx.OptimizeTracer.Physical require.NotNil(t, otrace) diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 5aa6738cbd0c6..3795942aa1bdc 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -502,8 +502,19 @@ type PlanBuilder struct { buildingRecursivePartForCTE bool buildingCTE bool +<<<<<<< HEAD // disableSubQueryPreprocessing indicates whether to pre-process uncorrelated sub-queries in rewriting stage. disableSubQueryPreprocessing bool +======= + // checkSemiJoinHint checks whether the SEMI_JOIN_REWRITE hint is possible to be applied on the current SELECT stmt. + // We need this variable for the hint since the hint is set in subquery, but we check its availability in its outer scope. + // e.g. select * from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t1 where t.a=t1.a) + // Whether the hint can be applied or not is checked after the subquery is fully built. + checkSemiJoinHint bool + // hasValidSemijoinHint would tell the outer APPLY/JOIN operator that there's valid hint to be checked later + // if there's SEMI_JOIN_REWRITE hint and we find checkSemiJoinHint is true. + hasValidSemiJoinHint bool +>>>>>>> b4cd14d71... planner: use SEMI_JOIN_REWRITE hint to rewrite the semi join (#35325) } type handleColHelper struct { diff --git a/planner/core/rule_build_key_info.go b/planner/core/rule_build_key_info.go index 22ec84d150bc4..361a821c8f2cd 100644 --- a/planner/core/rule_build_key_info.go +++ b/planner/core/rule_build_key_info.go @@ -48,6 +48,10 @@ func (la *LogicalAggregation) BuildKeyInfo(selfSchema *expression.Schema, childS return } la.logicalSchemaProducer.BuildKeyInfo(selfSchema, childSchema) + la.buildSelfKeyInfo(selfSchema) +} + +func (la *LogicalAggregation) buildSelfKeyInfo(selfSchema *expression.Schema) { groupByCols := la.GetGroupByCols() if len(groupByCols) == len(la.GroupByItems) && len(la.GroupByItems) > 0 { indices := selfSchema.ColumnsIndices(groupByCols) diff --git a/planner/core/rule_semi_join_rewrite.go b/planner/core/rule_semi_join_rewrite.go new file mode 100644 index 0000000000000..0bce68183b59e --- /dev/null +++ b/planner/core/rule_semi_join_rewrite.go @@ -0,0 +1,115 @@ +// Copyright 2022 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 core + +import ( + "context" + + "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/expression/aggregation" + "github.com/pingcap/tidb/parser/ast" +) + +type semiJoinRewriter struct { +} + +func (smj *semiJoinRewriter) optimize(_ context.Context, p LogicalPlan, opt *logicalOptimizeOp) (LogicalPlan, error) { + return smj.recursivePlan(p) +} + +func (smj *semiJoinRewriter) name() string { + return "semi_join_rewrite" +} + +func (smj *semiJoinRewriter) recursivePlan(p LogicalPlan) (LogicalPlan, error) { + newChildren := make([]LogicalPlan, 0, len(p.Children())) + for _, child := range p.Children() { + newChild, err := smj.recursivePlan(child) + if err != nil { + return nil, err + } + newChildren = append(newChildren, newChild) + } + p.SetChildren(newChildren...) + join, ok := p.(*LogicalJoin) + // If it's not a join, or not a (outer) semi join. We just return it since no optimization is needed. + // Actually the check of the preferRewriteSemiJoin is a superset of checking the join type. We remain them for a better understanding. + if !ok || !(join.JoinType == SemiJoin || join.JoinType == LeftOuterSemiJoin) || (join.preferJoinType&preferRewriteSemiJoin == 0) { + return p, nil + } + + if join.JoinType == LeftOuterSemiJoin { + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("SEMI_JOIN_REWRITE() is inapplicable for LeftOuterSemiJoin.")) + return p, nil + } + + // If we have jumped the above if condition. We can make sure that the current join is a non-correlated one. + + // If there's left condition or other condition, we cannot rewrite + if len(join.LeftConditions) > 0 || len(join.OtherConditions) > 0 { + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("SEMI_JOIN_REWRITE() is inapplicable for SemiJoin with left conditions or other conditions.")) + return p, nil + } + + innerChild := join.Children()[1] + + // If there's right conditions: + // - If it's semi join, then right condition should be pushed. + // - If it's outer semi join, then it still should be pushed since the outer join should not remain any cond of the inner side. + // But the aggregation we added may block the predicate push down since we've not maintained the functional dependency to pass the equiv class to guide the push down. + // So we create a selection before we build the aggregation. + if len(join.RightConditions) > 0 { + sel := LogicalSelection{Conditions: make([]expression.Expression, len(join.RightConditions))}.Init(p.SCtx(), innerChild.SelectBlockOffset()) + copy(sel.Conditions, join.RightConditions) + sel.SetChildren(innerChild) + innerChild = sel + } + + subAgg := LogicalAggregation{ + AggFuncs: make([]*aggregation.AggFuncDesc, 0, len(join.EqualConditions)), + GroupByItems: make([]expression.Expression, 0, len(join.EqualConditions)), + }.Init(p.SCtx(), p.Children()[1].SelectBlockOffset()) + + aggOutputCols := make([]*expression.Column, 0, len(join.EqualConditions)) + for i := range join.EqualConditions { + innerCol := join.EqualConditions[i].GetArgs()[1].(*expression.Column) + firstRow, err := aggregation.NewAggFuncDesc(join.SCtx(), ast.AggFuncFirstRow, []expression.Expression{innerCol}, false) + if err != nil { + return nil, err + } + subAgg.AggFuncs = append(subAgg.AggFuncs, firstRow) + subAgg.GroupByItems = append(subAgg.GroupByItems, innerCol) + aggOutputCols = append(aggOutputCols, innerCol) + } + subAgg.SetChildren(innerChild) + subAgg.SetSchema(expression.NewSchema(aggOutputCols...)) + subAgg.buildSelfKeyInfo(subAgg.Schema()) + + innerJoin := LogicalJoin{ + JoinType: InnerJoin, + EqualConditions: make([]*expression.ScalarFunction, 0, len(join.EqualConditions)), + }.Init(p.SCtx(), p.SelectBlockOffset()) + innerJoin.SetChildren(join.Children()[0], subAgg) + innerJoin.SetSchema(expression.MergeSchema(join.Children()[0].Schema(), subAgg.schema)) + innerJoin.AttachOnConds(expression.ScalarFuncs2Exprs(join.EqualConditions)) + + proj := LogicalProjection{ + Exprs: expression.Column2Exprs(join.Children()[0].Schema().Columns), + }.Init(p.SCtx(), p.SelectBlockOffset()) + proj.SetChildren(innerJoin) + proj.SetSchema(join.Children()[0].Schema()) + + return proj, nil +} diff --git a/planner/core/testdata/integration_suite_in.json b/planner/core/testdata/integration_suite_in.json index a2a6c7e655efc..e3aff32273794 100644 --- a/planner/core/testdata/integration_suite_in.json +++ b/planner/core/testdata/integration_suite_in.json @@ -285,7 +285,9 @@ "cases": [ // Query with WHERE or ON should have the same plan, i.e, the Apply has been decorrelated. "explain format = 'brief' select * from t where exists (select 1 from t t1 join t t2 where t1.a = t2.a and t1.a = t.a)", - "explain format = 'brief' select * from t where exists (select 1 from t t1 join t t2 on t1.a = t2.a and t1.a = t.a)" + "explain format = 'brief' select * from t where exists (select 1 from t t1 join t t2 on t1.a = t2.a and t1.a = t.a)", + "explain format = 'brief' select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 join t t2 where t1.a = t2.a and t1.a = t.a)", + "explain format = 'brief' select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 join t t2 on t1.a = t2.a and t1.a = t.a)" ] }, { @@ -499,6 +501,8 @@ "explain format = 'brief' select count(*) from fact_t right join d1_t on fact_t.d1_k = d1_t.d1_k and d1_t.value > 10 and fact_t.col1 > d1_t.value", "explain format = 'brief' select count(*) from fact_t where exists (select 1 from d1_t where d1_k = fact_t.d1_k)", "explain format = 'brief' select count(*) from fact_t where exists (select 1 from d1_t where d1_k = fact_t.d1_k and value > fact_t.col1)", + "explain format = 'brief' select count(*) from fact_t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from d1_t where d1_k = fact_t.d1_k)", + "explain format = 'brief' select count(*) from fact_t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from d1_t where d1_k = fact_t.d1_k and value > fact_t.col1)", "explain format = 'brief' select count(*) from fact_t where not exists (select 1 from d1_t where d1_k = fact_t.d1_k)", "explain format = 'brief' select count(*) from fact_t where not exists (select 1 from d1_t where d1_k = fact_t.d1_k and value > fact_t.col1)", "explain format = 'brief' select count(*) from fact_t join d1_t on fact_t.d1_k > d1_t.d1_k", diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index f3f200872b93e..10a8475503219 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -1535,6 +1535,34 @@ "└─TableReader(Probe) 10000.00 root data:TableFullScan", " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ] + }, + { + "SQL": "explain format = 'brief' select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 join t t2 where t1.a = t2.a and t1.a = t.a)", + "Plan": [ + "HashJoin 10000.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "├─HashAgg(Build) 8000.00 root group by:test.t.a, funcs:firstrow(test.t.a)->test.t.a", + "│ └─HashJoin 12500.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "│ ├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "│ └─TableReader(Probe) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain format = 'brief' select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 join t t2 on t1.a = t2.a and t1.a = t.a)", + "Plan": [ + "HashJoin 10000.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "├─HashAgg(Build) 8000.00 root group by:test.t.a, funcs:firstrow(test.t.a)->test.t.a", + "│ └─HashJoin 12500.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "│ ├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "│ └─TableReader(Probe) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] } ] }, @@ -2960,6 +2988,40 @@ " └─TableFullScan 8.00 mpp[tiflash] table:fact_t keep order:false" ] }, + { + "SQL": "explain format = 'brief' select count(*) from fact_t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from d1_t where d1_k = fact_t.d1_k)", + "Plan": [ + "StreamAgg 1.00 root funcs:count(1)->Column#12", + "└─TableReader 8.00 root data:ExchangeSender", + " └─ExchangeSender 8.00 mpp[tiflash] ExchangeType: PassThrough", + " └─HashJoin 8.00 mpp[tiflash] inner join, equal:[eq(test.fact_t.d1_k, test.d1_t.d1_k)]", + " ├─ExchangeReceiver(Build) 2.00 mpp[tiflash] ", + " │ └─ExchangeSender 2.00 mpp[tiflash] ExchangeType: Broadcast", + " │ └─Projection 2.00 mpp[tiflash] test.d1_t.d1_k", + " │ └─HashAgg 2.00 mpp[tiflash] group by:test.d1_t.d1_k, funcs:firstrow(test.d1_t.d1_k)->test.d1_t.d1_k", + " │ └─ExchangeReceiver 2.00 mpp[tiflash] ", + " │ └─ExchangeSender 2.00 mpp[tiflash] ExchangeType: HashPartition, Hash Cols: [name: test.d1_t.d1_k, collate: binary]", + " │ └─Selection 2.00 mpp[tiflash] not(isnull(test.d1_t.d1_k))", + " │ └─TableFullScan 2.00 mpp[tiflash] table:d1_t keep order:false", + " └─Selection(Probe) 8.00 mpp[tiflash] not(isnull(test.fact_t.d1_k))", + " └─TableFullScan 8.00 mpp[tiflash] table:fact_t keep order:false" + ] + }, + { + "SQL": "explain format = 'brief' select count(*) from fact_t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from d1_t where d1_k = fact_t.d1_k and value > fact_t.col1)", + "Plan": [ + "StreamAgg 1.00 root funcs:count(1)->Column#12", + "└─TableReader 6.40 root data:ExchangeSender", + " └─ExchangeSender 6.40 mpp[tiflash] ExchangeType: PassThrough", + " └─HashJoin 6.40 mpp[tiflash] semi join, equal:[eq(test.fact_t.d1_k, test.d1_t.d1_k)], other cond:gt(test.d1_t.value, test.fact_t.col1)", + " ├─ExchangeReceiver(Build) 2.00 mpp[tiflash] ", + " │ └─ExchangeSender 2.00 mpp[tiflash] ExchangeType: Broadcast", + " │ └─Selection 2.00 mpp[tiflash] not(isnull(test.d1_t.d1_k)), not(isnull(test.d1_t.value))", + " │ └─TableFullScan 2.00 mpp[tiflash] table:d1_t keep order:false", + " └─Selection(Probe) 8.00 mpp[tiflash] not(isnull(test.fact_t.col1)), not(isnull(test.fact_t.d1_k))", + " └─TableFullScan 8.00 mpp[tiflash] table:fact_t keep order:false" + ] + }, { "SQL": "explain format = 'brief' select count(*) from fact_t where not exists (select 1 from d1_t where d1_k = fact_t.d1_k)", "Plan": [ diff --git a/planner/core/testdata/plan_suite_in.json b/planner/core/testdata/plan_suite_in.json index ce4c38cbc695d..627621aa1bfe6 100644 --- a/planner/core/testdata/plan_suite_in.json +++ b/planner/core/testdata/plan_suite_in.json @@ -783,5 +783,16 @@ "select * from employee e1 join (select deptid+1 d, count(empid) a from employee group by d) e2 on e1.deptid = e2.d", "select * from (select deptid+1 d, count(empid) a from employee group by d) e1 join (select deptid+1 d, count(empid) a from employee group by d) e2 on e1.d = e2.d" ] + }, + { + "name": "TestSemiJoinRewriteHints", + "cases": [ + "select /*+ SEMI_JOIN_REWRITE() */ * from t", + "select * from t where a > (select /*+ SEMI_JOIN_REWRITE() */ min(b) from t t1 where t1.c = t.c)", + "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 where t1.a=t.a)", + "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ t.b from t t1 where t1.a=t.a)", + "select exists(select /*+ SEMI_JOIN_REWRITE() */ * from t t1 where t1.a=t.a) from t", + "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 where t1.a > t.a)" + ] } ] diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index 43c97667fd49d..49b92cb6e9afa 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -3078,5 +3078,86 @@ ] } ] + }, + { + "Name": "TestSemiJoinRewriteHints", + "Cases": [ + { + "SQL": "select /*+ SEMI_JOIN_REWRITE() */ * from t", + "Plan": [ + "TableReader 10000.00 root data:TableFullScan", + "└─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "[planner:1815]The SEMI_JOIN_REWRITE hint is not used correctly, maybe it's not in a subquery or the subquery is not EXISTS clause." + }, + { + "SQL": "select * from t where a > (select /*+ SEMI_JOIN_REWRITE() */ min(b) from t t1 where t1.c = t.c)", + "Plan": [ + "HashJoin 7992.00 root inner join, equal:[eq(test.t.c, test.t.c)], other cond:gt(test.t.a, Column#9)", + "├─Selection(Build) 6393.60 root not(isnull(Column#9))", + "│ └─HashAgg 7992.00 root group by:test.t.c, funcs:min(Column#10)->Column#9, funcs:firstrow(test.t.c)->test.t.c", + "│ └─TableReader 7992.00 root data:HashAgg", + "│ └─HashAgg 7992.00 cop[tikv] group by:test.t.c, funcs:min(test.t.b)->Column#10", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t.c))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 9980.01 root data:Selection", + " └─Selection 9980.01 cop[tikv] not(isnull(test.t.a)), not(isnull(test.t.c))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "[planner:1815]The SEMI_JOIN_REWRITE hint is not used correctly, maybe it's not in a subquery or the subquery is not EXISTS clause." + }, + { + "SQL": "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 where t1.a=t.a)", + "Plan": [ + "HashJoin 9990.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "├─HashAgg(Build) 7992.00 root group by:test.t.a, funcs:firstrow(test.t.a)->test.t.a", + "│ └─TableReader 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 9990.00 root data:Selection", + " └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "" + }, + { + "SQL": "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ t.b from t t1 where t1.a=t.a)", + "Plan": [ + "HashJoin 9990.00 root inner join, equal:[eq(test.t.a, test.t.a)]", + "├─HashAgg(Build) 7992.00 root group by:test.t.a, funcs:firstrow(test.t.a)->test.t.a", + "│ └─TableReader 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 9990.00 root data:Selection", + " └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "" + }, + { + "SQL": "select exists(select /*+ SEMI_JOIN_REWRITE() */ * from t t1 where t1.a=t.a) from t", + "Plan": [ + "HashJoin 10000.00 root left outer semi join, equal:[eq(test.t.a, test.t.a)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "[planner:1815]SEMI_JOIN_REWRITE() is inapplicable for LeftOuterSemiJoin." + }, + { + "SQL": "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 where t1.a > t.a)", + "Plan": [ + "HashJoin 7992.00 root CARTESIAN semi join, other cond:gt(test.t.a, test.t.a)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 9990.00 root data:Selection", + " └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Warning": "[planner:1815]SEMI_JOIN_REWRITE() is inapplicable for SemiJoin with left conditions or other conditions." + } + ] } ] diff --git a/planner/core/testdata/plan_suite_unexported_in.json b/planner/core/testdata/plan_suite_unexported_in.json index 98c7b9b9b5985..9855bbc7a7689 100644 --- a/planner/core/testdata/plan_suite_unexported_in.json +++ b/planner/core/testdata/plan_suite_unexported_in.json @@ -130,6 +130,17 @@ "select t1.b from t t1 where t1.b = (select max(t2.a) from t t2 where t1.b=t2.b order by t1.a)", "select t1.b from t t1 where t1.b in (select t2.b from t t2 where t2.a = t1.a order by t2.a)", "select t1.b from t t1 where exists(select t2.b from t t2 where t2.a = t1.a order by t2.a)", + "select t1.b from t t1 where exists(select /*+ SEMI_JOIN_REWRITE() */ t2.b from t t2 where t2.a = t1.a order by t2.a)", + "select a from t where exists(select 1 from t as x where x.a < t.a)", + "select a from t where exists(select 1 from t as x where x.a = t.a and t.a < 1 and x.a < 1)", + "select a from t where exists(select 1 from t as x where x.a = t.a and x.a < 1) and a < 1", + "select a from t where exists(select 1 from t as x where x.a = t.a) and exists(select 1 from t as x where x.a = t.a)", + "select a from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a < t.a)", + "select a from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a and t.a < 1 and x.a < 1)", + "select a from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a and x.a < 1) and a < 1", + "select a from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a) and exists(select 1 from t as x where x.a = t.a)", + "select a from t where exists(select 1 from t as x where x.a = t.a) and exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a)", + "select a from t where exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a) and exists(select /*+ SEMI_JOIN_REWRITE() */ 1 from t as x where x.a = t.a)", // `Sort` will not be eliminated, if it is not the top level operator. "select t1.b from t t1 where t1.b = (select t2.b from t t2 where t2.a = t1.a order by t2.a limit 1)", "select (select 1 from t t1 where t1.a = t2.a) from t t2" diff --git a/planner/core/testdata/plan_suite_unexported_out.json b/planner/core/testdata/plan_suite_unexported_out.json index 218880d0aa713..630ed1a11db24 100644 --- a/planner/core/testdata/plan_suite_unexported_out.json +++ b/planner/core/testdata/plan_suite_unexported_out.json @@ -117,6 +117,17 @@ "Join{DataScan(t1)->DataScan(t2)->Aggr(max(test.t.a),firstrow(test.t.b))}(test.t.b,test.t.b)->Projection->Sel([eq(test.t.b, Column#25)])->Projection", "Join{DataScan(t1)->DataScan(t2)}(test.t.a,test.t.a)(test.t.b,test.t.b)->Projection", "Join{DataScan(t1)->DataScan(t2)}(test.t.a,test.t.a)->Projection", + "Join{DataScan(t1)->DataScan(t2)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->Projection", + "Join{DataScan(t)->DataScan(x)}->Projection", + "Join{DataScan(t)->DataScan(x)}(test.t.a,test.t.a)->Projection", + "Join{DataScan(t)->DataScan(x)}(test.t.a,test.t.a)->Sel([lt(test.t.a, 1)])->Projection", + "Join{Join{DataScan(t)->DataScan(x)}(test.t.a,test.t.a)->DataScan(x)}(test.t.a,test.t.a)->Projection", + "Join{DataScan(t)->DataScan(x)}->Projection", + "Join{DataScan(t)->DataScan(x)}(test.t.a,test.t.a)->Projection", + "Join{DataScan(t)->DataScan(x)->Sel([lt(test.t.a, 1)])->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->Sel([lt(test.t.a, 1)])->Projection", + "Join{Join{DataScan(t)->DataScan(x)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->DataScan(x)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->Projection", + "Join{Join{DataScan(t)->DataScan(x)}(test.t.a,test.t.a)->DataScan(x)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->Projection", + "Join{Join{DataScan(t)->DataScan(x)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->DataScan(x)->Aggr(firstrow(test.t.a))}(test.t.a,test.t.a)->Projection->Projection", "Apply{DataScan(t1)->DataScan(t2)->Sel([eq(test.t.a, test.t.a)])->Projection->Sort->Limit}->Projection->Sel([eq(test.t.b, test.t.b)])->Projection", "Apply{DataScan(t2)->DataScan(t1)->Sel([eq(test.t.a, test.t.a)])->Projection}->Projection" ] diff --git a/testkit/testkit.go b/testkit/testkit.go index 4631861445b39..69679e95866a3 100644 --- a/testkit/testkit.go +++ b/testkit/testkit.go @@ -182,6 +182,28 @@ func (tk *TestKit) HasPlan(sql string, plan string, args ...interface{}) bool { return false } +// HasKeywordInOperatorInfo checks if the result execution plan contains specific keyword in the operator info. +func (tk *TestKit) HasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][4], keyword) { + return true + } + } + return false +} + +// NotHasKeywordInOperatorInfo checks if the result execution plan doesn't contain specific keyword in the operator info. +func (tk *TestKit) NotHasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][4], keyword) { + return false + } + } + return true +} + // HasPlan4ExplainFor checks if the result execution plan contains specific plan. func (tk *TestKit) HasPlan4ExplainFor(result *Result, plan string) bool { for i := range result.rows {