mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-19 13:40:15 +00:00
Add basic support for GRANT and REVOKE (#365)
* Add basic support for GRANT privileges on tables and sequences * Cargo fmt * Make enum for granted privileges * Implement and use Display for GrantObjects * Simplify Display for GrantPrivileges * Add column privileges * Add second column privilege to test * Add REVOKE privileges and reformat * Fix some clippy warnings * Fix more clippy Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
parent
d33bacf679
commit
d7e84be3e1
4 changed files with 528 additions and 0 deletions
181
src/ast/mod.rs
181
src/ast/mod.rs
|
@ -763,6 +763,22 @@ pub enum Statement {
|
||||||
condition: Expr,
|
condition: Expr,
|
||||||
message: Option<Expr>,
|
message: Option<Expr>,
|
||||||
},
|
},
|
||||||
|
/// GRANT privileges ON objects TO grantees
|
||||||
|
Grant {
|
||||||
|
privileges: Privileges,
|
||||||
|
objects: GrantObjects,
|
||||||
|
grantees: Vec<Ident>,
|
||||||
|
with_grant_option: bool,
|
||||||
|
granted_by: Option<Ident>,
|
||||||
|
},
|
||||||
|
/// REVOKE privileges ON objects FROM grantees
|
||||||
|
Revoke {
|
||||||
|
privileges: Privileges,
|
||||||
|
objects: GrantObjects,
|
||||||
|
grantees: Vec<Ident>,
|
||||||
|
granted_by: Option<Ident>,
|
||||||
|
cascade: bool,
|
||||||
|
},
|
||||||
/// `DEALLOCATE [ PREPARE ] { name | ALL }`
|
/// `DEALLOCATE [ PREPARE ] { name | ALL }`
|
||||||
///
|
///
|
||||||
/// Note: this is a PostgreSQL-specific statement.
|
/// Note: this is a PostgreSQL-specific statement.
|
||||||
|
@ -1330,6 +1346,40 @@ impl fmt::Display for Statement {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
with_grant_option,
|
||||||
|
granted_by,
|
||||||
|
} => {
|
||||||
|
write!(f, "GRANT {} ", privileges)?;
|
||||||
|
write!(f, "ON {} ", objects)?;
|
||||||
|
write!(f, "TO {}", display_comma_separated(grantees))?;
|
||||||
|
if *with_grant_option {
|
||||||
|
write!(f, " WITH GRANT OPTION")?;
|
||||||
|
}
|
||||||
|
if let Some(grantor) = granted_by {
|
||||||
|
write!(f, " GRANTED BY {}", grantor)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Statement::Revoke {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
granted_by,
|
||||||
|
cascade,
|
||||||
|
} => {
|
||||||
|
write!(f, "REVOKE {} ", privileges)?;
|
||||||
|
write!(f, "ON {} ", objects)?;
|
||||||
|
write!(f, "FROM {}", display_comma_separated(grantees))?;
|
||||||
|
if let Some(grantor) = granted_by {
|
||||||
|
write!(f, " GRANTED BY {}", grantor)?;
|
||||||
|
}
|
||||||
|
write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Statement::Deallocate { name, prepare } => write!(
|
Statement::Deallocate { name, prepare } => write!(
|
||||||
f,
|
f,
|
||||||
"DEALLOCATE {prepare}{name}",
|
"DEALLOCATE {prepare}{name}",
|
||||||
|
@ -1358,6 +1408,137 @@ impl fmt::Display for Statement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum Privileges {
|
||||||
|
/// All privileges applicable to the object type
|
||||||
|
All {
|
||||||
|
/// Optional keyword from the spec, ignored in practice
|
||||||
|
with_privileges_keyword: bool,
|
||||||
|
},
|
||||||
|
/// Specific privileges (e.g. `SELECT`, `INSERT`)
|
||||||
|
Actions(Vec<Action>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Privileges {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Privileges::All {
|
||||||
|
with_privileges_keyword,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ALL{}",
|
||||||
|
if *with_privileges_keyword {
|
||||||
|
" PRIVILEGES"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Privileges::Actions(actions) => {
|
||||||
|
write!(f, "{}", display_comma_separated(actions).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A privilege on a database object (table, sequence, etc.).
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum Action {
|
||||||
|
Connect,
|
||||||
|
Create,
|
||||||
|
Delete,
|
||||||
|
Execute,
|
||||||
|
Insert { columns: Option<Vec<Ident>> },
|
||||||
|
References { columns: Option<Vec<Ident>> },
|
||||||
|
Select { columns: Option<Vec<Ident>> },
|
||||||
|
Temporary,
|
||||||
|
Trigger,
|
||||||
|
Truncate,
|
||||||
|
Update { columns: Option<Vec<Ident>> },
|
||||||
|
Usage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Action {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Action::Connect => f.write_str("CONNECT")?,
|
||||||
|
Action::Create => f.write_str("CREATE")?,
|
||||||
|
Action::Delete => f.write_str("DELETE")?,
|
||||||
|
Action::Execute => f.write_str("EXECUTE")?,
|
||||||
|
Action::Insert { .. } => f.write_str("INSERT")?,
|
||||||
|
Action::References { .. } => f.write_str("REFERENCES")?,
|
||||||
|
Action::Select { .. } => f.write_str("SELECT")?,
|
||||||
|
Action::Temporary => f.write_str("TEMPORARY")?,
|
||||||
|
Action::Trigger => f.write_str("TRIGGER")?,
|
||||||
|
Action::Truncate => f.write_str("TRUNCATE")?,
|
||||||
|
Action::Update { .. } => f.write_str("UPDATE")?,
|
||||||
|
Action::Usage => f.write_str("USAGE")?,
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
Action::Insert { columns }
|
||||||
|
| Action::References { columns }
|
||||||
|
| Action::Select { columns }
|
||||||
|
| Action::Update { columns } => {
|
||||||
|
if let Some(columns) = columns {
|
||||||
|
write!(f, " ({})", display_comma_separated(columns))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Objects on which privileges are granted in a GRANT statement.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum GrantObjects {
|
||||||
|
/// Grant privileges on `ALL SEQUENCES IN SCHEMA <schema_name> [, ...]`
|
||||||
|
AllSequencesInSchema { schemas: Vec<ObjectName> },
|
||||||
|
/// Grant privileges on `ALL TABLES IN SCHEMA <schema_name> [, ...]`
|
||||||
|
AllTablesInSchema { schemas: Vec<ObjectName> },
|
||||||
|
/// Grant privileges on specific schemas
|
||||||
|
Schemas(Vec<ObjectName>),
|
||||||
|
/// Grant privileges on specific sequences
|
||||||
|
Sequences(Vec<ObjectName>),
|
||||||
|
/// Grant privileges on specific tables
|
||||||
|
Tables(Vec<ObjectName>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for GrantObjects {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
GrantObjects::Sequences(sequences) => {
|
||||||
|
write!(f, "SEQUENCE {}", display_comma_separated(sequences))
|
||||||
|
}
|
||||||
|
GrantObjects::Schemas(schemas) => {
|
||||||
|
write!(f, "SCHEMA {}", display_comma_separated(schemas))
|
||||||
|
}
|
||||||
|
GrantObjects::Tables(tables) => {
|
||||||
|
write!(f, "{}", display_comma_separated(tables))
|
||||||
|
}
|
||||||
|
GrantObjects::AllSequencesInSchema { schemas } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ALL SEQUENCES IN SCHEMA {}",
|
||||||
|
display_comma_separated(schemas)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
GrantObjects::AllTablesInSchema { schemas } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ALL TABLES IN SCHEMA {}",
|
||||||
|
display_comma_separated(schemas)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// SQL assignment `foo = expr` as used in SQLUpdate
|
/// SQL assignment `foo = expr` as used in SQLUpdate
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
|
|
@ -225,6 +225,7 @@ define_keywords!(
|
||||||
GET,
|
GET,
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
GRANT,
|
GRANT,
|
||||||
|
GRANTED,
|
||||||
GROUP,
|
GROUP,
|
||||||
GROUPING,
|
GROUPING,
|
||||||
GROUPS,
|
GROUPS,
|
||||||
|
@ -318,6 +319,7 @@ define_keywords!(
|
||||||
ON,
|
ON,
|
||||||
ONLY,
|
ONLY,
|
||||||
OPEN,
|
OPEN,
|
||||||
|
OPTION,
|
||||||
OR,
|
OR,
|
||||||
ORC,
|
ORC,
|
||||||
ORDER,
|
ORDER,
|
||||||
|
@ -348,6 +350,7 @@ define_keywords!(
|
||||||
PRECISION,
|
PRECISION,
|
||||||
PREPARE,
|
PREPARE,
|
||||||
PRIMARY,
|
PRIMARY,
|
||||||
|
PRIVILEGES,
|
||||||
PROCEDURE,
|
PROCEDURE,
|
||||||
PURGE,
|
PURGE,
|
||||||
RANGE,
|
RANGE,
|
||||||
|
@ -395,7 +398,9 @@ define_keywords!(
|
||||||
SECOND,
|
SECOND,
|
||||||
SELECT,
|
SELECT,
|
||||||
SENSITIVE,
|
SENSITIVE,
|
||||||
|
SEQUENCE,
|
||||||
SEQUENCEFILE,
|
SEQUENCEFILE,
|
||||||
|
SEQUENCES,
|
||||||
SERDE,
|
SERDE,
|
||||||
SERIALIZABLE,
|
SERIALIZABLE,
|
||||||
SESSION,
|
SESSION,
|
||||||
|
@ -432,6 +437,7 @@ define_keywords!(
|
||||||
SYSTEM_TIME,
|
SYSTEM_TIME,
|
||||||
SYSTEM_USER,
|
SYSTEM_USER,
|
||||||
TABLE,
|
TABLE,
|
||||||
|
TABLES,
|
||||||
TABLESAMPLE,
|
TABLESAMPLE,
|
||||||
TBLPROPERTIES,
|
TBLPROPERTIES,
|
||||||
TEMP,
|
TEMP,
|
||||||
|
@ -468,6 +474,7 @@ define_keywords!(
|
||||||
UNNEST,
|
UNNEST,
|
||||||
UPDATE,
|
UPDATE,
|
||||||
UPPER,
|
UPPER,
|
||||||
|
USAGE,
|
||||||
USER,
|
USER,
|
||||||
USING,
|
USING,
|
||||||
UUID,
|
UUID,
|
||||||
|
|
144
src/parser.rs
144
src/parser.rs
|
@ -156,6 +156,8 @@ impl<'a> Parser<'a> {
|
||||||
Keyword::COPY => Ok(self.parse_copy()?),
|
Keyword::COPY => Ok(self.parse_copy()?),
|
||||||
Keyword::SET => Ok(self.parse_set()?),
|
Keyword::SET => Ok(self.parse_set()?),
|
||||||
Keyword::SHOW => Ok(self.parse_show()?),
|
Keyword::SHOW => Ok(self.parse_show()?),
|
||||||
|
Keyword::GRANT => Ok(self.parse_grant()?),
|
||||||
|
Keyword::REVOKE => Ok(self.parse_revoke()?),
|
||||||
Keyword::START => Ok(self.parse_start_transaction()?),
|
Keyword::START => Ok(self.parse_start_transaction()?),
|
||||||
// `BEGIN` is a nonstandard but common alias for the
|
// `BEGIN` is a nonstandard but common alias for the
|
||||||
// standard `START TRANSACTION` statement. It is supported
|
// standard `START TRANSACTION` statement. It is supported
|
||||||
|
@ -2882,6 +2884,148 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a GRANT statement.
|
||||||
|
pub fn parse_grant(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?;
|
||||||
|
|
||||||
|
self.expect_keyword(Keyword::TO)?;
|
||||||
|
let grantees = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||||
|
|
||||||
|
let with_grant_option =
|
||||||
|
self.parse_keywords(&[Keyword::WITH, Keyword::GRANT, Keyword::OPTION]);
|
||||||
|
|
||||||
|
let granted_by = self
|
||||||
|
.parse_keywords(&[Keyword::GRANTED, Keyword::BY])
|
||||||
|
.then(|| self.parse_identifier().unwrap());
|
||||||
|
|
||||||
|
Ok(Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
with_grant_option,
|
||||||
|
granted_by,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_grant_revoke_privileges_objects(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<(Privileges, GrantObjects), ParserError> {
|
||||||
|
let privileges = if self.parse_keyword(Keyword::ALL) {
|
||||||
|
Privileges::All {
|
||||||
|
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Privileges::Actions(
|
||||||
|
self.parse_comma_separated(Parser::parse_grant_permission)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(kw, columns)| match kw {
|
||||||
|
Keyword::DELETE => Action::Delete,
|
||||||
|
Keyword::INSERT => Action::Insert { columns },
|
||||||
|
Keyword::REFERENCES => Action::References { columns },
|
||||||
|
Keyword::SELECT => Action::Select { columns },
|
||||||
|
Keyword::TRIGGER => Action::Trigger,
|
||||||
|
Keyword::TRUNCATE => Action::Truncate,
|
||||||
|
Keyword::UPDATE => Action::Update { columns },
|
||||||
|
Keyword::USAGE => Action::Usage,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.expect_keyword(Keyword::ON)?;
|
||||||
|
|
||||||
|
let objects = if self.parse_keywords(&[
|
||||||
|
Keyword::ALL,
|
||||||
|
Keyword::TABLES,
|
||||||
|
Keyword::IN,
|
||||||
|
Keyword::SCHEMA,
|
||||||
|
]) {
|
||||||
|
GrantObjects::AllTablesInSchema {
|
||||||
|
schemas: self.parse_comma_separated(Parser::parse_object_name)?,
|
||||||
|
}
|
||||||
|
} else if self.parse_keywords(&[
|
||||||
|
Keyword::ALL,
|
||||||
|
Keyword::SEQUENCES,
|
||||||
|
Keyword::IN,
|
||||||
|
Keyword::SCHEMA,
|
||||||
|
]) {
|
||||||
|
GrantObjects::AllSequencesInSchema {
|
||||||
|
schemas: self.parse_comma_separated(Parser::parse_object_name)?,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let object_type =
|
||||||
|
self.parse_one_of_keywords(&[Keyword::SEQUENCE, Keyword::SCHEMA, Keyword::TABLE]);
|
||||||
|
let objects = self.parse_comma_separated(Parser::parse_object_name);
|
||||||
|
match object_type {
|
||||||
|
Some(Keyword::SCHEMA) => GrantObjects::Schemas(objects?),
|
||||||
|
Some(Keyword::SEQUENCE) => GrantObjects::Sequences(objects?),
|
||||||
|
Some(Keyword::TABLE) | None => GrantObjects::Tables(objects?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((privileges, objects))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_grant_permission(&mut self) -> Result<(Keyword, Option<Vec<Ident>>), ParserError> {
|
||||||
|
if let Some(kw) = self.parse_one_of_keywords(&[
|
||||||
|
Keyword::CONNECT,
|
||||||
|
Keyword::CREATE,
|
||||||
|
Keyword::DELETE,
|
||||||
|
Keyword::EXECUTE,
|
||||||
|
Keyword::INSERT,
|
||||||
|
Keyword::REFERENCES,
|
||||||
|
Keyword::SELECT,
|
||||||
|
Keyword::TEMPORARY,
|
||||||
|
Keyword::TRIGGER,
|
||||||
|
Keyword::TRUNCATE,
|
||||||
|
Keyword::UPDATE,
|
||||||
|
Keyword::USAGE,
|
||||||
|
]) {
|
||||||
|
let columns = match kw {
|
||||||
|
Keyword::INSERT | Keyword::REFERENCES | Keyword::SELECT | Keyword::UPDATE => {
|
||||||
|
let columns = self.parse_parenthesized_column_list(Optional)?;
|
||||||
|
if columns.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(columns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
Ok((kw, columns))
|
||||||
|
} else {
|
||||||
|
self.expected("a privilege keyword", self.peek_token())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a REVOKE statement
|
||||||
|
pub fn parse_revoke(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?;
|
||||||
|
|
||||||
|
self.expect_keyword(Keyword::FROM)?;
|
||||||
|
let grantees = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||||
|
|
||||||
|
let granted_by = self
|
||||||
|
.parse_keywords(&[Keyword::GRANTED, Keyword::BY])
|
||||||
|
.then(|| self.parse_identifier().unwrap());
|
||||||
|
|
||||||
|
let cascade = self.parse_keyword(Keyword::CASCADE);
|
||||||
|
let restrict = self.parse_keyword(Keyword::RESTRICT);
|
||||||
|
if cascade && restrict {
|
||||||
|
return parser_err!("Cannot specify both CASCADE and RESTRICT in REVOKE");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Statement::Revoke {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
granted_by,
|
||||||
|
cascade,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an INSERT statement
|
/// Parse an INSERT statement
|
||||||
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
|
||||||
let or = if !dialect_of!(self is SQLiteDialect) {
|
let or = if !dialect_of!(self is SQLiteDialect) {
|
||||||
|
|
|
@ -3589,6 +3589,202 @@ fn parse_drop_index() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_grant() {
|
||||||
|
let sql = "GRANT SELECT, INSERT, UPDATE (shape, size), USAGE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON abc, def TO xyz, m WITH GRANT OPTION GRANTED BY jj";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
with_grant_option,
|
||||||
|
granted_by,
|
||||||
|
..
|
||||||
|
} => match (privileges, objects) {
|
||||||
|
(Privileges::Actions(actions), GrantObjects::Tables(objects)) => {
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
Action::Select { columns: None },
|
||||||
|
Action::Insert { columns: None },
|
||||||
|
Action::Update {
|
||||||
|
columns: Some(vec![
|
||||||
|
Ident {
|
||||||
|
value: "shape".into(),
|
||||||
|
quote_style: None
|
||||||
|
},
|
||||||
|
Ident {
|
||||||
|
value: "size".into(),
|
||||||
|
quote_style: None
|
||||||
|
}
|
||||||
|
])
|
||||||
|
},
|
||||||
|
Action::Usage,
|
||||||
|
Action::Delete,
|
||||||
|
Action::Truncate,
|
||||||
|
Action::References { columns: None },
|
||||||
|
Action::Trigger,
|
||||||
|
],
|
||||||
|
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!(with_grant_option);
|
||||||
|
assert_eq!("jj", granted_by.unwrap().to_string());
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql2 = "GRANT INSERT ON ALL TABLES IN SCHEMA public TO browser";
|
||||||
|
match verified_stmt(sql2) {
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
with_grant_option,
|
||||||
|
..
|
||||||
|
} => 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!(!with_grant_option);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql3 = "GRANT USAGE, SELECT ON SEQUENCE p TO u";
|
||||||
|
match verified_stmt(sql3) {
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
grantees,
|
||||||
|
granted_by,
|
||||||
|
..
|
||||||
|
} => match (privileges, objects, granted_by) {
|
||||||
|
(Privileges::Actions(actions), GrantObjects::Sequences(objects), None) => {
|
||||||
|
assert_eq!(
|
||||||
|
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<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql4 = "GRANT ALL PRIVILEGES ON aa, b TO z";
|
||||||
|
match verified_stmt(sql4) {
|
||||||
|
Statement::Grant { privileges, .. } => {
|
||||||
|
assert_eq!(
|
||||||
|
Privileges::All {
|
||||||
|
with_privileges_keyword: true
|
||||||
|
},
|
||||||
|
privileges
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql5 = "GRANT ALL ON SCHEMA aa, b TO z";
|
||||||
|
match verified_stmt(sql5) {
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
..
|
||||||
|
} => match (privileges, objects) {
|
||||||
|
(
|
||||||
|
Privileges::All {
|
||||||
|
with_privileges_keyword,
|
||||||
|
},
|
||||||
|
GrantObjects::Schemas(schemas),
|
||||||
|
) => {
|
||||||
|
assert!(!with_privileges_keyword);
|
||||||
|
assert_eq!(
|
||||||
|
vec!["aa", "b"],
|
||||||
|
schemas.iter().map(ToString::to_string).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql6 = "GRANT USAGE ON ALL SEQUENCES IN SCHEMA bus TO a, beta WITH GRANT OPTION";
|
||||||
|
match verified_stmt(sql6) {
|
||||||
|
Statement::Grant {
|
||||||
|
privileges,
|
||||||
|
objects,
|
||||||
|
..
|
||||||
|
} => 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<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_revoke() {
|
||||||
|
let sql = "REVOKE ALL PRIVILEGES ON users, auth FROM analyst CASCADE";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
Statement::Revoke {
|
||||||
|
privileges,
|
||||||
|
objects: GrantObjects::Tables(tables),
|
||||||
|
grantees,
|
||||||
|
cascade,
|
||||||
|
granted_by,
|
||||||
|
} => {
|
||||||
|
assert_eq!(
|
||||||
|
Privileges::All {
|
||||||
|
with_privileges_keyword: true
|
||||||
|
},
|
||||||
|
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!(cascade);
|
||||||
|
assert_eq!(None, granted_by);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn all_keywords_sorted() {
|
fn all_keywords_sorted() {
|
||||||
// assert!(ALL_KEYWORDS.is_sorted())
|
// assert!(ALL_KEYWORDS.is_sorted())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue