Skip to main content

rupg_func/
acl.rs

1//! The access privilege inquiry functions: `has_table_privilege` and the other `has_*_privilege` functions, `pg_has_role`, and `acldefault` and `aclexplode`, which give the items of an `aclitem[]` value. The module also has the input of `aclitem`, `makeaclitem`, `aclcontains` and the hash functions of `aclitem`.
2//!
3//! A port of the functions of `src/backend/utils/adt/acl.c` and of the checks of `aclchk.c` that they call. The privileges are the `aclitem[]` values of the static rows, which have the privileges that `initdb` gives. Each function has forms with a role name or OID first, or no role for the current user, and with the object as text or as an OID. The form of a call is clear from its values: a name is text and an OID is an OID. A form with the OID of an object that does not exist gives null.
4
5use std::collections::BTreeSet;
6
7use rupg_analyze::PermInfo;
8use rupg_catalog::{ObjRef, PG_CLASS, PG_LANGUAGE, PG_NAMESPACE, PG_PROC, PG_TYPE, RelKind};
9use rupg_common::{Error, Result, SqlState};
10use rupg_pgcatalog::builtin::{self, Owned};
11use rupg_types::{RegKind, Value, qualified_name_list};
12
13use crate::reg;
14use crate::{Call, Kernel, Session, bad_value, type_error};
15
16const ACL_INSERT: u32 = 1 << 0;
17const ACL_SELECT: u32 = 1 << 1;
18const ACL_UPDATE: u32 = 1 << 2;
19const ACL_DELETE: u32 = 1 << 3;
20const ACL_TRUNCATE: u32 = 1 << 4;
21const ACL_REFERENCES: u32 = 1 << 5;
22const ACL_TRIGGER: u32 = 1 << 6;
23const ACL_EXECUTE: u32 = 1 << 7;
24const ACL_USAGE: u32 = 1 << 8;
25pub(crate) const ACL_CREATE: u32 = 1 << 9;
26const ACL_CREATE_TEMP: u32 = 1 << 10;
27pub(crate) const ACL_CONNECT: u32 = 1 << 11;
28const ACL_SET: u32 = 1 << 12;
29const ACL_ALTER_SYSTEM: u32 = 1 << 13;
30const ACL_MAINTAIN: u32 = 1 << 14;
31/// The grant option bits of all privileges, `ACLITEM_ALL_GOPTION_BITS`.
32const ALL_GRANT_OPTIONS: u32 = 0xFFFF << 16;
33
34/// `ACL_ALL_RIGHTS_STR`: the letter of each privilege bit, from bit 0.
35const RIGHTS: &str = "arwdDxtXUCTcsAm";
36
37/// `ACL_ID_PUBLIC`, the grantee of an item for all roles.
38const PUBLIC: u32 = 0;
39/// `BOOTSTRAP_SUPERUSERID`.
40const BOOTSTRAP_SUPERUSER: u32 = 10;
41/// `ROLE_PG_DATABASE_OWNER`.
42const PG_DATABASE_OWNER: u32 = 6171;
43/// `ROLE_PG_READ_ALL_DATA`.
44const PG_READ_ALL_DATA: u32 = 6181;
45/// `ROLE_PG_WRITE_ALL_DATA`.
46const PG_WRITE_ALL_DATA: u32 = 6182;
47/// `ROLE_PG_MAINTAIN`.
48const PG_MAINTAIN: u32 = 6337;
49/// `FirstUnpinnedObjectId`: a relation with a lower OID is a system catalog.
50const FIRST_UNPINNED_OBJECT_ID: u32 = 12000;
51/// `PG_TOAST_NAMESPACE`.
52const PG_TOAST: u32 = 99;
53/// `F_ARRAY_SUBSCRIPT_HANDLER`, the subscript handler of a true array type.
54const ARRAY_SUBSCRIPT_HANDLER: u32 = 6179;
55
56/// A privilege name of a function and its bits, as the `priv_map` arrays of `acl.c` give them.
57type PrivMap = &'static [(&'static str, u32)];
58
59/// `ACL_GRANT_OPTION_FOR`.
60const fn go(bits: u32) -> u32 {
61    bits << 16
62}
63
64const TABLE_PRIVS: PrivMap = &[
65    ("SELECT", ACL_SELECT),
66    ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
67    ("INSERT", ACL_INSERT),
68    ("INSERT WITH GRANT OPTION", go(ACL_INSERT)),
69    ("UPDATE", ACL_UPDATE),
70    ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
71    ("DELETE", ACL_DELETE),
72    ("DELETE WITH GRANT OPTION", go(ACL_DELETE)),
73    ("TRUNCATE", ACL_TRUNCATE),
74    ("TRUNCATE WITH GRANT OPTION", go(ACL_TRUNCATE)),
75    ("REFERENCES", ACL_REFERENCES),
76    ("REFERENCES WITH GRANT OPTION", go(ACL_REFERENCES)),
77    ("TRIGGER", ACL_TRIGGER),
78    ("TRIGGER WITH GRANT OPTION", go(ACL_TRIGGER)),
79    ("MAINTAIN", ACL_MAINTAIN),
80    ("MAINTAIN WITH GRANT OPTION", go(ACL_MAINTAIN)),
81];
82const SEQUENCE_PRIVS: PrivMap = &[
83    ("USAGE", ACL_USAGE),
84    ("USAGE WITH GRANT OPTION", go(ACL_USAGE)),
85    ("SELECT", ACL_SELECT),
86    ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
87    ("UPDATE", ACL_UPDATE),
88    ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
89];
90const COLUMN_PRIVS: PrivMap = &[
91    ("SELECT", ACL_SELECT),
92    ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
93    ("INSERT", ACL_INSERT),
94    ("INSERT WITH GRANT OPTION", go(ACL_INSERT)),
95    ("UPDATE", ACL_UPDATE),
96    ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
97    ("REFERENCES", ACL_REFERENCES),
98    ("REFERENCES WITH GRANT OPTION", go(ACL_REFERENCES)),
99];
100const DATABASE_PRIVS: PrivMap = &[
101    ("CREATE", ACL_CREATE),
102    ("CREATE WITH GRANT OPTION", go(ACL_CREATE)),
103    ("TEMPORARY", ACL_CREATE_TEMP),
104    ("TEMPORARY WITH GRANT OPTION", go(ACL_CREATE_TEMP)),
105    ("TEMP", ACL_CREATE_TEMP),
106    ("TEMP WITH GRANT OPTION", go(ACL_CREATE_TEMP)),
107    ("CONNECT", ACL_CONNECT),
108    ("CONNECT WITH GRANT OPTION", go(ACL_CONNECT)),
109];
110const USAGE_PRIVS: PrivMap = &[("USAGE", ACL_USAGE), ("USAGE WITH GRANT OPTION", go(ACL_USAGE))];
111const FUNCTION_PRIVS: PrivMap =
112    &[("EXECUTE", ACL_EXECUTE), ("EXECUTE WITH GRANT OPTION", go(ACL_EXECUTE))];
113const SCHEMA_PRIVS: PrivMap = &[
114    ("CREATE", ACL_CREATE),
115    ("CREATE WITH GRANT OPTION", go(ACL_CREATE)),
116    ("USAGE", ACL_USAGE),
117    ("USAGE WITH GRANT OPTION", go(ACL_USAGE)),
118];
119const TABLESPACE_PRIVS: PrivMap =
120    &[("CREATE", ACL_CREATE), ("CREATE WITH GRANT OPTION", go(ACL_CREATE))];
121const PARAMETER_PRIVS: PrivMap = &[
122    ("SET", ACL_SET),
123    ("SET WITH GRANT OPTION", go(ACL_SET)),
124    ("ALTER SYSTEM", ACL_ALTER_SYSTEM),
125    ("ALTER SYSTEM WITH GRANT OPTION", go(ACL_ALTER_SYSTEM)),
126];
127const LARGEOBJECT_PRIVS: PrivMap = &[
128    ("SELECT", ACL_SELECT),
129    ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
130    ("UPDATE", ACL_UPDATE),
131    ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
132];
133/// The privileges of `pg_has_role`. `MEMBER` is `ACL_CREATE`, and each form with an option is the grant option of `ACL_CREATE`.
134const ROLE_PRIVS: PrivMap = &[
135    ("USAGE", ACL_USAGE),
136    ("MEMBER", ACL_CREATE),
137    ("SET", ACL_SET),
138    ("USAGE WITH GRANT OPTION", go(ACL_CREATE)),
139    ("USAGE WITH ADMIN OPTION", go(ACL_CREATE)),
140    ("MEMBER WITH GRANT OPTION", go(ACL_CREATE)),
141    ("MEMBER WITH ADMIN OPTION", go(ACL_CREATE)),
142    ("SET WITH GRANT OPTION", go(ACL_CREATE)),
143    ("SET WITH ADMIN OPTION", go(ACL_CREATE)),
144];
145
146/// The white space of `isspace` in the C locale.
147fn is_space(c: char) -> bool {
148    matches!(c, ' ' | '\t' | '\n' | '\r' | '\x0b' | '\x0c')
149}
150
151/// `convert_any_priv_string`: the bits of a list of privilege names with commas between them. Each name is compared without case after the white space at its ends is removed.
152///
153/// # Errors
154///
155/// `22023` for a name that the map does not have, which includes an empty name.
156fn privileges(text: &str, map: PrivMap) -> Result<u32> {
157    let mut bits = 0;
158    for chunk in text.split(',') {
159        let chunk = chunk.trim_matches(is_space);
160        let Some(found) = map.iter().find(|p| p.0.eq_ignore_ascii_case(chunk)) else {
161            return Err(Error::new(
162                SqlState::INVALID_PARAMETER_VALUE,
163                format!("unrecognized privilege type: \"{chunk}\""),
164            ));
165        };
166        bits |= found.1;
167    }
168    Ok(bits)
169}
170
171/// The OID of the role with this name. The login role of the server is the bootstrap superuser, so the name of the current user also gives its OID.
172fn role_oid(name: &str, session: &dyn Session) -> Option<u32> {
173    rupg_pgcatalog::roles::current().oid(name).or_else(|| {
174        (name == session.user() || name == session.session_user()).then_some(BOOTSTRAP_SUPERUSER)
175    })
176}
177
178/// `get_role_oid`.
179///
180/// # Errors
181///
182/// `42704` for a role that does not exist.
183fn get_role_oid(name: &str, session: &dyn Session) -> Result<u32> {
184    role_oid(name, session).ok_or_else(|| {
185        Error::new(SqlState::UNDEFINED_OBJECT, format!("role \"{name}\" does not exist"))
186    })
187}
188
189/// `get_role_oid_or_public`: [`get_role_oid`], with `public` for all roles.
190fn get_role_oid_or_public(name: &str, session: &dyn Session) -> Result<u32> {
191    if name == "public" { Ok(PUBLIC) } else { get_role_oid(name, session) }
192}
193
194/// `GetUserId`: the OID of the current user.
195fn current_user(session: &dyn Session) -> u32 {
196    role_oid(&session.user(), session).unwrap_or(BOOTSTRAP_SUPERUSER)
197}
198
199/// `superuser_arg`. A role that does not exist is not a superuser.
200fn superuser(role: u32) -> bool {
201    rupg_pgcatalog::roles::current().superuser(role)
202}
203
204/// The walks of `roles_is_member_of`.
205#[derive(Clone, Copy, PartialEq)]
206enum Recurse {
207    /// Each membership.
208    Members,
209    /// The memberships with `inherit_option`.
210    Privs,
211    /// The memberships with `set_option`.
212    SetRole,
213}
214
215/// `roles_is_member_of`: the roles that `role` is a member of, directly or indirectly, with `role` first. The owner of the current database is a member of `pg_database_owner`, and `initdb` makes the bootstrap superuser the owner of each database. The second value is true when a role of the walk has the admin option on `admin_of`.
216fn roles_of(role: u32, how: Recurse, admin_of: Option<u32>) -> (Vec<u32>, bool) {
217    let created = rupg_pgcatalog::roles::current();
218    let mut roles = vec![role];
219    let mut admin = false;
220    let mut at = 0;
221    while let Some(&member) = roles.get(at) {
222        at += 1;
223        for edge in created.edges().filter(|m| m.member == member) {
224            if Some(edge.role) == admin_of && edge.admin {
225                admin = true;
226            }
227            if (how == Recurse::Privs && !edge.inherit) || (how == Recurse::SetRole && !edge.set) {
228                continue;
229            }
230            if !roles.contains(&edge.role) {
231                roles.push(edge.role);
232            }
233        }
234        if member == BOOTSTRAP_SUPERUSER && !roles.contains(&PG_DATABASE_OWNER) {
235            roles.push(PG_DATABASE_OWNER);
236        }
237    }
238    (roles, admin)
239}
240
241/// `has_privs_of_role`, `is_member_of_role` and `member_can_set_role`: true when `member` is `role`, is a superuser, or reaches `role` with the walk.
242fn reaches(member: u32, role: u32, how: Recurse) -> bool {
243    member == role || superuser(member) || roles_of(member, how, None).0.contains(&role)
244}
245
246/// `has_privs_of_role`.
247fn has_privs_of_role(member: u32, role: u32) -> bool {
248    reaches(member, role, Recurse::Privs)
249}
250
251/// `HAS_PGSTAT_PERMISSIONS`: the current user has the privileges of `pg_read_all_stats` or of `role`, so it sees all the columns of a session of `role`.
252pub(crate) fn has_pgstat_permissions(session: &dyn Session, role: u32) -> bool {
253    reads_all_stats(session) || has_privs_of_role(current_user(session), role)
254}
255
256/// True when the current user has the privileges of `pg_read_all_stats`.
257pub(crate) fn reads_all_stats(session: &dyn Session) -> bool {
258    let user = current_user(session);
259    role_oid("pg_read_all_stats", session).is_some_and(|stats| has_privs_of_role(user, stats))
260}
261
262/// `object_aclcheck` for the current user: true when the user has a privilege of `mask` on the object.
263pub(crate) fn current_user_has(
264    kind: Owned,
265    oid: u32,
266    mask: u32,
267    session: &dyn Session,
268) -> Result<bool> {
269    Ok(object_check(kind, oid, current_user(session), mask, session)?.unwrap_or(false))
270}
271
272/// `is_admin_of_role`: true when `member` is a superuser, or when a role that `member` is a member of has the admin option on `role`. A role is not an admin of itself.
273fn is_admin_of_role(member: u32, role: u32) -> bool {
274    if superuser(member) {
275        return true;
276    }
277    member != role && roles_of(member, Recurse::Members, Some(role)).1
278}
279
280/// The kinds of object of `acldefault`.
281#[derive(Clone, Copy)]
282enum AclKind {
283    Table,
284    Sequence,
285    Database,
286    Function,
287    Language,
288    Schema,
289    Tablespace,
290    Type,
291    Other,
292    Column,
293    LargeObject,
294    Parameter,
295}
296
297/// `acldefault`: the privileges of an object with a null ACL, as (grantee, bits). The owner has all the privileges of the kind, and PUBLIC has some of them on some kinds.
298fn acl_default(kind: AclKind, owner: u32) -> Vec<(u32, u32)> {
299    let (world, all) = match kind {
300        AclKind::Table => (0, "arwdDxtm"),
301        AclKind::Sequence => (0, "rwU"),
302        AclKind::Database => (ACL_CREATE_TEMP | ACL_CONNECT, "CTc"),
303        AclKind::Function => (ACL_EXECUTE, "X"),
304        AclKind::Language | AclKind::Type => (ACL_USAGE, "U"),
305        AclKind::Schema => (0, "UC"),
306        AclKind::Tablespace => (0, "C"),
307        AclKind::Other => (0, "U"),
308        AclKind::Column => (0, ""),
309        AclKind::LargeObject => (0, "rw"),
310        AclKind::Parameter => (0, "sA"),
311    };
312    let all = all.chars().filter_map(|c| RIGHTS.find(c)).fold(0, |bits, at| bits | 1 << at);
313    let mut items = Vec::new();
314    if world != 0 {
315        items.push((PUBLIC, world));
316    }
317    if all != 0 {
318        items.push((owner, all));
319    }
320    items
321}
322
323/// Reads an `aclitem[]` value as (grantee, bits).
324fn parse_acl(acl: &[&str]) -> Result<Vec<(u32, u32)>> {
325    let bad = || Error::internal("a static aclitem that the engine cannot read");
326    let mut items = Vec::with_capacity(acl.len());
327    for item in acl {
328        let (grantee, rest) = item.split_once('=').ok_or_else(bad)?;
329        let (letters, _grantor) = rest.split_once('/').ok_or_else(bad)?;
330        let grantee = if grantee.is_empty() {
331            PUBLIC
332        } else {
333            let name = match grantee.strip_prefix('"').and_then(|g| g.strip_suffix('"')) {
334                Some(quoted) => quoted.replace("\"\"", "\""),
335                None => grantee.to_string(),
336            };
337            // A role that a session dropped or renamed after the grant has no row.
338            let Some(oid) = rupg_pgcatalog::roles::current().oid(&name) else { continue };
339            oid
340        };
341        let mut bits = 0;
342        let mut last = 0;
343        for c in letters.chars() {
344            if c == '*' {
345                bits |= last << 16;
346            } else {
347                last = 1 << RIGHTS.find(c).ok_or_else(bad)?;
348                bits |= last;
349            }
350        }
351        items.push((grantee, bits));
352    }
353    Ok(items)
354}
355
356/// The privileges of an object: its `aclitem[]` value, or the default of its kind.
357fn acl_of(acl: Option<&[&str]>, kind: AclKind, owner: u32) -> Result<Vec<(u32, u32)>> {
358    match acl {
359        Some(acl) => parse_acl(acl),
360        None => Ok(acl_default(kind, owner)),
361    }
362}
363
364/// `aclmask` with `ACLMASK_ANY`: the bits of `mask` that `role` has. The owner, and each role with the privileges of the owner, has all grant options. Then the items of PUBLIC and of `role` count, then the items of the roles whose privileges `role` has.
365fn aclmask(acl: &[(u32, u32)], role: u32, owner: u32, mask: u32) -> u32 {
366    if mask & ALL_GRANT_OPTIONS != 0 && has_privs_of_role(role, owner) {
367        return mask & ALL_GRANT_OPTIONS;
368    }
369    let mut result = 0;
370    for &(grantee, bits) in acl {
371        if grantee == PUBLIC || grantee == role {
372            result |= bits & mask;
373            if result != 0 {
374                return result;
375            }
376        }
377    }
378    for &(grantee, bits) in acl {
379        if grantee == PUBLIC || grantee == role || bits & mask & !result == 0 {
380            continue;
381        }
382        if has_privs_of_role(role, grantee) {
383            result |= bits & mask;
384            if result != 0 {
385                return result;
386            }
387        }
388    }
389    result
390}
391
392/// The result of a check: `None` when the object does not exist, as the `is_missing` of PostgreSQL gives it.
393type Check = Result<Option<bool>>;
394
395/// A relation as the checks of its privileges read it.
396struct Class<'a> {
397    oid: u32,
398    name: &'a str,
399    namespace: u32,
400    /// The `relkind` letter.
401    kind: char,
402    /// The number of user columns.
403    natts: i16,
404    owner: u32,
405    /// `relacl`, or `None` for the default privileges of the owner.
406    acl: Option<Vec<&'a str>>,
407}
408
409/// The ACL that `GRANT` or `REVOKE` gave to an object of the catalog of the session or a built-in object, or `None` when the object keeps the ACL that it was made with. The ACL of a column is empty when it is null.
410fn granted(object: ObjRef, session: &dyn Session) -> Option<Vec<&str>> {
411    let acl = session.catalog()?.object_acl(object)?;
412    Some(acl.iter().map(String::as_str).collect())
413}
414
415/// The ACL of a column: the ACL that `GRANT` gave it, or else the ACL that it was made with.
416fn column_acl<'a>(
417    relid: u32,
418    num: i16,
419    acl: Option<&'a [&'a str]>,
420    session: &'a dyn Session,
421) -> Option<Vec<&'a str>> {
422    match granted(ObjRef::column(relid, num), session) {
423        Some(acl) => Some(acl).filter(|acl| !acl.is_empty()),
424        None => acl.map(<[&str]>::to_vec),
425    }
426}
427
428/// The relation with this OID, from the built-in catalogs or from the catalog of the session. The system views and the relations that the user makes are in the catalog of the session.
429fn class_of(relid: u32, session: &dyn Session) -> Option<Class<'_>> {
430    if let Some(class) = builtin::class_by_oid(relid) {
431        return Some(Class {
432            oid: class.oid,
433            name: class.name,
434            namespace: class.namespace,
435            kind: char::from(class.kind),
436            natts: class.natts,
437            owner: class.owner,
438            acl: granted(ObjRef::new(PG_CLASS, relid), session)
439                .or_else(|| class.acl.map(<[&str]>::to_vec)),
440        });
441    }
442    let rel = session.catalog()?.relation(relid)?;
443    Some(Class {
444        oid: rel.oid,
445        name: &rel.name,
446        namespace: rel.namespace,
447        kind: rel.kind.code(),
448        natts: i16::try_from(rel.columns.len()).unwrap_or(i16::MAX),
449        owner: rel.owner,
450        acl: rel.acl.as_ref().map(|acl| acl.iter().map(String::as_str).collect()),
451    })
452}
453
454/// A column of a relation as the checks of its privileges read it.
455struct Attribute<'a> {
456    name: &'a str,
457    num: i16,
458    dropped: bool,
459    /// `attacl`, or `None` for a column with no privileges of its own.
460    acl: Option<Vec<&'a str>>,
461}
462
463/// The system columns of a relation with storage, with their numbers.
464const SYSTEM_COLUMNS: [(&str, i16); 6] =
465    [("ctid", -1), ("xmin", -2), ("cmin", -3), ("xmax", -4), ("cmax", -5), ("tableoid", -6)];
466
467/// The columns of a relation, with the system columns. A view or an index of the catalog of the session has no system columns.
468fn attributes_of(relid: u32, session: &dyn Session) -> Vec<Attribute<'_>> {
469    if builtin::class_by_oid(relid).is_some() {
470        return builtin::attributes(relid)
471            .iter()
472            .map(|a| Attribute {
473                name: a.name,
474                num: a.num,
475                dropped: a.dropped,
476                acl: column_acl(relid, a.num, a.acl, session),
477            })
478            .collect();
479    }
480    let Some(rel) = session.catalog().and_then(|c| c.relation(relid)) else { return Vec::new() };
481    let system: &[(&str, i16)] = match rel.kind {
482        RelKind::Table
483        | RelKind::Sequence
484        | RelKind::Toast
485        | RelKind::Partitioned
486        | RelKind::MatView => &SYSTEM_COLUMNS,
487        RelKind::Index | RelKind::PartitionedIndex | RelKind::View | RelKind::Composite => &[],
488    };
489    let system = system.iter().map(|&(name, num)| Attribute {
490        name,
491        num,
492        dropped: false,
493        acl: column_acl(relid, num, None, session),
494    });
495    let user = rel.columns.iter().zip(1..).map(|(column, num)| Attribute {
496        name: &column.name,
497        num,
498        dropped: column.dropped,
499        acl: column_acl(relid, num, None, session),
500    });
501    system.chain(user).collect()
502}
503
504/// `IsSystemClass`: a toast table or a relation with an OID below `FirstUnpinnedObjectId`.
505fn is_system_class(class: &Class<'_>) -> bool {
506    class.namespace == PG_TOAST || class.oid < FIRST_UNPINNED_OBJECT_ID
507}
508
509/// `pg_class_aclmask_ext` with `ACLMASK_ANY`. Only a superuser can change a system catalog. The roles `pg_read_all_data`, `pg_write_all_data` and `pg_maintain` add their privileges to the result.
510fn class_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
511    let Some(class) = class_of(relid, session) else { return Ok(None) };
512    let mut mask = mask;
513    let changes = ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE;
514    if mask & changes != 0 && is_system_class(&class) && class.kind != 'v' && !superuser(role) {
515        mask &= !changes;
516    }
517    if superuser(role) {
518        return Ok(Some(mask != 0));
519    }
520    let kind = if class.kind == 'S' { AclKind::Sequence } else { AclKind::Table };
521    let acl = acl_of(class.acl.as_deref(), kind, class.owner)?;
522    let mut result = aclmask(&acl, role, class.owner, mask);
523    if mask & ACL_SELECT != 0
524        && result & ACL_SELECT == 0
525        && has_privs_of_role(role, PG_READ_ALL_DATA)
526    {
527        result |= ACL_SELECT;
528    }
529    let writes = ACL_INSERT | ACL_UPDATE | ACL_DELETE;
530    if mask & writes != 0 && result & writes == 0 && has_privs_of_role(role, PG_WRITE_ALL_DATA) {
531        result |= mask & writes;
532    }
533    if mask & ACL_MAINTAIN != 0
534        && result & ACL_MAINTAIN == 0
535        && has_privs_of_role(role, PG_MAINTAIN)
536    {
537        result |= ACL_MAINTAIN;
538    }
539    Ok(Some(result != 0))
540}
541
542/// `pg_attribute_aclmask_ext` with `ACLMASK_ANY`. A column with a null ACL has no privileges of its own, also for a superuser.
543fn attribute_check(relid: u32, attnum: i16, role: u32, mask: u32, session: &dyn Session) -> Check {
544    let attributes = attributes_of(relid, session);
545    let found = attributes.iter().find(|a| a.num == attnum && !a.dropped);
546    let (Some(attribute), Some(class)) = (found, class_of(relid, session)) else {
547        return Ok(None);
548    };
549    let Some(acl) = &attribute.acl else { return Ok(Some(false)) };
550    Ok(Some(aclmask(&parse_acl(acl)?, role, class.owner, mask) != 0))
551}
552
553/// `pg_attribute_aclcheck_all_ext` with `ACLMASK_ANY`: true when a user column that is not dropped has a privilege of `mask`.
554fn all_attributes_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
555    let Some(class) = class_of(relid, session) else { return Ok(None) };
556    for attribute in attributes_of(relid, session) {
557        if attribute.num < 1 || attribute.num > class.natts || attribute.dropped {
558            continue;
559        }
560        let Some(acl) = &attribute.acl else { continue };
561        if aclmask(&parse_acl(acl)?, role, class.owner, mask) != 0 {
562            return Ok(Some(true));
563        }
564    }
565    Ok(Some(false))
566}
567
568/// `column_privilege_check`: null for a column that does not exist or is dropped, else true when the role has a privilege on the column or on the table.
569fn column_check(relid: u32, attnum: i16, role: u32, mask: u32, session: &dyn Session) -> Check {
570    if attnum == 0 {
571        return Ok(None);
572    }
573    match attribute_check(relid, attnum, role, mask, session)? {
574        Some(true) => return Ok(Some(true)),
575        None => return Ok(None),
576        Some(false) => {}
577    }
578    class_check(relid, role, mask, session)
579}
580
581/// The privileges and the owner of a database: a static row, or a database that `CREATE DATABASE` made.
582fn database_acl(oid: u32, session: &dyn Session) -> Result<Option<Privileges>> {
583    if let Some(row) = builtin::owned_by_oid(Owned::Database, oid) {
584        return Ok(Some((acl_of(row.acl, AclKind::Database, row.owner)?, row.owner)));
585    }
586    let created = session.databases();
587    let Some(database) = created.iter().find(|d| d.oid == oid) else { return Ok(None) };
588    let acl = match &database.acl {
589        Some(items) => parse_acl(&items.iter().map(String::as_str).collect::<Vec<_>>())?,
590        None => acl_default(AclKind::Database, database.owner),
591    };
592    Ok(Some((acl, database.owner)))
593}
594
595/// The kind of `acldefault` of an object kind.
596fn owned_kind(kind: Owned) -> AclKind {
597    match kind {
598        Owned::Namespace => AclKind::Schema,
599        Owned::Database => AclKind::Database,
600        Owned::Function => AclKind::Function,
601        Owned::Type => AclKind::Type,
602        Owned::Language => AclKind::Language,
603        Owned::Tablespace => AclKind::Tablespace,
604        Owned::ForeignDataWrapper | Owned::ForeignServer => AclKind::Other,
605        Owned::LargeObject => AclKind::LargeObject,
606    }
607}
608
609/// The items of a parsed `aclitem[]` value with the owner of the object.
610type Privileges = (Vec<(u32, u32)>, u32);
611
612/// The OID of the catalog that holds the objects of a kind that `GRANT` can give an ACL to.
613fn owned_class(kind: Owned) -> Option<u32> {
614    match kind {
615        Owned::Namespace => Some(PG_NAMESPACE),
616        Owned::Type => Some(PG_TYPE),
617        Owned::Function => Some(PG_PROC),
618        Owned::Language => Some(PG_LANGUAGE),
619        _ => None,
620    }
621}
622
623/// The ACL that `GRANT` gave to an object of a kind, or `None` when the object keeps the ACL that it was made with.
624fn granted_object(kind: Owned, oid: u32, session: &dyn Session) -> Option<Vec<&str>> {
625    granted(ObjRef::new(owned_class(kind)?, oid), session)
626}
627
628/// The privileges and the owner of a schema, a type, a function or a language of the catalog of the session, or `None` when the catalog has no such object.
629fn session_object(kind: Owned, oid: u32, session: &dyn Session) -> Result<Option<Privileges>> {
630    let Some(catalog) = session.catalog() else { return Ok(None) };
631    let (acl, owner) = match kind {
632        Owned::Namespace => match catalog.schema(oid) {
633            Some(schema) => (schema.acl.as_ref(), schema.owner),
634            None => return Ok(None),
635        },
636        Owned::Type => match catalog.type_by_oid(oid) {
637            Some(ty) => (None, ty.owner),
638            None => return Ok(None),
639        },
640        Owned::Function => match catalog.function(oid) {
641            Some(function) => (None, function.owner),
642            None => return Ok(None),
643        },
644        Owned::Language => match catalog.languages().find(|l| l.oid == oid) {
645            Some(language) => (None, language.owner),
646            None => return Ok(None),
647        },
648        Owned::Tablespace => {
649            let created = session.tablespaces();
650            let Some(tablespace) = created.iter().find(|t| t.oid == oid) else { return Ok(None) };
651            let acl: Option<Vec<&str>> =
652                tablespace.acl.as_ref().map(|acl| acl.iter().map(String::as_str).collect());
653            return Ok(Some((
654                acl_of(acl.as_deref(), AclKind::Tablespace, tablespace.owner)?,
655                tablespace.owner,
656            )));
657        }
658        _ => return Ok(None),
659    };
660    let acl: Option<Vec<&str>> = granted_object(kind, oid, session)
661        .or_else(|| acl.map(|acl| acl.iter().map(String::as_str).collect()));
662    Ok(Some((acl_of(acl.as_deref(), owned_kind(kind), owner)?, owner)))
663}
664
665/// `object_aclmask_ext` with `ACLMASK_ANY`, with `pg_namespace_aclmask_ext` and `pg_type_aclmask_ext`. A superuser has each privilege, also on an object that does not exist. A true array type and a multirange type have the privileges of their element type and their range type. `pg_read_all_data` and `pg_write_all_data` have `USAGE` on each schema. An object that is not in the built-in catalogs comes from the catalog of the session.
666fn object_check(kind: Owned, oid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
667    if superuser(role) {
668        return Ok(Some(mask != 0));
669    }
670    let mut oid = oid;
671    if kind == Owned::Type {
672        if let Some(ty) = builtin::type_by_oid(oid) {
673            if ty.elem != 0 && ty.subscript == ARRAY_SUBSCRIPT_HANDLER {
674                oid = ty.elem;
675            }
676            if builtin::type_by_oid(oid).is_some_and(|t| t.kind == b'm') {
677                let Some(range) = builtin::multirange_range(oid) else { return Ok(None) };
678                oid = range;
679            }
680        } else {
681            let found = session.catalog().and_then(|c| c.type_by_oid(oid));
682            let Some(ty) = found else { return Ok(None) };
683            if ty.element != 0 {
684                oid = ty.element;
685            }
686        }
687    }
688    let (acl, owner) = if kind == Owned::Database {
689        let Some(found) = database_acl(oid, session)? else { return Ok(None) };
690        found
691    } else if let Some(row) = builtin::owned_by_oid(kind, oid) {
692        let acl = granted_object(kind, oid, session).or_else(|| row.acl.map(<[&str]>::to_vec));
693        (acl_of(acl.as_deref(), owned_kind(kind), row.owner)?, row.owner)
694    } else {
695        let Some(object) = session_object(kind, oid, session)? else { return Ok(None) };
696        object
697    };
698    let mut result = aclmask(&acl, role, owner, mask);
699    if kind == Owned::Namespace
700        && mask & ACL_USAGE != 0
701        && result & ACL_USAGE == 0
702        && (has_privs_of_role(role, PG_READ_ALL_DATA) || has_privs_of_role(role, PG_WRITE_ALL_DATA))
703    {
704        result |= ACL_USAGE;
705    }
706    Ok(Some(result != 0))
707}
708
709/// `pg_role_aclcheck`: the admin option, then the membership, then the privileges, then `SET ROLE`.
710fn role_check(role: u32, member: u32, mask: u32) -> bool {
711    (mask & go(ACL_CREATE) != 0 && is_admin_of_role(member, role))
712        || (mask & ACL_CREATE != 0 && reaches(member, role, Recurse::Members))
713        || (mask & ACL_USAGE != 0 && reaches(member, role, Recurse::Privs))
714        || (mask & ACL_SET != 0 && reaches(member, role, Recurse::SetRole))
715}
716
717/// `pg_parameter_aclmask` with `ACLMASK_ANY`. A superuser has each privilege on each name. The catalog `pg_parameter_acl` has no rows after `initdb`, so no other role has a privilege.
718fn parameter_check(role: u32, mask: u32) -> bool {
719    superuser(role) && mask != 0
720}
721
722/// The text value at `index`.
723fn text(args: &[Value], index: usize) -> Result<&str> {
724    args.get(index).and_then(Value::as_str).ok_or_else(bad_value)
725}
726
727/// The role of a call: the first value when the call has `with_role` values, else the current user. A role name may be `public`.
728fn role_arg(call: &Call<'_>, args: &[Value], with_role: bool, public: bool) -> Result<u32> {
729    if !with_role {
730        return Ok(current_user(call.session));
731    }
732    match &args[0] {
733        Value::Oid(oid) => Ok(*oid),
734        Value::Text(name) if public => get_role_oid_or_public(name, call.session),
735        Value::Text(name) => get_role_oid(name, call.session),
736        _ => Err(bad_value()),
737    }
738}
739
740/// `convert_table_name`: the relation of a qualified name, as `RangeVarGetRelid` finds it.
741fn table_name(text: &str, session: &dyn Session) -> Result<u32> {
742    let names = qualified_name_list(text).map_err(type_error)?;
743    reg::relation_oid(&names, session)
744}
745
746/// The object of a function by name: the input of an OID alias type, with the error of `acl.c` for an OID of 0.
747fn reg_name(kind: RegKind, text: &str, session: &dyn Session, what: &str) -> Result<u32> {
748    match reg::input(kind, text, session)?? {
749        0 => Err(Error::new(
750            if kind == RegKind::Procedure {
751                SqlState::UNDEFINED_FUNCTION
752            } else {
753                SqlState::UNDEFINED_OBJECT
754            },
755            format!("{what} \"{text}\" does not exist"),
756        )),
757        oid => Ok(oid),
758    }
759}
760
761/// The OID of an object of a kind without a schema by its name, with the error of `get_*_oid`.
762pub(crate) fn simple_name(kind: Owned, name: &str, session: &dyn Session) -> Result<u32> {
763    let mut found = builtin::owned(kind).iter().find(|r| r.name == name).map(|r| r.oid);
764    if found.is_none() && kind == Owned::Namespace {
765        found = session.catalog().and_then(|c| c.schema_by_name(name)).map(|s| s.oid);
766    }
767    if found.is_none() && kind == Owned::Database {
768        found = session.databases().iter().find(|d| d.name == name).map(|d| d.oid);
769    }
770    if found.is_none() && kind == Owned::Tablespace {
771        found = session.tablespaces().iter().find(|t| t.name == name).map(|t| t.oid);
772    }
773    found.ok_or_else(|| {
774        let (state, what) = match kind {
775            Owned::Namespace => (SqlState::UNDEFINED_SCHEMA, "schema"),
776            Owned::Database => (SqlState::UNDEFINED_DATABASE, "database"),
777            Owned::Language => (SqlState::UNDEFINED_OBJECT, "language"),
778            Owned::Tablespace => (SqlState::UNDEFINED_OBJECT, "tablespace"),
779            Owned::ForeignDataWrapper => (SqlState::UNDEFINED_OBJECT, "foreign-data wrapper"),
780            _ => (SqlState::UNDEFINED_OBJECT, "server"),
781        };
782        Error::new(state, format!("{what} \"{name}\" does not exist"))
783    })
784}
785
786/// The privilege map of an object kind.
787fn owned_privs(kind: Owned) -> PrivMap {
788    match kind {
789        Owned::Namespace => SCHEMA_PRIVS,
790        Owned::Database => DATABASE_PRIVS,
791        Owned::Function => FUNCTION_PRIVS,
792        Owned::Tablespace => TABLESPACE_PRIVS,
793        Owned::LargeObject => LARGEOBJECT_PRIVS,
794        Owned::Type | Owned::Language | Owned::ForeignDataWrapper | Owned::ForeignServer => {
795            USAGE_PRIVS
796        }
797    }
798}
799
800/// The result of a check as a value.
801fn result(check: Option<bool>) -> Value {
802    check.map_or(Value::Null, Value::Bool)
803}
804
805/// `has_table_privilege`. The forms with a name take the role, then the relation, then the privileges. The forms with an OID take the privileges first.
806fn has_table_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
807    let with_role = args.len() == 3;
808    let role = role_arg(call, args, with_role, true)?;
809    let at = usize::from(with_role);
810    let relid = match &args[at] {
811        Value::Text(name) => table_name(name, call.session)?,
812        Value::Oid(oid) => *oid,
813        _ => return Err(bad_value()),
814    };
815    let mask = privileges(text(args, at + 1)?, TABLE_PRIVS)?;
816    Ok(result(class_check(relid, role, mask, call.session)?))
817}
818
819/// `LockTableAclCheck`: true when `role` can lock the relation `relid` in the lock mode `mode`, where `AccessShareLock` is 1. `MAINTAIN`, `UPDATE`, `DELETE` and `TRUNCATE` allow each mode, `SELECT` allows `ACCESS SHARE`, and `INSERT` allows the modes up to `ROW EXCLUSIVE`.
820///
821/// # Errors
822///
823/// The error of an ACL that is not correct.
824pub fn lock_table_allowed(relid: u32, role: u32, mode: i32, session: &dyn Session) -> Result<bool> {
825    let mut mask = ACL_MAINTAIN | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
826    if mode <= 1 {
827        mask |= ACL_SELECT;
828    }
829    if mode <= 3 {
830        mask |= ACL_INSERT;
831    }
832    Ok(class_check(relid, role, mask, session)?.unwrap_or(false))
833}
834
835/// `ExecCheckOneRelPerms` for `INSERT` into the columns `attnums`: true when the role has `INSERT` on the table, or on each of the columns. With no columns, `INSERT` on any column is enough.
836///
837/// # Errors
838///
839/// The error of an ACL that is not correct.
840pub fn insert_allowed(
841    relid: u32,
842    role: u32,
843    attnums: &[i16],
844    session: &dyn Session,
845) -> Result<bool> {
846    if class_check(relid, role, ACL_INSERT, session)?.unwrap_or(false) {
847        return Ok(true);
848    }
849    if attnums.is_empty() {
850        return Ok(all_attributes_check(relid, role, ACL_INSERT, session)?.unwrap_or(false));
851    }
852    for &attnum in attnums {
853        if !attribute_check(relid, attnum, role, ACL_INSERT, session)?.unwrap_or(false) {
854            return Ok(false);
855        }
856    }
857    Ok(true)
858}
859
860/// `pg_class_aclmask` with `ACLMASK_ALL`: the bits of `mask` that `role` has on the relation, one bit at a time.
861fn class_bits(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Result<u32> {
862    let mut result = 0;
863    for bit in [ACL_INSERT, ACL_SELECT, ACL_UPDATE, ACL_DELETE] {
864        if mask & bit != 0 && class_check(relid, role, bit, session)?.unwrap_or(false) {
865            result |= bit;
866        }
867    }
868    Ok(result)
869}
870
871/// `pg_attribute_aclcheck_all` with `ACLMASK_ALL`: true when each user column that is not dropped has the privilege. A relation with no columns does not have it.
872fn each_attribute_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Result<bool> {
873    let Some(class) = class_of(relid, session) else { return Ok(false) };
874    let mut result = false;
875    for attribute in attributes_of(relid, session) {
876        if attribute.num < 1 || attribute.num > class.natts || attribute.dropped {
877            continue;
878        }
879        let Some(acl) = &attribute.acl else { return Ok(false) };
880        if aclmask(&parse_acl(acl)?, role, class.owner, mask) == 0 {
881            return Ok(false);
882        }
883        result = true;
884    }
885    Ok(result)
886}
887
888/// `ExecCheckPermissionsModified`: true when the role has the privilege on each of the columns, or on any column when there are no columns.
889fn modified_allowed(
890    relid: u32,
891    role: u32,
892    columns: &BTreeSet<i16>,
893    mask: u32,
894    session: &dyn Session,
895) -> Result<bool> {
896    if columns.is_empty() {
897        return Ok(all_attributes_check(relid, role, mask, session)?.unwrap_or(false));
898    }
899    for &attnum in columns {
900        if !attribute_check(relid, attnum, role, mask, session)?.unwrap_or(false) {
901            return Ok(false);
902        }
903    }
904    Ok(true)
905}
906
907/// `ExecCheckOneRelPerms`: true when the role has the privileges of the statement on the relation, on the table or else on each column that the statement uses.
908fn one_relation_allowed(perm: &PermInfo, role: u32, session: &dyn Session) -> Result<bool> {
909    let relid = perm.relid;
910    let remaining = perm.required & !class_bits(relid, role, perm.required, session)?;
911    if remaining == 0 {
912        return Ok(true);
913    }
914    if remaining & !(ACL_SELECT | ACL_INSERT | ACL_UPDATE) != 0 {
915        return Ok(false);
916    }
917    if remaining & ACL_SELECT != 0 {
918        if perm.selected.is_empty()
919            && !all_attributes_check(relid, role, ACL_SELECT, session)?.unwrap_or(false)
920        {
921            return Ok(false);
922        }
923        for &attnum in &perm.selected {
924            let allowed = if attnum == 0 {
925                each_attribute_check(relid, role, ACL_SELECT, session)?
926            } else {
927                attribute_check(relid, attnum, role, ACL_SELECT, session)?.unwrap_or(false)
928            };
929            if !allowed {
930                return Ok(false);
931            }
932        }
933    }
934    if remaining & ACL_INSERT != 0
935        && !modified_allowed(relid, role, &perm.inserted, ACL_INSERT, session)?
936    {
937        return Ok(false);
938    }
939    if remaining & ACL_UPDATE != 0
940        && !modified_allowed(relid, role, &perm.updated, ACL_UPDATE, session)?
941    {
942        return Ok(false);
943    }
944    Ok(true)
945}
946
947/// `ExecCheckPermissions`: an error for the first relation that the statement uses without the privileges that it needs. A relation of the query of a view counts as the owner of the view. A superuser has each privilege, and a relation that does not exist is not checked.
948///
949/// # Errors
950///
951/// `permission denied` for the relation, or the error of an ACL that is not correct.
952pub fn check_permissions(perms: &[PermInfo], session: &dyn Session) -> Result<()> {
953    for perm in perms {
954        if perm.required == 0 {
955            continue;
956        }
957        let role = if perm.check_as == 0 { current_user(session) } else { perm.check_as };
958        if superuser(role) {
959            continue;
960        }
961        let Some(class) = class_of(perm.relid, session) else { continue };
962        if one_relation_allowed(perm, role, session)? {
963            continue;
964        }
965        let object = match class.kind {
966            'v' => "view",
967            'm' => "materialized view",
968            'S' => "sequence",
969            'f' => "foreign table",
970            'i' | 'I' => "index",
971            _ => "table",
972        };
973        return Err(Error::new(
974            SqlState::INSUFFICIENT_PRIVILEGE,
975            format!("permission denied for {object} {}", class.name),
976        ));
977    }
978    Ok(())
979}
980
981/// The `EXECUTE` checks of `ExecInitFunc` and `ExecInitAgg`: an error for the first function that the current user cannot call. A superuser can call each function, and a function that does not exist is not checked.
982///
983/// # Errors
984///
985/// `permission denied` for the function or the aggregate.
986pub fn check_execute(funcs: &[u32], session: &dyn Session) -> Result<()> {
987    if funcs.is_empty() {
988        return Ok(());
989    }
990    let role = current_user(session);
991    if superuser(role) {
992        return Ok(());
993    }
994    for (i, &func) in funcs.iter().enumerate() {
995        if funcs[..i].contains(&func)
996            || object_check(Owned::Function, func, role, ACL_EXECUTE, session)? != Some(false)
997        {
998            continue;
999        }
1000        let proc = builtin::proc_by_oid(func);
1001        let object = if proc.is_some_and(|p| p.kind == b'a') { "aggregate" } else { "function" };
1002        let name = proc
1003            .map(|p| p.name.to_string())
1004            .or_else(|| session.catalog().and_then(|c| c.function(func)).map(|f| f.name.clone()));
1005        return Err(Error::new(
1006            SqlState::INSUFFICIENT_PRIVILEGE,
1007            format!("permission denied for {object} {}", name.unwrap_or_default()),
1008        ));
1009    }
1010    Ok(())
1011}
1012
1013/// The privileges that `nextval` needs: `USAGE` or `UPDATE`.
1014pub(crate) const USAGE_UPDATE: u32 = ACL_USAGE | ACL_UPDATE;
1015/// The privileges that `currval` and `lastval` need: `SELECT` or `USAGE`.
1016pub(crate) const SELECT_USAGE: u32 = ACL_SELECT | ACL_USAGE;
1017/// The privilege that `setval` needs.
1018pub(crate) const UPDATE: u32 = ACL_UPDATE;
1019
1020/// `pg_class_aclcheck` for `SELECT` or `USAGE` on a sequence as the current user, as `pg_get_sequence_data` does it.
1021pub(crate) fn sequence_readable(relid: u32, session: &dyn Session) -> Result<bool> {
1022    sequence_allowed(relid, SELECT_USAGE, session)
1023}
1024
1025/// `pg_class_aclcheck` on a sequence as the current user: true when the user has one of the privileges of `mask`.
1026pub(crate) fn sequence_allowed(relid: u32, mask: u32, session: &dyn Session) -> Result<bool> {
1027    Ok(class_check(relid, current_user(session), mask, session)?.unwrap_or(false))
1028}
1029
1030/// `has_sequence_privilege`: the privileges come before the relation. A relation that is not a sequence is an error.
1031fn has_sequence_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1032    let with_role = args.len() == 3;
1033    let role = role_arg(call, args, with_role, true)?;
1034    let at = usize::from(with_role);
1035    let mask = privileges(text(args, at + 1)?, SEQUENCE_PRIVS)?;
1036    let (relid, shown) = match &args[at] {
1037        Value::Text(name) => (table_name(name, call.session)?, name.clone()),
1038        Value::Oid(oid) => match class_of(*oid, call.session) {
1039            Some(class) => (*oid, class.name.to_string()),
1040            None => return Ok(Value::Null),
1041        },
1042        _ => return Err(bad_value()),
1043    };
1044    if class_of(relid, call.session).is_none_or(|c| c.kind != 'S') {
1045        return Err(Error::new(
1046            SqlState::WRONG_OBJECT_TYPE,
1047            format!("\"{shown}\" is not a sequence"),
1048        ));
1049    }
1050    Ok(result(class_check(relid, role, mask, call.session)?))
1051}
1052
1053/// `convert_column_name`: the number of a column by name. A dropped column gives 0, and so does a relation that does not exist.
1054fn column_number(relid: u32, name: &str, session: &dyn Session) -> Result<i16> {
1055    if let Some(attribute) = attributes_of(relid, session).iter().find(|a| a.name == name) {
1056        return Ok(if attribute.dropped { 0 } else { attribute.num });
1057    }
1058    match class_of(relid, session) {
1059        Some(class) => Err(Error::new(
1060            SqlState::UNDEFINED_COLUMN,
1061            format!("column \"{name}\" of relation \"{}\" does not exist", class.name),
1062        )),
1063        None => Ok(0),
1064    }
1065}
1066
1067/// `get_attnum`: the number of a column of a relation by name, with the system columns. A dropped column has no number.
1068pub(crate) fn attnum(relid: u32, name: &str, session: &dyn Session) -> Option<i16> {
1069    attributes_of(relid, session).iter().find(|a| a.name == name && !a.dropped).map(|a| a.num)
1070}
1071
1072/// `has_column_privilege`: the role, the relation, the column, then the privileges.
1073fn has_column_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1074    let with_role = args.len() == 4;
1075    let role = role_arg(call, args, with_role, true)?;
1076    let at = usize::from(with_role);
1077    let relid = match &args[at] {
1078        Value::Text(name) => table_name(name, call.session)?,
1079        Value::Oid(oid) => *oid,
1080        _ => return Err(bad_value()),
1081    };
1082    let attnum = match &args[at + 1] {
1083        Value::Text(name) => column_number(relid, name, call.session)?,
1084        value => value.as_i64().and_then(|n| i16::try_from(n).ok()).ok_or_else(bad_value)?,
1085    };
1086    let mask = privileges(text(args, at + 2)?, COLUMN_PRIVS)?;
1087    Ok(result(column_check(relid, attnum, role, mask, call.session)?))
1088}
1089
1090/// `has_any_column_privilege`: the privilege on the relation, or on any column of it.
1091fn has_any_column_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1092    let with_role = args.len() == 3;
1093    let role = role_arg(call, args, with_role, true)?;
1094    let at = usize::from(with_role);
1095    let relid = match &args[at] {
1096        Value::Text(name) => table_name(name, call.session)?,
1097        Value::Oid(oid) => *oid,
1098        _ => return Err(bad_value()),
1099    };
1100    let mask = privileges(text(args, at + 1)?, COLUMN_PRIVS)?;
1101    Ok(result(match class_check(relid, role, mask, call.session)? {
1102        Some(false) => all_attributes_check(relid, role, mask, call.session)?,
1103        other => other,
1104    }))
1105}
1106
1107/// The `has_*_privilege` functions of the objects that `object_aclcheck` checks. The name of a function or a type is read as `regprocedure` or `regtype` reads it.
1108fn has_object_privilege(kind: Owned, call: &Call<'_>, args: &[Value]) -> Result<Value> {
1109    let with_role = args.len() == 3;
1110    let role = role_arg(call, args, with_role, true)?;
1111    let at = usize::from(with_role);
1112    let oid = match &args[at] {
1113        Value::Text(name) => match kind {
1114            Owned::Function => reg_name(RegKind::Procedure, name, call.session, "function")?,
1115            Owned::Type => reg_name(RegKind::Type, name, call.session, "type")?,
1116            _ => simple_name(kind, name, call.session)?,
1117        },
1118        Value::Oid(oid) => *oid,
1119        _ => return Err(bad_value()),
1120    };
1121    let mask = privileges(text(args, at + 1)?, owned_privs(kind))?;
1122    Ok(result(object_check(kind, oid, role, mask, call.session)?))
1123}
1124
1125/// `has_largeobject_privilege`: null for a large object that does not exist. The static rows have no large objects.
1126fn has_largeobject_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1127    let with_role = args.len() == 3;
1128    let role = role_arg(call, args, with_role, true)?;
1129    let at = usize::from(with_role);
1130    let oid = args[at].as_oid().ok_or_else(bad_value)?;
1131    let mask = privileges(text(args, at + 1)?, LARGEOBJECT_PRIVS)?;
1132    if builtin::owned_by_oid(Owned::LargeObject, oid).is_none() {
1133        return Ok(Value::Null);
1134    }
1135    Ok(result(object_check(Owned::LargeObject, oid, role, mask, call.session)?))
1136}
1137
1138/// `has_parameter_privilege`: the privileges come before the role.
1139fn has_parameter_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1140    let with_role = args.len() == 3;
1141    let at = usize::from(with_role);
1142    let mask = privileges(text(args, at + 1)?, PARAMETER_PRIVS)?;
1143    let role = role_arg(call, args, with_role, true)?;
1144    text(args, at)?;
1145    Ok(Value::Bool(parameter_check(role, mask)))
1146}
1147
1148/// `pg_has_role`: the member, then the role, then the privileges. A role name may not be `public`.
1149fn pg_has_role(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1150    let with_member = args.len() == 3;
1151    let member = role_arg(call, args, with_member, false)?;
1152    let at = usize::from(with_member);
1153    let role = match &args[at] {
1154        Value::Text(name) => get_role_oid(name, call.session)?,
1155        Value::Oid(oid) => *oid,
1156        _ => return Err(bad_value()),
1157    };
1158    let mask = privileges(text(args, at + 1)?, ROLE_PRIVS)?;
1159    Ok(Value::Bool(role_check(role, member, mask)))
1160}
1161
1162/// The kernel of a function of this module by its `prosrc`, such as `has_table_privilege_name_id`. One kernel serves each form of a function.
1163/// `convert_aclright_to_string`: the name of each privilege bit, from bit 0.
1164const RIGHT_NAMES: [&str; 15] = [
1165    "INSERT",
1166    "SELECT",
1167    "UPDATE",
1168    "DELETE",
1169    "TRUNCATE",
1170    "REFERENCES",
1171    "TRIGGER",
1172    "EXECUTE",
1173    "USAGE",
1174    "CREATE",
1175    "TEMPORARY",
1176    "CONNECT",
1177    "SET",
1178    "ALTER SYSTEM",
1179    "MAINTAIN",
1180];
1181
1182/// `aclexplode`: a row for each privilege of each item of an `aclitem[]` value, with the grantor, the grantee, the name of the privilege and its grant option. The items come in order, and the privileges of an item come in the order of their bits.
1183///
1184/// # Errors
1185///
1186/// `42704` for an item with a role that does not exist.
1187pub(crate) fn aclexplode(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
1188    let Some(Value::Array(acl)) = args.first() else { return Err(bad_value()) };
1189    let mut rows = Vec::new();
1190    for item in acl.values.iter().flatten() {
1191        let item = stored_item(item, call.session)?;
1192        for (bit, name) in RIGHT_NAMES.iter().enumerate() {
1193            if item.bits & (1 << bit) != 0 {
1194                rows.push(vec![
1195                    Value::Oid(item.grantor),
1196                    Value::Oid(item.grantee),
1197                    Value::text(*name),
1198                    Value::Bool(item.bits & go(1 << bit) != 0),
1199                ]);
1200            }
1201        }
1202    }
1203    Ok(rows)
1204}
1205
1206/// An `aclitem` value: the grantee, the privileges with their grant options in the bits of [`go`], and the grantor.
1207#[derive(Debug, Clone, Copy)]
1208struct AclItem {
1209    grantee: u32,
1210    bits: u32,
1211    grantor: u32,
1212}
1213
1214/// `NAMEDATALEN`: a role name has fewer bytes than this.
1215const NAMEDATALEN: usize = 64;
1216
1217/// `is_safe_acl_char` of `getid`: a byte of a role name with no quotes. A byte with the high bit set counts.
1218fn is_name_byte(c: u8) -> bool {
1219    c >= 0x80 || c.is_ascii_alphanumeric() || c == b'_'
1220}
1221
1222/// The text after the white space at its start.
1223fn skip_spaces(s: &[u8]) -> &[u8] {
1224    let n = s.iter().take_while(|&&c| is_space(char::from(c))).count();
1225    &s[n..]
1226}
1227
1228/// `getid`: a role name after any white space, with double quotes around all or part of it or with no quotes. In quotes, `""` is a quote. It gives the name and the text after the name and any white space.
1229///
1230/// # Errors
1231///
1232/// `42622` for a name of `NAMEDATALEN` bytes or more.
1233fn getid(s: &[u8]) -> Result<(String, &[u8])> {
1234    let mut s = skip_spaces(s);
1235    let mut name = Vec::new();
1236    let mut in_quotes = false;
1237    while let Some((&c, rest)) = s.split_first() {
1238        if !(in_quotes || c == b'"' || is_name_byte(c)) {
1239            break;
1240        }
1241        s = rest;
1242        if c == b'"' {
1243            if !in_quotes {
1244                in_quotes = true;
1245                continue;
1246            }
1247            if s.first() != Some(&b'"') {
1248                in_quotes = false;
1249                continue;
1250            }
1251            s = &s[1..];
1252        }
1253        if name.len() >= NAMEDATALEN - 1 {
1254            return Err(Error::new(SqlState::NAME_TOO_LONG, "identifier too long")
1255                .with_detail(format!("Identifier must be less than {NAMEDATALEN} characters.")));
1256        }
1257        name.push(c);
1258    }
1259    Ok((String::from_utf8_lossy(&name).into_owned(), skip_spaces(s)))
1260}
1261
1262/// The error `22P02` of a bad `aclitem` text.
1263fn acl_syntax(message: impl Into<String>) -> Error {
1264    Error::new(SqlState::INVALID_TEXT_REPRESENTATION, message)
1265}
1266
1267/// The OID of a role of an `aclitem` text. In the text of a stored value, a role that does not exist shows as its OID, as `aclitemout` writes it.
1268///
1269/// # Errors
1270///
1271/// `42704` for a role that does not exist.
1272fn acl_role(name: &str, session: &dyn Session, stored: bool) -> Result<u32> {
1273    if stored
1274        && role_oid(name, session).is_none()
1275        && let Ok(oid) = name.parse::<u32>()
1276    {
1277        return Ok(oid);
1278    }
1279    get_role_oid(name, session)
1280}
1281
1282/// `aclparse`: an item in the form `grantee=privileges/grantor`, and the text after it. The key word `group` or `user` can come before the grantee, and an empty grantee is PUBLIC. A `*` after a privilege is its grant option. An item with no grantor has the bootstrap superuser as its grantor, with a warning. `stored` reads the text of a stored value.
1283///
1284/// # Errors
1285///
1286/// `22P02` for a bad item, `42622` for a name that is too long, and `42704` for a role that does not exist.
1287fn aclparse<'a>(s: &'a [u8], session: &dyn Session, stored: bool) -> Result<(AclItem, &'a [u8])> {
1288    let (mut name, mut s) = getid(s)?;
1289    if s.first() != Some(&b'=') {
1290        if name != "group" && name != "user" {
1291            return Err(acl_syntax(format!("unrecognized key word: \"{name}\""))
1292                .with_hint("ACL key word must be \"group\" or \"user\"."));
1293        }
1294        (name, s) = getid(s)?;
1295        if name.is_empty() {
1296            return Err(acl_syntax("missing name")
1297                .with_hint("A name must follow the \"group\" or \"user\" key word."));
1298        }
1299    }
1300    let Some(mut s) = s.strip_prefix(b"=") else {
1301        return Err(acl_syntax("missing \"=\" sign"));
1302    };
1303    let mut bits = 0;
1304    let mut read = 0;
1305    while let Some((&c, rest)) = s.split_first() {
1306        if c == b'*' {
1307            bits |= go(read);
1308        } else if c.is_ascii_alphabetic() {
1309            let Some(bit) = RIGHTS.find(char::from(c)) else {
1310                return Err(acl_syntax(format!(
1311                    "invalid mode character: must be one of \"{RIGHTS}\""
1312                )));
1313            };
1314            read = 1 << bit;
1315            bits |= read;
1316        } else {
1317            break;
1318        }
1319        s = rest;
1320    }
1321    let grantee = if name.is_empty() { PUBLIC } else { acl_role(&name, session, stored)? };
1322    let grantor = if let Some(rest) = s.strip_prefix(b"/") {
1323        let (name, rest) = getid(rest)?;
1324        s = rest;
1325        if name.is_empty() {
1326            return Err(acl_syntax("a name must follow the \"/\" sign"));
1327        }
1328        acl_role(&name, session, stored)?
1329    } else {
1330        session.warn(Error::new(
1331            SqlState::INVALID_GRANTOR,
1332            format!("defaulting grantor to user ID {BOOTSTRAP_SUPERUSER}"),
1333        ));
1334        BOOTSTRAP_SUPERUSER
1335    };
1336    Ok((AclItem { grantee, bits, grantor }, s))
1337}
1338
1339/// `aclitemin`: an `aclitem` value as the text of `aclitemout`.
1340///
1341/// # Errors
1342///
1343/// The errors of [`aclparse`], and `22P02` for text after the item.
1344pub(crate) fn aclitem_in(text: &str, session: &dyn Session) -> Result<String> {
1345    let (item, rest) = aclparse(text.as_bytes(), session, false)?;
1346    if !skip_spaces(rest).is_empty() {
1347        return Err(acl_syntax("extra garbage at the end of the ACL specification"));
1348    }
1349    Ok(acl_item(item.grantee, item.bits, item.grantor))
1350}
1351
1352/// The parts of an `aclitem` value, which has the text of `aclitemout`.
1353fn stored_item(value: &Value, session: &dyn Session) -> Result<AclItem> {
1354    let text = value.as_str().ok_or_else(bad_value)?;
1355    Ok(aclparse(text.as_bytes(), session, true)?.0)
1356}
1357
1358/// `check_acl`: the items of an `aclitem[]` value, which must have one dimension and no null items.
1359///
1360/// # Errors
1361///
1362/// `22023` for an array that does not have one dimension, and `22004` for a null item.
1363fn acl_items(value: &Value, session: &dyn Session) -> Result<Vec<AclItem>> {
1364    let Value::Array(acl) = value else { return Err(bad_value()) };
1365    if acl.dims.len() != 1 {
1366        return Err(Error::new(
1367            SqlState::INVALID_PARAMETER_VALUE,
1368            "ACL arrays must be one-dimensional",
1369        ));
1370    }
1371    if acl.values.iter().any(Option::is_none) {
1372        return Err(Error::new(
1373            SqlState::NULL_VALUE_NOT_ALLOWED,
1374            "ACL arrays must not contain null values",
1375        ));
1376    }
1377    acl.values.iter().flatten().map(|item| stored_item(item, session)).collect()
1378}
1379
1380/// `aclitem_eq`: the grantees, the privileges and the grantors are equal. The text of a value has one form, so the texts are equal.
1381fn aclitem_eq(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1382    Ok(Value::Bool(text(args, 0)? == text(args, 1)?))
1383}
1384
1385/// `aclcontains`: true when the array has an item with the grantee and the grantor of the item, and with all its privileges and grant options.
1386///
1387/// # Errors
1388///
1389/// The errors of [`acl_items`].
1390fn aclcontains(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1391    let [acl, item] = args else { return Err(bad_value()) };
1392    let items = acl_items(acl, call.session)?;
1393    let item = stored_item(item, call.session)?;
1394    Ok(Value::Bool(items.iter().any(|a| {
1395        a.grantee == item.grantee && a.grantor == item.grantor && (a.bits & item.bits) == item.bits
1396    })))
1397}
1398
1399/// The privileges of `makeaclitem`: the privileges of all kinds of objects.
1400const ANY_PRIVS: PrivMap = &[
1401    ("SELECT", ACL_SELECT),
1402    ("INSERT", ACL_INSERT),
1403    ("UPDATE", ACL_UPDATE),
1404    ("DELETE", ACL_DELETE),
1405    ("TRUNCATE", ACL_TRUNCATE),
1406    ("REFERENCES", ACL_REFERENCES),
1407    ("TRIGGER", ACL_TRIGGER),
1408    ("EXECUTE", ACL_EXECUTE),
1409    ("USAGE", ACL_USAGE),
1410    ("CREATE", ACL_CREATE),
1411    ("TEMP", ACL_CREATE_TEMP),
1412    ("TEMPORARY", ACL_CREATE_TEMP),
1413    ("CONNECT", ACL_CONNECT),
1414    ("SET", ACL_SET),
1415    ("ALTER SYSTEM", ACL_ALTER_SYSTEM),
1416    ("MAINTAIN", ACL_MAINTAIN),
1417];
1418
1419/// `makeaclitem(grantee, grantor, privileges, is_grantable)`: an item with the privileges of a list of names, with their grant options when `is_grantable` is true.
1420///
1421/// # Errors
1422///
1423/// `22023` for a name that is not the name of a privilege.
1424fn makeaclitem(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1425    let (Some(Value::Oid(grantee)), Some(Value::Oid(grantor)), Some(Value::Bool(grantable))) =
1426        (args.first(), args.get(1), args.get(3))
1427    else {
1428        return Err(bad_value());
1429    };
1430    let privs = privileges(text(args, 2)?, ANY_PRIVS)?;
1431    let bits = if *grantable { privs | go(privs) } else { privs };
1432    Ok(Value::text(acl_item(*grantee, bits, *grantor)))
1433}
1434
1435/// The hash of `hash_aclitem`: the sum of the privileges with no grant options, the grantee and the grantor, in 32 bits.
1436fn aclitem_sum(item: AclItem) -> u32 {
1437    (item.bits & 0xFFFF).wrapping_add(item.grantee).wrapping_add(item.grantor)
1438}
1439
1440/// `hash_aclitem`.
1441fn hash_aclitem(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1442    let [item] = args else { return Err(bad_value()) };
1443    Ok(Value::Int4(aclitem_sum(stored_item(item, call.session)?).cast_signed()))
1444}
1445
1446/// `hash_aclitem_extended`: the sum of `hash_aclitem` for the seed 0, and else the sum with `hash_uint32_extended`.
1447fn hash_aclitem_extended(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1448    let [item, Value::Int8(seed)] = args else { return Err(bad_value()) };
1449    let sum = aclitem_sum(stored_item(item, call.session)?);
1450    if *seed == 0 {
1451        return Ok(Value::Int8(i64::from(sum)));
1452    }
1453    Ok(Value::Int8(crate::hash::uint32_extended(sum, seed.cast_unsigned()).cast_signed()))
1454}
1455
1456/// `aclinsert`, which PostgreSQL no longer has.
1457fn aclinsert(_call: &Call<'_>, _args: &[Value]) -> Result<Value> {
1458    Err(Error::new(SqlState::FEATURE_NOT_SUPPORTED, "aclinsert is no longer supported"))
1459}
1460
1461/// `aclremove`, which PostgreSQL no longer has.
1462fn aclremove(_call: &Call<'_>, _args: &[Value]) -> Result<Value> {
1463    Err(Error::new(SqlState::FEATURE_NOT_SUPPORTED, "aclremove is no longer supported"))
1464}
1465
1466/// `putid`: the name of a role in an `aclitem` value, in double quotes when it has a character that is not an ASCII letter, an ASCII digit or `_`. A role that does not exist shows as its OID.
1467fn put_role(out: &mut String, role: u32) {
1468    let roles = rupg_pgcatalog::roles::current();
1469    let Some(name) = roles.name(role) else {
1470        out.push_str(&role.to_string());
1471        return;
1472    };
1473    if name.bytes().all(|c| c.is_ascii_alphanumeric() || c == b'_') {
1474        out.push_str(name);
1475        return;
1476    }
1477    out.push('"');
1478    for c in name.chars() {
1479        if c == '"' {
1480            out.push('"');
1481        }
1482        out.push(c);
1483    }
1484    out.push('"');
1485}
1486
1487/// `aclitemout`: the text of an item with no grant options, as `grantee=privileges/grantor`. The grantee of PUBLIC is empty.
1488fn acl_item(grantee: u32, bits: u32, grantor: u32) -> String {
1489    let mut out = String::new();
1490    if grantee != PUBLIC {
1491        put_role(&mut out, grantee);
1492    }
1493    out.push('=');
1494    for (bit, letter) in RIGHTS.chars().enumerate() {
1495        if bits & (1 << bit) != 0 {
1496            out.push(letter);
1497            if bits & go(1 << bit) != 0 {
1498                out.push('*');
1499            }
1500        }
1501    }
1502    out.push('/');
1503    put_role(&mut out, grantor);
1504    out
1505}
1506
1507/// `acldefault(char, oid)`: the privileges of an object of a kind with a null ACL and this owner, as an `aclitem[]` value.
1508///
1509/// # Errors
1510///
1511/// `XX000` for a letter that is not the letter of a kind.
1512fn acldefault(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1513    let (Some(Value::Char(kind)), Some(Value::Oid(owner))) = (args.first(), args.get(1)) else {
1514        return Err(bad_value());
1515    };
1516    let kind = match kind {
1517        b'c' => AclKind::Column,
1518        b'r' => AclKind::Table,
1519        b's' => AclKind::Sequence,
1520        b'd' => AclKind::Database,
1521        b'f' => AclKind::Function,
1522        b'l' => AclKind::Language,
1523        b'L' => AclKind::LargeObject,
1524        b'n' => AclKind::Schema,
1525        b'p' => AclKind::Parameter,
1526        b't' => AclKind::Tablespace,
1527        b'F' | b'S' => AclKind::Other,
1528        b'T' => AclKind::Type,
1529        other => {
1530            return Err(Error::internal(format!(
1531                "unrecognized object type abbreviation: {}",
1532                char::from(*other)
1533            )));
1534        }
1535    };
1536    let items = acl_default(kind, *owner)
1537        .into_iter()
1538        .map(|(grantee, bits)| Some(Value::text(acl_item(grantee, bits, *owner))))
1539        .collect();
1540    Ok(Value::Array(Box::new(rupg_types::Array::one(items))))
1541}
1542
1543/// `row_security_active(oid)` and `row_security_active(text)`: true when row security applies to the relation for the current user. No relation of rupg has row security, so the result is false. A name of a relation that does not exist is an error.
1544fn row_security_active(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1545    if let Some(Value::Text(name)) = args.first() {
1546        table_name(name, call.session)?;
1547    }
1548    Ok(Value::Bool(false))
1549}
1550
1551pub(crate) fn by_src(src: &str) -> Option<Kernel> {
1552    if matches!(src, "row_security_active" | "row_security_active_name") {
1553        return Some(row_security_active);
1554    }
1555    let kernel: Kernel = match src {
1556        "acldefault_sql" => acldefault,
1557        "aclitem_eq" => aclitem_eq,
1558        "aclcontains" => aclcontains,
1559        "makeaclitem" => makeaclitem,
1560        "hash_aclitem" => hash_aclitem,
1561        "hash_aclitem_extended" => hash_aclitem_extended,
1562        "aclinsert" => aclinsert,
1563        "aclremove" => aclremove,
1564        _ => return privilege_kernel(src),
1565    };
1566    Some(kernel)
1567}
1568
1569/// The kernel of a `has_*_privilege` function or of `pg_has_role`.
1570fn privilege_kernel(src: &str) -> Option<Kernel> {
1571    let family = if src.starts_with("pg_has_role") {
1572        "pg_has_role"
1573    } else {
1574        let end = src.find("_privilege")? + "_privilege".len();
1575        &src[..end]
1576    };
1577    let kernel: Kernel = match family {
1578        "has_table_privilege" => has_table_privilege,
1579        "has_sequence_privilege" => has_sequence_privilege,
1580        "has_column_privilege" => has_column_privilege,
1581        "has_any_column_privilege" => has_any_column_privilege,
1582        "has_database_privilege" => |c, a| has_object_privilege(Owned::Database, c, a),
1583        "has_foreign_data_wrapper_privilege" => {
1584            |c, a| has_object_privilege(Owned::ForeignDataWrapper, c, a)
1585        }
1586        "has_function_privilege" => |c, a| has_object_privilege(Owned::Function, c, a),
1587        "has_language_privilege" => |c, a| has_object_privilege(Owned::Language, c, a),
1588        "has_schema_privilege" => |c, a| has_object_privilege(Owned::Namespace, c, a),
1589        "has_server_privilege" => |c, a| has_object_privilege(Owned::ForeignServer, c, a),
1590        "has_tablespace_privilege" => |c, a| has_object_privilege(Owned::Tablespace, c, a),
1591        "has_type_privilege" => |c, a| has_object_privilege(Owned::Type, c, a),
1592        "has_largeobject_privilege" => has_largeobject_privilege,
1593        "has_parameter_privilege" => has_parameter_privilege,
1594        "pg_has_role" => pg_has_role,
1595        _ => return None,
1596    };
1597    Some(kernel)
1598}