diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f38e5a39d..6f6359ea0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -327,6 +327,8 @@ pub enum Expr { }, /// An array expression e.g. `ARRAY[1, 2]` Array(Array), + /// An array subquery constructor, e.g. `array(SELECT 1 UNION SELECT 2)` + ArraySubquery(Box), /// An expression with field access, such as `SELECT (udf_returning_struct()).field` DotExpr { expr: Box, @@ -440,6 +442,7 @@ impl fmt::Display for Expr { } Expr::Exists(s) => write!(f, "EXISTS ({})", s), Expr::Subquery(s) => write!(f, "({})", s), + Expr::ArraySubquery(s) => write!(f, "ARRAY({})", s), Expr::ListAgg(listagg) => write!(f, "{}", listagg), Expr::GroupingSets(sets) => { write!(f, "GROUPING SETS (")?; diff --git a/src/parser.rs b/src/parser.rs index 57dd0a97a..57408ba49 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -451,6 +451,13 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LBracket)?; self.parse_array_expr(true) } + Keyword::ARRAY + if dialect_of!(self is PostgreSqlDialect | GenericDialect) + && self.peek_token() == Token::LParen => + { + self.expect_token(&Token::LParen)?; + self.parse_array_subquery() + } Keyword::NOT => Ok(Expr::UnaryOp { op: UnaryOperator::Not, expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), @@ -882,6 +889,13 @@ impl<'a> Parser<'a> { Ok(Expr::Array(Array { elem: exprs, named })) } + /// Parses an array subquery `ARRAY(SELECT 1 UNION SELECT 2)` + pub fn parse_array_subquery(&mut self) -> Result { + let subquery = self.parse_query()?; + self.expect_token(&Token::RParen)?; + Ok(Expr::ArraySubquery(Box::new(subquery))) + } + /// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`. pub fn parse_listagg_expr(&mut self) -> Result { self.expect_token(&Token::LParen)?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 022339058..44bba3931 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2307,7 +2307,7 @@ fn parse_bad_constraint() { #[test] fn parse_scalar_function_in_projection() { - let names = vec!["sqrt", "array", "foo"]; + let names = vec!["sqrt", "foo"]; for function_name in names { // like SELECT sqrt(id) FROM foo diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 3b0a6c57e..754fb3fdf 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1186,6 +1186,67 @@ fn parse_array_index_expr() { ); } +#[test] +fn parse_array_subquery_expr() { + let sql = "SELECT ARRAY(SELECT 1 UNION SELECT 2)"; + let select = pg().verified_only_select(sql); + assert_eq!( + &Expr::ArraySubquery(Box::new(Query { + with: None, + body: SetExpr::SetOperation { + op: SetOperator::Union, + all: false, + left: Box::new(SetExpr::Select(Box::new(Select { + distinct: false, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number( + #[cfg(not(feature = "bigdecimal"))] + "1".to_string(), + #[cfg(feature = "bigdecimal")] + "1".parse().unwrap(), + false, + )))], + into: None, + from: vec![], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + }))), + right: Box::new(SetExpr::Select(Box::new(Select { + distinct: false, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number( + #[cfg(not(feature = "bigdecimal"))] + "2".to_string(), + #[cfg(feature = "bigdecimal")] + "2".parse().unwrap(), + false, + )))], + into: None, + from: vec![], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + }))), + }, + order_by: vec![], + limit: None, + offset: None, + fetch: None, + lock: None, + })), + expr_from_projection(only(&select.projection)), + ); +} + #[test] fn test_transaction_statement() { let statement = pg().verified_stmt("SET TRANSACTION SNAPSHOT '000003A1-1'");