Skip to main content

qusql_parse/
create_role.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12use crate::{
13    Identifier, SString, Span, Spanned,
14    create_option::CreateOption,
15    keywords::Keyword,
16    lexer::Token,
17    parser::{ParseError, Parser},
18};
19use alloc::vec::Vec;
20
21/// Role option for CREATE ROLE / ALTER ROLE
22#[derive(Clone, Debug)]
23pub enum RoleOption<'a> {
24    SuperUser(Span),
25    NoSuperUser(Span),
26    CreateDb(Span),
27    NoCreateDb(Span),
28    CreateRole(Span),
29    NoCreateRole(Span),
30    Inherit(Span),
31    NoInherit(Span),
32    Login(Span),
33    NoLogin(Span),
34    Replication(Span),
35    NoReplication(Span),
36    BypassRls(Span),
37    NoBypassRls(Span),
38    /// CONNECTION LIMIT value
39    ConnectionLimit {
40        connection_span: Span,
41        limit_span: Span,
42        value: i64,
43        value_span: Span,
44    },
45    /// ENCRYPTED PASSWORD 'password'
46    EncryptedPassword {
47        encrypted_span: Span,
48        password_span: Span,
49        password: SString<'a>,
50    },
51    /// PASSWORD 'password'
52    Password {
53        password_span: Span,
54        password: SString<'a>,
55    },
56    /// PASSWORD NULL
57    PasswordNull(Span),
58    /// VALID UNTIL 'timestamp'
59    ValidUntil {
60        valid_span: Span,
61        until_span: Span,
62        timestamp: SString<'a>,
63    },
64    /// SYSID value
65    Sysid {
66        sysid_span: Span,
67        value: i64,
68        value_span: Span,
69    },
70}
71
72impl<'a> Spanned for RoleOption<'a> {
73    fn span(&self) -> Span {
74        match self {
75            RoleOption::SuperUser(s) => s.span(),
76            RoleOption::NoSuperUser(s) => s.span(),
77            RoleOption::CreateDb(s) => s.span(),
78            RoleOption::NoCreateDb(s) => s.span(),
79            RoleOption::CreateRole(s) => s.span(),
80            RoleOption::NoCreateRole(s) => s.span(),
81            RoleOption::Inherit(s) => s.span(),
82            RoleOption::NoInherit(s) => s.span(),
83            RoleOption::Login(s) => s.span(),
84            RoleOption::NoLogin(s) => s.span(),
85            RoleOption::Replication(s) => s.span(),
86            RoleOption::NoReplication(s) => s.span(),
87            RoleOption::BypassRls(s) => s.span(),
88            RoleOption::NoBypassRls(s) => s.span(),
89            RoleOption::ConnectionLimit {
90                connection_span,
91                value_span,
92                ..
93            } => connection_span.join_span(value_span),
94            RoleOption::EncryptedPassword {
95                encrypted_span,
96                password,
97                ..
98            } => encrypted_span.join_span(password),
99            RoleOption::Password {
100                password_span,
101                password,
102            } => password_span.join_span(password),
103            RoleOption::PasswordNull(s) => s.span(),
104            RoleOption::ValidUntil {
105                valid_span,
106                timestamp,
107                ..
108            } => valid_span.join_span(timestamp),
109            RoleOption::Sysid {
110                sysid_span,
111                value_span,
112                ..
113            } => sysid_span.join_span(value_span),
114        }
115    }
116}
117
118/// Parse a single role option for CREATE/ALTER ROLE
119pub(crate) fn parse_role_option<'a>(
120    parser: &mut Parser<'a, '_>,
121) -> Result<Option<RoleOption<'a>>, ParseError> {
122    Ok(match &parser.token {
123        Token::Ident(_, Keyword::SUPERUSER) => {
124            let span = parser.consume_keyword(Keyword::SUPERUSER)?;
125            Some(RoleOption::SuperUser(span))
126        }
127        Token::Ident(_, Keyword::NOSUPERUSER) => {
128            let span = parser.consume_keyword(Keyword::NOSUPERUSER)?;
129            Some(RoleOption::NoSuperUser(span))
130        }
131        Token::Ident(_, Keyword::CREATEDB) => {
132            let span = parser.consume_keyword(Keyword::CREATEDB)?;
133            Some(RoleOption::CreateDb(span))
134        }
135        Token::Ident(_, Keyword::NOCREATEDB) => {
136            let span = parser.consume_keyword(Keyword::NOCREATEDB)?;
137            Some(RoleOption::NoCreateDb(span))
138        }
139        Token::Ident(_, Keyword::CREATEROLE) => {
140            let span = parser.consume_keyword(Keyword::CREATEROLE)?;
141            Some(RoleOption::CreateRole(span))
142        }
143        Token::Ident(_, Keyword::NOCREATEROLE) => {
144            let span = parser.consume_keyword(Keyword::NOCREATEROLE)?;
145            Some(RoleOption::NoCreateRole(span))
146        }
147        Token::Ident(_, Keyword::INHERIT) => {
148            let span = parser.consume_keyword(Keyword::INHERIT)?;
149            Some(RoleOption::Inherit(span))
150        }
151        Token::Ident(_, Keyword::NOINHERIT) => {
152            let span = parser.consume_keyword(Keyword::NOINHERIT)?;
153            Some(RoleOption::NoInherit(span))
154        }
155        Token::Ident(_, Keyword::LOGIN) => {
156            let span = parser.consume_keyword(Keyword::LOGIN)?;
157            Some(RoleOption::Login(span))
158        }
159        Token::Ident(_, Keyword::NOLOGIN) => {
160            let span = parser.consume_keyword(Keyword::NOLOGIN)?;
161            Some(RoleOption::NoLogin(span))
162        }
163        Token::Ident(_, Keyword::REPLICATION) => {
164            let span = parser.consume_keyword(Keyword::REPLICATION)?;
165            Some(RoleOption::Replication(span))
166        }
167        Token::Ident(_, Keyword::NOREPLICATION) => {
168            let span = parser.consume_keyword(Keyword::NOREPLICATION)?;
169            Some(RoleOption::NoReplication(span))
170        }
171        Token::Ident(_, Keyword::BYPASSRLS) => {
172            let span = parser.consume_keyword(Keyword::BYPASSRLS)?;
173            Some(RoleOption::BypassRls(span))
174        }
175        Token::Ident(_, Keyword::NOBYPASSRLS) => {
176            let span = parser.consume_keyword(Keyword::NOBYPASSRLS)?;
177            Some(RoleOption::NoBypassRls(span))
178        }
179        Token::Ident(_, Keyword::CONNECTION) => {
180            let connection_span = parser.consume_keyword(Keyword::CONNECTION)?;
181            let limit_span = parser.consume_keyword(Keyword::LIMIT)?;
182            let (value, value_span) = parser.consume_signed_int::<i64>()?;
183            Some(RoleOption::ConnectionLimit {
184                connection_span,
185                limit_span,
186                value,
187                value_span,
188            })
189        }
190        Token::Ident(_, Keyword::ENCRYPTED) => {
191            let encrypted_span = parser.consume_keyword(Keyword::ENCRYPTED)?;
192            let password_span = parser.consume_keyword(Keyword::PASSWORD)?;
193            let password = parser.consume_string()?;
194            Some(RoleOption::EncryptedPassword {
195                encrypted_span,
196                password_span,
197                password,
198            })
199        }
200        Token::Ident(_, Keyword::PASSWORD) => {
201            let password_span = parser.consume_keyword(Keyword::PASSWORD)?;
202            if parser.skip_keyword(Keyword::NULL).is_some() {
203                Some(RoleOption::PasswordNull(password_span))
204            } else {
205                let password = parser.consume_string()?;
206                Some(RoleOption::Password {
207                    password_span,
208                    password,
209                })
210            }
211        }
212        Token::Ident(_, Keyword::VALID) => {
213            let valid_span = parser.consume_keyword(Keyword::VALID)?;
214            let until_span = parser.consume_keyword(Keyword::UNTIL)?;
215            let timestamp = parser.consume_string()?;
216            Some(RoleOption::ValidUntil {
217                valid_span,
218                until_span,
219                timestamp,
220            })
221        }
222        Token::Ident(_, Keyword::SYSID) => {
223            let sysid_span = parser.consume_keyword(Keyword::SYSID)?;
224            let (value, value_span) = parser.consume_signed_int::<i64>()?;
225            Some(RoleOption::Sysid {
226                sysid_span,
227                value,
228                value_span,
229            })
230        }
231        _ => None,
232    })
233}
234
235/// Role membership type for CREATE ROLE
236#[derive(Clone, Debug)]
237pub enum RoleMembershipType {
238    User(Span),
239    Role(Span),
240    Admin(Span),
241    InRole(Span),
242}
243
244impl Spanned for RoleMembershipType {
245    fn span(&self) -> Span {
246        match self {
247            RoleMembershipType::User(s) => s.span(),
248            RoleMembershipType::Role(s) => s.span(),
249            RoleMembershipType::Admin(s) => s.span(),
250            RoleMembershipType::InRole(s) => s.span(),
251        }
252    }
253}
254
255/// Role membership clause in CREATE ROLE
256#[derive(Clone, Debug)]
257pub struct RoleMembership<'a> {
258    pub type_: RoleMembershipType,
259    pub roles: Vec<Identifier<'a>>,
260}
261
262impl<'a> Spanned for RoleMembership<'a> {
263    fn span(&self) -> Span {
264        self.type_.join_span(&self.roles)
265    }
266}
267
268/// CREATE ROLE statement (PostgreSQL)
269#[derive(Clone, Debug)]
270pub struct CreateRole<'a> {
271    /// Span of "CREATE"
272    pub create_span: Span,
273    /// Span of "ROLE"
274    pub role_span: Span,
275    /// Span of "IF NOT EXISTS" if specified
276    pub if_not_exists: Option<Span>,
277    /// Names of the roles to create
278    pub role_names: Vec<Identifier<'a>>,
279    /// Optional WITH keyword span
280    pub with_span: Option<Span>,
281    /// Role options
282    pub options: Vec<RoleOption<'a>>,
283    /// Role membership clauses (USER, ROLE, ADMIN, IN ROLE)
284    pub memberships: Vec<RoleMembership<'a>>,
285}
286
287impl<'a> Spanned for CreateRole<'a> {
288    fn span(&self) -> Span {
289        self.create_span
290            .join_span(&self.role_span)
291            .join_span(&self.if_not_exists)
292            .join_span(&self.role_names)
293            .join_span(&self.with_span)
294            .join_span(&self.options)
295            .join_span(&self.memberships)
296    }
297}
298
299pub(crate) fn parse_create_role<'a>(
300    parser: &mut Parser<'a, '_>,
301    create_span: Span,
302    create_options: Vec<CreateOption<'a>>,
303) -> Result<CreateRole<'a>, ParseError> {
304    let role_span = parser.consume_keyword(Keyword::ROLE)?;
305    parser.postgres_only(&role_span);
306
307    for option in create_options {
308        parser.err("Not supported for CREATE ROLE", &option.span());
309    }
310
311    let if_not_exists = if let Some(if_span) = parser.skip_keyword(Keyword::IF) {
312        Some(
313            parser
314                .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
315                .join_span(&if_span),
316        )
317    } else {
318        None
319    };
320
321    // Parse role names (comma-separated list)
322    let mut role_names = Vec::new();
323    loop {
324        role_names.push(parser.consume_plain_identifier_unreserved()?);
325        if parser.skip_token(Token::Comma).is_none() {
326            break;
327        }
328    }
329
330    // Optional WITH keyword
331    let with_span = parser.skip_keyword(Keyword::WITH);
332
333    // Parse options and memberships
334    let mut options = Vec::new();
335    let mut memberships = Vec::new();
336
337    loop {
338        match &parser.token {
339            Token::Ident(_, Keyword::USER) => {
340                let user_span = parser.consume_keyword(Keyword::USER)?;
341                let mut roles = Vec::new();
342                loop {
343                    roles.push(parser.consume_plain_identifier_unreserved()?);
344                    if parser.skip_token(Token::Comma).is_none() {
345                        break;
346                    }
347                }
348                memberships.push(RoleMembership {
349                    type_: RoleMembershipType::User(user_span),
350                    roles,
351                });
352            }
353            Token::Ident(_, Keyword::ROLE) => {
354                let role_span = parser.consume_keyword(Keyword::ROLE)?;
355                let mut roles = Vec::new();
356                loop {
357                    roles.push(parser.consume_plain_identifier_unreserved()?);
358                    if parser.skip_token(Token::Comma).is_none() {
359                        break;
360                    }
361                }
362                memberships.push(RoleMembership {
363                    type_: RoleMembershipType::Role(role_span),
364                    roles,
365                });
366            }
367            Token::Ident(_, Keyword::ADMIN) => {
368                let admin_span = parser.consume_keyword(Keyword::ADMIN)?;
369                let mut roles = Vec::new();
370                loop {
371                    roles.push(parser.consume_plain_identifier_unreserved()?);
372                    if parser.skip_token(Token::Comma).is_none() {
373                        break;
374                    }
375                }
376                memberships.push(RoleMembership {
377                    type_: RoleMembershipType::Admin(admin_span),
378                    roles,
379                });
380            }
381            Token::Ident(_, Keyword::IN) => {
382                let in_role_span = parser.consume_keywords(&[Keyword::IN, Keyword::ROLE])?;
383                let mut roles = Vec::new();
384                loop {
385                    roles.push(parser.consume_plain_identifier_unreserved()?);
386                    if parser.skip_token(Token::Comma).is_none() {
387                        break;
388                    }
389                }
390                memberships.push(RoleMembership {
391                    type_: RoleMembershipType::InRole(in_role_span),
392                    roles,
393                });
394            }
395            _ => {
396                // Not a membership clause, try parsing as an option
397                if let Some(opt) = parse_role_option(parser)? {
398                    options.push(opt);
399                    continue;
400                } else {
401                    // Neither membership nor option, break the loop
402                    break;
403                }
404            }
405        }
406    }
407
408    Ok(CreateRole {
409        create_span,
410        role_span,
411        if_not_exists,
412        role_names,
413        with_span,
414        options,
415        memberships,
416    })
417}