Skip to content

Commit c015076

Browse files
authored
Merge pull request #146510 from DrewKimball/backport25.2-146005
release-25.2: opt: add rule to pull filters out of EXISTS condition
2 parents acb53d3 + 98ffcdb commit c015076

File tree

14 files changed

+799
-3
lines changed

14 files changed

+799
-3
lines changed

pkg/sql/exec_util.go

+4
Original file line numberDiff line numberDiff line change
@@ -4140,6 +4140,10 @@ func (m *sessionDataMutator) SetPropagateAdmissionHeaderToLeafTransactions(val b
41404140
m.data.PropagateAdmissionHeaderToLeafTransactions = val
41414141
}
41424142

4143+
func (m *sessionDataMutator) SetOptimizerUseExistsFilterHoistRule(val bool) {
4144+
m.data.OptimizerUseExistsFilterHoistRule = val
4145+
}
4146+
41434147
// Utility functions related to scrubbing sensitive information on SQL Stats.
41444148

41454149
// quantizeCounts ensures that the Count field in the

pkg/sql/logictest/testdata/logic_test/information_schema

+1
Original file line numberDiff line numberDiff line change
@@ -4026,6 +4026,7 @@ optimizer_push_limit_into_project_filtered_scan on
40264026
optimizer_push_offset_into_index_join on
40274027
optimizer_use_conditional_hoist_fix on
40284028
optimizer_use_delete_range_fast_path on
4029+
optimizer_use_exists_filter_hoist_rule off
40294030
optimizer_use_forecasts on
40304031
optimizer_use_histograms on
40314032
optimizer_use_improved_computed_column_filters_derivation on

pkg/sql/logictest/testdata/logic_test/pg_catalog

+3
Original file line numberDiff line numberDiff line change
@@ -3035,6 +3035,7 @@ optimizer_push_limit_into_project_filtered_scan on N
30353035
optimizer_push_offset_into_index_join on NULL NULL NULL string
30363036
optimizer_use_conditional_hoist_fix on NULL NULL NULL string
30373037
optimizer_use_delete_range_fast_path on NULL NULL NULL string
3038+
optimizer_use_exists_filter_hoist_rule off NULL NULL NULL string
30383039
optimizer_use_forecasts on NULL NULL NULL string
30393040
optimizer_use_histograms on NULL NULL NULL string
30403041
optimizer_use_improved_computed_column_filters_derivation on NULL NULL NULL string
@@ -3255,6 +3256,7 @@ optimizer_push_limit_into_project_filtered_scan on N
32553256
optimizer_push_offset_into_index_join on NULL user NULL on on
32563257
optimizer_use_conditional_hoist_fix on NULL user NULL on on
32573258
optimizer_use_delete_range_fast_path on NULL user NULL on on
3259+
optimizer_use_exists_filter_hoist_rule off NULL user NULL off off
32583260
optimizer_use_forecasts on NULL user NULL on on
32593261
optimizer_use_histograms on NULL user NULL on on
32603262
optimizer_use_improved_computed_column_filters_derivation on NULL user NULL on on
@@ -3474,6 +3476,7 @@ optimizer_push_limit_into_project_filtered_scan NULL NULL NULL
34743476
optimizer_push_offset_into_index_join NULL NULL NULL NULL NULL
34753477
optimizer_use_conditional_hoist_fix NULL NULL NULL NULL NULL
34763478
optimizer_use_delete_range_fast_path NULL NULL NULL NULL NULL
3479+
optimizer_use_exists_filter_hoist_rule NULL NULL NULL NULL NULL
34773480
optimizer_use_forecasts NULL NULL NULL NULL NULL
34783481
optimizer_use_histograms NULL NULL NULL NULL NULL
34793482
optimizer_use_improved_computed_column_filters_derivation NULL NULL NULL NULL NULL

pkg/sql/logictest/testdata/logic_test/show_source

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ optimizer_push_limit_into_project_filtered_scan on
151151
optimizer_push_offset_into_index_join on
152152
optimizer_use_conditional_hoist_fix on
153153
optimizer_use_delete_range_fast_path on
154+
optimizer_use_exists_filter_hoist_rule off
154155
optimizer_use_forecasts on
155156
optimizer_use_histograms on
156157
optimizer_use_improved_computed_column_filters_derivation on

