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

View file

@ -5999,7 +5999,7 @@ impl<'a> Parser<'a> {
&& self.parse_keyword(Keyword::UNNEST)
{
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)?;
let alias = match self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS) {
@ -6025,7 +6025,7 @@ impl<'a> Parser<'a> {
Ok(TableFactor::UNNEST {
alias,
array_expr: Box::new(expr),
array_exprs,
with_offset,
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 {
Value::Number(n.parse().unwrap(), false)
}

View file

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

View file

@ -4130,7 +4130,16 @@ fn parse_table_function() {
#[test]
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(
array_exprs: &str,
alias: bool,
with_offset: bool,
with_offset_alias: bool,
@ -4138,7 +4147,8 @@ fn parse_unnest() {
want: Vec<TableWithJoins>,
) {
let sql = &format!(
"SELECT * FROM UNNEST(expr){}{}{}",
"SELECT * FROM UNNEST({}){}{}{}",
array_exprs,
if alias { " AS numbers" } else { "" },
if with_offset { " WITH OFFSET" } else { "" },
if with_offset_alias {
@ -4156,6 +4166,7 @@ fn parse_unnest() {
};
// 1. both Alias and WITH OFFSET clauses.
chk(
"expr",
true,
true,
false,
@ -4166,7 +4177,7 @@ fn parse_unnest() {
name: Ident::new("numbers"),
columns: vec![],
}),
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))),
array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: true,
with_offset_alias: None,
},
@ -4175,6 +4186,7 @@ fn parse_unnest() {
);
// 2. neither Alias nor WITH OFFSET clause.
chk(
"expr",
false,
false,
false,
@ -4182,7 +4194,7 @@ fn parse_unnest() {
vec![TableWithJoins {
relation: TableFactor::UNNEST {
alias: None,
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))),
array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: false,
with_offset_alias: None,
},
@ -4191,6 +4203,7 @@ fn parse_unnest() {
);
// 3. Alias but no WITH OFFSET clause.
chk(
"expr",
false,
true,
false,
@ -4198,7 +4211,7 @@ fn parse_unnest() {
vec![TableWithJoins {
relation: TableFactor::UNNEST {
alias: None,
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))),
array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: true,
with_offset_alias: None,
},
@ -4207,6 +4220,7 @@ fn parse_unnest() {
);
// 4. WITH OFFSET but no Alias.
chk(
"expr",
true,
false,
false,
@ -4217,13 +4231,82 @@ fn parse_unnest() {
name: Ident::new("numbers"),
columns: vec![],
}),
array_expr: Box::new(Expr::Identifier(Ident::new("expr"))),
array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
with_offset: false,
with_offset_alias: None,
},
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]

View file

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