Skip to content

Commit 9ab3e4d

Browse files
craig[bot]spilchen
andcommitted
Merge #148531
148531: sql/rls: push join through permeable Barriers when ON filters are leakproof r=spilchen a=spilchen Adds normalization rules to push InnerJoin and InnerJoinApply operators below a permeable Barrier when all ON clause filters are leakproof. The Barrier is rewrapped above the join to preserve its semantics while allowing better plan shapes. This change helps optimize queries affected by RLS, which typically involve barriers around injected RLS predicates. Note: A similar rule for semi-joins was considered, but no test scenario was found where a barrier incorrectly appears below a semi-join. Existing optgen rules seem to avoid that situation. The semi-join query remains in the join test I added. Closes #146952 Epic: CRDB-48807 Release note: none Co-authored-by: Matt Spilchen <[email protected]>
2 parents 6cd7729 + 6a4b2a7 commit 9ab3e4d

File tree

4 files changed

+476
-7
lines changed

4 files changed

+476
-7
lines changed

pkg/sql/opt/exec/explain/testdata/row_level_security

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,10 @@ select t1.c1 from t1, t2 where t1.c1 = t2.c1 and t1.c1 = 1;
174174
----
175175
• cross join
176176
177-
├── • index join
178-
│ │ table: t1@t1_pkey
179-
│ │
180-
│ └── • scan
181-
│ table: t1@t1_idx
182-
│ spans: 1+ spans
183-
│ policies: policy 1, p2, p3, r1, r2
177+
├── • scan
178+
│ table: t1@t1_idx
179+
│ spans: 1+ spans
180+
│ policies: policy 1, p2, p3, r1, r2
184181
185182
└── • filter
186183
│ filter: c1 = _

pkg/sql/opt/norm/general_funcs.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,11 @@ func (c *CustomFuncs) FuncDeps(expr memo.RelExpr) *props.FuncDepSet {
635635
return &expr.Relational().FuncDeps
636636
}
637637

638+
// IsLeakproof returns true if the given expression is leakproof.
639+
func (c *CustomFuncs) IsLeakproof(expr memo.RelExpr) bool {
640+
return expr.Relational().VolatilitySet.IsLeakproof()
641+
}
642+
638643
// ----------------------------------------------------------------------
639644
//
640645
// Ordering functions
@@ -1605,3 +1610,13 @@ func (c *CustomFuncs) SplitLeakproofFilters(
16051610
}
16061611
return leakproofFilters, remainingFilters, true
16071612
}
1613+
1614+
// HasAllLeakProofFilters returns true if every filter given is leakproof.
1615+
func (c *CustomFuncs) HasAllLeakProofFilters(filters memo.FiltersExpr) bool {
1616+
for i := range filters {
1617+
if !filters[i].ScalarProps().VolatilitySet.IsLeakproof() {
1618+
return false
1619+
}
1620+
}
1621+
return true
1622+
}

pkg/sql/opt/norm/rules/join.opt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,3 +845,47 @@ $left
845845
)
846846
$on
847847
)
848+
849+
# PushLeakproofJoinIntoPermeableBarrierLeft moves a join below a permeable
850+
# Barrier on its left input when all ON filters are leakproof. The Barrier is
851+
# then placed above the join. This is safe because leakproof filters can be
852+
# reordered freely, and the Barrier allows such movement when marked as
853+
# LeakproofPermeable. This rule is effectively pushing the left input into the
854+
# Barrier, so the left input must be leakproof as well.
855+
[PushLeakproofJoinIntoPermeableBarrierLeft, Normalize]
856+
(InnerJoin | InnerJoinApply
857+
(Barrier
858+
$left:*
859+
$leakproofPermeable:* & (If $leakproofPermeable)
860+
)
861+
$right:* & (IsLeakproof $right)
862+
$on:* & (HasAllLeakProofFilters $on)
863+
$private:*
864+
)
865+
=>
866+
(Barrier
867+
((OpName) $left $right $on $private)
868+
$leakproofPermeable
869+
)
870+
871+
# PushLeakproofJoinIntoPermeableBarrierRight is the right-side variant.
872+
# It moves a join below a permeable Barrier on its right input when all ON
873+
# filters are leakproof, then rewraps the join in the Barrier to preserve its
874+
# blocking behavior for non-leakproof expressions higher in the plan. This rule
875+
# is effectively pushing the right input into the Barrier, so the right input
876+
# must be leakproof as well.
877+
[PushLeakproofJoinIntoPermeableBarrierRight, Normalize]
878+
(InnerJoin | InnerJoinApply
879+
$left:* & (IsLeakproof $left)
880+
(Barrier
881+
$right:*
882+
$leakproofPermeable:* & (If $leakproofPermeable)
883+
)
884+
$on:* & (HasAllLeakProofFilters $on)
885+
$private:*
886+
)
887+
=>
888+
(Barrier
889+
((OpName) $left $right $on $private)
890+
$leakproofPermeable
891+
)

0 commit comments

Comments
 (0)