Skip to content

Commit c35dcc9

Browse files
mskrzypkowsMaciej Skrzypkowskialamb
authored
Support redshift's columns definition list for system information functions (#769)
* parsing of redshift's column definition list for pg_get_late_binding_view_cols pg_get_cols pg_get_grantee_by_iam_role pg_get_iam_role_by_user * Renamed ColsDefinition to TableAliasDefinition added generic dialect * Tests fixed * Visitor for IdentPair * Parsing redshift table alias based on indentifier and parentheses instead of function name * fix clippy --------- Co-authored-by: Maciej Skrzypkowski <maciej.skrzypkowski@satoricyber.com> Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
1 parent a2fea10 commit c35dcc9

14 files changed

+194
-0
lines changed

src/ast/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4141,6 +4141,35 @@ impl fmt::Display for SearchModifier {
41414141
}
41424142
}
41434143

4144+
/// A result table definition i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)`
4145+
/// used for redshift functions: pg_get_late_binding_view_cols, pg_get_cols, pg_get_grantee_by_iam_role,pg_get_iam_role_by_user
4146+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4147+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4148+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4149+
pub struct TableAliasDefinition {
4150+
pub name: Ident,
4151+
pub args: Vec<IdentPair>,
4152+
}
4153+
4154+
impl fmt::Display for TableAliasDefinition {
4155+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4156+
write!(f, "{}({})", self.name, display_comma_separated(&self.args))?;
4157+
Ok(())
4158+
}
4159+
}
4160+
4161+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4162+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4163+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4164+
pub struct IdentPair(pub Ident, pub Ident);
4165+
4166+
impl fmt::Display for IdentPair {
4167+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4168+
write!(f, "{} {}", self.0, self.1)?;
4169+
Ok(())
4170+
}
4171+
}
4172+
41444173
#[cfg(test)]
41454174
mod tests {
41464175
use super::*;

src/ast/query.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,9 @@ pub enum TableFactor {
580580
/// vector of arguments, in the case of a table-valued function call,
581581
/// whereas it's `None` in the case of a regular table name.
582582
args: Option<Vec<FunctionArg>>,
583+
/// A table alias definition i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)`
584+
/// used for redshift functions: pg_get_late_binding_view_cols, pg_get_cols, pg_get_grantee_by_iam_role,pg_get_iam_role_by_user)
585+
columns_definition: Option<TableAliasDefinition>,
583586
/// MSSQL-specific `WITH (...)` hints such as NOLOCK.
584587
with_hints: Vec<Expr>,
585588
},
@@ -628,6 +631,7 @@ impl fmt::Display for TableFactor {
628631
name,
629632
alias,
630633
args,
634+
columns_definition,
631635
with_hints,
632636
} => {
633637
write!(f, "{name}")?;
@@ -637,6 +641,9 @@ impl fmt::Display for TableFactor {
637641
if let Some(alias) = alias {
638642
write!(f, " AS {alias}")?;
639643
}
644+
if let Some(columns_definition) = columns_definition {
645+
write!(f, " {columns_definition}")?;
646+
}
640647
if !with_hints.is_empty() {
641648
write!(f, " WITH ({})", display_comma_separated(with_hints))?;
642649
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
669669
Keyword::OUTER,
670670
Keyword::SET,
671671
Keyword::QUALIFY,
672+
Keyword::AS,
672673
];
673674

674675
/// Can't be used as a column alias, so that `SELECT <expr> alias`
@@ -698,4 +699,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
698699
// Reserved only as a column alias in the `SELECT` clause
699700
Keyword::FROM,
700701
Keyword::INTO,
702+
Keyword::AS,
701703
];

src/parser.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5649,6 +5649,7 @@ impl<'a> Parser<'a> {
56495649
} else {
56505650
None
56515651
};
5652+
let columns_definition = self.parse_redshift_columns_definition_list()?;
56525653
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
56535654
// MSSQL-specific table hints:
56545655
let mut with_hints = vec![];
@@ -5665,11 +5666,56 @@ impl<'a> Parser<'a> {
56655666
name,
56665667
alias,
56675668
args,
5669+
columns_definition,
56685670
with_hints,
56695671
})
56705672
}
56715673
}
56725674

