1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! `ALTER DEFAULT PRIVILEGES` — future-object grants.
//!
//! `pg_default_acl` rows. Distinct from per-object `grants`: these say
//! "future objects of type X in schema Y created by role Z get these
//! grants automatically."
use serde::{Deserialize, Serialize};
use crate::identifier::Identifier;
use crate::ir::eq::DiffMacro;
use crate::ir::grant::Grant;
/// One `ALTER DEFAULT PRIVILEGES` rule.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DiffMacro)]
pub struct DefaultPrivilegeRule {
/// `FOR ROLE x` — whose future objects this applies to.
pub target_role: Identifier,
/// `IN SCHEMA y` — scope. `None` = "all schemas owned by `target_role`".
#[diff(via_debug)]
pub schema: Option<Identifier>,
/// Object type this rule applies to.
#[diff(via_debug)]
pub object_type: DefaultPrivObjectType,
/// Grants applied. Canonicalized (sorted, deduped).
#[diff(via_debug)]
pub grants: Vec<Grant>,
}
/// Object-type discriminant for default-privilege rules.
///
/// PG's grouping: `TABLES` covers tables + views + MVs;
/// `FUNCTIONS` covers functions + procedures (alias `ROUTINES` in PG 11+).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DefaultPrivObjectType {
/// `TABLES` — covers tables, views, and materialized views.
Tables,
/// `SEQUENCES`.
Sequences,
/// `FUNCTIONS` — covers procedures too (PG alias `ROUTINES`).
Functions,
/// `TYPES`.
Types,
/// `SCHEMAS` — PG 14+ only.
Schemas,
}
impl DefaultPrivObjectType {
/// PG `pg_default_acl.defaclobjtype` single-char code.
#[must_use]
pub const fn pg_char(self) -> char {
match self {
Self::Tables => 'r',
Self::Sequences => 'S',
Self::Functions => 'f',
Self::Types => 'T',
Self::Schemas => 'n',
}
}
/// Decode from `pg_default_acl.defaclobjtype`.
#[must_use]
pub const fn from_pg_char(c: char) -> Option<Self> {
Some(match c {
'r' => Self::Tables,
'S' => Self::Sequences,
'f' => Self::Functions,
'T' => Self::Types,
'n' => Self::Schemas,
_ => return None,
})
}
/// SQL keyword in `ALTER DEFAULT PRIVILEGES ... GRANT ... ON <KIND> ...`.
#[must_use]
pub const fn sql_keyword(self) -> &'static str {
match self {
Self::Tables => "TABLES",
Self::Sequences => "SEQUENCES",
Self::Functions => "FUNCTIONS",
Self::Types => "TYPES",
Self::Schemas => "SCHEMAS",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pg_char_roundtrips() {
for kind in [
DefaultPrivObjectType::Tables,
DefaultPrivObjectType::Sequences,
DefaultPrivObjectType::Functions,
DefaultPrivObjectType::Types,
DefaultPrivObjectType::Schemas,
] {
assert_eq!(
DefaultPrivObjectType::from_pg_char(kind.pg_char()),
Some(kind)
);
}
}
#[test]
fn from_pg_char_rejects_unknown() {
assert_eq!(DefaultPrivObjectType::from_pg_char('q'), None);
}
}