Skip to content

Commit 2210860

Browse files
mvzinkayman-sigma
authored andcommitted
Fix MySQL parsing of GRANT, REVOKE, and CREATE VIEW (apache#1538)
1 parent 3357edc commit 2210860

11 files changed

+589
-67
lines changed

src/ast/mod.rs

+125-13
Original file line numberDiff line numberDiff line change
@@ -2386,7 +2386,7 @@ pub enum Statement {
23862386
identity: Option<TruncateIdentityOption>,
23872387
/// Postgres-specific option
23882388
/// [ CASCADE | RESTRICT ]
2389-
cascade: Option<TruncateCascadeOption>,
2389+
cascade: Option<CascadeOption>,
23902390
/// ClickHouse-specific option
23912391
/// [ ON CLUSTER cluster_name ]
23922392
///
@@ -2527,6 +2527,8 @@ pub enum Statement {
25272527
/// if not None, has Clickhouse `TO` clause, specify the table into which to insert results
25282528
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/view#materialized-view>
25292529
to: Option<ObjectName>,
2530+
/// MySQL: Optional parameters for the view algorithm, definer, and security context
2531+
params: Option<CreateViewParams>,
25302532
},
25312533
/// ```sql
25322534
/// CREATE TABLE
@@ -3196,9 +3198,9 @@ pub enum Statement {
31963198
Revoke {
31973199
privileges: Privileges,
31983200
objects: GrantObjects,
3199-
grantees: Vec<Ident>,
3201+
grantees: Vec<Grantee>,
32003202
granted_by: Option<Ident>,
3201-
cascade: bool,
3203+
cascade: Option<CascadeOption>,
32023204
},
32033205
/// ```sql
32043206
/// DEALLOCATE [ PREPARE ] { name | ALL }
@@ -3634,8 +3636,8 @@ impl fmt::Display for Statement {
36343636
}
36353637
if let Some(cascade) = cascade {
36363638
match cascade {
3637-
TruncateCascadeOption::Cascade => write!(f, " CASCADE")?,
3638-
TruncateCascadeOption::Restrict => write!(f, " RESTRICT")?,
3639+
CascadeOption::Cascade => write!(f, " CASCADE")?,
3640+
CascadeOption::Restrict => write!(f, " RESTRICT")?,
36393641
}
36403642
}
36413643

@@ -3964,11 +3966,19 @@ impl fmt::Display for Statement {
39643966
if_not_exists,
39653967
temporary,
39663968
to,
3969+
params,
39673970
} => {
39683971
write!(
39693972
f,
3970-
"CREATE {or_replace}{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
3973+
"CREATE {or_replace}",
39713974
or_replace = if *or_replace { "OR REPLACE " } else { "" },
3975+
)?;
3976+
if let Some(params) = params {
3977+
params.fmt(f)?;
3978+
}
3979+
write!(
3980+
f,
3981+
"{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
39723982
materialized = if *materialized { "MATERIALIZED " } else { "" },
39733983
name = name,
39743984
temporary = if *temporary { "TEMPORARY " } else { "" },
@@ -4719,7 +4729,9 @@ impl fmt::Display for Statement {
47194729
if let Some(grantor) = granted_by {
47204730
write!(f, " GRANTED BY {grantor}")?;
47214731
}
4722-
write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?;
4732+
if let Some(cascade) = cascade {
4733+
write!(f, " {}", cascade)?;
4734+
}
47234735
Ok(())
47244736
}
47254737
Statement::Deallocate { name, prepare } => write!(
@@ -5121,16 +5133,25 @@ pub enum TruncateIdentityOption {
51215133
Continue,
51225134
}
51235135

5124-
/// PostgreSQL cascade option for TRUNCATE table
5136+
/// Cascade/restrict option for Postgres TRUNCATE table, MySQL GRANT/REVOKE, etc.
51255137
/// [ CASCADE | RESTRICT ]
51265138
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
51275139
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51285140
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5129-
pub enum TruncateCascadeOption {
5141+
pub enum CascadeOption {
51305142
Cascade,
51315143
Restrict,
51325144
}
51335145

5146+
impl Display for CascadeOption {
5147+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5148+
match self {
5149+
CascadeOption::Cascade => write!(f, "CASCADE"),
5150+
CascadeOption::Restrict => write!(f, "RESTRICT"),
5151+
}
5152+
}
5153+
}
5154+
51345155
/// Transaction started with [ TRANSACTION | WORK ]
51355156
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
51365157
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -5422,7 +5443,7 @@ impl fmt::Display for Action {
54225443
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
54235444
pub struct Grantee {
54245445
pub grantee_type: GranteesType,
5425-
pub name: Option<ObjectName>,
5446+
pub name: Option<GranteeName>,
54265447
}
54275448

54285449
impl fmt::Display for Grantee {
@@ -5455,7 +5476,7 @@ impl fmt::Display for Grantee {
54555476
GranteesType::None => (),
54565477
}
54575478
if let Some(ref name) = self.name {
5458-
write!(f, "{}", name)?;
5479+
name.fmt(f)?;
54595480
}
54605481
Ok(())
54615482
}
@@ -5476,6 +5497,28 @@ pub enum GranteesType {
54765497
None,
54775498
}
54785499

5500+
/// Users/roles designated in a GRANT/REVOKE
5501+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
5502+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5503+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5504+
pub enum GranteeName {
5505+
/// A bare identifier
5506+
ObjectName(ObjectName),
5507+
/// A MySQL user/host pair such as 'root'@'%'
5508+
UserHost { user: Ident, host: Ident },
5509+
}
5510+
5511+
impl fmt::Display for GranteeName {
5512+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5513+
match self {
5514+
GranteeName::ObjectName(name) => name.fmt(f),
5515+
GranteeName::UserHost { user, host } => {
5516+
write!(f, "{}@{}", user, host)
5517+
}
5518+
}
5519+
}
5520+
}
5521+
54795522
/// Objects on which privileges are granted in a GRANT statement.
54805523
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
54815524
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -7478,15 +7521,84 @@ pub enum MySQLColumnPosition {
74787521
impl Display for MySQLColumnPosition {
74797522
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74807523
match self {
7481-
MySQLColumnPosition::First => Ok(write!(f, "FIRST")?),
7524+
MySQLColumnPosition::First => write!(f, "FIRST"),
74827525
MySQLColumnPosition::After(ident) => {
74837526
let column_name = &ident.value;
7484-
Ok(write!(f, "AFTER {column_name}")?)
7527+
write!(f, "AFTER {column_name}")
74857528
}
74867529
}
74877530
}
74887531
}
74897532

7533+
/// MySQL `CREATE VIEW` algorithm parameter: [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
7534+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7535+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7536+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7537+
pub enum CreateViewAlgorithm {
7538+
Undefined,
7539+
Merge,
7540+
TempTable,
7541+
}
7542+
7543+
impl Display for CreateViewAlgorithm {
7544+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7545+
match self {
7546+
CreateViewAlgorithm::Undefined => write!(f, "UNDEFINED"),
7547+
CreateViewAlgorithm::Merge => write!(f, "MERGE"),
7548+
CreateViewAlgorithm::TempTable => write!(f, "TEMPTABLE"),
7549+
}
7550+
}
7551+
}
7552+
/// MySQL `CREATE VIEW` security parameter: [SQL SECURITY { DEFINER | INVOKER }]
7553+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7554+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7555+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7556+
pub enum CreateViewSecurity {
7557+
Definer,
7558+
Invoker,
7559+
}
7560+
7561+
impl Display for CreateViewSecurity {
7562+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7563+
match self {
7564+
CreateViewSecurity::Definer => write!(f, "DEFINER"),
7565+
CreateViewSecurity::Invoker => write!(f, "INVOKER"),
7566+
}
7567+
}
7568+
}
7569+
7570+
/// [MySQL] `CREATE VIEW` additional parameters
7571+
///
7572+
/// [MySQL]: https://dev.mysql.com/doc/refman/9.1/en/create-view.html
7573+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7574+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7575+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7576+
pub struct CreateViewParams {
7577+
pub algorithm: Option<CreateViewAlgorithm>,
7578+
pub definer: Option<GranteeName>,
7579+
pub security: Option<CreateViewSecurity>,
7580+
}
7581+
7582+
impl Display for CreateViewParams {
7583+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7584+
let CreateViewParams {
7585+
algorithm,
7586+
definer,
7587+
security,
7588+
} = self;
7589+
if let Some(algorithm) = algorithm {
7590+
write!(f, "ALGORITHM = {algorithm} ")?;
7591+
}
7592+
if let Some(definers) = definer {
7593+
write!(f, "DEFINER = {definers} ")?;
7594+
}
7595+
if let Some(security) = security {
7596+
write!(f, "SQL SECURITY {security} ")?;
7597+
}
7598+
Ok(())
7599+
}
7600+
}
7601+
74907602
/// Engine of DB. Some warehouse has parameters of engine, e.g. [clickhouse]
74917603
///
74927604
/// [clickhouse]: https://clickhouse.com/docs/en/engines/table-engines

src/ast/spans.rs

+1
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ impl Spanned for Statement {
373373
if_not_exists: _,
374374
temporary: _,
375375
to,
376+
params: _,
376377
} => union_spans(
377378
core::iter::once(name.span())
378379
.chain(columns.iter().map(|i| i.span()))

src/dialect/generic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,8 @@ impl Dialect for GenericDialect {
135135
fn supports_nested_comments(&self) -> bool {
136136
true
137137
}
138+
139+
fn supports_user_host_grantee(&self) -> bool {
140+
true
141+
}
138142
}

src/dialect/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,11 @@ pub trait Dialect: Debug + Any {
434434
false
435435
}
436436

437+
/// Does the dialect support MySQL-style `'user'@'host'` grantee syntax?
438+
fn supports_user_host_grantee(&self) -> bool {
439+
false
440+
}
441+
437442
/// Dialect-specific infix parser override
438443
///
439444
/// This method is called to parse the next infix expression.

src/dialect/mysql.rs

+4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ impl Dialect for MySqlDialect {
107107
fn supports_insert_set(&self) -> bool {
108108
true
109109
}
110+
111+
fn supports_user_host_grantee(&self) -> bool {
112+
true
113+
}
110114
}
111115

112116
/// `LOCK TABLES`

src/keywords.rs

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ define_keywords!(
8484
AFTER,
8585
AGAINST,
8686
AGGREGATION,
87+
ALGORITHM,
8788
ALIAS,
8889
ALL,
8990
ALLOCATE,
@@ -248,6 +249,7 @@ define_keywords!(
248249
DEFERRED,
249250
DEFINE,
250251
DEFINED,
252+
DEFINER,
251253
DELAYED,
252254
DELETE,
253255
DELIMITED,
@@ -423,6 +425,7 @@ define_keywords!(
423425
INTERSECTION,
424426
INTERVAL,
425427
INTO,
428+
INVOKER,
426429
IS,
427430
ISODOW,
428431
ISOLATION,
@@ -780,6 +783,7 @@ define_keywords!(
780783
TBLPROPERTIES,
781784
TEMP,
782785
TEMPORARY,
786+
TEMPTABLE,
783787
TERMINATED,
784788
TERSE,
785789
TEXT,
@@ -828,6 +832,7 @@ define_keywords!(
828832
UNBOUNDED,
829833
UNCACHE,
830834
UNCOMMITTED,
835+
UNDEFINED,
831836
UNFREEZE,
832837
UNION,
833838
UNIQUE,

0 commit comments

Comments
 (0)