5675+
fn parse_redshift_columns_definition_list(
5676+
&mut self,
5677+
) -> Result<Option<TableAliasDefinition>, ParserError> {
5678+
if !dialect_of!(self is RedshiftSqlDialect | GenericDialect) {
5679+
return Ok(None);
5680+
}
5681+
5682+
if let Some(col_definition_list_name) = self.parse_optional_columns_definition_list_alias()
5683+
{
5684+
if self.consume_token(&Token::LParen) {
5685+
let names = self.parse_comma_separated(Parser::parse_ident_pair)?;
5686+
self.expect_token(&Token::RParen)?;
5687+
Ok(Some(TableAliasDefinition {
5688+
name: col_definition_list_name,
5689+
args: names,
5690+
}))
5691+
} else {
5692+
self.prev_token();
5693+
Ok(None)
5694+
}
5695+
} else {
5696+
Ok(None)
5697+
}
5698+
}
5699+
5700+
fn parse_optional_columns_definition_list_alias(&mut self) -> Option<Ident> {
5701+
match self.next_token().token {
5702+
Token::Word(w) if !keywords::RESERVED_FOR_TABLE_ALIAS.contains(&w.keyword) => {
5703+
Some(w.to_ident())
5704+
}
5705+
_ => {
5706+
self.prev_token();
5707+
None
5708+
}
5709+
}
5710+
}
5711+
5712+
fn parse_ident_pair(&mut self) -> Result<IdentPair, ParserError> {
5713+
Ok(IdentPair(
5714+
self.parse_identifier()?,
5715+
self.parse_identifier()?,
5716+
))
5717+
}
5718+
56735719
pub fn parse_derived_table_factor(
56745720
&mut self,
56755721
lateral: IsLateral,

src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ pub fn table(name: impl Into<String>) -> TableFactor {
185185
name: ObjectName(vec![Ident::new(name.into())]),
186186
alias: None,
187187
args: None,
188+
columns_definition: None,
188189
with_hints: vec![],
189190
}
190191
}

tests/sqlparser_bigquery.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ fn parse_table_identifiers() {
4444
name: ObjectName(expected),
4545
alias: None,
4646
args: None,
47+
columns_definition: None,
4748
with_hints: vec![],
4849
},
4950
joins: vec![]

tests/sqlparser_clickhouse.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ fn parse_map_access_expr() {
6060
name: ObjectName(vec![Ident::new("foos")]),
6161
alias: None,
6262
args: None,
63+
columns_definition: None,
6364
with_hints: vec![],
6465
},
6566
joins: vec![]
@@ -164,11 +165,13 @@ fn parse_delimited_identifiers() {
164165
name,
165166
alias,
166167
args,
168+
columns_definition,
167169
with_hints,
168170
} => {
169171
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
170172
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
171173
assert!(args.is_none());
174+
assert!(columns_definition.is_none());
172175
assert!(with_hints.is_empty());
173176
}
174177
_ => panic!("Expecting TableFactor::Table"),

