Skip to content

Commit 4edca9a

Browse files
committed
sql/opt/optbuilder: apply RLS filters after virtual column projection
Previously, row-level security (RLS) filters were applied before virtual columns were projected from their computed expressions. This caused issues when filters referenced virtual columns, since the projection had not yet occurred—leading to outer column references that violated optimizer invariants. A simple SELECT query with a virtual column and an RLS filter would produce a plan like: ``` project ├── columns: k:1!null a:2 b:3 c:4 v:5 └── project ├── ... ├── select │ ├── ... │ └── filters │ └── v:5 > 0 # ← RLS filter, but v:5 not yet projected └── projections └── c:4 + 1 [as=v:5] ``` This change moves the RLS filter construction to after the projection of virtual columns, ensuring filters reference valid columns. The revised plan correctly applies the RLS filter post-projection: ``` project ├── ... └── select ├── ... ├── project │ ├── ... │ └── projections │ └── c:4 + 1 [as=v:5] └── filters └── v:5 > 0 ``` Fixes #144771 Epic: CRDB-11724 Release note: none
1 parent 0999cc8 commit 4edca9a

File tree

3 files changed

+488
-1
lines changed

3 files changed

+488
-1
lines changed

pkg/sql/logictest/testdata/logic_test/row_level_security

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,6 +2176,202 @@ SET ROLE root
21762176
statement ok
21772177
DROP ROLE rls_cache_user;
21782178