pkg/sql/opt/memo/memo.go

+3
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ type Memo struct {
206206
useInsertFastPath bool
207207
internal bool
208208
usePre_25_2VariadicBuiltins bool
209+
useExistsFilterHoistRule bool
209210

210211
// txnIsoLevel is the isolation level under which the plan was created. This
211212
// affects the planning of some locking operations, so it must be included in
@@ -309,6 +310,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
309310
useInsertFastPath: evalCtx.SessionData().InsertFastPath,
310311
internal: evalCtx.SessionData().Internal,
311312
usePre_25_2VariadicBuiltins: evalCtx.SessionData().UsePre_25_2VariadicBuiltins,
313+
useExistsFilterHoistRule: evalCtx.SessionData().OptimizerUseExistsFilterHoistRule,
312314
txnIsoLevel: evalCtx.TxnIsoLevel,
313315
}
314316
m.metadata.Init()
@@ -485,6 +487,7 @@ func (m *Memo) IsStale(
485487
m.useInsertFastPath != evalCtx.SessionData().InsertFastPath ||
486488
m.internal != evalCtx.SessionData().Internal ||
487489
m.usePre_25_2VariadicBuiltins != evalCtx.SessionData().UsePre_25_2VariadicBuiltins ||
490+
m.useExistsFilterHoistRule != evalCtx.SessionData().OptimizerUseExistsFilterHoistRule ||
488491
m.txnIsoLevel != evalCtx.TxnIsoLevel {
489492
return true, nil
490493
}

pkg/sql/opt/memo/memo_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,11 @@ func TestMemoIsStale(t *testing.T) {
574574
evalCtx.SessionData().UsePre_25_2VariadicBuiltins = false
575575
notStale()
576576

577+
evalCtx.SessionData().OptimizerUseExistsFilterHoistRule = true
578+
stale()
579+
evalCtx.SessionData().OptimizerUseExistsFilterHoistRule = false
580+
notStale()
581+
577582
// User no longer has access to view.
578583
catalog.View(tree.NewTableNameWithSchema("t", catconstants.PublicSchemaName, "abcview")).Revoked = true
579584
_, err = o.Memo().IsStale(ctx, &evalCtx, catalog)

pkg/sql/opt/memo/testdata/stats/project

+1-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ exec-ddl
302302
CREATE TABLE t124831 (a INT, b INT);
303303
----
304304

305-
norm disable=(SimplifyZeroCardinalityGroup,EliminateExistsZeroRows,SimplifyZeroCardinalitySemiJoin,PushFilterIntoJoinLeft)
305+
norm disable=(SimplifyZeroCardinalityGroup,EliminateExistsZeroRows,SimplifyZeroCardinalitySemiJoin,PushFilterIntoJoinLeft,HoistUnboundFilterFromExistsSubquery)
306306
SELECT a FROM t124831 WHERE NULL::INT IN (SELECT 1 LIMIT b);
307307
----
308308
project

pkg/sql/opt/norm/decorrelate_funcs.go

+6
Original file line numberDiff line numberDiff line change
@@ -1566,3 +1566,9 @@ func (c *CustomFuncs) MakeAnyNotNullScalarGroupBy(input memo.RelExpr) memo.RelEx
15661566
memo.EmptyGroupingPrivate,
15671567
)
15681568
}
1569+
1570+
// CanHoistUnboundFilterFromExistsSubquery returns true if the
1571+
// HoistUnboundFilterFromExistsSubquery rule is enabled by session-setting.
1572+
func (c *CustomFuncs) CanHoistUnboundFilterFromExistsSubquery() bool {
1573+
return c.f.evalCtx.SessionData().OptimizerUseExistsFilterHoistRule
1574+
}

pkg/sql/opt/norm/general_funcs.go

