Support multi args for unnest (#909)

Signed-off-by: jayzhan211 <jayzhan211@gmail.com>
Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
Jay Zhan 2023-07-01 04:50:46 +08:00 committed by GitHub
parent f05f71e20d
commit 20ac38b4da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 14 deletions

View file

@ -677,7 +677,7 @@ pub enum TableFactor {
/// ``` /// ```
UNNEST { UNNEST {
alias: Option<TableAlias>, alias: Option<TableAlias>,
array_expr: Box<Expr>, array_exprs: Vec<Expr>,
with_offset: bool, with_offset: bool,
with_offset_alias: Option<Ident>, with_offset_alias: Option<Ident>,
}, },
@ -749,11 +749,12 @@ impl fmt::Display for TableFactor {
} }
TableFactor::UNNEST { TableFactor::UNNEST {
alias, alias,
array_expr, array_exprs,
with_offset, with_offset,
with_offset_alias, with_offset_alias,
} => { } => {
write!(f, "UNNEST({array_expr})")?; write!(f, "UNNEST({})", display_comma_separated(array_exprs))?;
if let Some(alias) = alias { if let Some(alias) = alias {
write!(f, " AS {alias}")?; write!(f, " AS {alias}")?;
} }

View file

@ -5999,7 +5999,7 @@ impl<'a> Parser<'a> {
&& self.parse_keyword(Keyword::UNNEST) && self.parse_keyword(Keyword::UNNEST)
{ {
self.expect_token(&Token::LParen)?; self.expect_token(&Token::LParen)?;
let expr = self.parse_expr()?; let array_exprs = self.parse_comma_separated(Parser::parse_expr)?;
self.expect_token(&Token::RParen)?; self.expect_token(&Token::RParen)?;
let alias = match self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS) { let alias = match self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS) {
@ -6025,7 +6025,7 @@ impl<'a> Parser<'a> {
Ok(TableFactor::UNNEST { Ok(TableFactor::UNNEST {
alias, alias,
array_expr: Box::new(expr), array_exprs,
with_offset, with_offset,
with_offset_alias, with_offset_alias,
}) })

View file

@ -197,6 +197,7 @@ pub fn expr_from_projection(item: &SelectItem) -> &Expr {
} }
} }
/// Creates a `Value::Number`, panic'ing if n is not a number
pub fn number(n: &'static str) -> Value { pub fn number(n: &'static str) -> Value {
Value::Number(n.parse().unwrap(), false) Value::Number(n.parse().unwrap(), false)
} }

View file

@ -160,10 +160,10 @@ fn parse_join_constraint_unnest_alias() {
vec![Join { vec![Join {
relation: TableFactor::UNNEST { relation: TableFactor::UNNEST {
alias: table_alias("f"), alias: table_alias("f"),
array_expr: Box::new(Expr::CompoundIdentifier(vec![ array_exprs: vec![Expr::CompoundIdentifier(vec![
Ident::new("t1"), Ident::new("t1"),
Ident::new("a") Ident::new("a")
])), ])],
with_offset: false, with_offset: false,
with_offset_alias: None with_offset_alias: None
}, },

View file

@ -4130,7 +4130,16 @@ fn parse_table_function() {
#[test] #[test]
fn parse_unnest() { fn parse_unnest() {
let sql = "SELECT UNNEST(make_array(1, 2, 3))";
one_statement_parses_to(sql, sql);
let sql = "SELECT UNNEST(make_array(1, 2, 3), make_array(4, 5))";
one_statement_parses_to(sql, sql);
}
#[test]
fn parse_unnest_in_from_clause() {
fn chk( fn chk(
array_exprs: &str,
alias: bool, alias: bool,
with_offset: bool, with_offset: bool,
with_offset_alias: bool, with_offset_alias: bool,
@ -4138,7 +4147,8 @@ fn parse_unnest() {
want: Vec<TableWithJoins>, want: Vec<TableWithJoins>,
) { ) {
let sql = &format!( let sql = &format!(
"SELECT * FROM UNNEST(expr){}{}{}", "SELECT * FROM UNNEST({}){}{}{}",
array_exprs,
if alias { " AS numbers" } else { "" }, if alias { " AS numbers" } else { "" },
if with_offset { " WITH OFFSET" } else { "" }, if with_offset { " WITH OFFSET" } else { "" },
if with_offset_alias { if with_offset_alias {
@ -4156,6 +4166,7 @@ fn parse_unnest() {
}; };
// 1. both Alias and WITH OFFSET clauses. // 1. both Alias and WITH OFFSET clauses.
chk( chk(
"expr",
true, true,
true, true,
false, false,
@ -4166,7 +4177,7 @@ fn parse_unnest() {
name: Ident::new("numbers"), name: Ident::new("numbers"),
columns: vec![], columns: vec![],
}), }),
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))), array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: true, with_offset: true,
with_offset_alias: None, with_offset_alias: None,
}, },
@ -4175,6 +4186,7 @@ fn parse_unnest() {
); );
// 2. neither Alias nor WITH OFFSET clause. // 2. neither Alias nor WITH OFFSET clause.
chk( chk(
"expr",
false, false,
false, false,
false, false,
@ -4182,7 +4194,7 @@ fn parse_unnest() {
vec![TableWithJoins { vec![TableWithJoins {
relation: TableFactor::UNNEST { relation: TableFactor::UNNEST {
alias: None, alias: None,
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))), array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: false, with_offset: false,
with_offset_alias: None, with_offset_alias: None,
}, },
@ -4191,6 +4203,7 @@ fn parse_unnest() {
); );
// 3. Alias but no WITH OFFSET clause. // 3. Alias but no WITH OFFSET clause.
chk( chk(
"expr",
false, false,
true, true,
false, false,
@ -4198,7 +4211,7 @@ fn parse_unnest() {
vec![TableWithJoins { vec![TableWithJoins {
relation: TableFactor::UNNEST { relation: TableFactor::UNNEST {
alias: None, alias: None,
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))), array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: true, with_offset: true,
with_offset_alias: None, with_offset_alias: None,
}, },
@ -4207,6 +4220,7 @@ fn parse_unnest() {
); );
// 4. WITH OFFSET but no Alias. // 4. WITH OFFSET but no Alias.
chk( chk(
"expr",
true, true,
false, false,
false, false,
@ -4217,13 +4231,82 @@ fn parse_unnest() {
name: Ident::new("numbers"), name: Ident::new("numbers"),
columns: vec![], columns: vec![],
}), }),
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))), array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: false, with_offset: false,
with_offset_alias: None, with_offset_alias: None,
}, },
joins: vec![], joins: vec![],
}], }],
); );
// 5. Simple array
chk(
"make_array(1, 2, 3)",
false,
false,
false,
&dialects,
vec![TableWithJoins {
relation: TableFactor::UNNEST {
alias: None,
array_exprs: vec![Expr::Function(Function {
name: ObjectName(vec![Ident::new("make_array")]),
args: vec![
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("3")))),
],
over: None,
distinct: false,
special: false,
order_by: vec![],
})],
with_offset: false,
with_offset_alias: None,
},
joins: vec![],
}],
);
// 6. Multiple arrays
chk(
"make_array(1, 2, 3), make_array(5, 6)",
false,
false,
false,
&dialects,
vec![TableWithJoins {
relation: TableFactor::UNNEST {
alias: None,
array_exprs: vec![
Expr::Function(Function {
name: ObjectName(vec![Ident::new("make_array")]),
args: vec![
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("3")))),
],
over: None,
distinct: false,
special: false,
order_by: vec![],
}),
Expr::Function(Function {
name: ObjectName(vec![Ident::new("make_array")]),
args: vec![
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("5")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("6")))),
],
over: None,
distinct: false,
special: false,
order_by: vec![],
}),
],
with_offset: false,
with_offset_alias: None,
},
joins: vec![],
}],
)
} }
#[test] #[test]

View file

@ -2407,7 +2407,7 @@ fn parse_create_role() {
in_role, in_role,
in_group, in_group,
role, role,
user, user: _,
admin, admin,
authorization_owner, authorization_owner,
}], }],
@ -2435,7 +2435,6 @@ fn parse_create_role() {
assert_eq_vec(&["role1", "role2"], in_role); assert_eq_vec(&["role1", "role2"], in_role);
assert!(in_group.is_empty()); assert!(in_group.is_empty());
assert_eq_vec(&["role3"], role); assert_eq_vec(&["role3"], role);
assert!(user.is_empty());
assert_eq_vec(&["role4", "role5"], admin); assert_eq_vec(&["role4", "role5"], admin);
assert_eq!(*authorization_owner, None); assert_eq!(*authorization_owner, None);
} }