2179+
subtest computed_virtual_cols
2180+
2181+
statement ok
2182+
CREATE ROLE alice LOGIN;
2183+
2184+
statement ok
2185+
CREATE TABLE t (
2186+
k INT PRIMARY KEY,
2187+
a INT,
2188+
b INT,
2189+
c INT,
2190+
v INT AS (c + 1) VIRTUAL
2191+
);
2192+
2193+
statement ok
2194+
INSERT INTO t VALUES (1,1,1,1),(-1,-1,-1,-1);
2195+
2196+
statement ok
2197+
GRANT SELECT, INSERT, UPDATE, DELETE ON t TO alice;
2198+
2199+
statement ok
2200+
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
2201+
2202+
statement ok
2203+
CREATE POLICY select_policy
2204+
ON t
2205+
FOR SELECT
2206+
TO alice
2207+
USING (v > 0);
2208+
2209+
statement ok
2210+
CREATE POLICY insert_policy
2211+
ON t
2212+
FOR INSERT
2213+
TO alice
2214+
WITH CHECK (v > 1);
2215+
2216+
statement ok
2217+
CREATE POLICY update_policy
2218+
ON t
2219+
FOR UPDATE
2220+
TO alice
2221+
USING (v > 2);
2222+
2223+
statement ok
2224+
CREATE POLICY delete_policy
2225+
ON t
2226+
FOR DELETE
2227+
TO alice
2228+
USING (v > 3);
2229+
2230+
statement ok
2231+
SET ROLE alice;
2232+
2233+
query IIIII
2234+
SELECT * FROM t;
2235+
----
2236+
1 1 1 1 2
2237+
2238+
query II
2239+
SELECT k,a FROM t WHERE b IS NOT NULL;;
2240+
----
2241+
1 1
2242+
2243+
statement ok
2244+
INSERT INTO t VALUES (2,2,2,2);
2245+
2246+
statement error pq: new row violates row-level security policy for table "t"
2247+
INSERT INTO t VALUES (3,0,0,0);
2248+
2249+
query IIIII
2250+
UPDATE t SET c = 4 WHERE k = 2 RETURNING *;
2251+
----
2252+
2 2 2 4 5
2253+
2254+
statement error pq: new row violates row-level security policy for table "t"
2255+
UPDATE t SET c = 0 WHERE k = 2 RETURNING *;
2256+
2257+
query I
2258+
DELETE FROM t RETURNING v;
2259+
----
2260+
5
2261+
2262+
statement ok
2263+
SET ROLE root;
2264+
2265+
query IIIII
2266+
SELECT * FROM t ORDER BY k;
2267+
----
2268+
-1 -1 -1 -1 0
2269+
1 1 1 1 2
2270+
2271+
statement ok
2272+
DROP TABLE t;
2273+
2274+
statement ok
2275+
DROP USER ALICE;
2276+
2277+
subtest computed_stored_cols
2278+
2279+
statement ok
2280+
CREATE ROLE pat LOGIN;
2281+
2282+
statement ok
2283+
CREATE TABLE t (
2284+
k INT PRIMARY KEY,
2285+
a INT,
2286+
b INT,
2287+
c INT,
2288+
v INT AS (c + 1) STORED
2289+
);
2290+
2291+
statement ok
2292+
INSERT INTO t VALUES (1,1,1,1),(-1,-1,-1,-1);
2293+
2294+
statement ok
2295+
GRANT SELECT, INSERT, UPDATE, DELETE ON t TO pat;
2296+
2297+
statement ok
2298+
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
2299+
2300+
statement ok
2301+
CREATE POLICY select_policy
2302+
ON t
2303+
FOR SELECT
2304+
TO pat
2305+
USING (v > 0);
2306+
2307+
statement ok
2308+
CREATE POLICY insert_policy
2309+
ON t
2310+
FOR INSERT
2311+
TO pat
2312+
WITH CHECK (v > 1);
2313+
2314+
statement ok
2315+
CREATE POLICY update_policy
2316+
ON t
2317+
FOR UPDATE
2318+
TO pat
2319+
USING (v > 2);
2320+
2321+
statement ok
2322+
CREATE POLICY delete_policy
2323+
ON t
2324+
FOR DELETE
2325+
TO pat
2326+
USING (v > 3);
2327+
2328+
statement ok
2329+
SET ROLE pat;
2330+
2331+
query IIIII
2332+
SELECT * FROM t;
2333+
----
2334+
1 1 1 1 2
2335+
2336+
query II
2337+
SELECT k,a FROM t WHERE b IS NOT NULL;;
2338+
----
2339+
1 1
2340+
2341+
statement ok
2342+
INSERT INTO t VALUES (2,2,2,2);
2343+
2344+
statement error pq: new row violates row-level security policy for table "t"
2345+
INSERT INTO t VALUES (3,0,0,0);
2346+
2347+
query IIIII
2348+
UPDATE t SET c = 4 WHERE k = 2 RETURNING *;
2349+
----
2350+
2 2 2 4 5
2351+
2352+
statement error pq: new row violates row-level security policy for table "t"
2353+
UPDATE t SET c = 0 WHERE k = 2 RETURNING *;
2354+
2355+
query I
2356+
DELETE FROM t RETURNING v;
2357+
----
2358+
5
2359+
2360+
statement ok
2361+
SET ROLE root;
2362+
2363+
query IIIII
2364+
SELECT * FROM t ORDER BY k;
2365+
----
2366+
-1 -1 -1 -1 0
2367+
1 1 1 1 2
2368+
2369+
statement ok
2370+
DROP TABLE t;
2371+
2372+
statement ok
2373+
DROP USER PAT;
2374+
21792375
subtest block_invalid_expressions
21802376

21812377
statement ok

pkg/sql/opt/optbuilder/select.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,6 @@ func (b *Builder) buildScan(
753753
// Add the partial indexes after constructing the scan so we can use the
754754
// logical properties of the scan to fully normalize the index predicates.
755755
b.addPartialIndexPredicatesForTable(tabMeta, outScope.expr)
756-
b.addRowLevelSecurityFilter(tabMeta, outScope, policyCommandScope)
757756

758757
if !virtualColIDs.Empty() {
759758
// Project the expressions for the virtual columns (and pass through all
@@ -771,6 +770,10 @@ func (b *Builder) buildScan(
771770
outScope.expr = b.factory.ConstructProject(outScope.expr, proj, scanColIDs)
772771
}
773772

773+
// Apply any filters required to enforce RLS policies. This must be done
774+
// after projecting out virtual columns, in case any policies reference them.
775+
b.addRowLevelSecurityFilter(tabMeta, outScope, policyCommandScope)
776+
774777
if b.trackSchemaDeps {
775778
dep := opt.SchemaDep{DataSource: tab}
776779
dep.ColumnIDToOrd = make(map[opt.ColumnID]int)

0 commit comments

Comments
 (0)