Skip to content

Commit

Permalink
Replacing the two booleans from 'SET ROLE' to a single enum. (#664)
Browse files Browse the repository at this point in the history
This way, we don't rely on the parser to have valid structures for that
statement, as the structure itself is valid.
  • Loading branch information
AugustoFKL committed Oct 10, 2022
1 parent a9939b0 commit 7776726
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 39 deletions.
75 changes: 47 additions & 28 deletions src/ast/mod.rs
Expand Up @@ -11,13 +11,6 @@
// limitations under the License.

//! SQL Abstract Syntax Tree (AST) types
mod data_type;
mod ddl;
pub mod helpers;
mod operator;
mod query;
mod value;

#[cfg(not(feature = "std"))]
use alloc::{
boxed::Box,
Expand All @@ -44,6 +37,13 @@ pub use self::query::{
};
pub use self::value::{DateTimeField, TrimWhereField, Value};

mod data_type;
mod ddl;
pub mod helpers;
mod operator;
mod query;
mod value;

struct DisplaySeparated<'a, T>
where
T: fmt::Display,
Expand Down Expand Up @@ -1227,14 +1227,16 @@ pub enum Statement {
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
Discard { object_type: DiscardObject },
/// SET [ SESSION | LOCAL ] ROLE role_name
/// SET `[ SESSION | LOCAL ]` ROLE role_name. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4].
///
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
/// [2]: https://www.postgresql.org/docs/14/sql-set-role.html
/// [3]: https://dev.mysql.com/doc/refman/8.0/en/set-role.html
/// [4]: https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10004.htm
SetRole {
local: bool,
// SESSION is the default if neither SESSION nor LOCAL appears.
session: bool,
/// Non-ANSI optional identifier to inform if the role is defined inside the current session (`SESSION`) or transaction (`LOCAL`).
context_modifier: ContextModifier,
/// Role name. If NONE is specified, then the current role name is removed.
role_name: Option<Ident>,
},
/// SET <variable>
Expand Down Expand Up @@ -2136,23 +2138,12 @@ impl fmt::Display for Statement {
write!(f, "DISCARD {object_type}", object_type = object_type)?;
Ok(())
}
Statement::SetRole {
local,
session,
Self::SetRole {
context_modifier,
role_name,
} => {
write!(
f,
"SET {local}{session}ROLE",
local = if *local { "LOCAL " } else { "" },
session = if *session { "SESSION " } else { "" },
)?;
if let Some(role_name) = role_name {
write!(f, " {}", role_name)?;
} else {
f.write_str(" NONE")?;
}
Ok(())
let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE"));
write!(f, "SET{context_modifier} ROLE {role_name}")
}
Statement::SetVariable {
local,
Expand Down Expand Up @@ -3283,6 +3274,34 @@ impl fmt::Display for DiscardObject {
}
}

/// Optional context modifier for statements that can be or `LOCAL`, or `SESSION`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ContextModifier {
/// No context defined. Each dialect defines the default in this scenario.
None,
/// `LOCAL` identifier, usually related to transactional states.
Local,
/// `SESSION` identifier
Session,
}

impl fmt::Display for ContextModifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::None => {
write!(f, "")
}
Self::Local => {
write!(f, " LOCAL")
}
Self::Session => {
write!(f, " SESSION")
}
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CreateFunctionUsing {
Expand Down
9 changes: 7 additions & 2 deletions src/parser.rs
Expand Up @@ -4113,14 +4113,19 @@ impl<'a> Parser<'a> {
if let Some(Keyword::HIVEVAR) = modifier {
self.expect_token(&Token::Colon)?;
} else if self.parse_keyword(Keyword::ROLE) {
let context_modifier = match modifier {
Some(keyword) if keyword == Keyword::LOCAL => ContextModifier::Local,
Some(keyword) if keyword == Keyword::SESSION => ContextModifier::Session,
_ => ContextModifier::None,
};

let role_name = if self.parse_keyword(Keyword::NONE) {
None
} else {
Some(self.parse_identifier()?)
};
return Ok(Statement::SetRole {
local: modifier == Some(Keyword::LOCAL),
session: modifier == Some(Keyword::SESSION),
context_modifier,
role_name,
});
}
Expand Down
21 changes: 12 additions & 9 deletions tests/sqlparser_postgres.rs
Expand Up @@ -902,41 +902,44 @@ fn parse_set() {

#[test]
fn parse_set_role() {
let stmt = pg_and_generic().verified_stmt("SET SESSION ROLE NONE");
let query = "SET SESSION ROLE NONE";
let stmt = pg_and_generic().verified_stmt(query);
assert_eq!(
stmt,
Statement::SetRole {
local: false,
session: true,
context_modifier: ContextModifier::Session,
role_name: None,
}
);
assert_eq!(query, stmt.to_string());

let stmt = pg_and_generic().verified_stmt("SET LOCAL ROLE \"rolename\"");
let query = "SET LOCAL ROLE \"rolename\"";
let stmt = pg_and_generic().verified_stmt(query);
assert_eq!(
stmt,
Statement::SetRole {
local: true,
session: false,
context_modifier: ContextModifier::Local,
role_name: Some(Ident {
value: "rolename".to_string(),
quote_style: Some('\"'),
}),
}
);
assert_eq!(query, stmt.to_string());

let stmt = pg_and_generic().verified_stmt("SET ROLE 'rolename'");
let query = "SET ROLE 'rolename'";
let stmt = pg_and_generic().verified_stmt(query);
assert_eq!(
stmt,
Statement::SetRole {
local: false,
session: false,
context_modifier: ContextModifier::None,
role_name: Some(Ident {
value: "rolename".to_string(),
quote_style: Some('\''),
}),
}
);
assert_eq!(query, stmt.to_string());
}

#[test]
Expand Down

0 comments on commit 7776726

Please sign in to comment.