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.)
|
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -1109,6 +1116,27 @@ pub enum Statement {
|
||||||
unique: bool,
|
unique: bool,
|
||||||
if_not_exists: 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
|
/// ALTER TABLE
|
||||||
AlterTable {
|
AlterTable {
|
||||||
/// Table name
|
/// Table name
|
||||||
|
@ -1963,6 +1991,90 @@ impl fmt::Display for Statement {
|
||||||
table_name = table_name,
|
table_name = table_name,
|
||||||
columns = display_separated(columns, ",")
|
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 } => {
|
Statement::AlterTable { name, operation } => {
|
||||||
write!(f, "ALTER TABLE {} {}", name, operation)
|
write!(f, "ALTER TABLE {} {}", name, operation)
|
||||||
}
|
}
|
||||||
|
@ -2701,6 +2813,7 @@ pub enum ObjectType {
|
||||||
View,
|
View,
|
||||||
Index,
|
Index,
|
||||||
Schema,
|
Schema,
|
||||||
|
Role,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ObjectType {
|
impl fmt::Display for ObjectType {
|
||||||
|
@ -2710,6 +2823,7 @@ impl fmt::Display for ObjectType {
|
||||||
ObjectType::View => "VIEW",
|
ObjectType::View => "VIEW",
|
||||||
ObjectType::Index => "INDEX",
|
ObjectType::Index => "INDEX",
|
||||||
ObjectType::Schema => "SCHEMA",
|
ObjectType::Schema => "SCHEMA",
|
||||||
|
ObjectType::Role => "ROLE",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ define_keywords!(
|
||||||
ABSOLUTE,
|
ABSOLUTE,
|
||||||
ACTION,
|
ACTION,
|
||||||
ADD,
|
ADD,
|
||||||
|
ADMIN,
|
||||||
ALL,
|
ALL,
|
||||||
ALLOCATE,
|
ALLOCATE,
|
||||||
ALTER,
|
ALTER,
|
||||||
|
@ -105,6 +106,7 @@ define_keywords!(
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
BOTH,
|
BOTH,
|
||||||
BY,
|
BY,
|
||||||
|
BYPASSRLS,
|
||||||
BYTEA,
|
BYTEA,
|
||||||
CACHE,
|
CACHE,
|
||||||
CALL,
|
CALL,
|
||||||
|
@ -152,6 +154,8 @@ define_keywords!(
|
||||||
COVAR_POP,
|
COVAR_POP,
|
||||||
COVAR_SAMP,
|
COVAR_SAMP,
|
||||||
CREATE,
|
CREATE,
|
||||||
|
CREATEDB,
|
||||||
|
CREATEROLE,
|
||||||
CROSS,
|
CROSS,
|
||||||
CSV,
|
CSV,
|
||||||
CUBE,
|
CUBE,
|
||||||
|
@ -272,6 +276,7 @@ define_keywords!(
|
||||||
IN,
|
IN,
|
||||||
INDEX,
|
INDEX,
|
||||||
INDICATOR,
|
INDICATOR,
|
||||||
|
INHERIT,
|
||||||
INNER,
|
INNER,
|
||||||
INOUT,
|
INOUT,
|
||||||
INPUTFORMAT,
|
INPUTFORMAT,
|
||||||
|
@ -313,6 +318,7 @@ define_keywords!(
|
||||||
LOCALTIME,
|
LOCALTIME,
|
||||||
LOCALTIMESTAMP,
|
LOCALTIMESTAMP,
|
||||||
LOCATION,
|
LOCATION,
|
||||||
|
LOGIN,
|
||||||
LOWER,
|
LOWER,
|
||||||
MANAGEDLOCATION,
|
MANAGEDLOCATION,
|
||||||
MATCH,
|
MATCH,
|
||||||
|
@ -342,9 +348,16 @@ define_keywords!(
|
||||||
NEW,
|
NEW,
|
||||||
NEXT,
|
NEXT,
|
||||||
NO,
|
NO,
|
||||||
|
NOBYPASSRLS,
|
||||||
|
NOCREATEDB,
|
||||||
|
NOCREATEROLE,
|
||||||
|
NOINHERIT,
|
||||||
|
NOLOGIN,
|
||||||
NONE,
|
NONE,
|
||||||
|
NOREPLICATION,
|
||||||
NORMALIZE,
|
NORMALIZE,
|
||||||
NOSCAN,
|
NOSCAN,
|
||||||
|
NOSUPERUSER,
|
||||||
NOT,
|
NOT,
|
||||||
NTH_VALUE,
|
NTH_VALUE,
|
||||||
NTILE,
|
NTILE,
|
||||||
|
@ -380,6 +393,7 @@ define_keywords!(
|
||||||
PARTITION,
|
PARTITION,
|
||||||
PARTITIONED,
|
PARTITIONED,
|
||||||
PARTITIONS,
|
PARTITIONS,
|
||||||
|
PASSWORD,
|
||||||
PERCENT,
|
PERCENT,
|
||||||
PERCENTILE_CONT,
|
PERCENTILE_CONT,
|
||||||
PERCENTILE_DISC,
|
PERCENTILE_DISC,
|
||||||
|
@ -432,6 +446,7 @@ define_keywords!(
|
||||||
REPAIR,
|
REPAIR,
|
||||||
REPEATABLE,
|
REPEATABLE,
|
||||||
REPLACE,
|
REPLACE,
|
||||||
|
REPLICATION,
|
||||||
RESTRICT,
|
RESTRICT,
|
||||||
RESULT,
|
RESULT,
|
||||||
RETURN,
|
RETURN,
|
||||||
|
@ -492,6 +507,7 @@ define_keywords!(
|
||||||
SUCCEEDS,
|
SUCCEEDS,
|
||||||
SUM,
|
SUM,
|
||||||
SUPER,
|
SUPER,
|
||||||
|
SUPERUSER,
|
||||||
SYMMETRIC,
|
SYMMETRIC,
|
||||||
SYNC,
|
SYNC,
|
||||||
SYSTEM,
|
SYSTEM,
|
||||||
|
@ -538,6 +554,7 @@ define_keywords!(
|
||||||
UNLOGGED,
|
UNLOGGED,
|
||||||
UNNEST,
|
UNNEST,
|
||||||
UNSIGNED,
|
UNSIGNED,
|
||||||
|
UNTIL,
|
||||||
UPDATE,
|
UPDATE,
|
||||||
UPPER,
|
UPPER,
|
||||||
USAGE,
|
USAGE,
|
||||||
|
@ -545,6 +562,7 @@ define_keywords!(
|
||||||
USER,
|
USER,
|
||||||
USING,
|
USING,
|
||||||
UUID,
|
UUID,
|
||||||
|
VALID,
|
||||||
VALUE,
|
VALUE,
|
||||||
VALUES,
|
VALUES,
|
||||||
VALUE_OF,
|
VALUE_OF,
|
||||||
|
|
213
src/parser.rs
213
src/parser.rs
|
@ -1866,6 +1866,8 @@ impl<'a> Parser<'a> {
|
||||||
self.parse_create_database()
|
self.parse_create_database()
|
||||||
} else if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::FUNCTION) {
|
} else if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::FUNCTION) {
|
||||||
self.parse_create_function(temporary)
|
self.parse_create_function(temporary)
|
||||||
|
} else if self.parse_keyword(Keyword::ROLE) {
|
||||||
|
self.parse_create_role()
|
||||||
} else {
|
} else {
|
||||||
self.expected("an object type after CREATE", self.peek_token())
|
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> {
|
pub fn parse_drop(&mut self) -> Result<Statement, ParserError> {
|
||||||
let object_type = if self.parse_keyword(Keyword::TABLE) {
|
let object_type = if self.parse_keyword(Keyword::TABLE) {
|
||||||
ObjectType::Table
|
ObjectType::Table
|
||||||
|
@ -2063,10 +2266,15 @@ impl<'a> Parser<'a> {
|
||||||
ObjectType::View
|
ObjectType::View
|
||||||
} else if self.parse_keyword(Keyword::INDEX) {
|
} else if self.parse_keyword(Keyword::INDEX) {
|
||||||
ObjectType::Index
|
ObjectType::Index
|
||||||
|
} else if self.parse_keyword(Keyword::ROLE) {
|
||||||
|
ObjectType::Role
|
||||||
} else if self.parse_keyword(Keyword::SCHEMA) {
|
} else if self.parse_keyword(Keyword::SCHEMA) {
|
||||||
ObjectType::Schema
|
ObjectType::Schema
|
||||||
} else {
|
} 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
|
// Many dialects support the non standard `IF EXISTS` clause and allow
|
||||||
// specifying multiple objects to delete in a single statement
|
// specifying multiple objects to delete in a single statement
|
||||||
|
@ -2078,6 +2286,9 @@ impl<'a> Parser<'a> {
|
||||||
if cascade && restrict {
|
if cascade && restrict {
|
||||||
return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP");
|
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 {
|
Ok(Statement::Drop {
|
||||||
object_type,
|
object_type,
|
||||||
if_exists,
|
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 {
|
pub fn only<T>(v: impl IntoIterator<Item = T>) -> T {
|
||||||
let mut iter = v.into_iter();
|
let mut iter = v.into_iter();
|
||||||
if let (Some(item), None) = (iter.next(), iter.next()) {
|
if let (Some(item), None) = (iter.next(), iter.next()) {
|
||||||
|
|
|
@ -32,7 +32,8 @@ use sqlparser::keywords::ALL_KEYWORDS;
|
||||||
use sqlparser::parser::{Parser, ParserError};
|
use sqlparser::parser::{Parser, ParserError};
|
||||||
|
|
||||||
use test_utils::{
|
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]
|
#[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]
|
#[test]
|
||||||
fn parse_grant() {
|
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";
|
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
|
actions
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq_vec(&["abc", "def"], &objects);
|
||||||
vec!["abc", "def"],
|
assert_eq_vec(&["xyz", "m"], &grantees);
|
||||||
objects.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec!["xyz", "m"],
|
|
||||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert!(with_grant_option);
|
assert!(with_grant_option);
|
||||||
assert_eq!("jj", granted_by.unwrap().to_string());
|
assert_eq!("jj", granted_by.unwrap().to_string());
|
||||||
}
|
}
|
||||||
|
@ -4918,14 +4970,8 @@ fn parse_grant() {
|
||||||
} => match (privileges, objects) {
|
} => match (privileges, objects) {
|
||||||
(Privileges::Actions(actions), GrantObjects::AllTablesInSchema { schemas }) => {
|
(Privileges::Actions(actions), GrantObjects::AllTablesInSchema { schemas }) => {
|
||||||
assert_eq!(vec![Action::Insert { columns: None }], actions);
|
assert_eq!(vec![Action::Insert { columns: None }], actions);
|
||||||
assert_eq!(
|
assert_eq_vec(&["public"], &schemas);
|
||||||
vec!["public"],
|
assert_eq_vec(&["browser"], &grantees);
|
||||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec!["browser"],
|
|
||||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert!(!with_grant_option);
|
assert!(!with_grant_option);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -4947,14 +4993,8 @@ fn parse_grant() {
|
||||||
vec![Action::Usage, Action::Select { columns: None }],
|
vec![Action::Usage, Action::Select { columns: None }],
|
||||||
actions
|
actions
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq_vec(&["p"], &objects);
|
||||||
vec!["p"],
|
assert_eq_vec(&["u"], &grantees);
|
||||||
objects.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec!["u"],
|
|
||||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -4988,10 +5028,7 @@ fn parse_grant() {
|
||||||
GrantObjects::Schemas(schemas),
|
GrantObjects::Schemas(schemas),
|
||||||
) => {
|
) => {
|
||||||
assert!(!with_privileges_keyword);
|
assert!(!with_privileges_keyword);
|
||||||
assert_eq!(
|
assert_eq_vec(&["aa", "b"], &schemas);
|
||||||
vec!["aa", "b"],
|
|
||||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -5007,10 +5044,7 @@ fn parse_grant() {
|
||||||
} => match (privileges, objects) {
|
} => match (privileges, objects) {
|
||||||
(Privileges::Actions(actions), GrantObjects::AllSequencesInSchema { schemas }) => {
|
(Privileges::Actions(actions), GrantObjects::AllSequencesInSchema { schemas }) => {
|
||||||
assert_eq!(vec![Action::Usage], actions);
|
assert_eq!(vec![Action::Usage], actions);
|
||||||
assert_eq!(
|
assert_eq_vec(&["bus"], &schemas);
|
||||||
vec!["bus"],
|
|
||||||
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -5035,14 +5069,8 @@ fn test_revoke() {
|
||||||
},
|
},
|
||||||
privileges
|
privileges
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq_vec(&["users", "auth"], &tables);
|
||||||
vec!["users", "auth"],
|
assert_eq_vec(&["analyst"], &grantees);
|
||||||
tables.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec!["analyst"],
|
|
||||||
grantees.iter().map(ToString::to_string).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert!(cascade);
|
assert!(cascade);
|
||||||
assert_eq!(None, granted_by);
|
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'");
|
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 {
|
fn ms() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(MsSqlDialect {})],
|
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