tests/sqlparser_common.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ fn parse_update_set_from() {
210210
name: ObjectName(vec![Ident::new("t1")]),
211211
alias: None,
212212
args: None,
213+
columns_definition: None,
213214
with_hints: vec![],
214215
},
215216
joins: vec![],
@@ -236,6 +237,7 @@ fn parse_update_set_from() {
236237
name: ObjectName(vec![Ident::new("t1")]),
237238
alias: None,
238239
args: None,
240+
columns_definition: None,
239241
with_hints: vec![],
240242
},
241243
joins: vec![],
@@ -298,6 +300,7 @@ fn parse_update_with_table_alias() {
298300
columns: vec![],
299301
}),
300302
args: None,
303+
columns_definition: None,
301304
with_hints: vec![],
302305
},
303306
joins: vec![],
@@ -353,6 +356,7 @@ fn parse_delete_statement() {
353356
name: ObjectName(vec![Ident::with_quote('"', "table")]),
354357
alias: None,
355358
args: None,
359+
columns_definition: None,
356360
with_hints: vec![],
357361
},
358362
table_name
@@ -379,6 +383,7 @@ fn parse_where_delete_statement() {
379383
name: ObjectName(vec![Ident::new("foo")]),
380384
alias: None,
381385
args: None,
386+
columns_definition: None,
382387
with_hints: vec![],
383388
},
384389
table_name,
@@ -419,6 +424,7 @@ fn parse_where_delete_with_alias_statement() {
419424
columns: vec![],
420425
}),
421426
args: None,
427+
columns_definition: None,
422428
with_hints: vec![],
423429
},
424430
table_name,
@@ -432,6 +438,7 @@ fn parse_where_delete_with_alias_statement() {
432438
columns: vec![],
433439
}),
434440
args: None,
441+
columns_definition: None,
435442
with_hints: vec![],
436443
}),
437444
using
@@ -3447,6 +3454,7 @@ fn parse_interval_and_or_xor() {
34473454
}]),
34483455
alias: None,
34493456
args: None,
3457+
columns_definition: None,
34503458
with_hints: vec![],
34513459
},
34523460
joins: vec![],
@@ -3893,6 +3901,7 @@ fn parse_implicit_join() {
38933901
name: ObjectName(vec!["t1".into()]),
38943902
alias: None,
38953903
args: None,
3904+
columns_definition: None,
38963905
with_hints: vec![],
38973906
},
38983907
joins: vec![],
@@ -3902,6 +3911,7 @@ fn parse_implicit_join() {
39023911
name: ObjectName(vec!["t2".into()]),
39033912
alias: None,
39043913
args: None,
3914+
columns_definition: None,
39053915
with_hints: vec![],
39063916
},
39073917
joins: vec![],
@@ -3919,13 +3929,15 @@ fn parse_implicit_join() {
39193929
name: ObjectName(vec!["t1a".into()]),
39203930
alias: None,
39213931
args: None,
3932+
columns_definition: None,
39223933
with_hints: vec![],
39233934
},
39243935
joins: vec![Join {
39253936
relation: TableFactor::Table {
39263937
name: ObjectName(vec!["t1b".into()]),
39273938
alias: None,
39283939
args: None,
3940+
columns_definition: None,
39293941
with_hints: vec![],
39303942
},
39313943
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
@@ -3936,13 +3948,15 @@ fn parse_implicit_join() {
39363948
name: ObjectName(vec!["t2a".into()]),
39373949
alias: None,
39383950
args: None,
3951+
columns_definition: None,
39393952
with_hints: vec![],
39403953
},
39413954
joins: vec![Join {
39423955
relation: TableFactor::Table {
39433956
name: ObjectName(vec!["t2b".into()]),
39443957
alias: None,
39453958
args: None,
3959+
columns_definition: None,
39463960
with_hints: vec![],
39473961
},
39483962
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
@@ -3963,6 +3977,7 @@ fn parse_cross_join() {
39633977
name: ObjectName(vec![Ident::new("t2")]),
39643978
alias: None,
39653979
args: None,
3980+
columns_definition: None,
39663981
with_hints: vec![],
39673982
},
39683983
join_operator: JoinOperator::CrossJoin,
@@ -3983,6 +3998,7 @@ fn parse_joins_on() {
39833998
name: ObjectName(vec![Ident::new(relation.into())]),
39843999
alias,
39854000
args: None,
4001+
columns_definition: None,
39864002
with_hints: vec![],
39874003
},
39884004
join_operator: f(JoinConstraint::On(Expr::BinaryOp {
@@ -4052,6 +4068,7 @@ fn parse_joins_using() {
40524068
name: ObjectName(vec![Ident::new(relation.into())]),
40534069
alias,
40544070
args: None,
4071+
columns_definition: None,
40554072
with_hints: vec![],
40564073
},
40574074
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
@@ -4113,6 +4130,7 @@ fn parse_natural_join() {
41134130
name: ObjectName(vec![Ident::new("t2")]),
41144131
alias,
41154132
args: None,
4133+
columns_definition: None,
41164134
with_hints: vec![],
41174135
},
41184136
join_operator: f(JoinConstraint::Natural),
@@ -4377,6 +4395,7 @@ fn parse_derived_tables() {
43774395
name: ObjectName(vec!["t2".into()]),
43784396
alias: None,
43794397
args: None,
4398+
columns_definition: None,
43804399
with_hints: vec![],
43814400
},
43824401
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
@@ -5668,6 +5687,7 @@ fn parse_merge() {
56685687
columns: vec![],
56695688
}),
56705689
args: None,
5690+
columns_definition: None,
56715691
with_hints: vec![],
56725692
}
56735693
);
@@ -5691,6 +5711,7 @@ fn parse_merge() {
56915711
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),
56925712
alias: None,
56935713
args: None,
5714+
columns_definition: None,
56945715
with_hints: vec![],
56955716
},
56965717
joins: vec![],

tests/sqlparser_hive.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,13 @@ fn parse_delimited_identifiers() {
320320
name,
321321
alias,
322322
args,
323+
columns_definition,
323324
with_hints,
324325
} => {
325326
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
326327
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
327328
assert!(args.is_none());
329+
assert!(columns_definition.is_none());
328330
assert!(with_hints.is_empty());
329331
}
330332
_ => panic!("Expecting TableFactor::Table"),

tests/sqlparser_mssql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,13 @@ fn parse_delimited_identifiers() {
152152
name,
153153
alias,
154154
args,
155+
columns_definition,
155156
with_hints,
156157
} => {
157158
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
158159
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
159160
assert!(args.is_none());
161+
assert!(columns_definition.is_none());
160162
assert!(with_hints.is_empty());
161163
}
162164
_ => panic!("Expecting TableFactor::Table"),

0 commit comments

Comments
 (0)