| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| //! AST types specific to GRANT/REVOKE/ROLE variants of [`Statement`](crate::ast::Statement) |
| //! (commonly referred to as Data Control Language, or DCL) |
| |
| #[cfg(not(feature = "std"))] |
| use alloc::vec::Vec; |
| use core::fmt; |
| |
| #[cfg(feature = "serde")] |
| use serde::{Deserialize, Serialize}; |
| |
| #[cfg(feature = "visitor")] |
| use sqlparser_derive::{Visit, VisitMut}; |
| |
| use super::{display_comma_separated, Expr, Ident, Password, Spanned}; |
| use crate::ast::{display_separated, ObjectName}; |
| use crate::tokenizer::Span; |
| |
| /// An option in `ROLE` statement. |
| /// |
| /// <https://www.postgresql.org/docs/current/sql-createrole.html> |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum RoleOption { |
| BypassRLS(bool), |
| ConnectionLimit(Expr), |
| CreateDB(bool), |
| CreateRole(bool), |
| Inherit(bool), |
| Login(bool), |
| Password(Password), |
| Replication(bool), |
| SuperUser(bool), |
| ValidUntil(Expr), |
| } |
| |
| impl fmt::Display for RoleOption { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| RoleOption::BypassRLS(value) => { |
| write!(f, "{}", if *value { "BYPASSRLS" } else { "NOBYPASSRLS" }) |
| } |
| RoleOption::ConnectionLimit(expr) => { |
| write!(f, "CONNECTION LIMIT {expr}") |
| } |
| RoleOption::CreateDB(value) => { |
| write!(f, "{}", if *value { "CREATEDB" } else { "NOCREATEDB" }) |
| } |
| RoleOption::CreateRole(value) => { |
| write!(f, "{}", if *value { "CREATEROLE" } else { "NOCREATEROLE" }) |
| } |
| RoleOption::Inherit(value) => { |
| write!(f, "{}", if *value { "INHERIT" } else { "NOINHERIT" }) |
| } |
| RoleOption::Login(value) => { |
| write!(f, "{}", if *value { "LOGIN" } else { "NOLOGIN" }) |
| } |
| RoleOption::Password(password) => match password { |
| Password::Password(expr) => write!(f, "PASSWORD {expr}"), |
| Password::NullPassword => write!(f, "PASSWORD NULL"), |
| }, |
| RoleOption::Replication(value) => { |
| write!( |
| f, |
| "{}", |
| if *value { |
| "REPLICATION" |
| } else { |
| "NOREPLICATION" |
| } |
| ) |
| } |
| RoleOption::SuperUser(value) => { |
| write!(f, "{}", if *value { "SUPERUSER" } else { "NOSUPERUSER" }) |
| } |
| RoleOption::ValidUntil(expr) => { |
| write!(f, "VALID UNTIL {expr}") |
| } |
| } |
| } |
| } |
| |
| /// SET config value option: |
| /// * SET `configuration_parameter` { TO | = } { `value` | DEFAULT } |
| /// * SET `configuration_parameter` FROM CURRENT |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum SetConfigValue { |
| Default, |
| FromCurrent, |
| Value(Expr), |
| } |
| |
| /// RESET config option: |
| /// * RESET `configuration_parameter` |
| /// * RESET ALL |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum ResetConfig { |
| ALL, |
| ConfigName(ObjectName), |
| } |
| |
| /// An `ALTER ROLE` (`Statement::AlterRole`) operation |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum AlterRoleOperation { |
| /// Generic |
| RenameRole { |
| role_name: Ident, |
| }, |
| /// MS SQL Server |
| /// <https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-role-transact-sql> |
| AddMember { |
| member_name: Ident, |
| }, |
| DropMember { |
| member_name: Ident, |
| }, |
| /// PostgreSQL |
| /// <https://www.postgresql.org/docs/current/sql-alterrole.html> |
| WithOptions { |
| options: Vec<RoleOption>, |
| }, |
| Set { |
| config_name: ObjectName, |
| config_value: SetConfigValue, |
| in_database: Option<ObjectName>, |
| }, |
| Reset { |
| config_name: ResetConfig, |
| in_database: Option<ObjectName>, |
| }, |
| } |
| |
| impl fmt::Display for AlterRoleOperation { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| AlterRoleOperation::RenameRole { role_name } => { |
| write!(f, "RENAME TO {role_name}") |
| } |
| AlterRoleOperation::AddMember { member_name } => { |
| write!(f, "ADD MEMBER {member_name}") |
| } |
| AlterRoleOperation::DropMember { member_name } => { |
| write!(f, "DROP MEMBER {member_name}") |
| } |
| AlterRoleOperation::WithOptions { options } => { |
| write!(f, "WITH {}", display_separated(options, " ")) |
| } |
| AlterRoleOperation::Set { |
| config_name, |
| config_value, |
| in_database, |
| } => { |
| if let Some(database_name) = in_database { |
| write!(f, "IN DATABASE {database_name} ")?; |
| } |
| |
| match config_value { |
| SetConfigValue::Default => write!(f, "SET {config_name} TO DEFAULT"), |
| SetConfigValue::FromCurrent => write!(f, "SET {config_name} FROM CURRENT"), |
| SetConfigValue::Value(expr) => write!(f, "SET {config_name} TO {expr}"), |
| } |
| } |
| AlterRoleOperation::Reset { |
| config_name, |
| in_database, |
| } => { |
| if let Some(database_name) = in_database { |
| write!(f, "IN DATABASE {database_name} ")?; |
| } |
| |
| match config_name { |
| ResetConfig::ALL => write!(f, "RESET ALL"), |
| ResetConfig::ConfigName(name) => write!(f, "RESET {name}"), |
| } |
| } |
| } |
| } |
| } |
| |
| /// A `USE` (`Statement::Use`) operation |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum Use { |
| Catalog(ObjectName), // e.g. `USE CATALOG foo.bar` |
| Schema(ObjectName), // e.g. `USE SCHEMA foo.bar` |
| Database(ObjectName), // e.g. `USE DATABASE foo.bar` |
| Warehouse(ObjectName), // e.g. `USE WAREHOUSE foo.bar` |
| Role(ObjectName), // e.g. `USE ROLE PUBLIC` |
| SecondaryRoles(SecondaryRoles), // e.g. `USE SECONDARY ROLES ALL` |
| Object(ObjectName), // e.g. `USE foo.bar` |
| Default, // e.g. `USE DEFAULT` |
| } |
| |
| impl fmt::Display for Use { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.write_str("USE ")?; |
| match self { |
| Use::Catalog(name) => write!(f, "CATALOG {name}"), |
| Use::Schema(name) => write!(f, "SCHEMA {name}"), |
| Use::Database(name) => write!(f, "DATABASE {name}"), |
| Use::Warehouse(name) => write!(f, "WAREHOUSE {name}"), |
| Use::Role(name) => write!(f, "ROLE {name}"), |
| Use::SecondaryRoles(secondary_roles) => { |
| write!(f, "SECONDARY ROLES {secondary_roles}") |
| } |
| Use::Object(name) => write!(f, "{name}"), |
| Use::Default => write!(f, "DEFAULT"), |
| } |
| } |
| } |
| |
| /// Snowflake `SECONDARY ROLES` USE variant |
| /// See: <https://docs.snowflake.com/en/sql-reference/sql/use-secondary-roles> |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub enum SecondaryRoles { |
| All, |
| None, |
| List(Vec<Ident>), |
| } |
| |
| impl fmt::Display for SecondaryRoles { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| SecondaryRoles::All => write!(f, "ALL"), |
| SecondaryRoles::None => write!(f, "NONE"), |
| SecondaryRoles::List(roles) => write!(f, "{}", display_comma_separated(roles)), |
| } |
| } |
| } |
| |
| /// CREATE ROLE statement |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.html) |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| pub struct CreateRole { |
| pub names: Vec<ObjectName>, |
| pub if_not_exists: bool, |
| // Postgres |
| pub login: Option<bool>, |
| pub inherit: Option<bool>, |
| pub bypassrls: Option<bool>, |
| pub password: Option<Password>, |
| pub superuser: Option<bool>, |
| pub create_db: Option<bool>, |
| pub create_role: Option<bool>, |
| pub replication: Option<bool>, |
| pub connection_limit: Option<Expr>, |
| pub valid_until: Option<Expr>, |
| pub in_role: Vec<Ident>, |
| pub in_group: Vec<Ident>, |
| pub role: Vec<Ident>, |
| pub user: Vec<Ident>, |
| pub admin: Vec<Ident>, |
| // MSSQL |
| pub authorization_owner: Option<ObjectName>, |
| } |
| |
| impl fmt::Display for CreateRole { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!( |
| f, |
| "CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}", |
| if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" }, |
| names = display_separated(&self.names, ", "), |
| superuser = match self.superuser { |
| Some(true) => " SUPERUSER", |
| Some(false) => " NOSUPERUSER", |
| None => "" |
| }, |
| create_db = match self.create_db { |
| Some(true) => " CREATEDB", |
| Some(false) => " NOCREATEDB", |
| None => "" |
| }, |
| create_role = match self.create_role { |
| Some(true) => " CREATEROLE", |
| Some(false) => " NOCREATEROLE", |
| None => "" |
| }, |
| inherit = match self.inherit { |
| Some(true) => " INHERIT", |
| Some(false) => " NOINHERIT", |
| None => "" |
| }, |
| login = match self.login { |
| Some(true) => " LOGIN", |
| Some(false) => " NOLOGIN", |
| None => "" |
| }, |
| replication = match self.replication { |
| Some(true) => " REPLICATION", |
| Some(false) => " NOREPLICATION", |
| None => "" |
| }, |
| bypassrls = match self.bypassrls { |
| Some(true) => " BYPASSRLS", |
| Some(false) => " NOBYPASSRLS", |
| None => "" |
| } |
| )?; |
| if let Some(limit) = &self.connection_limit { |
| write!(f, " CONNECTION LIMIT {limit}")?; |
| } |
| match &self.password { |
| Some(Password::Password(pass)) => write!(f, " PASSWORD {pass}")?, |
| Some(Password::NullPassword) => write!(f, " PASSWORD NULL")?, |
| None => {} |
| }; |
| if let Some(until) = &self.valid_until { |
| write!(f, " VALID UNTIL {until}")?; |
| } |
| if !self.in_role.is_empty() { |
| write!(f, " IN ROLE {}", display_comma_separated(&self.in_role))?; |
| } |
| if !self.in_group.is_empty() { |
| write!(f, " IN GROUP {}", display_comma_separated(&self.in_group))?; |
| } |
| if !self.role.is_empty() { |
| write!(f, " ROLE {}", display_comma_separated(&self.role))?; |
| } |
| if !self.user.is_empty() { |
| write!(f, " USER {}", display_comma_separated(&self.user))?; |
| } |
| if !self.admin.is_empty() { |
| write!(f, " ADMIN {}", display_comma_separated(&self.admin))?; |
| } |
| if let Some(owner) = &self.authorization_owner { |
| write!(f, " AUTHORIZATION {owner}")?; |
| } |
| Ok(()) |
| } |
| } |
| |
| impl Spanned for CreateRole { |
| fn span(&self) -> Span { |
| Span::empty() |
| } |
| } |