mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +00:00
Support CREATE ROLE and DROP ROLE (#598)
* Parse GRANT ROLE and DROP ROLE * Gate create role on dialect * cargo fmt * clippy * no-std * clippy again
This commit is contained in:
parent
604f755a59
commit
91087fcba0
7 changed files with 543 additions and 42 deletions
114
src/ast/mod.rs
114
src/ast/mod.rs
|
@ -954,6 +954,13 @@ impl fmt::Display for CommentObject {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Password {
|
||||
Password(Expr),
|
||||
NullPassword,
|
||||
}
|
||||
|
||||
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -1109,6 +1116,27 @@ pub enum Statement {
|
|||
unique: bool,
|
||||
if_not_exists: bool,
|
||||
},
|
||||
/// CREATE ROLE
|
||||
CreateRole {
|
||||
names: Vec<ObjectName>,
|
||||
if_not_exists: bool,
|
||||
// Postgres
|
||||
login: Option<bool>,
|
||||
inherit: Option<bool>,
|
||||
bypassrls: Option<bool>,
|
||||
password: Option<Password>,
|
||||
superuser: Option<bool>,
|
||||
create_db: Option<bool>,
|
||||
create_role: Option<bool>,
|
||||
replication: Option<bool>,
|
||||
connection_limit: Option<Expr>,
|
||||
valid_until: Option<Expr>,
|
||||
in_role: Vec<Ident>,
|
||||
role: Vec<Ident>,
|
||||
admin: Vec<Ident>,
|
||||
// MSSQL
|
||||
authorization_owner: Option<ObjectName>,
|
||||
},
|
||||
/// ALTER TABLE
|
||||
AlterTable {
|
||||
/// Table name
|
||||
|
@ -1963,6 +1991,90 @@ impl fmt::Display for Statement {
|
|||
table_name = table_name,
|
||||
columns = display_separated(columns, ",")
|
||||
),
|
||||
Statement::CreateRole {
|
||||
names,
|
||||
if_not_exists,
|
||||
inherit,
|
||||
login,
|
||||
bypassrls,
|
||||
password,
|
||||
create_db,
|
||||
create_role,
|
||||
superuser,
|
||||
replication,
|
||||
connection_limit,
|
||||
valid_until,
|
||||
in_role,
|
||||
role,
|
||||
admin,
|
||||
authorization_owner,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}",
|
||||
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
|
||||
names = display_separated(names, ", "),
|
||||
superuser = match *superuser {
|
||||
Some(true) => " SUPERUSER",
|
||||
Some(false) => " NOSUPERUSER",
|
||||
None => ""
|
||||
},
|
||||
create_db = match *create_db {
|
||||
Some(true) => " CREATEDB",
|
||||
Some(false) => " NOCREATEDB",
|
||||
None => ""
|
||||
},
|
||||
create_role = match *create_role {
|
||||
Some(true) => " CREATEROLE",
|
||||
Some(false) => " NOCREATEROLE",
|
||||
None => ""
|
||||
},
|
||||
inherit = match *inherit {
|
||||
Some(true) => " INHERIT",
|
||||
Some(false) => " NOINHERIT",
|
||||
None => ""
|
||||
},
|
||||
login = match *login {
|
||||
Some(true) => " LOGIN",
|
||||
Some(false) => " NOLOGIN",
|
||||
None => ""
|
||||
},
|
||||
replication = match *replication {
|
||||
Some(true) => " REPLICATION",
|
||||
Some(false) => " NOREPLICATION",
|
||||
None => ""
|
||||
},
|
||||
bypassrls = match *bypassrls {
|
||||
Some(true) => " BYPASSRLS",
|
||||
Some(false) => " NOBYPASSRLS",
|
||||
None => ""
|
||||
}
|
||||
)?;
|
||||
if let Some(limit) = connection_limit {
|
||||
write!(f, " CONNECTION LIMIT {}", limit)?;
|
||||
}
|
||||
match password {
|
||||
Some(Password::Password(pass)) => write!(f, " PASSWORD {}", pass),
|
||||
Some(Password::NullPassword) => write!(f, " PASSWORD NULL"),
|
||||
None => Ok(()),
|
||||
}?;
|
||||
if let Some(until) = valid_until {
|
||||
write!(f, " VALID UNTIL {}", until)?;
|
||||
}
|
||||
if !in_role.is_empty() {
|
||||
write!(f, " IN ROLE {}", display_comma_separated(in_role))?;
|
||||
}
|
||||
if !role.is_empty() {
|
||||
write!(f, " ROLE {}", display_comma_separated(role))?;
|
||||
}
|
||||
if !admin.is_empty() {
|
||||
write!(f, " ADMIN {}", display_comma_separated(admin))?;
|
||||
}
|
||||
if let Some(owner) = authorization_owner {
|
||||
write!(f, " AUTHORIZATION {}", owner)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Statement::AlterTable { name, operation } => {
|
||||
write!(f, "ALTER TABLE {} {}", name, operation)
|
||||
}
|
||||
|
@ -2701,6 +2813,7 @@ pub enum ObjectType {
|
|||
View,
|
||||
Index,
|
||||
Schema,
|
||||
Role,
|
||||
}
|
||||
|
||||
impl fmt::Display for ObjectType {
|
||||
|
@ -2710,6 +2823,7 @@ impl fmt::Display for ObjectType {
|
|||
ObjectType::View => "VIEW",
|
||||
ObjectType::Index => "INDEX",
|
||||
ObjectType::Schema => "SCHEMA",
|
||||
ObjectType::Role => "ROLE",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ define_keywords!(
|
|||
ABSOLUTE,
|
||||
ACTION,
|
||||
ADD,
|
||||
ADMIN,
|
||||
ALL,
|
||||
ALLOCATE,
|
||||
ALTER,
|
||||
|
@ -105,6 +106,7 @@ define_keywords!(
|
|||
BOOLEAN,
|
||||
BOTH,
|
||||
BY,
|
||||
BYPASSRLS,
|
||||
BYTEA,
|
||||
CACHE,
|
||||
CALL,
|
||||
|
@ -152,6 +154,8 @@ define_keywords!(
|
|||
COVAR_POP,
|
||||
COVAR_SAMP,
|
||||
CREATE,
|
||||
CREATEDB,
|
||||
CREATEROLE,
|
||||
CROSS,
|
||||
CSV,
|
||||
CUBE,
|
||||
|
@ -272,6 +276,7 @@ define_keywords!(
|
|||
IN,
|
||||
INDEX,
|
||||
INDICATOR,
|
||||
INHERIT,
|
||||
INNER,
|
||||
INOUT,
|
||||
INPUTFORMAT,
|
||||
|
@ -313,6 +318,7 @@ define_keywords!(
|
|||
LOCALTIME,
|
||||
LOCALTIMESTAMP,
|
||||
LOCATION,
|
||||
LOGIN,
|
||||
LOWER,
|
||||
MANAGEDLOCATION,
|
||||
MATCH,
|
||||
|
@ -342,9 +348,16 @@ define_keywords!(
|
|||
NEW,
|
||||
NEXT,
|
||||
NO,
|
||||
NOBYPASSRLS,
|
||||
NOCREATEDB,
|
||||
NOCREATEROLE,
|
||||
NOINHERIT,
|
||||
NOLOGIN,
|
||||
NONE,
|
||||
NOREPLICATION,
|
||||
NORMALIZE,
|
||||
NOSCAN,
|
||||
NOSUPERUSER,
|
||||
NOT,
|
||||
NTH_VALUE,
|
||||
NTILE,
|
||||
|
@ -380,6 +393,7 @@ define_keywords!(
|
|||
PARTITION,
|
||||
PARTITIONED,
|
||||
PARTITIONS,
|
||||
PASSWORD,
|
||||
PERCENT,
|
||||
PERCENTILE_CONT,
|
||||
PERCENTILE_DISC,
|
||||
|
@ -432,6 +446,7 @@ define_keywords!(
|
|||
REPAIR,
|
||||
REPEATABLE,
|
||||
REPLACE,
|
||||
REPLICATION,
|
||||
RESTRICT,
|
||||
RESULT,
|
||||
RETURN,
|
||||
|
@ -492,6 +507,7 @@ define_keywords!(
|
|||
SUCCEEDS,
|
||||
SUM,
|
||||
SUPER,
|
||||
SUPERUSER,
|
||||
SYMMETRIC,
|
||||
SYNC,
|
||||
SYSTEM,
|
||||
|
@ -538,6 +554,7 @@ define_keywords!(
|
|||
UNLOGGED,
|
||||
UNNEST,
|
||||
UNSIGNED,
|
||||
UNTIL,
|
||||
UPDATE,
|
||||
UPPER,
|
||||
USAGE,
|
||||
|
@ -545,6 +562,7 @@ define_keywords!(
|
|||
USER,
|
||||
USING,
|
||||
UUID,
|
||||
VALID,
|
||||
VALUE,
|
||||
VALUES,
|
||||
VALUE_OF,
|
||||
|
|
213
src/parser.rs
213
src/parser.rs
|
@ -1866,6 +1866,8 @@ impl<'a> Parser<'a> {
|
|||
self.parse_create_database()
|
||||
} else if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::FUNCTION) {
|
||||
self.parse_create_function(temporary)
|
||||
} else if self.parse_keyword(Keyword::ROLE) {
|
||||
self.parse_create_role()
|
||||
} else {
|
||||
self.expected("an object type after CREATE", self.peek_token())
|
||||
}
|
||||
|
@ -2056,6 +2058,207 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn parse_create_role(&mut self) -> Result<Statement, ParserError> {
|
||||
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
|
||||
let names = self.parse_comma_separated(Parser::parse_object_name)?;
|
||||
|
||||
let _ = self.parse_keyword(Keyword::WITH);
|
||||
|
||||
let optional_keywords = if dialect_of!(self is MsSqlDialect) {
|
||||
vec![Keyword::AUTHORIZATION]
|
||||
} else if dialect_of!(self is PostgreSqlDialect) {
|
||||
vec![
|
||||
Keyword::LOGIN,
|
||||
Keyword::NOLOGIN,
|
||||
Keyword::INHERIT,
|
||||
Keyword::NOINHERIT,
|
||||
Keyword::BYPASSRLS,
|
||||
Keyword::NOBYPASSRLS,
|
||||
Keyword::PASSWORD,
|
||||
Keyword::CREATEDB,
|
||||
Keyword::NOCREATEDB,
|
||||
Keyword::CREATEROLE,
|
||||
Keyword::NOCREATEROLE,
|
||||
Keyword::SUPERUSER,
|
||||
Keyword::NOSUPERUSER,
|
||||
Keyword::REPLICATION,
|
||||
Keyword::NOREPLICATION,
|
||||
Keyword::CONNECTION,
|
||||
Keyword::VALID,
|
||||
Keyword::IN,
|
||||
Keyword::ROLE,
|
||||
Keyword::ADMIN,
|
||||
Keyword::USER,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// MSSQL
|
||||
let mut authorization_owner = None;
|
||||
// Postgres
|
||||
let mut login = None;
|
||||
let mut inherit = None;
|
||||
let mut bypassrls = None;
|
||||
let mut password = None;
|
||||
let mut create_db = None;
|
||||
let mut create_role = None;
|
||||
let mut superuser = None;
|
||||
let mut replication = None;
|
||||
let mut connection_limit = None;
|
||||
let mut valid_until = None;
|
||||
let mut in_role = vec![];
|
||||
let mut roles = vec![];
|
||||
let mut admin = vec![];
|
||||
|
||||
while let Some(keyword) = self.parse_one_of_keywords(&optional_keywords) {
|
||||
match keyword {
|
||||
Keyword::AUTHORIZATION => {
|
||||
if authorization_owner.is_some() {
|
||||
parser_err!("Found multiple AUTHORIZATION")
|
||||
} else {
|
||||
authorization_owner = Some(self.parse_object_name()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::LOGIN | Keyword::NOLOGIN => {
|
||||
if login.is_some() {
|
||||
parser_err!("Found multiple LOGIN or NOLOGIN")
|
||||
} else {
|
||||
login = Some(keyword == Keyword::LOGIN);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::INHERIT | Keyword::NOINHERIT => {
|
||||
if inherit.is_some() {
|
||||
parser_err!("Found multiple INHERIT or NOINHERIT")
|
||||
} else {
|
||||
inherit = Some(keyword == Keyword::INHERIT);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::BYPASSRLS | Keyword::NOBYPASSRLS => {
|
||||
if bypassrls.is_some() {
|
||||
parser_err!("Found multiple BYPASSRLS or NOBYPASSRLS")
|
||||
} else {
|
||||
bypassrls = Some(keyword == Keyword::BYPASSRLS);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::CREATEDB | Keyword::NOCREATEDB => {
|
||||
if create_db.is_some() {
|
||||
parser_err!("Found multiple CREATEDB or NOCREATEDB")
|
||||
} else {
|
||||
create_db = Some(keyword == Keyword::CREATEDB);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::CREATEROLE | Keyword::NOCREATEROLE => {
|
||||
if create_role.is_some() {
|
||||
parser_err!("Found multiple CREATEROLE or NOCREATEROLE")
|
||||
} else {
|
||||
create_role = Some(keyword == Keyword::CREATEROLE);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::SUPERUSER | Keyword::NOSUPERUSER => {
|
||||
if superuser.is_some() {
|
||||
parser_err!("Found multiple SUPERUSER or NOSUPERUSER")
|
||||
} else {
|
||||
superuser = Some(keyword == Keyword::SUPERUSER);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::REPLICATION | Keyword::NOREPLICATION => {
|
||||
if replication.is_some() {
|
||||
parser_err!("Found multiple REPLICATION or NOREPLICATION")
|
||||
} else {
|
||||
replication = Some(keyword == Keyword::REPLICATION);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::PASSWORD => {
|
||||
if password.is_some() {
|
||||
parser_err!("Found multiple PASSWORD")
|
||||
} else {
|
||||
password = if self.parse_keyword(Keyword::NULL) {
|
||||
Some(Password::NullPassword)
|
||||
} else {
|
||||
Some(Password::Password(Expr::Value(self.parse_value()?)))
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::CONNECTION => {
|
||||
self.expect_keyword(Keyword::LIMIT)?;
|
||||
if connection_limit.is_some() {
|
||||
parser_err!("Found multiple CONNECTION LIMIT")
|
||||
} else {
|
||||
connection_limit = Some(Expr::Value(self.parse_number_value()?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::VALID => {
|
||||
self.expect_keyword(Keyword::UNTIL)?;
|
||||
if valid_until.is_some() {
|
||||
parser_err!("Found multiple VALID UNTIL")
|
||||
} else {
|
||||
valid_until = Some(Expr::Value(self.parse_value()?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::IN => {
|
||||
if self.parse_keyword(Keyword::ROLE) || self.parse_keyword(Keyword::GROUP) {
|
||||
if !in_role.is_empty() {
|
||||
parser_err!("Found multiple IN ROLE or IN GROUP")
|
||||
} else {
|
||||
in_role = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
self.expected("ROLE or GROUP after IN", self.peek_token())
|
||||
}
|
||||
}
|
||||
Keyword::ROLE | Keyword::USER => {
|
||||
if !roles.is_empty() {
|
||||
parser_err!("Found multiple ROLE or USER")
|
||||
} else {
|
||||
roles = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Keyword::ADMIN => {
|
||||
if !admin.is_empty() {
|
||||
parser_err!("Found multiple ADMIN")
|
||||
} else {
|
||||
admin = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}?
|
||||
}
|
||||
|
||||
Ok(Statement::CreateRole {
|
||||
names,
|
||||
if_not_exists,
|
||||
login,
|
||||
inherit,
|
||||
bypassrls,
|
||||
password,
|
||||
create_db,
|
||||
create_role,
|
||||
replication,
|
||||
superuser,
|
||||
connection_limit,
|
||||
valid_until,
|
||||
in_role,
|
||||
role: roles,
|
||||
admin,
|
||||
authorization_owner,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_drop(&mut self) -> Result<Statement, ParserError> {
|
||||
let object_type = if self.parse_keyword(Keyword::TABLE) {
|
||||
ObjectType::Table
|
||||
|
@ -2063,10 +2266,15 @@ impl<'a> Parser<'a> {
|
|||
ObjectType::View
|
||||
} else if self.parse_keyword(Keyword::INDEX) {
|
||||
ObjectType::Index
|
||||
} else if self.parse_keyword(Keyword::ROLE) {
|
||||
ObjectType::Role
|
||||
} else if self.parse_keyword(Keyword::SCHEMA) {
|
||||
ObjectType::Schema
|
||||
} else {
|
||||
return self.expected("TABLE, VIEW, INDEX or SCHEMA after DROP", self.peek_token());
|
||||
return self.expected(
|
||||
"TABLE, VIEW, INDEX, ROLE, or SCHEMA after DROP",
|
||||
self.peek_token(),
|
||||
);
|
||||
};
|
||||
// Many dialects support the non standard `IF EXISTS` clause and allow
|
||||
// specifying multiple objects to delete in a single statement
|
||||
|
@ -2078,6 +2286,9 @@ impl<'a> Parser<'a> {
|
|||
if cascade && restrict {
|
||||
return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP");
|
||||
}
|
||||
if object_type == ObjectType::Role && (cascade || restrict || purge) {
|
||||
return parser_err!("Cannot specify CASCADE, RESTRICT, or PURGE in DROP ROLE");
|
||||
}
|
||||
Ok(Statement::Drop {
|
||||
object_type,
|
||||
if_exists,
|
||||
|
|
|
@ -146,6 +146,13 @@ pub fn all_dialects() -> TestedDialects {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn assert_eq_vec<T: ToString>(expected: &[&str], actual: &[T]) {
|
||||
assert_eq!(
|
||||
expected,
|
||||
actual.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn only<T>(v: impl IntoIterator<Item = T>) -> T {
|
||||
let mut iter = v.into_iter();
|
||||
if let (Some(item), None) = (iter.next(), iter.next()) {
|
||||
|
|
|
@ -32,7 +32,8 @@ use sqlparser::keywords::ALL_KEYWORDS;
|
|||
use sqlparser::parser::{Parser, ParserError};
|
||||
|
||||
use test_utils::{
|
||||
all_dialects, expr_from_projection, join, number, only, table, table_alias, TestedDialects,
|
||||
all_dialects, assert_eq_vec, expr_from_projection, join, number, only, table, table_alias,
|
||||
TestedDialects,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -4850,6 +4851,63 @@ fn parse_drop_index() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_role() {
|
||||
let sql = "CREATE ROLE consultant";
|
||||
match verified_stmt(sql) {
|
||||
Statement::CreateRole { names, .. } => {
|
||||
assert_eq_vec(&["consultant"], &names);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let sql = "CREATE ROLE IF NOT EXISTS mysql_a, mysql_b";
|
||||
match verified_stmt(sql) {
|
||||
Statement::CreateRole {
|
||||
names,
|
||||
if_not_exists,
|
||||
..
|
||||
} => {
|
||||
assert_eq_vec(&["mysql_a", "mysql_b"], &names);
|
||||
assert!(if_not_exists);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_drop_role() {
|
||||
let sql = "DROP ROLE abc";
|
||||
match verified_stmt(sql) {
|
||||
Statement::Drop {
|
||||
names,
|
||||
object_type,
|
||||
if_exists,
|
||||
..
|
||||
} => {
|
||||
assert_eq_vec(&["abc"], &names);
|
||||
assert_eq!(ObjectType::Role, object_type);
|
||||
assert!(!if_exists);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let sql = "DROP ROLE IF EXISTS def, magician, quaternion";
|
||||
match verified_stmt(sql) {
|
||||
Statement::Drop {
|
||||
names,
|
||||
object_type,
|
||||
if_exists,
|
||||
..
|
||||
} => {
|
||||
assert_eq_vec(&["def", "magician", "quaternion"], &names);
|
||||
assert_eq!(ObjectType::Role, object_type);
|
||||
assert!(if_exists);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_grant() {
|
||||
let sql = "GRANT SELECT, INSERT, UPDATE (shape, size), USAGE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CONNECT, CREATE, EXECUTE, TEMPORARY ON abc, def TO xyz, m WITH GRANT OPTION GRANTED BY jj";
|
||||
|
@ -4891,14 +4949,8 @@ fn parse_grant() {
|
|||
],
|
||||
actions
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["abc", "def"],
|
||||
objects.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["xyz", "m"],
|
||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["abc", "def"], &objects);
|
||||
assert_eq_vec(&["xyz", "m"], &grantees);
|
||||
assert!(with_grant_option);
|
||||
assert_eq!("jj", granted_by.unwrap().to_string());
|
||||
}
|
||||
|
@ -4918,14 +4970,8 @@ fn parse_grant() {
|
|||
} => match (privileges, objects) {
|
||||
(Privileges::Actions(actions), GrantObjects::AllTablesInSchema { schemas }) => {
|
||||
assert_eq!(vec![Action::Insert { columns: None }], actions);
|
||||
assert_eq!(
|
||||
vec!["public"],
|
||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["browser"],
|
||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["public"], &schemas);
|
||||
assert_eq_vec(&["browser"], &grantees);
|
||||
assert!(!with_grant_option);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -4947,14 +4993,8 @@ fn parse_grant() {
|
|||
vec![Action::Usage, Action::Select { columns: None }],
|
||||
actions
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["p"],
|
||||
objects.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["u"],
|
||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["p"], &objects);
|
||||
assert_eq_vec(&["u"], &grantees);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -4988,10 +5028,7 @@ fn parse_grant() {
|
|||
GrantObjects::Schemas(schemas),
|
||||
) => {
|
||||
assert!(!with_privileges_keyword);
|
||||
assert_eq!(
|
||||
vec!["aa", "b"],
|
||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["aa", "b"], &schemas);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -5007,10 +5044,7 @@ fn parse_grant() {
|
|||
} => match (privileges, objects) {
|
||||
(Privileges::Actions(actions), GrantObjects::AllSequencesInSchema { schemas }) => {
|
||||
assert_eq!(vec![Action::Usage], actions);
|
||||
assert_eq!(
|
||||
vec!["bus"],
|
||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["bus"], &schemas);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
@ -5035,14 +5069,8 @@ fn test_revoke() {
|
|||
},
|
||||
privileges
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["users", "auth"],
|
||||
tables.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
vec!["analyst"],
|
||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq_vec(&["users", "auth"], &tables);
|
||||
assert_eq_vec(&["analyst"], &grantees);
|
||||
assert!(cascade);
|
||||
assert_eq!(None, granted_by);
|
||||
}
|
||||
|
|
|
@ -118,6 +118,28 @@ fn parse_mssql_bin_literal() {
|
|||
let _ = ms_and_generic().one_statement_parses_to("SELECT 0xdeadBEEF", "SELECT X'deadBEEF'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mssql_create_role() {
|
||||
let sql = "CREATE ROLE mssql AUTHORIZATION helena";
|
||||
match ms().verified_stmt(sql) {
|
||||
Statement::CreateRole {
|
||||
names,
|
||||
authorization_owner,
|
||||
..
|
||||
} => {
|
||||
assert_eq_vec(&["mssql"], &names);
|
||||
assert_eq!(
|
||||
authorization_owner,
|
||||
Some(ObjectName(vec![Ident {
|
||||
value: "helena".into(),
|
||||
quote_style: None
|
||||
}]))
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ms() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(MsSqlDialect {})],
|
||||
|
|
|
@ -1739,3 +1739,104 @@ fn parse_custom_operator() {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_role() {
|
||||
let sql = "CREATE ROLE IF NOT EXISTS mysql_a, mysql_b";
|
||||
match pg().verified_stmt(sql) {
|
||||
Statement::CreateRole {
|
||||
names,
|
||||
if_not_exists,
|
||||
..
|
||||
} => {
|
||||
assert_eq_vec(&["mysql_a", "mysql_b"], &names);
|
||||
assert!(if_not_exists);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let sql = "CREATE ROLE abc LOGIN PASSWORD NULL";
|
||||
match pg().parse_sql_statements(sql).as_deref() {
|
||||
Ok(
|
||||
[Statement::CreateRole {
|
||||
names,
|
||||
login,
|
||||
password,
|
||||
..
|
||||
}],
|
||||
) => {
|
||||
assert_eq_vec(&["abc"], names);
|
||||
assert_eq!(*login, Some(true));
|
||||
assert_eq!(*password, Some(Password::NullPassword));
|
||||
}
|
||||
err => panic!("Failed to parse CREATE ROLE test case: {:?}", err),
|
||||
}
|
||||
|
||||
let sql = "CREATE ROLE magician WITH SUPERUSER CREATEROLE NOCREATEDB BYPASSRLS INHERIT PASSWORD 'abcdef' LOGIN VALID UNTIL '2025-01-01' IN ROLE role1, role2 ROLE role3 ADMIN role4, role5 REPLICATION";
|
||||
// Roundtrip order of optional parameters is not preserved
|
||||
match pg().parse_sql_statements(sql).as_deref() {
|
||||
Ok(
|
||||
[Statement::CreateRole {
|
||||
names,
|
||||
if_not_exists,
|
||||
bypassrls,
|
||||
login,
|
||||
inherit,
|
||||
password,
|
||||
superuser,
|
||||
create_db,
|
||||
create_role,
|
||||
replication,
|
||||
connection_limit,
|
||||
valid_until,
|
||||
in_role,
|
||||
role,
|
||||
admin,
|
||||
authorization_owner,
|
||||
}],
|
||||
) => {
|
||||
assert_eq_vec(&["magician"], names);
|
||||
assert!(!*if_not_exists);
|
||||
assert_eq!(*login, Some(true));
|
||||
assert_eq!(*inherit, Some(true));
|
||||
assert_eq!(*bypassrls, Some(true));
|
||||
assert_eq!(
|
||||
*password,
|
||||
Some(Password::Password(Expr::Value(Value::SingleQuotedString(
|
||||
"abcdef".into()
|
||||
))))
|
||||
);
|
||||
assert_eq!(*superuser, Some(true));
|
||||
assert_eq!(*create_db, Some(false));
|
||||
assert_eq!(*create_role, Some(true));
|
||||
assert_eq!(*replication, Some(true));
|
||||
assert_eq!(*connection_limit, None);
|
||||
assert_eq!(
|
||||
*valid_until,
|
||||
Some(Expr::Value(Value::SingleQuotedString("2025-01-01".into())))
|
||||
);
|
||||
assert_eq_vec(&["role1", "role2"], in_role);
|
||||
assert_eq_vec(&["role3"], role);
|
||||
assert_eq_vec(&["role4", "role5"], admin);
|
||||
assert_eq!(*authorization_owner, None);
|
||||
}
|
||||
err => panic!("Failed to parse CREATE ROLE test case: {:?}", err),
|
||||
}
|
||||
|
||||
let negatables = vec![
|
||||
"BYPASSRLS",
|
||||
"CREATEDB",
|
||||
"CREATEROLE",
|
||||
"INHERIT",
|
||||
"LOGIN",
|
||||
"REPLICATION",
|
||||
"SUPERUSER",
|
||||
];
|
||||
|
||||
for negatable_kw in negatables.iter() {
|
||||
let sql = format!("CREATE ROLE abc {kw} NO{kw}", kw = negatable_kw);
|
||||
if pg().parse_sql_statements(&sql).is_ok() {
|
||||
panic!("Should not be able to parse CREATE ROLE containing both negated and non-negated versions of the same keyword: {}", negatable_kw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue