From db9df86ae733cf44ba6fe40f6eac6c4efe3fb3f9 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 23 Dec 2022 14:15:32 +0100 Subject: [PATCH 1/6] 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 --- src/ast/mod.rs | 27 +++++++++++++ src/ast/query.rs | 7 ++++ src/parser.rs | 47 ++++++++++++++++++++++ src/test_utils.rs | 1 + tests/sqlparser_bigquery.rs | 1 + tests/sqlparser_clickhouse.rs | 3 ++ tests/sqlparser_common.rs | 21 ++++++++++ tests/sqlparser_hive.rs | 2 + tests/sqlparser_mssql.rs | 2 + tests/sqlparser_mysql.rs | 3 ++ tests/sqlparser_postgres.rs | 2 + tests/sqlparser_redshift.rs | 73 +++++++++++++++++++++++++++++++++++ tests/sqlparser_snowflake.rs | 2 + 13 files changed, 191 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index cf936d3c9..a9e2e9d54 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3954,6 +3954,33 @@ impl fmt::Display for SearchModifier { } } +/// A cols definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` +/// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ColsDefinition { + pub name: Ident, + pub args: Vec, +} + +impl fmt::Display for ColsDefinition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}({})", self.name, display_comma_separated(&self.args))?; + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct IdentPair(pub Ident, pub Ident); + +impl fmt::Display for IdentPair { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.0, self.1)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ast/query.rs b/src/ast/query.rs index 9d23b1ae7..0f85ed2c9 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -497,6 +497,9 @@ pub enum TableFactor { /// vector of arguments, in the case of a table-valued function call, /// whereas it's `None` in the case of a regular table name. args: Option>, + /// A cols definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` + /// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) + columns_definition: Option, /// MSSQL-specific `WITH (...)` hints such as NOLOCK. with_hints: Vec, }, @@ -543,6 +546,7 @@ impl fmt::Display for TableFactor { name, alias, args, + columns_definition, with_hints, } => { write!(f, "{}", name)?; @@ -552,6 +556,9 @@ impl fmt::Display for TableFactor { if let Some(alias) = alias { write!(f, " AS {}", alias)?; } + if let Some(columns_definition) = columns_definition { + write!(f, " {}", columns_definition)?; + } if !with_hints.is_empty() { write!(f, " WITH ({})", display_comma_separated(with_hints))?; } diff --git a/src/parser.rs b/src/parser.rs index 528b8c6fe..020da62f2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5356,6 +5356,7 @@ impl<'a> Parser<'a> { } else { None }; + let columns_definition = self.parse_redshift_columns_definition_list(&name)?; let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; // MSSQL-specific table hints: let mut with_hints = vec![]; @@ -5372,11 +5373,57 @@ impl<'a> Parser<'a> { name, alias, args, + columns_definition, with_hints, }) } } + fn parse_redshift_columns_definition_list( + &mut self, + name: &ObjectName, + ) -> Result, ParserError> { + if !dialect_of!(self is RedshiftSqlDialect) { + return Ok(None); + } + + let fname = name + .0 + .last() + .ok_or_else(|| { + ParserError::ParserError("Empty identifier vector for ObjectName".to_string()) + })? + .value + .to_lowercase(); + + if fname == "pg_get_late_binding_view_cols" + || fname == "pg_get_cols" + || fname == "pg_get_grantee_by_iam_role" + || fname == "pg_get_iam_role_by_user" + { + if let Ok(col_definition_list_name) = self.parse_identifier() { + self.expect_token(&Token::LParen)?; + let names = self.parse_comma_separated(Parser::parse_ident_pair)?; + self.expect_token(&Token::RParen)?; + Ok(Some(ColsDefinition { + name: col_definition_list_name, + args: names, + })) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + fn parse_ident_pair(&mut self) -> Result { + Ok(IdentPair( + self.parse_identifier()?, + self.parse_identifier()?, + )) + } + pub fn parse_derived_table_factor( &mut self, lateral: IsLateral, diff --git a/src/test_utils.rs b/src/test_utils.rs index cbb929285..8fba3be72 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -187,6 +187,7 @@ pub fn table(name: impl Into) -> TableFactor { name: ObjectName(vec![Ident::new(name.into())]), alias: None, args: None, + columns_definition: None, with_hints: vec![], } } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 4bf26ba81..2ddf5e3c0 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -45,6 +45,7 @@ fn parse_table_identifiers() { name: ObjectName(expected), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![] diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 3e974d56d..10e0d9ea5 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -60,6 +60,7 @@ fn parse_map_access_expr() { name: ObjectName(vec![Ident::new("foos")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![] @@ -164,11 +165,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e5ed0bb80..9df799f65 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -210,6 +210,7 @@ fn parse_update_set_from() { name: ObjectName(vec![Ident::new("t1")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -236,6 +237,7 @@ fn parse_update_set_from() { name: ObjectName(vec![Ident::new("t1")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -298,6 +300,7 @@ fn parse_update_with_table_alias() { columns: vec![], }), args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -353,6 +356,7 @@ fn parse_delete_statement() { name: ObjectName(vec![Ident::with_quote('"', "table")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, table_name @@ -379,6 +383,7 @@ fn parse_where_delete_statement() { name: ObjectName(vec![Ident::new("foo")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, table_name, @@ -419,6 +424,7 @@ fn parse_where_delete_with_alias_statement() { columns: vec![], }), args: None, + columns_definition: None, with_hints: vec![], }, table_name, @@ -432,6 +438,7 @@ fn parse_where_delete_with_alias_statement() { columns: vec![], }), args: None, + columns_definition: None, with_hints: vec![], }), using @@ -3391,6 +3398,7 @@ fn parse_interval_and_or_xor() { }]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -3785,6 +3793,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t1".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -3794,6 +3803,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t2".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -3811,6 +3821,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t1a".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![Join { @@ -3818,6 +3829,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t1b".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -3828,6 +3840,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t2a".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![Join { @@ -3835,6 +3848,7 @@ fn parse_implicit_join() { name: ObjectName(vec!["t2b".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -3855,6 +3869,7 @@ fn parse_cross_join() { name: ObjectName(vec![Ident::new("t2")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: JoinOperator::CrossJoin, @@ -3875,6 +3890,7 @@ fn parse_joins_on() { name: ObjectName(vec![Ident::new(relation.into())]), alias, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: f(JoinConstraint::On(Expr::BinaryOp { @@ -3944,6 +3960,7 @@ fn parse_joins_using() { name: ObjectName(vec![Ident::new(relation.into())]), alias, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: f(JoinConstraint::Using(vec!["c1".into()])), @@ -4005,6 +4022,7 @@ fn parse_natural_join() { name: ObjectName(vec![Ident::new("t2")]), alias, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: f(JoinConstraint::Natural), @@ -4272,6 +4290,7 @@ fn parse_derived_tables() { name: ObjectName(vec!["t2".into()]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, join_operator: JoinOperator::Inner(JoinConstraint::Natural), @@ -5564,6 +5583,7 @@ fn parse_merge() { columns: vec![], }), args: None, + columns_definition: None, with_hints: vec![], } ); @@ -5587,6 +5607,7 @@ fn parse_merge() { name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 064a090f7..7c17ea120 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -320,11 +320,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 41b0803e4..874dccfab 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -152,11 +152,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 67850b1b9..f70bb972f 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -835,6 +835,7 @@ fn parse_update_with_joins() { columns: vec![] }), args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![Join { @@ -845,6 +846,7 @@ fn parse_update_with_joins() { columns: vec![] }), args: None, + columns_definition: None, with_hints: vec![], }, join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { @@ -966,6 +968,7 @@ fn parse_substring_in_select() { }]), alias: None, args: None, + columns_definition: None, with_hints: vec![] }, joins: vec![] diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 6b26fe48b..2466d8d91 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2188,11 +2188,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 7597ee981..ba56ee76e 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -44,6 +44,7 @@ fn test_square_brackets_over_db_schema_table_name() { ]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -88,6 +89,7 @@ fn test_double_quotes_over_db_schema_table_name() { ]), alias: None, args: None, + columns_definition: None, with_hints: vec![], }, joins: vec![], @@ -107,11 +109,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), @@ -272,3 +276,72 @@ fn test_sharp() { select.projection[0] ); } + +#[test] +fn test_parse_pg_get_late_binding_view_cols() { + let sql = "select * from pg_get_late_binding_view_cols() some_name_cols(view_schema name, view_name name, col_name name)"; + let expected = "SELECT * FROM pg_get_late_binding_view_cols() some_name_cols(view_schema name, view_name name, col_name name)"; + redshift().one_statement_parses_to(sql, expected); + + let select = redshift().verified_only_select(expected); + assert_eq!( + TableFactor::Table { + name: ObjectName(vec![Ident::new("pg_get_late_binding_view_cols")],), + args: Some(vec![]), + alias: None, + columns_definition: Some(ColsDefinition { + name: Ident::new("some_name_cols"), + args: vec![ + IdentPair(Ident::new("view_schema"), Ident::new("name")), + IdentPair(Ident::new("view_name"), Ident::new("name")), + IdentPair(Ident::new("col_name"), Ident::new("name")) + ] + }), + with_hints: vec![] + }, + select.from[0].relation + ); +} + +#[test] +fn test_parse_pg_get_cols() { + let sql = + "SELECT * FROM pg_get_cols() some_name(view_schema name, view_name name, col_name name)"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_grantee_by_iam_role() { + let sql = "SELECT grantee, grantee_type, cmd_type FROM pg_get_grantee_by_iam_role('arn:aws:iam::123456789012:role/Redshift-S3-Write') res_grantee(grantee text, grantee_type text, cmd_type text)"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_iam_role_by_user() { + let sql = "SELECT username, iam_role, cmd FROM pg_get_iam_role_by_user('reg_user1') res_iam_role(username text, iam_role text, cmd text)"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_late_binding_view_cols_in_select() { + let sql = "SELECT pg_get_late_binding_view_cols()"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_cols_in_select() { + let sql = "SELECT pg_get_cols()"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_grantee_by_iam_role_in_select() { + let sql = "SELECT pg_get_grantee_by_iam_role()"; + redshift().verified_stmt(sql); +} + +#[test] +fn test_parse_pg_get_iam_role_by_user_in_select() { + let sql = "SELECT pg_get_iam_role_by_user()"; + redshift().verified_stmt(sql); +} diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index e10141545..2820d64e1 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -206,11 +206,13 @@ fn parse_delimited_identifiers() { name, alias, args, + columns_definition, with_hints, } => { assert_eq!(vec![Ident::with_quote('"', "a table")], name.0); assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name); assert!(args.is_none()); + assert!(columns_definition.is_none()); assert!(with_hints.is_empty()); } _ => panic!("Expecting TableFactor::Table"), From fec98efc231bca12d6e6bdc0b2c4b5f5e9f17dac Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Mon, 9 Jan 2023 12:16:09 +0100 Subject: [PATCH 2/6] Renamed ColsDefinition to TableAliasDefinition added generic dialect --- src/ast/mod.rs | 6 +++--- src/ast/query.rs | 4 ++-- src/parser.rs | 6 +++--- tests/sqlparser_redshift.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index dc0501d3c..1397ae884 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4147,16 +4147,16 @@ impl fmt::Display for SearchModifier { } } -/// A cols definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` +/// A result table definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` /// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct ColsDefinition { +pub struct TableAliasDefinition { pub name: Ident, pub args: Vec, } -impl fmt::Display for ColsDefinition { +impl fmt::Display for TableAliasDefinition { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}({})", self.name, display_comma_separated(&self.args))?; Ok(()) diff --git a/src/ast/query.rs b/src/ast/query.rs index 22a11ac32..693b01009 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -582,9 +582,9 @@ pub enum TableFactor { /// vector of arguments, in the case of a table-valued function call, /// whereas it's `None` in the case of a regular table name. args: Option>, - /// A cols definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` + /// A table alias definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` /// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) - columns_definition: Option, + columns_definition: Option, /// MSSQL-specific `WITH (...)` hints such as NOLOCK. with_hints: Vec, }, diff --git a/src/parser.rs b/src/parser.rs index 1ea4086d8..2539e0e8c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5641,8 +5641,8 @@ impl<'a> Parser<'a> { fn parse_redshift_columns_definition_list( &mut self, name: &ObjectName, - ) -> Result, ParserError> { - if !dialect_of!(self is RedshiftSqlDialect) { + ) -> Result, ParserError> { + if !dialect_of!(self is RedshiftSqlDialect | GenericDialect) { return Ok(None); } @@ -5664,7 +5664,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let names = self.parse_comma_separated(Parser::parse_ident_pair)?; self.expect_token(&Token::RParen)?; - Ok(Some(ColsDefinition { + Ok(Some(TableAliasDefinition { name: col_definition_list_name, args: names, })) diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index ba56ee76e..fdbd66e79 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -289,7 +289,7 @@ fn test_parse_pg_get_late_binding_view_cols() { name: ObjectName(vec![Ident::new("pg_get_late_binding_view_cols")],), args: Some(vec![]), alias: None, - columns_definition: Some(ColsDefinition { + columns_definition: Some(TableAliasDefinition { name: Ident::new("some_name_cols"), args: vec![ IdentPair(Ident::new("view_schema"), Ident::new("name")), From d81d4462e36d3503e45ed9b2fee7302446204dec Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Tue, 10 Jan 2023 21:43:00 +0100 Subject: [PATCH 3/6] Tests fixed --- src/ast/mod.rs | 5 +++-- src/ast/query.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1397ae884..f35790a36 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4147,10 +4147,11 @@ impl fmt::Display for SearchModifier { } } -/// A result table definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` -/// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) +/// A result table definition i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` +/// 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 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct TableAliasDefinition { pub name: Ident, pub args: Vec, diff --git a/src/ast/query.rs b/src/ast/query.rs index 693b01009..52267ff0b 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -582,8 +582,8 @@ pub enum TableFactor { /// vector of arguments, in the case of a table-valued function call, /// whereas it's `None` in the case of a regular table name. args: Option>, - /// A table alias definition (i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` - /// when used with redshift pg_get_late_binding_view_cols/pg_get_cols) + /// A table alias definition i.e. `cols(view_schema name, view_name name, col_name name, col_type varchar, col_num int)` + /// 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) columns_definition: Option, /// MSSQL-specific `WITH (...)` hints such as NOLOCK. with_hints: Vec, From 17e11a1fb3972c2b7f5b24a7b47595d0c7753e94 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Wed, 11 Jan 2023 10:06:14 +0100 Subject: [PATCH 4/6] Visitor for IdentPair --- src/ast/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f35790a36..e5ffd9887 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4166,6 +4166,7 @@ impl fmt::Display for TableAliasDefinition { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct IdentPair(pub Ident, pub Ident); impl fmt::Display for IdentPair { From 4f570a73dac5e2b914401b37dd1b4d040726032f Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 20 Jan 2023 21:56:01 +0100 Subject: [PATCH 5/6] Parsing redshift table alias based on indentifier and parentheses instead of function name --- src/keywords.rs | 2 ++ src/parser.rs | 33 ++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/keywords.rs b/src/keywords.rs index ad4e51e14..ee11038be 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -668,6 +668,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ Keyword::OUTER, Keyword::SET, Keyword::QUALIFY, + Keyword::AS, ]; /// Can't be used as a column alias, so that `SELECT alias` @@ -697,4 +698,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ // Reserved only as a column alias in the `SELECT` clause Keyword::FROM, Keyword::INTO, + Keyword::AS, ]; diff --git a/src/parser.rs b/src/parser.rs index 2539e0e8c..a656798f8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5615,7 +5615,7 @@ impl<'a> Parser<'a> { } else { None }; - let columns_definition = self.parse_redshift_columns_definition_list(&name)?; + let columns_definition = self.parse_redshift_columns_definition_list()?; let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; // MSSQL-specific table hints: let mut with_hints = vec![]; @@ -5640,28 +5640,14 @@ impl<'a> Parser<'a> { fn parse_redshift_columns_definition_list( &mut self, - name: &ObjectName, ) -> Result, ParserError> { if !dialect_of!(self is RedshiftSqlDialect | GenericDialect) { return Ok(None); } - let fname = name - .0 - .last() - .ok_or_else(|| { - ParserError::ParserError("Empty identifier vector for ObjectName".to_string()) - })? - .value - .to_lowercase(); - - if fname == "pg_get_late_binding_view_cols" - || fname == "pg_get_cols" - || fname == "pg_get_grantee_by_iam_role" - || fname == "pg_get_iam_role_by_user" + if let Some(col_definition_list_name) = self.parse_optional_columns_definition_list_alias() { - if let Ok(col_definition_list_name) = self.parse_identifier() { - self.expect_token(&Token::LParen)?; + if self.consume_token(&Token::LParen) { let names = self.parse_comma_separated(Parser::parse_ident_pair)?; self.expect_token(&Token::RParen)?; Ok(Some(TableAliasDefinition { @@ -5669,6 +5655,7 @@ impl<'a> Parser<'a> { args: names, })) } else { + self.prev_token(); Ok(None) } } else { @@ -5676,6 +5663,18 @@ impl<'a> Parser<'a> { } } + fn parse_optional_columns_definition_list_alias(&mut self) -> Option { + match self.next_token().token { + Token::Word(w) if !keywords::RESERVED_FOR_TABLE_ALIAS.contains(&w.keyword) => { + Some(w.to_ident()) + } + _ => { + self.prev_token(); + None + } + } + } + fn parse_ident_pair(&mut self) -> Result { Ok(IdentPair( self.parse_identifier()?, From 4d42aa17fab705845c4f5dd58cbfbf0af7764737 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 17 Feb 2023 14:01:17 -0500 Subject: [PATCH 6/6] fix clippy --- src/ast/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index b7e05b805..a97013eb9 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -642,7 +642,7 @@ impl fmt::Display for TableFactor { write!(f, " AS {alias}")?; } if let Some(columns_definition) = columns_definition { - write!(f, " {}", columns_definition)?; + write!(f, " {columns_definition}")?; } if !with_hints.is_empty() { write!(f, " WITH ({})", display_comma_separated(with_hints))?;