+11
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,17 @@ func (c *CustomFuncs) RemoveFiltersItem(
769769
return filters.RemoveFiltersItem(search)
770770
}
771771

772+
// AppendFiltersItem returns a new list that is a copy of the given list, except
773+
// that the given item has been appended to the end of the list.
774+
func (c *CustomFuncs) AppendFiltersItem(
775+
filters memo.FiltersExpr, toAppend opt.ScalarExpr,
776+
) memo.FiltersExpr {
777+
newFilters := make(memo.FiltersExpr, len(filters)+1)
778+
copy(newFilters, filters)
779+
newFilters[len(filters)] = c.f.ConstructFiltersItem(toAppend)
780+
return newFilters
781+
}
782+
772783
// ReplaceFiltersItem returns a new list that is a copy of the given list,
773784
// except that the given search item has been replaced by the given replace
774785
// item. If the list contains the search item multiple times, then only the

pkg/sql/opt/norm/rules/decorrelate.opt

+99
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,105 @@
911911
(OutputCols2 $left $right)
912912
)
913913

914+
# HoistUnboundFilterFromExistsSubquery pulls a filter condition out of an
915+
# Exists subquery if the filter condition only depends on columns from the
916+
# outer query. This is useful because it allows other optimization rules to
917+
# apply to the filter which was previously hidden inside the subquery.
918+
[HoistUnboundFilterFromExistsSubquery, Normalize]
919+
(Select
920+
$input:* & (CanHoistUnboundFilterFromExistsSubquery)
921+
$filters:[
922+
...
923+
$item:(FiltersItem
924+
(Exists
925+
(Select
926+
$innerInput:*
927+
$innerFilters:[
928+
...
929+
$innerItem:(FiltersItem $unboundCond:*) &
930+
(IsBoundBy
931+
$innerItem
932+
$inputCols:(OutputCols $input)
933+
)
934+
...
935+
]
936+
)
937+
$existsPrivate:*
938+
)
939+
)
940+
...
941+
]
942+
)
943+
=>
944+
(Select
945+
$input
946+
(AppendFiltersItem
947+
(ReplaceFiltersItem
948+
$filters
949+
$item
950+
(Exists
951+
(Select
952+
$innerInput
953+
(RemoveFiltersItem $innerFilters $innerItem)
954+
)
955+
$existsPrivate
956+
)
957+
)
958+
$unboundCond
959+
)
960+
)
961+
962+
# HoistUnboundJoinFilterFromExistsSubquery is similar to
963+
# HoistUnboundFilterFromExistsSubquery, but it applies to a join filter.
964+
[HoistUnboundJoinFilterFromExistsSubquery, Normalize]
965+
(Select
966+
$input:* & (CanHoistUnboundFilterFromExistsSubquery)
967+
$filters:[
968+
...
969+
$item:(FiltersItem
970+
(Exists
971+
$join:(InnerJoin | InnerJoinApply | SemiJoin
972+
| SemiJoinApply
973+
$left:*
974+
$right:*
975+
$joinFilters:[
976+
...
977+
$innerItem:(FiltersItem $unboundCond:*) &
978+
(IsBoundBy
979+
$innerItem
980+
$inputCols:(OutputCols $input)
981+
)
982+
...
983+
]
984+
$joinPrivate:*
985+
)
986+
$existsPrivate:*
987+
)
988+
)
989+
...
990+
]
991+
)
992+
=>
993+
(Select
994+
$input
995+
(AppendFiltersItem
996+
(ReplaceFiltersItem
997+
$filters
998+
$item
999+
(Exists
1000+
((OpName $join)
1001+
$left
1002+
$right
1003+
(RemoveFiltersItem $joinFilters $innerItem)
1004+
$joinPrivate
1005+
)
1006+
$existsPrivate
1007+
)
1008+
)
1009+
$unboundCond
1010+
)
1011+
)
1012+
9141013
# HoistSelectExists extracts existential subqueries from Select filters,
9151014
# turning them into semi-joins. This eliminates the subquery, which is often
9161015
# expensive to execute and restricts the optimizer's plan choices.

0 commit comments

Comments
 (0)