Skip to content

Commit b7c896c

Browse files
committed
sql: implement pg_catalog.pg_policies table
These code changes underneath the hood run a query on `pg_policy`, `pg_class`, `pg_namespace` and `pg_roles` to fill all the data in the pg_policies table. Fixes: #143444 Epic: CRDB-11724 Release note: none
1 parent 2e34a5d commit b7c896c

File tree

6 files changed

+149
-38
lines changed

6 files changed

+149
-38
lines changed

pkg/cli/clisqlshell/testdata/describe

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,8 @@ pg_catalog,pg_operator,table,node,permanent,prefix,"operators (incomplete)
559559
https://www.postgresql.org/docs/9.5/catalog-pg-operator.html"
560560
pg_catalog,pg_opfamily,table,node,permanent,prefix,pg_opfamily was created for compatibility and is currently unimplemented
561561
pg_catalog,pg_partitioned_table,table,node,permanent,prefix,pg_partitioned_table was created for compatibility and is currently unimplemented
562-
pg_catalog,pg_policies,table,node,permanent,prefix,pg_policies was created for compatibility and is currently unimplemented
562+
pg_catalog,pg_policies,table,node,permanent,prefix,"pg_policies provides a user-friendly view of row-level security policies
563+
https://www.postgresql.org/docs/17/view-pg-policies.html"
563564
pg_catalog,pg_policy,table,node,permanent,prefix,"stores row-level security policies for tables
564565
https://www.postgresql.org/docs/17/catalog-pg-policy.html"
565566
pg_catalog,pg_prepared_statements,table,node,permanent,prefix,"prepared statements

