Skip to content

Commit 74a95fd

Browse files
authored
Add support for DENY statements (#1836)
1 parent 178a351 commit 74a95fd

File tree

9 files changed

+185
-15
lines changed

9 files changed

+185
-15
lines changed

src/ast/mod.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,9 +3903,14 @@ pub enum Statement {
39033903
objects: Option<GrantObjects>,
39043904
grantees: Vec<Grantee>,
39053905
with_grant_option: bool,
3906+
as_grantor: Option<Ident>,
39063907
granted_by: Option<Ident>,
39073908
},
39083909
/// ```sql
3910+
/// DENY privileges ON object TO grantees
3911+
/// ```
3912+
Deny(DenyStatement),
3913+
/// ```sql
39093914
/// REVOKE privileges ON objects FROM grantees
39103915
/// ```
39113916
Revoke {
@@ -5580,6 +5585,7 @@ impl fmt::Display for Statement {
55805585
objects,
55815586
grantees,
55825587
with_grant_option,
5588+
as_grantor,
55835589
granted_by,
55845590
} => {
55855591
write!(f, "GRANT {privileges} ")?;
@@ -5590,11 +5596,15 @@ impl fmt::Display for Statement {
55905596
if *with_grant_option {
55915597
write!(f, " WITH GRANT OPTION")?;
55925598
}
5599+
if let Some(grantor) = as_grantor {
5600+
write!(f, " AS {grantor}")?;
5601+
}
55935602
if let Some(grantor) = granted_by {
55945603
write!(f, " GRANTED BY {grantor}")?;
55955604
}
55965605
Ok(())
55975606
}
5607+
Statement::Deny(s) => write!(f, "{s}"),
55985608
Statement::Revoke {
55995609
privileges,
56005610
objects,
@@ -6380,6 +6390,9 @@ pub enum Action {
63806390
},
63816391
Delete,
63826392
EvolveSchema,
6393+
Exec {
6394+
obj_type: Option<ActionExecuteObjectType>,
6395+
},
63836396
Execute {
63846397
obj_type: Option<ActionExecuteObjectType>,
63856398
},
@@ -6446,6 +6459,12 @@ impl fmt::Display for Action {
64466459
Action::DatabaseRole { role } => write!(f, "DATABASE ROLE {role}")?,
64476460
Action::Delete => f.write_str("DELETE")?,
64486461
Action::EvolveSchema => f.write_str("EVOLVE SCHEMA")?,
6462+
Action::Exec { obj_type } => {
6463+
f.write_str("EXEC")?;
6464+
if let Some(obj_type) = obj_type {
6465+
write!(f, " {obj_type}")?
6466+
}
6467+
}
64496468
Action::Execute { obj_type } => {
64506469
f.write_str("EXECUTE")?;
64516470
if let Some(obj_type) = obj_type {
@@ -6867,6 +6886,37 @@ impl fmt::Display for GrantObjects {
68676886
}
68686887
}
68696888

6889+
/// A `DENY` statement
6890+
///
6891+
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/deny-transact-sql)
6892+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6893+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6894+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6895+
pub struct DenyStatement {
6896+
pub privileges: Privileges,
6897+
pub objects: GrantObjects,
6898+
pub grantees: Vec<Grantee>,
6899+
pub granted_by: Option<Ident>,
6900+
pub cascade: Option<CascadeOption>,
6901+
}
6902+
6903+
impl fmt::Display for DenyStatement {
6904+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6905+
write!(f, "DENY {}", self.privileges)?;
6906+
write!(f, " ON {}", self.objects)?;
6907+
if !self.grantees.is_empty() {
6908+
write!(f, " TO {}", display_comma_separated(&self.grantees))?;
6909+
}
6910+
if let Some(cascade) = &self.cascade {
6911+
write!(f, " {cascade}")?;
6912+
}
6913+
if let Some(granted_by) = &self.granted_by {
6914+
write!(f, " AS {granted_by}")?;
6915+
}
6916+
Ok(())
6917+
}
6918+
}
6919+
68706920
/// SQL assignment `foo = expr` as used in SQLUpdate
68716921
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
68726922
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ impl Spanned for Statement {
491491
Statement::CreateStage { .. } => Span::empty(),
492492
Statement::Assert { .. } => Span::empty(),
493493
Statement::Grant { .. } => Span::empty(),
494+
Statement::Deny { .. } => Span::empty(),
494495
Statement::Revoke { .. } => Span::empty(),
495496
Statement::Deallocate { .. } => Span::empty(),
496497
Statement::Execute { .. } => Span::empty(),

src/dialect/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub use self::postgresql::PostgreSqlDialect;
4949
pub use self::redshift::RedshiftSqlDialect;
5050
pub use self::snowflake::SnowflakeDialect;
5151
pub use self::sqlite::SQLiteDialect;
52-
use crate::ast::{ColumnOption, Expr, Statement};
52+
use crate::ast::{ColumnOption, Expr, GranteesType, Statement};
5353
pub use crate::keywords;
5454
use crate::keywords::Keyword;
5555
use crate::parser::{Parser, ParserError};
@@ -910,6 +910,11 @@ pub trait Dialect: Debug + Any {
910910
&[]
911911
}
912912

913+
/// Returns grantee types that should be treated as identifiers
914+
fn get_reserved_grantees_types(&self) -> &[GranteesType] {
915+
&[]
916+
}
917+
913918
/// Returns true if this dialect supports the `TABLESAMPLE` option
914919
/// before the table alias option. For example:
915920
///

src/dialect/mssql.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
use crate::ast::helpers::attached_token::AttachedToken;
1919
use crate::ast::{
20-
BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, IfStatement, Statement,
21-
TriggerObject,
20+
BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, GranteesType,
21+
IfStatement, Statement, TriggerObject,
2222
};
2323
use crate::dialect::Dialect;
2424
use crate::keywords::{self, Keyword};
@@ -52,6 +52,10 @@ impl Dialect for MsSqlDialect {
5252
|| ch == '_'
5353
}
5454

55+
fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
56+
Some('[')
57+
}
58+
5559
/// SQL Server has `CONVERT(type, value)` instead of `CONVERT(value, type)`
5660
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16>
5761
fn convert_type_before_value(&self) -> bool {
@@ -119,6 +123,11 @@ impl Dialect for MsSqlDialect {
119123
true
120124
}
121125

126+
/// See <https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/server-level-roles>
127+
fn get_reserved_grantees_types(&self) -> &[GranteesType] {
128+
&[GranteesType::Public]
129+
}
130+
122131
fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
123132
!keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) && !RESERVED_FOR_COLUMN_ALIAS.contains(kw)
124133
}

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ define_keywords!(
278278
DELIMITER,
279279
DELTA,
280280
DENSE_RANK,
281+
DENY,
281282
DEREF,
282283
DESC,
283284
DESCRIBE,

src/parser/mod.rs

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,10 @@ impl<'a> Parser<'a> {
583583
Keyword::SHOW => self.parse_show(),
584584
Keyword::USE => self.parse_use(),
585585
Keyword::GRANT => self.parse_grant(),
586+
Keyword::DENY => {
587+
self.prev_token();
588+
self.parse_deny()
589+
}
586590
Keyword::REVOKE => self.parse_revoke(),
587591
Keyword::START => self.parse_start_transaction(),
588592
Keyword::BEGIN => self.parse_begin(),
@@ -13381,23 +13385,32 @@ impl<'a> Parser<'a> {
1338113385

1338213386
/// Parse a GRANT statement.
1338313387
pub fn parse_grant(&mut self) -> Result<Statement, ParserError> {
13384-
let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?;
13388+
let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?;
1338513389

1338613390
self.expect_keyword_is(Keyword::TO)?;
1338713391
let grantees = self.parse_grantees()?;
1338813392

1338913393
let with_grant_option =
1339013394
self.parse_keywords(&[Keyword::WITH, Keyword::GRANT, Keyword::OPTION]);
1339113395

13392-
let granted_by = self
13393-
.parse_keywords(&[Keyword::GRANTED, Keyword::BY])
13394-
.then(|| self.parse_identifier().unwrap());
13396+
let as_grantor = if self.parse_keywords(&[Keyword::AS]) {
13397+
Some(self.parse_identifier()?)
13398+
} else {
13399+
None
13400+
};
13401+
13402+
let granted_by = if self.parse_keywords(&[Keyword::GRANTED, Keyword::BY]) {
13403+
Some(self.parse_identifier()?)
13404+
} else {
13405+
None
13406+
};
1339513407

1339613408
Ok(Statement::Grant {
1339713409
privileges,
1339813410
objects,
1339913411
grantees,
1340013412
with_grant_option,
13413+
as_grantor,
1340113414
granted_by,
1340213415
})
1340313416
}
@@ -13406,7 +13419,7 @@ impl<'a> Parser<'a> {
1340613419
let mut values = vec![];
1340713420
let mut grantee_type = GranteesType::None;
1340813421
loop {
13409-
grantee_type = if self.parse_keyword(Keyword::ROLE) {
13422+
let new_grantee_type = if self.parse_keyword(Keyword::ROLE) {
1341013423
GranteesType::Role
1341113424
} else if self.parse_keyword(Keyword::USER) {
1341213425
GranteesType::User
@@ -13423,9 +13436,19 @@ impl<'a> Parser<'a> {
1342313436
} else if self.parse_keyword(Keyword::APPLICATION) {
1342413437
GranteesType::Application
1342513438
} else {
13426-
grantee_type // keep from previous iteraton, if not specified
13439+
grantee_type.clone() // keep from previous iteraton, if not specified
1342713440
};
1342813441

13442+
if self
13443+
.dialect
13444+
.get_reserved_grantees_types()
13445+
.contains(&new_grantee_type)
13446+
{
13447+
self.prev_token();
13448+
} else {
13449+
grantee_type = new_grantee_type;
13450+
}
13451+
1342913452
let grantee = if grantee_type == GranteesType::Public {
1343013453
Grantee {
1343113454
grantee_type: grantee_type.clone(),
@@ -13460,7 +13483,7 @@ impl<'a> Parser<'a> {
1346013483
Ok(values)
1346113484
}
1346213485

13463-
pub fn parse_grant_revoke_privileges_objects(
13486+
pub fn parse_grant_deny_revoke_privileges_objects(
1346413487
&mut self,
1346513488
) -> Result<(Privileges, Option<GrantObjects>), ParserError> {
1346613489
let privileges = if self.parse_keyword(Keyword::ALL) {
@@ -13510,7 +13533,6 @@ impl<'a> Parser<'a> {
1351013533
let object_type = self.parse_one_of_keywords(&[
1351113534
Keyword::SEQUENCE,
1351213535
Keyword::DATABASE,
13513-
Keyword::DATABASE,
1351413536
Keyword::SCHEMA,
1351513537
Keyword::TABLE,
1351613538
Keyword::VIEW,
@@ -13605,6 +13627,9 @@ impl<'a> Parser<'a> {
1360513627
Ok(Action::Create { obj_type })
1360613628
} else if self.parse_keyword(Keyword::DELETE) {
1360713629
Ok(Action::Delete)
13630+
} else if self.parse_keyword(Keyword::EXEC) {
13631+
let obj_type = self.maybe_parse_action_execute_obj_type();
13632+
Ok(Action::Exec { obj_type })
1360813633
} else if self.parse_keyword(Keyword::EXECUTE) {
1360913634
let obj_type = self.maybe_parse_action_execute_obj_type();
1361013635
Ok(Action::Execute { obj_type })
@@ -13803,16 +13828,51 @@ impl<'a> Parser<'a> {
1380313828
}
1380413829
}
1380513830

13831+
/// Parse [`Statement::Deny`]
13832+
pub fn parse_deny(&mut self) -> Result<Statement, ParserError> {
13833+
self.expect_keyword(Keyword::DENY)?;
13834+
13835+
let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?;
13836+
let objects = match objects {
13837+
Some(o) => o,
13838+
None => {
13839+
return parser_err!(
13840+
"DENY statements must specify an object",
13841+
self.peek_token().span.start
13842+
)
13843+
}
13844+
};
13845+
13846+
self.expect_keyword_is(Keyword::TO)?;
13847+
let grantees = self.parse_grantees()?;
13848+
let cascade = self.parse_cascade_option();
13849+
let granted_by = if self.parse_keywords(&[Keyword::AS]) {
13850+
Some(self.parse_identifier()?)
13851+
} else {
13852+
None
13853+
};
13854+
13855+
Ok(Statement::Deny(DenyStatement {
13856+
privileges,
13857+
objects,
13858+
grantees,
13859+
cascade,
13860+
granted_by,
13861+
}))
13862+
}
13863+
1380613864
/// Parse a REVOKE statement
1380713865
pub fn parse_revoke(&mut self) -> Result<Statement, ParserError> {
13808-
let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?;
13866+
let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?;
1380913867

1381013868
self.expect_keyword_is(Keyword::FROM)?;
1381113869
let grantees = self.parse_grantees()?;
1381213870

13813-
let granted_by = self
13814-
.parse_keywords(&[Keyword::GRANTED, Keyword::BY])
13815-
.then(|| self.parse_identifier().unwrap());
13871+
let granted_by = if self.parse_keywords(&[Keyword::GRANTED, Keyword::BY]) {
13872+
Some(self.parse_identifier()?)
13873+
} else {
13874+
None
13875+
};
1381613876

1381713877
let cascade = self.parse_cascade_option();
1381813878

tests/sqlparser_common.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9331,6 +9331,39 @@ fn parse_grant() {
93319331
verified_stmt("GRANT USAGE ON WAREHOUSE wh1 TO ROLE role1");
93329332
verified_stmt("GRANT OWNERSHIP ON INTEGRATION int1 TO ROLE role1");
93339333
verified_stmt("GRANT SELECT ON VIEW view1 TO ROLE role1");
9334+
verified_stmt("GRANT EXEC ON my_sp TO runner");
9335+
verified_stmt("GRANT UPDATE ON my_table TO updater_role AS dbo");
9336+
9337+
all_dialects_where(|d| d.identifier_quote_style("none") == Some('['))
9338+
.verified_stmt("GRANT SELECT ON [my_table] TO [public]");
9339+
}
9340+
9341+
#[test]
9342+
fn parse_deny() {
9343+
let sql = "DENY INSERT, DELETE ON users TO analyst CASCADE AS admin";
9344+
match verified_stmt(sql) {
9345+
Statement::Deny(deny) => {
9346+
assert_eq!(
9347+
Privileges::Actions(vec![Action::Insert { columns: None }, Action::Delete]),
9348+
deny.privileges
9349+
);
9350+
assert_eq!(
9351+
&GrantObjects::Tables(vec![ObjectName::from(vec![Ident::new("users")])]),
9352+
&deny.objects
9353+
);
9354+
assert_eq_vec(&["analyst"], &deny.grantees);
9355+
assert_eq!(Some(CascadeOption::Cascade), deny.cascade);
9356+
assert_eq!(Some(Ident::from("admin")), deny.granted_by);
9357+
}
9358+
_ => unreachable!(),
9359+
}
9360+
9361+
verified_stmt("DENY SELECT, INSERT, UPDATE, DELETE ON db1.sc1 TO role1, role2");
9362+
verified_stmt("DENY ALL ON db1.sc1 TO role1");
9363+
verified_stmt("DENY EXEC ON my_sp TO runner");
9364+
9365+
all_dialects_where(|d| d.identifier_quote_style("none") == Some('['))
9366+
.verified_stmt("DENY SELECT ON [my_table] TO [public]");
93349367
}
93359368

93369369
#[test]

tests/sqlparser_mssql.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,3 +2340,13 @@ fn parse_print() {
23402340
let _ = ms().verified_stmt("PRINT N'Hello, ⛄️!'");
23412341
let _ = ms().verified_stmt("PRINT @my_variable");
23422342
}
2343+
2344+
#[test]
2345+
fn parse_mssql_grant() {
2346+
ms().verified_stmt("GRANT SELECT ON my_table TO public, db_admin");
2347+
}
2348+
2349+
#[test]
2350+
fn parse_mssql_deny() {
2351+
ms().verified_stmt("DENY SELECT ON my_table TO public, db_admin");
2352+
}

tests/sqlparser_mysql.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,6 +3538,7 @@ fn parse_grant() {
35383538
objects,
35393539
grantees,
35403540
with_grant_option,
3541+
as_grantor: _,
35413542
granted_by,
35423543
} = stmt
35433544
{

0 commit comments

Comments
 (0)