Skip to main content

radixdb_sql/ast/
security.rs

1// Copyright 2026 RadixDB Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9use super::*;
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct CreateSchemaStatement {
13    pub token: Token,
14    pub name: ObjectName,
15}
16
17impl fmt::Display for CreateSchemaStatement {
18    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
19        write!(formatter, "CREATE SCHEMA {}", self.name)
20    }
21}
22
23#[derive(Clone, PartialEq)]
24pub struct CreatePrincipalStatement {
25    pub token: Token,
26    pub name: Identifier,
27    /// Plaintext exists only in the transient parsed statement. Display is
28    /// deliberately redacted and the catalog stores only a verifier.
29    pub password: Option<String>,
30}
31
32impl fmt::Debug for CreatePrincipalStatement {
33    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
34        formatter
35            .debug_struct("CreatePrincipalStatement")
36            .field("token", &self.token)
37            .field("name", &self.name)
38            .field("password", &self.password.as_ref().map(|_| "<redacted>"))
39            .finish()
40    }
41}
42
43impl fmt::Display for CreatePrincipalStatement {
44    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(formatter, "CREATE PRINCIPAL {}", self.name)?;
46        if self.password.is_some() {
47            formatter.write_str(" PASSWORD '<redacted>'")?;
48        }
49        Ok(())
50    }
51}
52
53#[derive(Debug, Clone, PartialEq)]
54pub struct CreateRoleStatement {
55    pub token: Token,
56    pub name: Identifier,
57}
58
59impl fmt::Display for CreateRoleStatement {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        write!(formatter, "CREATE ROLE {}", self.name)
62    }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum SecuritySubjectKindSyntax {
67    Principal,
68    Role,
69}
70
71impl fmt::Display for SecuritySubjectKindSyntax {
72    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73        formatter.write_str(match self {
74            Self::Principal => "PRINCIPAL",
75            Self::Role => "ROLE",
76        })
77    }
78}
79
80#[derive(Clone, PartialEq)]
81pub enum AlterSecuritySubjectActionSyntax {
82    Enable,
83    Disable,
84    RenameTo(Identifier),
85    SetPassword(String),
86    ClearPassword,
87}
88
89impl fmt::Debug for AlterSecuritySubjectActionSyntax {
90    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self {
92            Self::Enable => formatter.write_str("Enable"),
93            Self::Disable => formatter.write_str("Disable"),
94            Self::RenameTo(name) => formatter.debug_tuple("RenameTo").field(name).finish(),
95            Self::SetPassword(_) => formatter
96                .debug_tuple("SetPassword")
97                .field(&"<redacted>")
98                .finish(),
99            Self::ClearPassword => formatter.write_str("ClearPassword"),
100        }
101    }
102}
103
104#[derive(Debug, Clone, PartialEq)]
105pub struct AlterSecuritySubjectStatement {
106    pub token: Token,
107    pub kind: SecuritySubjectKindSyntax,
108    pub name: Identifier,
109    pub action: AlterSecuritySubjectActionSyntax,
110}
111
112impl fmt::Display for AlterSecuritySubjectStatement {
113    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(formatter, "ALTER {} {} ", self.kind, self.name)?;
115        match &self.action {
116            AlterSecuritySubjectActionSyntax::Enable => formatter.write_str("ENABLE"),
117            AlterSecuritySubjectActionSyntax::Disable => formatter.write_str("DISABLE"),
118            AlterSecuritySubjectActionSyntax::RenameTo(name) => {
119                write!(formatter, "RENAME TO {name}")
120            }
121            AlterSecuritySubjectActionSyntax::SetPassword(_) => {
122                formatter.write_str("PASSWORD '<redacted>'")
123            }
124            AlterSecuritySubjectActionSyntax::ClearPassword => formatter.write_str("PASSWORD NULL"),
125        }
126    }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum DropBehaviorSyntax {
131    Restrict,
132    Cascade,
133}
134
135impl fmt::Display for DropBehaviorSyntax {
136    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
137        formatter.write_str(match self {
138            Self::Restrict => "RESTRICT",
139            Self::Cascade => "CASCADE",
140        })
141    }
142}
143
144#[derive(Debug, Clone, PartialEq)]
145pub struct DropSecuritySubjectStatement {
146    pub token: Token,
147    pub kind: SecuritySubjectKindSyntax,
148    pub name: Identifier,
149    pub behavior: DropBehaviorSyntax,
150}
151
152impl fmt::Display for DropSecuritySubjectStatement {
153    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
154        write!(
155            formatter,
156            "DROP {} {} {}",
157            self.kind, self.name, self.behavior
158        )
159    }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163pub enum ObjectPrivilegeSyntax {
164    Connect,
165    Usage,
166    Create,
167    Select,
168    Insert,
169    Update,
170    Delete,
171    Execute,
172    Describe,
173}
174
175impl fmt::Display for ObjectPrivilegeSyntax {
176    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
177        formatter.write_str(match self {
178            Self::Connect => "CONNECT",
179            Self::Usage => "USAGE",
180            Self::Create => "CREATE",
181            Self::Select => "SELECT",
182            Self::Insert => "INSERT",
183            Self::Update => "UPDATE",
184            Self::Delete => "DELETE",
185            Self::Execute => "EXECUTE",
186            Self::Describe => "DESCRIBE",
187        })
188    }
189}
190
191#[derive(Debug, Clone, PartialEq)]
192pub struct PrivilegeSyntax {
193    pub kind: ObjectPrivilegeSyntax,
194    pub columns: Vec<Identifier>,
195}
196
197impl fmt::Display for PrivilegeSyntax {
198    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
199        write!(formatter, "{}", self.kind)?;
200        if !self.columns.is_empty() {
201            formatter.write_str(" (")?;
202            for (index, column) in self.columns.iter().enumerate() {
203                if index > 0 {
204                    formatter.write_str(", ")?;
205                }
206                write!(formatter, "{column}")?;
207            }
208            formatter.write_str(")")?;
209        }
210        Ok(())
211    }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub enum PrivilegeTargetSyntax {
216    Database(ObjectName),
217    Schema(ObjectName),
218    Table(ObjectName),
219    Function(RoutineSignatureSyntax),
220    Procedure(RoutineSignatureSyntax),
221}
222
223impl fmt::Display for PrivilegeTargetSyntax {
224    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
225        match self {
226            Self::Database(name) => write!(formatter, "DATABASE {name}"),
227            Self::Schema(name) => write!(formatter, "SCHEMA {name}"),
228            Self::Table(name) => write!(formatter, "TABLE {name}"),
229            Self::Function(signature) => write!(formatter, "FUNCTION {signature}"),
230            Self::Procedure(signature) => write!(formatter, "PROCEDURE {signature}"),
231        }
232    }
233}
234
235#[derive(Debug, Clone, PartialEq)]
236pub enum GrantSyntax {
237    RoleMembership {
238        role: Identifier,
239        member: Identifier,
240        admin_option: bool,
241    },
242    ObjectPrivileges {
243        privileges: Vec<PrivilegeSyntax>,
244        target: PrivilegeTargetSyntax,
245        grantee: Identifier,
246        grant_option: bool,
247    },
248}
249
250#[derive(Debug, Clone, PartialEq)]
251pub struct GrantStatement {
252    pub token: Token,
253    pub grant: GrantSyntax,
254}
255
256impl fmt::Display for GrantStatement {
257    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
258        formatter.write_str("GRANT ")?;
259        match &self.grant {
260            GrantSyntax::RoleMembership {
261                role,
262                member,
263                admin_option,
264            } => {
265                write!(formatter, "{role} TO {member}")?;
266                if *admin_option {
267                    formatter.write_str(" WITH ADMIN OPTION")?;
268                }
269                Ok(())
270            }
271            GrantSyntax::ObjectPrivileges {
272                privileges,
273                target,
274                grantee,
275                grant_option,
276            } => {
277                write_privileges(formatter, privileges)?;
278                write!(formatter, " ON {target} TO {grantee}")?;
279                if *grant_option {
280                    formatter.write_str(" WITH GRANT OPTION")?;
281                }
282                Ok(())
283            }
284        }
285    }
286}
287
288#[derive(Debug, Clone, PartialEq)]
289pub enum RevokeSyntax {
290    RoleMembership {
291        role: Identifier,
292        member: Identifier,
293        admin_option_only: bool,
294    },
295    ObjectPrivileges {
296        privileges: Vec<PrivilegeSyntax>,
297        target: PrivilegeTargetSyntax,
298        grantee: Identifier,
299        grant_option_only: bool,
300    },
301}
302
303#[derive(Debug, Clone, PartialEq)]
304pub struct RevokeStatement {
305    pub token: Token,
306    pub revoke: RevokeSyntax,
307    pub behavior: DropBehaviorSyntax,
308}
309
310impl fmt::Display for RevokeStatement {
311    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
312        formatter.write_str("REVOKE ")?;
313        match &self.revoke {
314            RevokeSyntax::RoleMembership {
315                role,
316                member,
317                admin_option_only,
318            } => {
319                if *admin_option_only {
320                    formatter.write_str("ADMIN OPTION FOR ")?;
321                }
322                write!(formatter, "{role} FROM {member}")
323            }
324            RevokeSyntax::ObjectPrivileges {
325                privileges,
326                target,
327                grantee,
328                grant_option_only,
329            } => {
330                if *grant_option_only {
331                    formatter.write_str("GRANT OPTION FOR ")?;
332                }
333                write_privileges(formatter, privileges)?;
334                write!(formatter, " ON {target} FROM {grantee}")
335            }
336        }?;
337        write!(formatter, " {}", self.behavior)
338    }
339}
340
341#[derive(Debug, Clone, PartialEq)]
342pub enum OwnershipTargetSyntax {
343    Table(ObjectName),
344    Function(RoutineSignatureSyntax),
345    Procedure(RoutineSignatureSyntax),
346}
347
348impl fmt::Display for OwnershipTargetSyntax {
349    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
350        match self {
351            Self::Table(name) => write!(formatter, "TABLE {name}"),
352            Self::Function(signature) => write!(formatter, "FUNCTION {signature}"),
353            Self::Procedure(signature) => write!(formatter, "PROCEDURE {signature}"),
354        }
355    }
356}
357
358#[derive(Debug, Clone, PartialEq)]
359pub struct AlterOwnerStatement {
360    pub token: Token,
361    pub target: OwnershipTargetSyntax,
362    pub owner: Identifier,
363}
364
365impl fmt::Display for AlterOwnerStatement {
366    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
367        write!(formatter, "ALTER {} OWNER TO {}", self.target, self.owner)
368    }
369}
370
371fn write_privileges(
372    formatter: &mut fmt::Formatter<'_>,
373    privileges: &[PrivilegeSyntax],
374) -> fmt::Result {
375    for (index, privilege) in privileges.iter().enumerate() {
376        if index > 0 {
377            formatter.write_str(", ")?;
378        }
379        write!(formatter, "{privilege}")?;
380    }
381    Ok(())
382}