pkg/sql/delegate/show_policies.go

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,14 @@ import "github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
1010
func (d *delegator) delegateShowPolicies(stmt *tree.ShowPolicies) (tree.Statement, error) {
1111
query := `
1212
SELECT
13-
p.polname AS name,
14-
CASE p.polcmd::text
15-
WHEN '*' THEN 'ALL'
16-
WHEN 'a' THEN 'INSERT'
17-
WHEN 'w' THEN 'UPDATE'
18-
WHEN 'd' THEN 'DELETE'
19-
WHEN 'r' THEN 'SELECT'
20-
END AS cmd,
21-
CASE p.polpermissive
22-
WHEN true THEN 'permissive'
23-
ELSE 'restrictive'
24-
END AS type,
25-
array_agg(
26-
CASE
27-
WHEN role_id.uid = 0 THEN 'public'
28-
ELSE r.rolname
29-
END
30-
ORDER BY r.rolname
31-
) AS roles,
32-
COALESCE(p.polqual::text, '') AS using_expr,
33-
COALESCE(p.polwithcheck::text, '') AS with_check_expr
34-
FROM pg_policy p
35-
LEFT JOIN LATERAL unnest(p.polroles) AS role_id(uid) ON true
36-
LEFT JOIN pg_catalog.pg_roles r ON r.oid = role_id.uid
37-
WHERE p.polrelid = %[6]d
38-
GROUP BY p.polname, p.polcmd, p.polpermissive, p.polqual, p.polwithcheck`
13+
policyname AS name,
14+
cmd,
15+
permissive AS type,
16+
roles,
17+
COALESCE(qual, '') AS using_expr,
18+
COALESCE(with_check, '') AS with_check_expr
19+
FROM pg_catalog.pg_policies
20+
WHERE schemaname = %[5]s AND tablename = %[2]s`
3921

4022
return d.showTableDetails(stmt.Table, query)
4123
}

pkg/sql/logictest/testdata/logic_test/crdb_internal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pg_opclass true
123123
pg_operator false
124124
pg_opfamily true
125125
pg_partitioned_table true
126-
pg_policies true
126+
pg_policies false
127127
pg_policy false
128128
pg_prepared_statements false
129129
pg_prepared_xacts false

pkg/sql/logictest/testdata/logic_test/row_level_security

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ statement ok
142142
CREATE POLICY "policy7" ON multi_pol_tab1 FOR SELECT
143143

144144
statement ok
145-
CREATE USER papa_roach;
145+
CREATE USER papa_roach
146146

147147
statement ok
148148
CREATE POLICY "policy8" ON multi_pol_tab1 FOR ALL TO papa_roach, public
@@ -176,6 +176,19 @@ oid polname polrelid polcmd polpermissive polroles polqual polwithch
176176
7 policy7 110 r true {0} NULL NULL
177177
8 policy8 110 * true {0,835509264} NULL NULL
178178

179+
query TTTTTTTT colnames,rowsort
180+
select schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check from pg_catalog.pg_policies
181+
----
182+
schemaname tablename policyname permissive roles cmd qual with_check
183+
public multi_pol_tab1 policy1 permissive {public} ALL NULL NULL
184+
public multi_pol_tab1 policy2 restrictive {public} ALL NULL NULL
185+
public multi_pol_tab1 policy3 permissive {public} ALL NULL NULL
186+
public multi_pol_tab1 policy4 permissive {public} INSERT NULL NULL
187+
public multi_pol_tab1 policy5 permissive {public} UPDATE NULL NULL
188+
public multi_pol_tab1 policy6 permissive {public} DELETE NULL NULL
189+
public multi_pol_tab1 policy7 permissive {public} SELECT NULL NULL
190+
public multi_pol_tab1 policy8 permissive {public,papa_roach} ALL NULL NULL
191+
179192
query TTTTTT colnames,rowsort
180193
SHOW POLICIES FOR multi_pol_tab1
181194
----
@@ -189,6 +202,23 @@ policy6 DELETE permissive {public} · ·
189202
policy7 SELECT permissive {public} · ·
190203
policy8 ALL permissive {public,papa_roach} · ·
191204

205+
statement ok
206+
CREATE DATABASE roachdb
207+
208+
statement ok
209+
USE roachdb
210+
211+
query TTTTTTTT colnames,rowsort
212+
select schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check from pg_catalog.pg_policies
213+
----
214+
schemaname tablename policyname permissive roles cmd qual with_check
215+
216+
statement ok
217+
USE db1
218+
219+
statement ok
220+
DROP DATABASE roachdb
221+
192222
statement ok
193223
CREATE TABLE multi_pol_tab2 (c1 INT NOT NULL PRIMARY KEY)
194224

@@ -224,15 +254,29 @@ query ITITBTTT colnames,rowsort
224254
select oid::INT, polname, polrelid::INT, polcmd, polpermissive, polroles::string, polqual, polwithcheck from pg_catalog.pg_policy WHERE polrelid = 'multi_pol_tab2'::regclass
225255
----
226256
oid polname polrelid polcmd polpermissive polroles polqual polwithcheck
227-
1 policy9 111 * true {0} NULL NULL
228-
2 policy10 111 * true {0} NULL NULL
257+
1 policy9 113 * true {0} NULL NULL
258+
2 policy10 113 * true {0} NULL NULL
259+
260+
query TTTTTTTT colnames,rowsort
261+
select schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check from pg_catalog.pg_policies where tablename = 'multi_pol_tab2'
262+
----
263+
schemaname tablename policyname permissive roles cmd qual with_check
264+
public multi_pol_tab2 policy9 permissive {public} ALL NULL NULL
265+
public multi_pol_tab2 policy10 permissive {public} ALL NULL NULL
229266

230267
query ITITBTTT colnames,rowsort
231268
select oid::INT, polname, polrelid::INT, polcmd, polpermissive, polroles::string, polqual, polwithcheck from pg_catalog.pg_policy WHERE polrelid = 'multi_pol_tab3'::regclass
232269
----
233270
oid polname polrelid polcmd polpermissive polroles polqual polwithcheck
234-
1 policy11 112 * true {0} NULL NULL
235-
2 policy12 112 * true {0} NULL NULL
271+
1 policy11 114 * true {0} NULL NULL
272+
2 policy12 114 * true {0} NULL NULL
273+
274+
query TTTTTTTT colnames,rowsort
275+
select schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check from pg_catalog.pg_policies where tablename = 'multi_pol_tab3'
276+
----
277+
schemaname tablename policyname permissive roles cmd qual with_check
278+
public multi_pol_tab3 policy11 permissive {public} ALL NULL NULL
279+
public multi_pol_tab3 policy12 permissive {public} ALL NULL NULL
236280

237281
query TTTTTT colnames,rowsort
238282
SHOW POLICIES FOR multi_pol_tab2
@@ -500,6 +544,12 @@ select using_expr from [SHOW POLICIES FOR funcref];
500544
----
501545
public.is_valid(c1)
502546

547+
query TT colnames,rowsort
548+
select qual, with_check from pg_catalog.pg_policies where tablename = 'funcref'
549+
----
550+
qual with_check
551+
public.is_valid(c1) public.is_valid(c1)
552+
503553
statement error pq: cannot drop function "is_valid" because other objects \(\[db1.public.funcref\]\) still depend on it
504554
DROP FUNCTION is_valid;
505555

pkg/sql/pg_catalog.go

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4098,12 +4098,89 @@ var pgCatalogStatioAllSequencesTable = virtualSchemaTable{
40984098
}
40994099

41004100
var pgCatalogPoliciesTable = virtualSchemaTable{
4101-
comment: "pg_policies was created for compatibility and is currently unimplemented",
4102-
schema: vtable.PgCatalogPolicies,
4103-
populate: func(ctx context.Context, p *planner, _ catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error {
4101+
comment: `pg_policies provides a user-friendly view of row-level security policies
4102+
https://www.postgresql.org/docs/17/view-pg-policies.html`,
4103+
schema: vtable.PgCatalogPolicies,
4104+
populate: func(ctx context.Context, p *planner, db catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error {
4105+
query := `
4106+
SELECT
4107+
n.nspname,
4108+
c.relname,
4109+
pol.polname,
4110+
pol.polpermissive,
4111+
array_agg(
4112+
CASE
4113+
WHEN role_id.uid = 0 THEN 'public'
4114+
ELSE r.rolname
4115+
END
4116+
ORDER BY r.rolname
4117+
) AS roles,
4118+
CASE pol.polcmd::text
4119+
WHEN '*' THEN 'ALL'
4120+
WHEN 'a' THEN 'INSERT'
4121+
WHEN 'w' THEN 'UPDATE'
4122+
WHEN 'd' THEN 'DELETE'
4123+
WHEN 'r' THEN 'SELECT'
4124+
END AS cmd,
4125+
pol.polqual,
4126+
pol.polwithcheck
4127+
FROM pg_catalog.pg_policy pol
4128+
JOIN pg_catalog.pg_class c ON pol.polrelid = c.oid
4129+
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
4130+
LEFT JOIN LATERAL unnest(pol.polroles) AS role_id(uid) ON true
4131+
LEFT JOIN pg_catalog.pg_roles r ON r.oid = role_id.uid
4132+
GROUP BY n.nspname, c.relname, pol.polname, pol.polpermissive, pol.polcmd, pol.polqual, pol.polwithcheck
4133+
`
4134+
4135+
rows, err := p.InternalSQLTxn().QueryBufferedEx(
4136+
ctx, "read-policies", p.txn,
4137+
sessiondata.NodeUserSessionDataOverride,
4138+
query,
4139+
)
4140+
if err != nil {
4141+
return err
4142+
}
4143+
4144+
for _, row := range rows {
4145+
schemaName := tree.MustBeDString(row[0])
4146+
tableName := tree.MustBeDString(row[1])
4147+
policyName := tree.MustBeDString(row[2])
4148+
isPermissive := tree.MustBeDBool(row[3])
4149+
roles := tree.MustBeDArray(row[4]) // This is now already a string array of role names
4150+
cmd := tree.MustBeDString(row[5])
4151+
qual := row[6]
4152+
withCheck := row[7]
4153+
4154+
// Convert permissive to string
4155+
permissive := "permissive"
4156+
if !isPermissive {
4157+
permissive = "restrictive"
4158+
}
4159+
4160+
// Create a NAME array for roles
4161+
roleNames := tree.NewDArray(types.Name)
4162+
for _, role := range roles.Array {
4163+
roleName := tree.MustBeDString(role)
4164+
if err := roleNames.Append(tree.NewDName(string(roleName))); err != nil {
4165+
return err
4166+
}
4167+
}
4168+
4169+
if err := addRow(
4170+
tree.NewDName(string(schemaName)), // schemaname
4171+
tree.NewDName(string(tableName)), // tablename
4172+
tree.NewDName(string(policyName)), // policyname
4173+
tree.NewDString(permissive), // permissive
4174+
roleNames, // roles
4175+
tree.NewDString(string(cmd)), // cmd (already in correct format from query)
4176+
qual, // qual
4177+
withCheck, // with_check
4178+
); err != nil {
4179+
return err
4180+
}
4181+
}
41044182
return nil
41054183
},
4106-
unimplemented: true,
41074184
}
41084185

41094186
var pgCatalogStatsExtTable = virtualSchemaTable{

pkg/sql/vtable/pg_catalog.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,8 @@ CREATE TABLE pg_catalog.pg_stat_gssapi (
12491249
encrypted BOOL
12501250
)`
12511251

1252-
// PgCatalogPolicies is an empty table in the pg_catalog that is not implemented yet
1252+
// PgCatalogPolicies provides a user-friendly view of row-level security policies.
1253+
// https://www.postgresql.org/docs/17/view-pg-policies.html
12531254
const PgCatalogPolicies = `
12541255
CREATE TABLE pg_catalog.pg_policies (
12551256
schemaname NAME,

0 commit comments

Comments
 (0)