Skip to content

Commit 93781d7

Browse files
committed
feat: Add PostgreSQL array subquery constructor
1 parent a4628ce commit 93781d7

4 files changed

Lines changed: 79 additions & 1 deletion

File tree

src/ast/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ pub enum Expr {
327327
},
328328
/// An array expression e.g. `ARRAY[1, 2]`
329329
Array(Array),
330+
/// An array subquery constructor, e.g. `array(SELECT 1 UNION SELECT 2)`
331+
ArraySubquery(Box<Query>),
330332
/// An expression with field access, such as `SELECT (udf_returning_struct()).field`
331333
DotExpr {
332334
expr: Box<Expr>,
@@ -440,6 +442,7 @@ impl fmt::Display for Expr {
440442
}
441443
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
442444
Expr::Subquery(s) => write!(f, "({})", s),
445+
Expr::ArraySubquery(s) => write!(f, "ARRAY({})", s),
443446
Expr::ListAgg(listagg) => write!(f, "{}", listagg),
444447
Expr::GroupingSets(sets) => {
445448
write!(f, "GROUPING SETS (")?;

src/parser.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,13 @@ impl<'a> Parser<'a> {
451451
self.expect_token(&Token::LBracket)?;
452452
self.parse_array_expr(true)
453453
}
454+
Keyword::ARRAY
455+
if dialect_of!(self is PostgreSqlDialect | GenericDialect)
456+
&& self.peek_token() == Token::LParen =>
457+
{
458+
self.expect_token(&Token::LParen)?;
459+
self.parse_array_subquery()
460+
}
454461
Keyword::NOT => Ok(Expr::UnaryOp {
455462
op: UnaryOperator::Not,
456463
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?),
@@ -882,6 +889,13 @@ impl<'a> Parser<'a> {
882889
Ok(Expr::Array(Array { elem: exprs, named }))
883890
}
884891

892+
/// Parses an array subquery `ARRAY(SELECT 1 UNION SELECT 2)`
893+
pub fn parse_array_subquery(&mut self) -> Result<Expr, ParserError> {
894+
let subquery = self.parse_query()?;
895+
self.expect_token(&Token::RParen)?;
896+
Ok(Expr::ArraySubquery(Box::new(subquery)))
897+
}
898+
885899
/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`.
886900
pub fn parse_listagg_expr(&mut self) -> Result<Expr, ParserError> {
887901
self.expect_token(&Token::LParen)?;

tests/sqlparser_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2307,7 +2307,7 @@ fn parse_bad_constraint() {
23072307

23082308
#[test]
23092309
fn parse_scalar_function_in_projection() {
2310-
let names = vec!["sqrt", "array", "foo"];
2310+
let names = vec!["sqrt", "foo"];
23112311

23122312
for function_name in names {
23132313
// like SELECT sqrt(id) FROM foo

tests/sqlparser_postgres.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,67 @@ fn parse_array_index_expr() {
11861186
);
11871187
}
11881188

1189+
#[test]
1190+
fn parse_array_subquery_expr() {
1191+
let sql = "SELECT ARRAY(SELECT 1 UNION SELECT 2)";
1192+
let select = pg().verified_only_select(sql);
1193+
assert_eq!(
1194+
&Expr::ArraySubquery(Box::new(Query {
1195+
with: None,
1196+
body: SetExpr::SetOperation {
1197+
op: SetOperator::Union,
1198+
all: false,
1199+
left: Box::new(SetExpr::Select(Box::new(Select {
1200+
distinct: false,
1201+
top: None,
1202+
projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number(
1203+
#[cfg(not(feature = "bigdecimal"))]
1204+
"1".to_string(),
1205+
#[cfg(feature = "bigdecimal")]
1206+
"1".parse().unwrap(),
1207+
false,
1208+
)))],
1209+
into: None,
1210+
from: vec![],
1211+
lateral_views: vec![],
1212+
selection: None,
1213+
group_by: vec![],
1214+
cluster_by: vec![],
1215+
distribute_by: vec![],
1216+
sort_by: vec![],
1217+
having: None,
1218+
}))),
1219+
right: Box::new(SetExpr::Select(Box::new(Select {
1220+
distinct: false,
1221+
top: None,
1222+
projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number(
1223+
#[cfg(not(feature = "bigdecimal"))]
1224+
"2".to_string(),
1225+
#[cfg(feature = "bigdecimal")]
1226+
"2".parse().unwrap(),
1227+
false,
1228+
)))],
1229+
into: None,
1230+
from: vec![],
1231+
lateral_views: vec![],
1232+
selection: None,
1233+
group_by: vec![],
1234+
cluster_by: vec![],
1235+
distribute_by: vec![],
1236+
sort_by: vec![],
1237+
having: None,
1238+
}))),
1239+
},
1240+
order_by: vec![],
1241+
limit: None,
1242+
offset: None,
1243+
fetch: None,
1244+
lock: None,
1245+
})),
1246+
expr_from_projection(only(&select.projection)),
1247+
);
1248+
}
1249+
11891250
#[test]
11901251
fn test_transaction_statement() {
11911252
let statement = pg().verified_stmt("SET TRANSACTION SNAPSHOT '000003A1-1'");

0 commit comments

Comments
 (0)