Using indexes in RLS policies (sub)queries

From: Grégory EL MAJJOUTI <gelmajjouti(at)gmail(dot)com>
To: pgsql-performance(at)postgresql(dot)org
Subject: Using indexes in RLS policies (sub)queries
Date: 2019-06-21 12:22:17
Message-ID: CABK1Ut3QnycrHd8ydmGaKCh5EC6HN-z=5S2FxmK_SNKhP4Fh0g@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-performance

Hello everyone,

I am attempting to set up a row level security policy based on geo-location
(and the PostGIS extension). I am struggling to have it make use of column
indexes.

The following example defines a table with geography points and aims to
restrict access to it based on distance to another set of points in a
secondary table. It has been tested on 11.2.

CREATE EXTENSION postgis;

-- This is the table we want to secure with RLS
DROP TABLE IF EXISTS example1;
CREATE TABLE example1 (
id serial NOT NULL,
geo geography NULL,
CONSTRAINT example1_pk PRIMARY KEY (id)
) with ( OIDS=FALSE );

-- Seed the table with 100k random points
INSERT INTO example1(geo)
SELECT ST_SetSRID(
ST_MakePoint(
(random()*360.0) - 180.0,
(random()*180.0) - 90.0),
4326) as geom
FROM generate_series(1, 100000);

CREATE INDEX example1_spx ON example1 USING GIST (geo);

-- This table will hold points for the row level policy
DROP TABLE IF EXISTS example_acl;
CREATE TABLE example_acl (
geo geography NULL
) with ( OIDS=FALSE );

INSERT INTO example_acl(geo)
SELECT ST_SetSRID(
ST_MakePoint(
(random()*360.0) - 180.0,
(random()*180.0) - 90.0),
4326) as geom
FROM generate_series(1, 100);

-- Simple query that performs an index scan
EXPLAIN ANALYZE VERBOSE SELECT count(*) from example1
INNER JOIN example_acl on st_dwithin(example_acl.geo, example1.geo, 1000)

Aggregate (cost=12364.11..12364.12 rows=1 width=8) (actual
time=4.802..4.802 rows=1 loops=1)
Output: count(*)
-> Nested Loop (cost=0.41..12364.00 rows=45 width=0) (actual
time=4.797..4.797 rows=0 loops=1)
-> Seq Scan on public.example_acl (cost=0.00..23.60 rows=1360
width=32) (actual time=0.034..0.066 rows=100 loops=1)
Output: example_acl.geo
-> Index Scan using example1_spx on public.example1
(cost=0.41..9.06 rows=1 width=32) (actual time=0.044..0.044 rows=0
loops=100)
Output: example1.id, example1.geo
Index Cond: (example1.geo && _st_expand(example_acl.geo,
'1000'::double precision))
Filter: ((example_acl.geo && _st_expand(example1.geo,
'1000'::double precision)) AND _st_dwithin(example_acl.geo, example1.geo,
'1000'::double precision, true))
Planning time: 60.690 ms
Execution time: 5.006 ms

-- Setting up the policy
CREATE ROLE example_role;
GRANT SELECT ON TABLE example1 to example_role;
GRANT SELECT ON TABLE example_acl to example_role;
ALTER TABLE example1 ENABLE ROW LEVEL SECURITY;

CREATE POLICY example_location_policy ON example1
AS permissive
FOR SELECT
TO example_role
USING (
EXISTS (
SELECT 1
FROM example_acl
WHERE (
st_dwithin(example_acl.geo, example1.geo, 1000)
)
)
);

SET ROLE example_role;
EXPLAIN ANALYZE VERBOSE SELECT count(*) from example1;

Aggregate (cost=5251959.00..5251959.01 rows=1 width=8) (actual
time=9256.606..9256.606 rows=1 loops=1)
Output: count(*)
-> Seq Scan on public.example1 (cost=0.00..5251834.00 rows=50000
width=0) (actual time=9256.601..9256.601 rows=0 loops=1)
Output: example1.id, example1.geo
Filter: (SubPlan 1)
Rows Removed by Filter: 100000
SubPlan 1
-> Seq Scan on public.example_acl (cost=0.00..52.50 rows=1
width=0) (actual time=0.089..0.089 rows=0 loops=100000)
Filter: ((example_acl.geo && _st_expand(example1.geo,
'1000'::double precision)) AND (example1.geo && _st_expand(example_acl.geo,
'1000'::double precision)) AND _st_dwithin(example_acl.geo, example1.geo,
'1000'::double precision, true))
Rows Removed by Filter: 100
Planning time: 67.601 ms
Execution time: 9256.812 ms

As you can see, the policy does not use the index example1_spx on the
geography column.
Is there a way to rewrite that policy so that it would make use of the
index?

Thank you in advance.

Best regards,
Grégory El Majjouti

Browse pgsql-performance by date

  From Date Subject
Next Message Rick Otten 2019-06-23 14:21:40 materialized view refresh of a foreign table
Previous Message Chris Wilson 2019-06-21 09:43:33 RE: EXPLAIN ANALYZE of BRIN bitmap index scan with disjunction