diff --git a/src/meta/src/manager/catalog/utils.rs b/src/meta/src/manager/catalog/utils.rs index d9759bdf00d4a..842569e24506d 100644 --- a/src/meta/src/manager/catalog/utils.rs +++ b/src/meta/src/manager/catalog/utils.rs @@ -319,7 +319,9 @@ impl QueryRewriter<'_> { self.visit_expr(expr2); } Expr::Function(function) => self.visit_function(function), - Expr::Exists(query) | Expr::Subquery(query) => self.visit_query(query), + Expr::Exists(query) | Expr::Subquery(query) | Expr::ArraySubquery(query) => { + self.visit_query(query) + } Expr::GroupingSets(exprs_vec) | Expr::Cube(exprs_vec) | Expr::Rollup(exprs_vec) => { for exprs in exprs_vec { diff --git a/src/sqlparser/src/ast/mod.rs b/src/sqlparser/src/ast/mod.rs index 77aaab72142a8..73b1f95bd6be8 100644 --- a/src/sqlparser/src/ast/mod.rs +++ b/src/sqlparser/src/ast/mod.rs @@ -411,12 +411,13 @@ pub enum Expr { Rollup(Vec>), /// The `ROW` expr. The `ROW` keyword can be omitted, Row(Vec), - /// The `ARRAY` expr. Alternative syntax for `ARRAY` is by utilizing curly braces, - /// e.g. {1, 2, 3}, + /// An array constructor `ARRAY[[2,3,4],[5,6,7]]` Array(Array), - /// An array index expression e.g. `(ARRAY[1, 2])[1]` or `(current_schemas(FALSE))[1]` + /// An array constructing subquery `ARRAY(SELECT 2 UNION SELECT 3)` + ArraySubquery(Box), + /// A subscript expression `arr[1]` ArrayIndex { obj: Box, index: Box }, - /// An array range index expression e.g. `(Array[1, 2, 3, 4])[1:3]` + /// A slice expression `arr[1:3]` ArrayRangeIndex { obj: Box, start: Option>, @@ -636,6 +637,7 @@ impl fmt::Display for Expr { Ok(()) } Expr::Array(exprs) => write!(f, "{}", exprs), + Expr::ArraySubquery(s) => write!(f, "ARRAY ({})", s), } } } diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index 1f6d8de20c4e9..1ed7aedb3e5bb 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -569,10 +569,17 @@ impl Parser { expr: Box::new(self.parse_subexpr(Precedence::UnaryNot)?), }), Keyword::ROW => self.parse_row_expr(), - Keyword::ARRAY => { + Keyword::ARRAY if self.peek_token() == Token::LBracket => { self.expect_token(&Token::LBracket)?; self.parse_array_expr(true) } + Keyword::ARRAY if self.peek_token() == Token::LParen => { + // similar to `exists(subquery)` + self.expect_token(&Token::LParen)?; + let exists_node = Expr::ArraySubquery(Box::new(self.parse_query()?)); + self.expect_token(&Token::RParen)?; + Ok(exists_node) + } // `LEFT` and `RIGHT` are reserved as identifier but okay as function Keyword::LEFT | Keyword::RIGHT => { self.parse_function(ObjectName(vec![w.to_ident()?])) diff --git a/src/sqlparser/tests/testdata/subquery.yaml b/src/sqlparser/tests/testdata/subquery.yaml new file mode 100644 index 0000000000000..bc3dbc59de77f --- /dev/null +++ b/src/sqlparser/tests/testdata/subquery.yaml @@ -0,0 +1,24 @@ +# This file is automatically generated. See `src/sqlparser/test_runner/src/bin/apply.rs` for more information. +- input: select a1 from a where exists (select 1 from b where a1 = b1); + formatted_sql: SELECT a1 FROM a WHERE EXISTS (SELECT 1 FROM b WHERE a1 = b1) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Identifier(Ident { value: "a1", quote_style: None }))], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "a", quote_style: None }]), alias: None, for_system_time_as_of_proctime: false }, joins: [] }], lateral_views: [], selection: Some(Exists(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Value(Number("1")))], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "b", quote_style: None }]), alias: None, for_system_time_as_of_proctime: false }, joins: [] }], lateral_views: [], selection: Some(BinaryOp { left: Identifier(Ident { value: "a1", quote_style: None }), op: Eq, right: Identifier(Ident { value: "b1", quote_style: None }) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: select a1 from a where a1 NOT IN (select b1 from b); + formatted_sql: SELECT a1 FROM a WHERE a1 NOT IN (SELECT b1 FROM b) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Identifier(Ident { value: "a1", quote_style: None }))], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "a", quote_style: None }]), alias: None, for_system_time_as_of_proctime: false }, joins: [] }], lateral_views: [], selection: Some(InSubquery { expr: Identifier(Ident { value: "a1", quote_style: None }), subquery: Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Identifier(Ident { value: "b1", quote_style: None }))], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "b", quote_style: None }]), alias: None, for_system_time_as_of_proctime: false }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, negated: true }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: select a1 from a where a1 < ALL (select b1 from b); + error_msg: |- + sql parser error: Expected ), found: b1 at line:1, column:43 + Near "where a1 < ALL (select" +- input: select a1 from a where a1 <> SOME (select b1 from b); + error_msg: |- + sql parser error: Expected ), found: b1 at line:1, column:44 + Near "where a1 <> SOME (select" +- input: select 1 + (select 2); + formatted_sql: SELECT 1 + (SELECT 2) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Value(Number("1")), op: Plus, right: Subquery(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Value(Number("2")))], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: select (1, true) < (select 2, false); + formatted_sql: SELECT ROW(1, true) < (SELECT 2, false) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Row([Value(Number("1")), Value(Boolean(true))]), op: Lt, right: Subquery(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Value(Number("2"))), UnnamedExpr(Value(Boolean(false)))], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: select array(select 2 union select 3); + formatted_sql: SELECT ARRAY (SELECT 2 UNION SELECT 3) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(ArraySubquery(Query { with: None, body: SetOperation { op: Union, all: false, left: Select(Select { distinct: All, projection: [UnnamedExpr(Value(Number("2")))], from: [], lateral_views: [], selection: None, group_by: [], having: None }), right: Select(Select { distinct: All, projection: [UnnamedExpr(Value(Number("3")))], from: [], lateral_views: [], selection: None, group_by: [], having: None }) }, order_by: [], limit: None, offset: None, fetch: None }))], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'