From fe3001eac15bb6b48a594571fc473823794a44c8 Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Wed, 22 Jul 2020 23:20:13 -0400 Subject: [PATCH 1/8] sql-parser: remove JoinOperator::{Cross,Outer}Apply These are MSSQL-specific and therefore irrelevant to us. --- src/sql-parser/src/ast/defs/query.rs | 12 ------------ src/sql/src/plan/query.rs | 5 ----- 2 files changed, 17 deletions(-) diff --git a/src/sql-parser/src/ast/defs/query.rs b/src/sql-parser/src/ast/defs/query.rs index 3eff6765743b..f77ea05c508c 100644 --- a/src/sql-parser/src/ast/defs/query.rs +++ b/src/sql-parser/src/ast/defs/query.rs @@ -462,14 +462,6 @@ impl AstDisplay for Join { f.write_str(" CROSS JOIN "); f.write_node(&self.relation); } - JoinOperator::CrossApply => { - f.write_str(" CROSS APPLY "); - f.write_node(&self.relation); - } - JoinOperator::OuterApply => { - f.write_str(" OUTER APPLY "); - f.write_node(&self.relation); - } } } } @@ -482,10 +474,6 @@ pub enum JoinOperator { RightOuter(JoinConstraint), FullOuter(JoinConstraint), CrossJoin, - /// CROSS APPLY (non-standard) - CrossApply, - /// OUTER APPLY (non-standard) - OuterApply, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index 200435a3ef00..acd02a1e8bb9 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -1208,11 +1208,6 @@ fn plan_join_operator( JoinKind::FullOuter, ), JoinOperator::CrossJoin => Ok((left.product(right), left_scope.product(right_scope))), - // The remaining join types are MSSQL-specific. We are unlikely to - // ever support them. The standard SQL equivalent is LATERAL, which - // we are not capable of even parsing at the moment. - JoinOperator::CrossApply => unsupported!("CROSS APPLY"), - JoinOperator::OuterApply => unsupported!("OUTER APPLY"), } } From 6fe78e80cc46dc0365db30ebe7b8d1e2cdbc74fd Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Wed, 22 Jul 2020 23:35:22 -0400 Subject: [PATCH 2/8] sql-parser: remove WITH hints from tables These are MSSQL-specific and therefore irrelevant to us. --- src/sql-parser/src/ast/defs/query.rs | 14 +-- src/sql-parser/src/parser.rs | 18 +-- src/sql-parser/tests/sqlparser_common.rs | 18 +-- src/sql-parser/tests/testdata/ddl | 12 +- src/sql-parser/tests/testdata/insert | 2 +- src/sql-parser/tests/testdata/select | 146 ++++++++++++----------- src/sql/src/normalize.rs | 10 +- src/sql/src/plan/query.rs | 10 +- 8 files changed, 95 insertions(+), 135 deletions(-) diff --git a/src/sql-parser/src/ast/defs/query.rs b/src/sql-parser/src/ast/defs/query.rs index f77ea05c508c..cc5be3383e13 100644 --- a/src/sql-parser/src/ast/defs/query.rs +++ b/src/sql-parser/src/ast/defs/query.rs @@ -305,8 +305,6 @@ pub enum TableFactor { /// and MSSQL. args: Option, alias: Option, - /// MSSQL-specific `WITH (...)` hints such as NOLOCK. - with_hints: Vec, }, Derived { lateral: bool, @@ -323,12 +321,7 @@ pub enum TableFactor { impl AstDisplay for TableFactor { fn fmt(&self, f: &mut AstFormatter) { match self { - TableFactor::Table { - name, - alias, - args, - with_hints, - } => { + TableFactor::Table { name, alias, args } => { f.write_node(name); if let Some(args) = args { f.write_str("("); @@ -339,11 +332,6 @@ impl AstDisplay for TableFactor { f.write_str(" AS "); f.write_node(alias); } - if !with_hints.is_empty() { - f.write_str(" WITH ("); - f.write_node(&display::comma_separated(with_hints)); - f.write_str(")"); - } } TableFactor::Derived { lateral, diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index ebc27715ee9f..8d95e4516d14 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -2800,23 +2800,7 @@ impl Parser { None }; let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; - // MSSQL-specific table hints: - let mut with_hints = vec![]; - if self.parse_keyword("WITH") { - if self.consume_token(&Token::LParen) { - with_hints = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - } else { - // rewind, as WITH may belong to the next statement's CTE - self.prev_token(); - } - }; - Ok(TableFactor::Table { - name, - alias, - args, - with_hints, - }) + Ok(TableFactor::Table { name, alias, args }) } } diff --git a/src/sql-parser/tests/sqlparser_common.rs b/src/sql-parser/tests/sqlparser_common.rs index 466dff05ef51..5201d624dfa2 100644 --- a/src/sql-parser/tests/sqlparser_common.rs +++ b/src/sql-parser/tests/sqlparser_common.rs @@ -185,15 +185,15 @@ fn test_basic_visitor() -> Result<(), Box> { AND EXTRACT(YEAR FROM a37) AND (SELECT a38) AND EXISTS (SELECT a39) - FROM a40(a41) AS a42 WITH (a43) - LEFT JOIN a44 ON false - RIGHT JOIN a45 ON false - FULL JOIN a46 ON false - JOIN a47 (a48) USING (a49) - NATURAL JOIN (a50 NATURAL JOIN a51) + FROM a40(a41) AS a42 + LEFT JOIN a43 ON false + RIGHT JOIN a44 ON false + FULL JOIN a45 ON false + JOIN a46 (a47) USING (a48) + NATURAL JOIN (a49 NATURAL JOIN a50) EXCEPT - (SELECT a52(a53) OVER (PARTITION BY a54 ORDER BY a55 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)) - ORDER BY a56 + (SELECT a51(a52) OVER (PARTITION BY a53 ORDER BY a54 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)) + ORDER BY a55 LIMIT 1; UPDATE b01 SET b02 = b03 WHERE b04; INSERT INTO c01 (c02) VALUES (c03); @@ -223,7 +223,7 @@ fn test_basic_visitor() -> Result<(), Box> { "a25", "a26", "a27", "a28", "a29", "a30", "a31", "a32", "a33", "a34", "a35", "a36", "date_part", "a37", "a38", "a39", "a40", "a41", "a42", "a43", "a44", "a45", "a46", "a47", "a48", - "a49", "a50", "a51", "a52", "a53", "a54", "a55", "a56", + "a49", "a50", "a51", "a52", "a53", "a54", "a55", "b01", "b02", "b03", "b04", "c01", "c02", "c03", "c04", "c05", "d01", "d02", diff --git a/src/sql-parser/tests/testdata/ddl b/src/sql-parser/tests/testdata/ddl index b06d2157b1fb..3baef1b2588e 100644 --- a/src/sql-parser/tests/testdata/ddl +++ b/src/sql-parser/tests/testdata/ddl @@ -200,21 +200,21 @@ CREATE VIEW myschema.myview AS SELECT foo FROM bar ---- CREATE VIEW myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } parse-statement CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar ---- CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } +CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } parse-statement CREATE TEMP VIEW myview AS SELECT foo FROM bar ---- CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } +CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } parse-statement CREATE OR REPLACE VIEW v AS SELECT 1 @@ -258,14 +258,14 @@ CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar ---- CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: true } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: true } parse-statement CREATE MATERIALIZED VIEW IF NOT EXISTS myschema.myview AS SELECT foo FROM bar ---- CREATE MATERIALIZED VIEW IF NOT EXISTS myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Skip, temporary: false, materialized: true } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Skip, temporary: false, materialized: true } parse-statement CREATE SOURCE foo FROM FILE 'bar' FORMAT AVRO USING SCHEMA 'baz' @@ -512,7 +512,7 @@ CREATE INDEX fizz ON baz (ascii(x), a IS NOT NULL, (EXISTS (SELECT y FROM boop W ---- CREATE INDEX fizz ON baz (ascii(x), a IS NOT NULL, (EXISTS (SELECT y FROM boop WHERE boop.z = z)), delta) => -CreateIndex { name: Some(Ident("fizz")), on_name: ObjectName([Ident("baz")]), key_parts: Some([Function(Function { name: ObjectName([Ident("ascii")]), args: Args([Identifier([Ident("x")])]), filter: None, over: None, distinct: false }), IsNull { expr: Identifier([Ident("a")]), negated: true }, Nested(Exists(Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("y")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("boop")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("boop"), Ident("z")]), op: Eq, right: Identifier([Ident("z")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })), Identifier([Ident("delta")])]), if_not_exists: false } +CreateIndex { name: Some(Ident("fizz")), on_name: ObjectName([Ident("baz")]), key_parts: Some([Function(Function { name: ObjectName([Ident("ascii")]), args: Args([Identifier([Ident("x")])]), filter: None, over: None, distinct: false }), IsNull { expr: Identifier([Ident("a")]), negated: true }, Nested(Exists(Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("y")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("boop")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("boop"), Ident("z")]), op: Eq, right: Identifier([Ident("z")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })), Identifier([Ident("delta")])]), if_not_exists: false } parse-statement CREATE INDEX ind ON tab ((col + 1)) diff --git a/src/sql-parser/tests/testdata/insert b/src/sql-parser/tests/testdata/insert index 67bbd175a088..d9ae17c71fa8 100644 --- a/src/sql-parser/tests/testdata/insert +++ b/src/sql-parser/tests/testdata/insert @@ -58,4 +58,4 @@ INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1) ---- INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1) => -Insert { table_name: ObjectName([Ident("customer")]), columns: [], source: Query { ctes: [Cte { alias: TableAlias { name: Ident("foo"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: None }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: SetOperation { op: Union, all: false, left: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), right: Values(Values([[Value(Number("1"))]])) }, order_by: [], limit: None, offset: None, fetch: None } } +Insert { table_name: ObjectName([Ident("customer")]), columns: [], source: Query { ctes: [Cte { alias: TableAlias { name: Ident("foo"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: None }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: SetOperation { op: Union, all: false, left: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), right: Values(Values([[Value(Number("1"))]])) }, order_by: [], limit: None, offset: None, fetch: None } } diff --git a/src/sql-parser/tests/testdata/select b/src/sql-parser/tests/testdata/select index 5fd703db70c8..b610193353f7 100644 --- a/src/sql-parser/tests/testdata/select +++ b/src/sql-parser/tests/testdata/select @@ -73,7 +73,7 @@ SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT 5 ---- SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT 5 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Eq, right: Value(Number("1")) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("5"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Eq, right: Value(Number("1")) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("5"))), offset: None, fetch: None }, as_of: None } # LIMIT should not be parsed as an alias. @@ -82,7 +82,7 @@ SELECT id FROM customer LIMIT 1 ---- SELECT id FROM customer LIMIT 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: None, fetch: None }, as_of: None } parse-statement SELECT 1 LIMIT 5 @@ -96,7 +96,7 @@ SELECT DISTINCT name FROM customer ---- SELECT DISTINCT name FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: true, projection: [Expr { expr: Identifier([Ident("name")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: true, projection: [Expr { expr: Identifier([Ident("name")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT ALL name FROM customer @@ -108,14 +108,14 @@ SELECT * FROM foo ---- SELECT * FROM foo => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo.* FROM foo ---- SELECT foo.* FROM foo => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: QualifiedWildcard([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: QualifiedWildcard([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT (x).a, (x).a.b.c @@ -152,7 +152,7 @@ SELECT a.col + 1 AS newname FROM foo AS a ---- SELECT a.col + 1 AS newname FROM foo AS a => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("a"), Ident("col")]), op: Plus, right: Value(Number("1")) }, alias: Some(Ident("newname")) }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: Some(TableAlias { name: Ident("a"), columns: [], strict: false }), with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("a"), Ident("col")]), op: Plus, right: Value(Number("1")) }, alias: Some(Ident("newname")) }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: Some(TableAlias { name: Ident("a"), columns: [], strict: false }) }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT a.col + 1 AS newname FROM foo AS a @@ -194,14 +194,14 @@ SELECT count(*) FILTER (WHERE foo) FROM customer ---- SELECT count(*) FILTER (WHERE foo) FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: Some(Identifier([Ident("foo")])), over: None, distinct: false }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: Some(Identifier([Ident("foo")])), over: None, distinct: false }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT count(DISTINCT + x) FROM customer ---- SELECT count(DISTINCT + x) FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Args([UnaryOp { op: Plus, expr: Identifier([Ident("x")]) }]), filter: None, over: None, distinct: true }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Args([UnaryOp { op: Plus, expr: Identifier([Ident("x")]) }]), filter: None, over: None, distinct: true }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT count(ALL + x) FROM customer @@ -275,70 +275,70 @@ SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar) ---- SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("segment")]), subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("segm")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("segment")]), subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("segm")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE x IN (VALUES (1)) ---- SELECT * FROM t WHERE x IN (VALUES (1)) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("x")]), subquery: Query { ctes: [], body: Values(Values([[Value(Number("1"))]])), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("x")]), subquery: Query { ctes: [], body: Values(Values([[Value(Number("1"))]])), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customers WHERE age BETWEEN 25 AND 32 ---- SELECT * FROM customers WHERE age BETWEEN 25 AND 32 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: false, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: false, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customers WHERE age NOT BETWEEN 25 AND 32 ---- SELECT * FROM customers WHERE age NOT BETWEEN 25 AND 32 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: true, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: true, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL ---- SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(IsNull { expr: Between { expr: Value(Number("1")), negated: false, low: BinaryOp { left: Value(Number("1")), op: Plus, right: Value(Number("2")) }, high: BinaryOp { left: Value(Number("3")), op: Plus, right: Value(Number("4")) } }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(IsNull { expr: Between { expr: Value(Number("1")), negated: false, low: BinaryOp { left: Value(Number("1")), op: Plus, right: Value(Number("2")) }, high: BinaryOp { left: Value(Number("3")), op: Plus, right: Value(Number("4")) } }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 ---- SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 ---- SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer WHERE id < 5 @@ -346,35 +346,35 @@ ORDER BY lname ASC, fname DESC LIMIT 2 ---- SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC LIMIT 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }], limit: Some(Value(Number("2"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }], limit: Some(Value(Number("2"))), offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer GROUP BY lname, fname ---- SELECT id, fname, lname FROM customer GROUP BY lname, fname => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [Identifier([Ident("lname")]), Identifier([Ident("fname")])], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("lname")]), Identifier([Ident("fname")])], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 ---- SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 ---- SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING 1 = 1 ---- SELECT foo FROM bar GROUP BY foo HAVING 1 = 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT ALL @@ -421,70 +421,74 @@ CREATE TABLE foo (bar int) parse-statement roundtrip SELECT * FROM fn(1, 2) AS foo, schema.bar AS bar WITH (nolock) ---- +error: +Parse error: SELECT * FROM fn(1, 2) AS foo, schema.bar AS bar WITH (nolock) + ^^^^ +Expected end of statement, found: WITH parse-statement SELECT * FROM t1, t2 ---- SELECT * FROM t1, t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1a NATURAL JOIN t1b, t2a NATURAL JOIN t2b ---- SELECT * FROM t1a NATURAL JOIN t1b, t2a NATURAL JOIN t2b => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t1b")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2b")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t1b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 CROSS JOIN t2 ---- SELECT * FROM t1 CROSS JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: CrossJoin }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: CrossJoin }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 JOIN t2 AS foo USING(c1) ---- SELECT * FROM t1 JOIN t2 AS foo USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }), with_hints: [] }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 JOIN t2 foo USING(c1) ---- SELECT * FROM t1 JOIN t2 AS foo USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }), with_hints: [] }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL JOIN t2 ---- SELECT * FROM t1 NATURAL JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL LEFT JOIN t2 ---- SELECT * FROM t1 NATURAL LEFT JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: LeftOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: LeftOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL RIGHT JOIN t2 ---- SELECT * FROM t1 NATURAL RIGHT JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: RightOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: RightOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL FULL JOIN t2 ---- SELECT * FROM t1 NATURAL FULL JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: FullOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: FullOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 natural @@ -500,35 +504,35 @@ SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE ---- SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE t4.c = t1.c => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }, Expr { expr: Identifier([Ident("c2")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t4")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(On(BinaryOp { left: Identifier([Ident("t2"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) })) }, Join { relation: Table { name: ObjectName([Ident("t3")]), args: None, alias: None, with_hints: [] }, join_operator: LeftOuter(Using([Ident("q"), Ident("c")])) }] }], selection: Some(BinaryOp { left: Identifier([Ident("t4"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }, Expr { expr: Identifier([Ident("c2")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t4")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(On(BinaryOp { left: Identifier([Ident("t2"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) })) }, Join { relation: Table { name: ObjectName([Ident("t3")]), args: None, alias: None }, join_operator: LeftOuter(Using([Ident("q"), Ident("c")])) }] }], selection: Some(BinaryOp { left: Identifier([Ident("t4"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a NATURAL JOIN (b NATURAL JOIN (c NATURAL JOIN d NATURAL JOIN e)) NATURAL JOIN (f NATURAL JOIN (g NATURAL JOIN h)) ---- SELECT * FROM a NATURAL JOIN (b NATURAL JOIN (c NATURAL JOIN d NATURAL JOIN e)) NATURAL JOIN (f NATURAL JOIN (g NATURAL JOIN h)) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("d")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }, Join { relation: Table { name: ObjectName([Ident("e")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }, Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("f")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("g")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("h")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("d")]), args: None, alias: None }, join_operator: Inner(Natural) }, Join { relation: Table { name: ObjectName([Ident("e")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }, Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("f")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("g")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("h")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (a NATURAL JOIN b) NATURAL JOIN c ---- SELECT * FROM (a NATURAL JOIN b) NATURAL JOIN c => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }), joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (((a NATURAL JOIN b))) ---- SELECT * FROM (((a NATURAL JOIN b))) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a NATURAL JOIN (((b NATURAL JOIN c))) ---- SELECT * FROM a NATURAL JOIN (((b NATURAL JOIN c))) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (a NATURAL JOIN (b)) @@ -544,28 +548,28 @@ SELECT c1 FROM t1 INNER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 LEFT JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: LeftOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: LeftOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: RightOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: RightOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 FULL JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None, with_hints: [] }, join_operator: FullOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: FullOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a OUTER JOIN b ON 1 @@ -584,7 +588,7 @@ SELECT foo + bar FROM a, b ---- WITH a AS (SELECT 1 AS foo), b AS (SELECT 2 AS bar) SELECT foo + bar FROM a, b => -Select { query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement CREATE VIEW v AS @@ -595,7 +599,7 @@ CREATE VIEW v AS ---- CREATE VIEW v AS WITH a AS (SELECT 1 AS foo), b AS (SELECT 2 AS bar) SELECT foo + bar FROM a, b => -CreateView { name: ObjectName([Ident("v")]), columns: [], with_options: [], query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None, with_hints: [] }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } +CreateView { name: ObjectName([Ident("v")]), columns: [], with_options: [], query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } parse-statement roundtrip WITH cte (col1, col2) AS (SELECT foo, bar FROM baz) SELECT * FROM cte @@ -676,35 +680,35 @@ SELECT foo FROM bar OFFSET 2 ROWS ---- SELECT foo FROM bar OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS ---- SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS ---- SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT 'foo' OFFSET 0 ROWS @@ -718,49 +722,49 @@ SELECT foo FROM bar OFFSET 2 ---- SELECT foo FROM bar OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ---- SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz OFFSET 2 ---- SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2) OFFSET 2 ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2) OFFSET 2 ROWS ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT 'foo' OFFSET 0 @@ -779,7 +783,7 @@ SELECT foo FROM bar FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT 'foo' FETCH FIRST 2 ROWS ONLY @@ -793,105 +797,105 @@ SELECT foo FROM bar FETCH FIRST ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar WHERE foo = 4 FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar ORDER BY baz FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz FETCH FIRST 2 ROWS WITH TIES ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz FETCH FIRST 2 ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST 50 PERCENT ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST 50 PERCENT ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: true, quantity: Some(Value(Number("50"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: true, quantity: Some(Value(Number("50"))) }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST 10 ROW ONLY ---- SELECT foo FROM bar FETCH FIRST 10 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT 10 ROW ONLY ---- SELECT foo FROM bar FETCH FIRST 10 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT 10 ROWS WITH TIES ---- SELECT foo FROM bar FETCH FIRST 10 ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT ROWS WITH TIES ---- SELECT foo FROM bar FETCH FIRST ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true ---- SELECT * FROM customer LEFT JOIN (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [Join { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN LATERAL (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true ---- SELECT * FROM customer LEFT JOIN LATERAL (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None, with_hints: [] }, joins: [Join { relation: Derived { lateral: true, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None, with_hints: [] }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [Join { relation: Derived { lateral: true, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id) diff --git a/src/sql/src/normalize.rs b/src/sql/src/normalize.rs index fbbbbff4a23c..f07c653518eb 100644 --- a/src/sql/src/normalize.rs +++ b/src/sql/src/normalize.rs @@ -117,12 +117,7 @@ pub fn create_statement(scx: &StatementContext, mut stmt: Statement) -> Result { + TableFactor::Table { name, args, alias } => { // Only attempt to resolve the name if it is not a table // function (i.e., there are no arguments). if args.is_none() { @@ -139,9 +134,6 @@ pub fn create_statement(scx: &StatementContext, mut stmt: Statement) -> Result( table_factor: &'a TableFactor, ) -> Result<(RelationExpr, Scope), anyhow::Error> { match table_factor { - TableFactor::Table { - name, - alias, - args, - with_hints, - } => { - if !with_hints.is_empty() { - unsupported!("WITH hints"); - } + TableFactor::Table { name, alias, args } => { if let Some(args) = args { let ecx = &ExprContext { qcx, From 1768307836b722559ba74f10d3aa116116d04dfb Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Wed, 22 Jul 2020 23:46:25 -0400 Subject: [PATCH 3/8] sql-parser: expose explicit TableFactor::Function variant It is confusing to use TableFactor::Table for both FROM foo and: FROM foo (a, b, c) With this patch, the latter gets a separate TableFactor::Function variant. --- src/sql-parser/src/ast/defs/query.rs | 23 +++-- src/sql-parser/src/parser.rs | 18 ++-- src/sql-parser/tests/testdata/ddl | 12 +-- src/sql-parser/tests/testdata/insert | 2 +- src/sql-parser/tests/testdata/select | 142 +++++++++++++-------------- src/sql/src/normalize.rs | 24 +++-- src/sql/src/plan/query.rs | 43 ++++---- 7 files changed, 140 insertions(+), 124 deletions(-) diff --git a/src/sql-parser/src/ast/defs/query.rs b/src/sql-parser/src/ast/defs/query.rs index cc5be3383e13..330e05cd8ffd 100644 --- a/src/sql-parser/src/ast/defs/query.rs +++ b/src/sql-parser/src/ast/defs/query.rs @@ -301,9 +301,11 @@ impl TableWithJoins { pub enum TableFactor { Table { name: ObjectName, - /// Arguments of a table-valued function, as supported by Postgres - /// and MSSQL. - args: Option, + alias: Option, + }, + Function { + name: ObjectName, + args: FunctionArgs, alias: Option, }, Derived { @@ -321,13 +323,18 @@ pub enum TableFactor { impl AstDisplay for TableFactor { fn fmt(&self, f: &mut AstFormatter) { match self { - TableFactor::Table { name, alias, args } => { + TableFactor::Table { name, alias } => { f.write_node(name); - if let Some(args) = args { - f.write_str("("); - f.write_node(args); - f.write_str(")"); + if let Some(alias) = alias { + f.write_str(" AS "); + f.write_node(alias); } + } + TableFactor::Function { name, args, alias } => { + f.write_node(name); + f.write_str("("); + f.write_node(args); + f.write_str(")"); if let Some(alias) = alias { f.write_str(" AS "); f.write_node(alias); diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index 8d95e4516d14..9d315590285e 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -2793,14 +2793,18 @@ impl Parser { Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) } else { let name = self.parse_object_name()?; - // Postgres, MSSQL: table-valued functions: - let args = if self.consume_token(&Token::LParen) { - Some(self.parse_optional_args()?) + if self.consume_token(&Token::LParen) { + Ok(TableFactor::Function { + name, + args: self.parse_optional_args()?, + alias: self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?, + }) } else { - None - }; - let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; - Ok(TableFactor::Table { name, alias, args }) + Ok(TableFactor::Table { + name, + alias: self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?, + }) + } } } diff --git a/src/sql-parser/tests/testdata/ddl b/src/sql-parser/tests/testdata/ddl index 3baef1b2588e..bb39c124e2df 100644 --- a/src/sql-parser/tests/testdata/ddl +++ b/src/sql-parser/tests/testdata/ddl @@ -200,21 +200,21 @@ CREATE VIEW myschema.myview AS SELECT foo FROM bar ---- CREATE VIEW myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } parse-statement CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar ---- CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } +CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } parse-statement CREATE TEMP VIEW myview AS SELECT foo FROM bar ---- CREATE TEMPORARY VIEW myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } +CreateView { name: ObjectName([Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: true, materialized: false } parse-statement CREATE OR REPLACE VIEW v AS SELECT 1 @@ -258,14 +258,14 @@ CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar ---- CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: true } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: true } parse-statement CREATE MATERIALIZED VIEW IF NOT EXISTS myschema.myview AS SELECT foo FROM bar ---- CREATE MATERIALIZED VIEW IF NOT EXISTS myschema.myview AS SELECT foo FROM bar => -CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Skip, temporary: false, materialized: true } +CreateView { name: ObjectName([Ident("myschema"), Ident("myview")]), columns: [], with_options: [], query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Skip, temporary: false, materialized: true } parse-statement CREATE SOURCE foo FROM FILE 'bar' FORMAT AVRO USING SCHEMA 'baz' @@ -512,7 +512,7 @@ CREATE INDEX fizz ON baz (ascii(x), a IS NOT NULL, (EXISTS (SELECT y FROM boop W ---- CREATE INDEX fizz ON baz (ascii(x), a IS NOT NULL, (EXISTS (SELECT y FROM boop WHERE boop.z = z)), delta) => -CreateIndex { name: Some(Ident("fizz")), on_name: ObjectName([Ident("baz")]), key_parts: Some([Function(Function { name: ObjectName([Ident("ascii")]), args: Args([Identifier([Ident("x")])]), filter: None, over: None, distinct: false }), IsNull { expr: Identifier([Ident("a")]), negated: true }, Nested(Exists(Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("y")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("boop")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("boop"), Ident("z")]), op: Eq, right: Identifier([Ident("z")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })), Identifier([Ident("delta")])]), if_not_exists: false } +CreateIndex { name: Some(Ident("fizz")), on_name: ObjectName([Ident("baz")]), key_parts: Some([Function(Function { name: ObjectName([Ident("ascii")]), args: Args([Identifier([Ident("x")])]), filter: None, over: None, distinct: false }), IsNull { expr: Identifier([Ident("a")]), negated: true }, Nested(Exists(Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("y")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("boop")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("boop"), Ident("z")]), op: Eq, right: Identifier([Ident("z")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })), Identifier([Ident("delta")])]), if_not_exists: false } parse-statement CREATE INDEX ind ON tab ((col + 1)) diff --git a/src/sql-parser/tests/testdata/insert b/src/sql-parser/tests/testdata/insert index d9ae17c71fa8..781a5923e6ad 100644 --- a/src/sql-parser/tests/testdata/insert +++ b/src/sql-parser/tests/testdata/insert @@ -58,4 +58,4 @@ INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1) ---- INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1) => -Insert { table_name: ObjectName([Ident("customer")]), columns: [], source: Query { ctes: [Cte { alias: TableAlias { name: Ident("foo"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: None }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: SetOperation { op: Union, all: false, left: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), right: Values(Values([[Value(Number("1"))]])) }, order_by: [], limit: None, offset: None, fetch: None } } +Insert { table_name: ObjectName([Ident("customer")]), columns: [], source: Query { ctes: [Cte { alias: TableAlias { name: Ident("foo"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: None }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: SetOperation { op: Union, all: false, left: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), right: Values(Values([[Value(Number("1"))]])) }, order_by: [], limit: None, offset: None, fetch: None } } diff --git a/src/sql-parser/tests/testdata/select b/src/sql-parser/tests/testdata/select index b610193353f7..eb16ff348b83 100644 --- a/src/sql-parser/tests/testdata/select +++ b/src/sql-parser/tests/testdata/select @@ -73,7 +73,7 @@ SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT 5 ---- SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT 5 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Eq, right: Value(Number("1")) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("5"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Eq, right: Value(Number("1")) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("5"))), offset: None, fetch: None }, as_of: None } # LIMIT should not be parsed as an alias. @@ -82,7 +82,7 @@ SELECT id FROM customer LIMIT 1 ---- SELECT id FROM customer LIMIT 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: None, fetch: None }, as_of: None } parse-statement SELECT 1 LIMIT 5 @@ -96,7 +96,7 @@ SELECT DISTINCT name FROM customer ---- SELECT DISTINCT name FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: true, projection: [Expr { expr: Identifier([Ident("name")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: true, projection: [Expr { expr: Identifier([Ident("name")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT ALL name FROM customer @@ -108,14 +108,14 @@ SELECT * FROM foo ---- SELECT * FROM foo => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo.* FROM foo ---- SELECT foo.* FROM foo => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: QualifiedWildcard([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: QualifiedWildcard([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT (x).a, (x).a.b.c @@ -152,7 +152,7 @@ SELECT a.col + 1 AS newname FROM foo AS a ---- SELECT a.col + 1 AS newname FROM foo AS a => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("a"), Ident("col")]), op: Plus, right: Value(Number("1")) }, alias: Some(Ident("newname")) }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), args: None, alias: Some(TableAlias { name: Ident("a"), columns: [], strict: false }) }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("a"), Ident("col")]), op: Plus, right: Value(Number("1")) }, alias: Some(Ident("newname")) }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("foo")]), alias: Some(TableAlias { name: Ident("a"), columns: [], strict: false }) }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT a.col + 1 AS newname FROM foo AS a @@ -194,14 +194,14 @@ SELECT count(*) FILTER (WHERE foo) FROM customer ---- SELECT count(*) FILTER (WHERE foo) FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: Some(Identifier([Ident("foo")])), over: None, distinct: false }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: Some(Identifier([Ident("foo")])), over: None, distinct: false }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT count(DISTINCT + x) FROM customer ---- SELECT count(DISTINCT + x) FROM customer => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Args([UnaryOp { op: Plus, expr: Identifier([Ident("x")]) }]), filter: None, over: None, distinct: true }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Function(Function { name: ObjectName([Ident("count")]), args: Args([UnaryOp { op: Plus, expr: Identifier([Ident("x")]) }]), filter: None, over: None, distinct: true }), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT count(ALL + x) FROM customer @@ -275,70 +275,70 @@ SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar) ---- SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("segment")]), subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("segm")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("segment")]), subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("segm")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE x IN (VALUES (1)) ---- SELECT * FROM t WHERE x IN (VALUES (1)) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("x")]), subquery: Query { ctes: [], body: Values(Values([[Value(Number("1"))]])), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), alias: None }, joins: [] }], selection: Some(InSubquery { expr: Identifier([Ident("x")]), subquery: Query { ctes: [], body: Values(Values([[Value(Number("1"))]])), order_by: [], limit: None, offset: None, fetch: None }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customers WHERE age BETWEEN 25 AND 32 ---- SELECT * FROM customers WHERE age BETWEEN 25 AND 32 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: false, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: false, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customers WHERE age NOT BETWEEN 25 AND 32 ---- SELECT * FROM customers WHERE age NOT BETWEEN 25 AND 32 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), args: None, alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: true, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customers")]), alias: None }, joins: [] }], selection: Some(Between { expr: Identifier([Ident("age")]), negated: true, low: Value(Number("25")), high: Value(Number("32")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL ---- SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(IsNull { expr: Between { expr: Value(Number("1")), negated: false, low: BinaryOp { left: Value(Number("1")), op: Plus, right: Value(Number("2")) }, high: BinaryOp { left: Value(Number("3")), op: Plus, right: Value(Number("4")) } }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), alias: None }, joins: [] }], selection: Some(IsNull { expr: Between { expr: Value(Number("1")), negated: false, low: BinaryOp { left: Value(Number("1")), op: Plus, right: Value(Number("2")) }, high: BinaryOp { left: Value(Number("3")), op: Plus, right: Value(Number("4")) } }, negated: false }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 ---- SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 ---- SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }, op: And, right: Between { expr: BinaryOp { left: Value(Number("1")), op: Plus, right: Identifier([Ident("x")]) }, negated: false, low: Value(Number("1")), high: Value(Number("2")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id ---- SELECT id, fname, lname FROM customer ORDER BY lname ASC, fname DESC, id => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }, OrderByExpr { expr: Identifier([Ident("id")]), asc: None }], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer WHERE id < 5 @@ -346,35 +346,35 @@ ORDER BY lname ASC, fname DESC LIMIT 2 ---- SELECT id, fname, lname FROM customer WHERE id < 5 ORDER BY lname ASC, fname DESC LIMIT 2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }], limit: Some(Value(Number("2"))), offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("id")]), op: Lt, right: Value(Number("5")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("lname")]), asc: Some(true) }, OrderByExpr { expr: Identifier([Ident("fname")]), asc: Some(false) }], limit: Some(Value(Number("2"))), offset: None, fetch: None }, as_of: None } parse-statement SELECT id, fname, lname FROM customer GROUP BY lname, fname ---- SELECT id, fname, lname FROM customer GROUP BY lname, fname => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("lname")]), Identifier([Ident("fname")])], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("id")]), alias: None }, Expr { expr: Identifier([Ident("fname")]), alias: None }, Expr { expr: Identifier([Ident("lname")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("lname")]), Identifier([Ident("fname")])], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 ---- SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 ---- SELECT foo FROM bar GROUP BY foo HAVING count(*) > 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Function(Function { name: ObjectName([Ident("count")]), args: Star, filter: None, over: None, distinct: false }), op: Gt, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT foo FROM bar GROUP BY foo HAVING 1 = 1 ---- SELECT foo FROM bar GROUP BY foo HAVING 1 = 1 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [Identifier([Ident("foo")])], having: Some(BinaryOp { left: Value(Number("1")), op: Eq, right: Value(Number("1")) }) }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement roundtrip SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT ALL @@ -432,63 +432,63 @@ SELECT * FROM t1, t2 ---- SELECT * FROM t1, t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1a NATURAL JOIN t1b, t2a NATURAL JOIN t2b ---- SELECT * FROM t1a NATURAL JOIN t1b, t2a NATURAL JOIN t2b => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t1b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1a")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t1b")]), alias: None }, join_operator: Inner(Natural) }] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t2a")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2b")]), alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 CROSS JOIN t2 ---- SELECT * FROM t1 CROSS JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: CrossJoin }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: CrossJoin }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 JOIN t2 AS foo USING(c1) ---- SELECT * FROM t1 JOIN t2 AS foo USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 JOIN t2 foo USING(c1) ---- SELECT * FROM t1 JOIN t2 AS foo USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: Some(TableAlias { name: Ident("foo"), columns: [], strict: false }) }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL JOIN t2 ---- SELECT * FROM t1 NATURAL JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL LEFT JOIN t2 ---- SELECT * FROM t1 NATURAL LEFT JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: LeftOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: LeftOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL RIGHT JOIN t2 ---- SELECT * FROM t1 NATURAL RIGHT JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: RightOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: RightOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 NATURAL FULL JOIN t2 ---- SELECT * FROM t1 NATURAL FULL JOIN t2 => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: FullOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: FullOuter(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM t1 natural @@ -504,35 +504,35 @@ SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE ---- SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE t4.c = t1.c => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }, Expr { expr: Identifier([Ident("c2")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t4")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(On(BinaryOp { left: Identifier([Ident("t2"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) })) }, Join { relation: Table { name: ObjectName([Ident("t3")]), args: None, alias: None }, join_operator: LeftOuter(Using([Ident("q"), Ident("c")])) }] }], selection: Some(BinaryOp { left: Identifier([Ident("t4"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }, Expr { expr: Identifier([Ident("c2")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("t4")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: Inner(On(BinaryOp { left: Identifier([Ident("t2"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) })) }, Join { relation: Table { name: ObjectName([Ident("t3")]), alias: None }, join_operator: LeftOuter(Using([Ident("q"), Ident("c")])) }] }], selection: Some(BinaryOp { left: Identifier([Ident("t4"), Ident("c")]), op: Eq, right: Identifier([Ident("t1"), Ident("c")]) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a NATURAL JOIN (b NATURAL JOIN (c NATURAL JOIN d NATURAL JOIN e)) NATURAL JOIN (f NATURAL JOIN (g NATURAL JOIN h)) ---- SELECT * FROM a NATURAL JOIN (b NATURAL JOIN (c NATURAL JOIN d NATURAL JOIN e)) NATURAL JOIN (f NATURAL JOIN (g NATURAL JOIN h)) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("d")]), args: None, alias: None }, join_operator: Inner(Natural) }, Join { relation: Table { name: ObjectName([Ident("e")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }, Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("f")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("g")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("h")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("c")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("d")]), alias: None }, join_operator: Inner(Natural) }, Join { relation: Table { name: ObjectName([Ident("e")]), alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }, Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("f")]), alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("g")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("h")]), alias: None }, join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (a NATURAL JOIN b) NATURAL JOIN c ---- SELECT * FROM (a NATURAL JOIN b) NATURAL JOIN c => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), alias: None }, join_operator: Inner(Natural) }] }), joins: [Join { relation: Table { name: ObjectName([Ident("c")]), alias: None }, join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (((a NATURAL JOIN b))) ---- SELECT * FROM (((a NATURAL JOIN b))) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("b")]), alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a NATURAL JOIN (((b NATURAL JOIN c))) ---- SELECT * FROM a NATURAL JOIN (((b NATURAL JOIN c))) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("c")]), args: None, alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [Join { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: NestedJoin(TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("c")]), alias: None }, join_operator: Inner(Natural) }] }), joins: [] }), joins: [] }), join_operator: Inner(Natural) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM (a NATURAL JOIN (b)) @@ -548,28 +548,28 @@ SELECT c1 FROM t1 INNER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: Inner(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 LEFT JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: LeftOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: LeftOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: RightOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: RightOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1) ---- SELECT c1 FROM t1 FULL JOIN t2 USING(c1) => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), args: None, alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), args: None, alias: None }, join_operator: FullOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("c1")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("t1")]), alias: None }, joins: [Join { relation: Table { name: ObjectName([Ident("t2")]), alias: None }, join_operator: FullOuter(Using([Ident("c1")])) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a OUTER JOIN b ON 1 @@ -588,7 +588,7 @@ SELECT foo + bar FROM a, b ---- WITH a AS (SELECT 1 AS foo), b AS (SELECT 2 AS bar) SELECT foo + bar FROM a, b => -Select { query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement CREATE VIEW v AS @@ -599,7 +599,7 @@ CREATE VIEW v AS ---- CREATE VIEW v AS WITH a AS (SELECT 1 AS foo), b AS (SELECT 2 AS bar) SELECT foo + bar FROM a, b => -CreateView { name: ObjectName([Ident("v")]), columns: [], with_options: [], query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), args: None, alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } +CreateView { name: ObjectName([Ident("v")]), columns: [], with_options: [], query: Query { ctes: [Cte { alias: TableAlias { name: Ident("a"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("1")), alias: Some(Ident("foo")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }, Cte { alias: TableAlias { name: Ident("b"), columns: [], strict: false }, query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Value(Number("2")), alias: Some(Ident("bar")) }], from: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None } }], body: Select(Select { distinct: false, projection: [Expr { expr: BinaryOp { left: Identifier([Ident("foo")]), op: Plus, right: Identifier([Ident("bar")]) }, alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("a")]), alias: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident("b")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, if_exists: Error, temporary: false, materialized: false } parse-statement roundtrip WITH cte (col1, col2) AS (SELECT foo, bar FROM baz) SELECT * FROM cte @@ -680,35 +680,35 @@ SELECT foo FROM bar OFFSET 2 ROWS ---- SELECT foo FROM bar OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS ---- SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS ---- SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT 'foo' OFFSET 0 ROWS @@ -722,49 +722,49 @@ SELECT foo FROM bar OFFSET 2 ---- SELECT foo FROM bar OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ---- SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz OFFSET 2 ---- SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2) OFFSET 2 ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2) OFFSET 2 ROWS ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } parse-statement SELECT 'foo' OFFSET 0 @@ -783,7 +783,7 @@ SELECT foo FROM bar FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT 'foo' FETCH FIRST 2 ROWS ONLY @@ -797,105 +797,105 @@ SELECT foo FROM bar FETCH FIRST ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar WHERE foo = 4 FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar ORDER BY baz FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar ORDER BY baz FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz FETCH FIRST 2 ROWS WITH TIES ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz FETCH FIRST 2 ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST 50 PERCENT ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST 50 PERCENT ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: true, quantity: Some(Value(Number("50"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: true, quantity: Some(Value(Number("50"))) }) }, as_of: None } parse-statement SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("foo")]), op: Eq, right: Value(Number("4")) }), group_by: [], having: None }), order_by: [OrderByExpr { expr: Identifier([Ident("baz")]), asc: None }], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY ---- SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("2"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST 10 ROW ONLY ---- SELECT foo FROM bar FETCH FIRST 10 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT 10 ROW ONLY ---- SELECT foo FROM bar FETCH FIRST 10 ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT 10 ROWS WITH TIES ---- SELECT foo FROM bar FETCH FIRST 10 ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: Some(Value(Number("10"))) }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH NEXT ROWS WITH TIES ---- SELECT foo FROM bar FETCH FIRST ROWS WITH TIES => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: true, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT foo FROM bar FETCH FIRST ROWS ONLY ---- SELECT foo FROM bar FETCH FIRST ROWS ONLY => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), args: None, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: Some(Fetch { with_ties: false, percent: false, quantity: None }) }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true ---- SELECT * FROM customer LEFT JOIN (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [Join { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [Join { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN LATERAL (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true ---- SELECT * FROM customer LEFT JOIN LATERAL (SELECT * FROM "order" WHERE "order".customer = customer.id LIMIT 3) AS "order" ON true => -Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), args: None, alias: None }, joins: [Join { relation: Derived { lateral: true, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), args: None, alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [Join { relation: Derived { lateral: true, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id) diff --git a/src/sql/src/normalize.rs b/src/sql/src/normalize.rs index f07c653518eb..5328e3c3a06e 100644 --- a/src/sql/src/normalize.rs +++ b/src/sql/src/normalize.rs @@ -117,15 +117,20 @@ pub fn create_statement(scx: &StatementContext, mut stmt: Statement) -> Result { - // Only attempt to resolve the name if it is not a table - // function (i.e., there are no arguments). - if args.is_none() { - self.visit_object_name_mut(name) + TableFactor::Table { name, alias } => { + self.visit_object_name_mut(name); + if let Some(alias) = alias { + self.visit_table_alias_mut(alias); } + } + TableFactor::Function { + name: _, + args, + alias, + } => { match args { - None | Some(FunctionArgs::Star) => (), - Some(FunctionArgs::Args(args)) => { + FunctionArgs::Star => (), + FunctionArgs::Args(args) => { for expr in args { self.visit_expr_mut(expr); } @@ -135,8 +140,9 @@ pub fn create_statement(scx: &StatementContext, mut stmt: Statement) -> Result visit_mut::visit_table_factor_mut(self, table_factor), } } diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index ecc18848555f..84a8feb248b3 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -923,28 +923,27 @@ fn plan_table_factor<'a>( table_factor: &'a TableFactor, ) -> Result<(RelationExpr, Scope), anyhow::Error> { match table_factor { - TableFactor::Table { name, alias, args } => { - if let Some(args) = args { - let ecx = &ExprContext { - qcx, - name: "FROM table function", - scope: &left_scope, - relation_type: &qcx.relation_type(&left), - allow_aggregates: false, - allow_subqueries: true, - }; - plan_table_function(ecx, left, &name, alias.as_ref(), args) - } else { - let name = qcx.scx.resolve_item(name.clone())?; - let item = qcx.scx.catalog.get_item(&name); - let expr = RelationExpr::Get { - id: Id::Global(item.id()), - typ: item.desc()?.typ().clone(), - }; - let column_names = item.desc()?.iter_names().map(|n| n.cloned()).collect(); - let scope = plan_table_alias(qcx, alias.as_ref(), Some(name.into()), column_names)?; - plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope) - } + TableFactor::Table { name, alias } => { + let name = qcx.scx.resolve_item(name.clone())?; + let item = qcx.scx.catalog.get_item(&name); + let expr = RelationExpr::Get { + id: Id::Global(item.id()), + typ: item.desc()?.typ().clone(), + }; + let column_names = item.desc()?.iter_names().map(|n| n.cloned()).collect(); + let scope = plan_table_alias(qcx, alias.as_ref(), Some(name.into()), column_names)?; + plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope) + } + TableFactor::Function { name, args, alias } => { + let ecx = &ExprContext { + qcx, + name: "FROM table function", + scope: &left_scope, + relation_type: &qcx.relation_type(&left), + allow_aggregates: false, + allow_subqueries: true, + }; + plan_table_function(ecx, left, &name, alias.as_ref(), args) } TableFactor::Derived { lateral, From 19aea09218e3db0c6db5a29d7dbdc592f128945e Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 23 Jul 2020 00:02:11 -0400 Subject: [PATCH 4/8] sql-parser: permit table functions to be preceded by LATERAL PostgreSQL permits table functions to be preceded by the optional noise word LATERAL, as in: SELECT * FROM foo, LATERAL bar() This does not change the semantics of the query, as table functions are always implicitly planned as if LATERAL was specified. This commit teaches the parser to accept this syntax, but not to retain the distinction. --- src/sql-parser/src/parser.rs | 19 +++++++++++-------- src/sql-parser/tests/testdata/select | 26 ++++++++++++++++++++------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index 9d315590285e..3a61c0a56839 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -2733,15 +2733,18 @@ impl Parser { /// A table name or a parenthesized subquery, followed by optional `[AS] alias` fn parse_table_factor(&mut self) -> Result { if self.parse_keyword("LATERAL") { - // LATERAL must always be followed by a subquery. - if !self.consume_token(&Token::LParen) { - self.expected( - self.peek_range(), - "subquery after LATERAL", - self.peek_token(), - )?; + // LATERAL must always be followed by a subquery or table function. + if self.consume_token(&Token::LParen) { + return self.parse_derived_table_factor(Lateral); + } else { + let name = self.parse_object_name()?; + self.expect_token(&Token::LParen)?; + return Ok(TableFactor::Function { + name, + args: self.parse_optional_args()?, + alias: self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?, + }); } - return self.parse_derived_table_factor(Lateral); } if self.consume_token(&Token::LParen) { diff --git a/src/sql-parser/tests/testdata/select b/src/sql-parser/tests/testdata/select index eb16ff348b83..d7fd9b4154be 100644 --- a/src/sql-parser/tests/testdata/select +++ b/src/sql-parser/tests/testdata/select @@ -710,6 +710,22 @@ SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS => Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Derived { lateral: false, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("bar")]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some(Value(Number("2"))), fetch: None }, as_of: None } +parse-statement +SELECT foo FROM LATERAL bar(1) +---- +SELECT foo FROM bar(1) +=> +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Expr { expr: Identifier([Ident("foo")]), alias: None }], from: [TableWithJoins { relation: Function { name: ObjectName([Ident("bar")]), args: Args([Value(Number("1"))]), alias: None }, joins: [] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } + +parse-statement +SELECT foo FROM LATERAL bar +---- +error: +Parse error: +SELECT foo FROM LATERAL bar + ^ +Expected (, found: EOF + parse-statement SELECT 'foo' OFFSET 0 ROWS ---- @@ -898,13 +914,11 @@ SELECT * FROM customer LEFT JOIN LATERAL (SELECT * FROM "order" WHERE "order".cu Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [Join { relation: Derived { lateral: true, subquery: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("order")]), alias: None }, joins: [] }], selection: Some(BinaryOp { left: Identifier([Ident("order"), Ident("customer")]), op: Eq, right: Identifier([Ident("customer"), Ident("id")]) }), group_by: [], having: None }), order_by: [], limit: Some(Value(Number("3"))), offset: None, fetch: None }, alias: Some(TableAlias { name: Ident("order"), columns: [], strict: false }) }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement -SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id) +SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id) ON true ---- -error: -Parse error: -SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id) - ^^^^^^^^^^^^^^^ -Expected subquery after LATERAL, found: generate_series +SELECT * FROM customer LEFT JOIN generate_series(1, customer.id) ON true +=> +Select { query: Query { ctes: [], body: Select(Select { distinct: false, projection: [Wildcard], from: [TableWithJoins { relation: Table { name: ObjectName([Ident("customer")]), alias: None }, joins: [Join { relation: Function { name: ObjectName([Ident("generate_series")]), args: Args([Value(Number("1")), Identifier([Ident("customer"), Ident("id")])]), alias: None }, join_operator: LeftOuter(On(Value(Boolean(true)))) }] }], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None }, as_of: None } parse-statement SELECT * FROM a LEFT JOIN LATERAL (b CROSS JOIN c) From 70bb0a2d08c76472a659f956f6d8982eec5cd8bc Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 23 Jul 2020 00:21:50 -0400 Subject: [PATCH 5/8] sql: allow deriving query context without intervening expr context This allows creating a new nested "scope" in SQL for lateral joins, as required for e.g. FROM a, LATERAL (...) b without going through a scalar expression scope first. (Most forms of correlation in SQL occur in scalar position.) --- src/sql/src/plan/query.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index 84a8feb248b3..863bb25e2fa0 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -2124,6 +2124,21 @@ impl<'a> QueryContext<'a> { fn relation_type(&self, expr: &RelationExpr) -> RelationType { expr.typ(&self.outer_relation_types, &self.param_types.borrow()) } + + fn derived_context(&self, scope: Scope, relation_type: &RelationType) -> QueryContext { + QueryContext { + scx: self.scx, + lifetime: self.lifetime, + outer_scope: scope, + outer_relation_types: self + .outer_relation_types + .iter() + .chain(std::iter::once(relation_type)) + .cloned() + .collect(), + param_types: self.param_types.clone(), + } + } } /// A bundle of unrelated things that we need for planning `Expr`s. @@ -2167,18 +2182,7 @@ impl<'a> ExprContext<'a> { } fn derived_query_context(&self) -> QueryContext { - QueryContext { - scx: self.qcx.scx, - lifetime: self.qcx.lifetime, - outer_scope: self.scope.clone(), - outer_relation_types: self - .qcx - .outer_relation_types - .iter() - .chain(std::iter::once(self.relation_type)) - .cloned() - .collect(), - param_types: self.qcx.param_types.clone(), - } + self.qcx + .derived_context(self.scope.clone(), self.relation_type) } } From 59432807d20f4d39b21bf218cbc91e2fb73a6d52 Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 23 Jul 2020 00:25:09 -0400 Subject: [PATCH 6/8] sql: inline RelationExpr::product This function is very much not-generalizable, and gets quite tricky with LATERAL joins. Just inline it in its one call site. --- src/sql/src/plan/expr.rs | 15 --------------- src/sql/src/plan/query.rs | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sql/src/plan/expr.rs b/src/sql/src/plan/expr.rs index 1eef463da60d..21f18e4e6a34 100644 --- a/src/sql/src/plan/expr.rs +++ b/src/sql/src/plan/expr.rs @@ -440,21 +440,6 @@ impl RelationExpr { } } - pub fn product(self, right: Self) -> Self { - if self.is_join_identity() { - right - } else if right.is_join_identity() { - self - } else { - RelationExpr::Join { - left: Box::new(self), - right: Box::new(right), - on: ScalarExpr::literal_true(), - kind: JoinKind::Inner, - } - } - } - pub fn reduce(self, group_key: Vec, aggregates: Vec) -> Self { RelationExpr::Reduce { input: Box::new(self), diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index 863bb25e2fa0..f424a98fe0e5 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -1198,7 +1198,21 @@ fn plan_join_operator( right_scope, JoinKind::FullOuter, ), - JoinOperator::CrossJoin => Ok((left.product(right), left_scope.product(right_scope))), + JoinOperator::CrossJoin => { + let join = if left.is_join_identity() { + right + } else if right.is_join_identity() { + left + } else { + RelationExpr::Join { + left: Box::new(left), + right: Box::new(right), + on: ScalarExpr::literal_true(), + kind: JoinKind::Inner, + } + }; + Ok((join, left_scope.product(right_scope))) + } } } From 7c72cf52bdcc87b61129a5f4f1db7a31c54f0bcb Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 23 Jul 2020 00:28:34 -0400 Subject: [PATCH 7/8] sql: manually recur in RelationExpr::visit_columns Using the visit_mut helper makes it difficult (impossible?) to increase `depth` for certain RelationExprs, which will be important for LATERAL joins. Just manually recur rather than using the visit_mut helper. There are intended to be no semantic changes in this commit. --- src/sql/src/plan/expr.rs | 49 ++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/sql/src/plan/expr.rs b/src/sql/src/plan/expr.rs index 21f18e4e6a34..bdcdd0c6c112 100644 --- a/src/sql/src/plan/expr.rs +++ b/src/sql/src/plan/expr.rs @@ -621,37 +621,56 @@ impl RelationExpr { where F: FnMut(usize, &mut ColumnRef), { - self.visit_mut(&mut |e| match e { - RelationExpr::Join { on, .. } => on.visit_columns(depth, f), - RelationExpr::Map { scalars, .. } => { + match self { + RelationExpr::Join { + kind: _, + on, + left, + right, + } => { + on.visit_columns(depth, f); + left.visit_columns(depth, f); + right.visit_columns(depth, f); + } + RelationExpr::Map { scalars, input } => { for scalar in scalars { scalar.visit_columns(depth, f); } + input.visit_columns(depth, f); } - RelationExpr::FlatMap { exprs, .. } => { + RelationExpr::FlatMap { exprs, input, .. } => { for expr in exprs { expr.visit_columns(depth, f); } + input.visit_columns(depth, f); } - RelationExpr::Filter { predicates, .. } => { + RelationExpr::Filter { predicates, input } => { for predicate in predicates { predicate.visit_columns(depth, f); } + input.visit_columns(depth, f); } - RelationExpr::Reduce { aggregates, .. } => { + RelationExpr::Reduce { + aggregates, input, .. + } => { for aggregate in aggregates { aggregate.visit_columns(depth, f); } + input.visit_columns(depth, f); } - RelationExpr::Constant { .. } - | RelationExpr::Get { .. } - | RelationExpr::Project { .. } - | RelationExpr::Distinct { .. } - | RelationExpr::TopK { .. } - | RelationExpr::Negate { .. } - | RelationExpr::Threshold { .. } - | RelationExpr::Union { .. } => (), - }) + RelationExpr::Union { left, right } => { + left.visit_columns(depth, f); + right.visit_columns(depth, f); + } + RelationExpr::Project { input, .. } + | RelationExpr::Distinct { input } + | RelationExpr::TopK { input, .. } + | RelationExpr::Negate { input } + | RelationExpr::Threshold { input } => { + input.visit_columns(depth, f); + } + RelationExpr::Constant { .. } | RelationExpr::Get { .. } => (), + } } /// Replaces any parameter references in the expression with the From d37fe57a022b3fddd737f4cf1dd2cbc1937e7133 Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 23 Jul 2020 00:32:08 -0400 Subject: [PATCH 8/8] sql: support LATERAL joins A LATERAL join allows the right-hand side of the join to access columns defined on the left-hand side of the join. A simple example from the PostgreSQL docs is the query SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss which is equivalent to: SELECT * FROM foo, bar WHERE bar.id = foo.bar_id; The hope is that LATERAL joins will be useful for expressing "top-k within a group", as in: SELECT * FROM (SELECT DISTINCT cat FROM foo) grp, JOIN LATERAL (SELECT * FROM foo WHERE foo.cat = grp.cat ORDER BY foo.val LIMIT $k) --- src/sql/src/plan/decorrelate.rs | 146 +++- src/sql/src/plan/explain.rs | 14 +- src/sql/src/plan/expr.rs | 78 ++- src/sql/src/plan/query.rs | 119 ++-- src/sql/src/plan/scope.rs | 1 - src/sql/src/plan/transform_expr.rs | 14 +- .../cockroach/subquery_correlated.slt | 35 +- test/sqllogictest/postgres/join-lateral.slt | 648 ++++++++++++++++++ test/sqllogictest/table_func.slt | 27 +- 9 files changed, 968 insertions(+), 114 deletions(-) create mode 100644 test/sqllogictest/postgres/join-lateral.slt diff --git a/src/sql/src/plan/decorrelate.rs b/src/sql/src/plan/decorrelate.rs index 3de3938744ad..fc4af55df650 100644 --- a/src/sql/src/plan/decorrelate.rs +++ b/src/sql/src/plan/decorrelate.rs @@ -37,6 +37,7 @@ use std::collections::{BTreeSet, HashMap}; use anyhow::bail; +use itertools::Itertools; use ore::collections::CollectionExt; use repr::RelationType; @@ -80,6 +81,32 @@ impl ColumnMap { fn len(&self) -> usize { self.inner.len() } + + /// Updates references in the `ColumnMap` for use in a nested scope. The + /// provided `arity` must specify the arity of the current scope. + fn enter_scope(&self, arity: usize) -> ColumnMap { + // From the perspective of the nested scope, all existing column + // references will be one level greater. + let existing = self + .inner + .clone() + .into_iter() + .update(|(col, _i)| col.level += 1); + + // All columns in the current scope become explicit entries in the + // immediate parent scope. + let new = (0..arity).map(|i| { + ( + ColumnRef { + level: 1, + column: i, + }, + self.len() + i, + ) + }); + + ColumnMap::new(existing.chain(new).collect()) + } } impl RelationExpr { @@ -165,13 +192,13 @@ impl RelationExpr { } input } - FlatMap { input, func, exprs } => { + CallTable { func, exprs } => { // FlatMap expressions may contain correlated subqueries. Unlike Map they are not // allowed to refer to the results of previous expressions, and we have a simpler // implementation that appends all relevant columns first, then applies the flatmap // operator to the result, then strips off any columns introduce by subqueries. - let mut input = input.applied_to(id_gen, get_outer, col_map); + let mut input = get_outer; let old_arity = input.arity(); let exprs = exprs @@ -209,6 +236,60 @@ impl RelationExpr { } input } + Join { + left, + right, + on, + kind, + } if kind.is_lateral() => { + // A LATERAL join is a join in which the right expression has + // access to the columns in the left expression. It turns out + // this is *exactly* our branch operator, plus some additional + // null handling in the case of left joins. (Right and full + // lateral joins are not permitted.) + // + // As with normal joins, the `on` predicate may be correlated, + // and we treat it as a filter that follows the branch. + + let left = left.applied_to(id_gen, get_outer, col_map); + left.let_in(id_gen, |id_gen, get_left| { + let mut join = branch( + id_gen, + get_left.clone(), + col_map, + *right, + |id_gen, right, get_left, col_map| { + right.applied_to(id_gen, get_left, col_map) + }, + ); + + // Plan the `on` predicate. + let old_arity = join.arity(); + let on = on.applied_to(id_gen, col_map, &mut join); + join = join.filter(vec![on]); + let new_arity = join.arity(); + if old_arity != new_arity { + // This means we added some columns to handle + // subqueries, and now we need to get rid of them. + join = join.project((0..old_arity).collect()); + } + + // If a left join, reintroduce any rows from the left that + // are missing, with nulls filled in for the right columns. + if let JoinKind::LeftOuter { .. } = kind { + let default = join + .typ() + .column_types + .into_iter() + .skip(get_left.arity()) + .map(|typ| (Datum::Null, typ.nullable(true))) + .collect(); + get_left.lookup(id_gen, join, default) + } else { + join + } + }) + } Join { left, right, @@ -257,7 +338,7 @@ impl RelationExpr { } join.let_in(id_gen, |id_gen, get_join| { let mut result = get_join.clone(); - if let JoinKind::LeftOuter | JoinKind::FullOuter = kind { + if let JoinKind::LeftOuter { .. } | JoinKind::FullOuter { .. } = kind { let left_outer = get_left.clone().anti_lookup( id_gen, get_join.clone(), @@ -549,6 +630,65 @@ where // at the least for purposes of understanding. It was difficult for one reader // to understand the required properties of `outer` and `col_map`. + // If the inner expression is sufficiently simple, it is safe to apply it + // *directly* to outer, rather than applying it to the distinctified key + // (see below). + // + // As an example, consider the following two queries: + // + // CREATE TABLE t (a int, b int); + // SELECT a, series FROM t, generate_series(1, t.b) series; + // + // The "simple" path for the `SELECT` yields + // + // %0 = + // | Get t + // | FlatMap generate_series(1, #1) + // + // while the non-simple path yields: + // + // %0 = + // | Get t + // + // %1 = + // | Get t + // | Distinct group=(#1) + // | FlatMap generate_series(1, #0) + // + // %2 = + // | LeftJoin %1 %2 (= #1 #2) + // + // There is a tradeoff here: the simple plan is stateless, but the non- + // simple plan may do (much) less computation if there are only a few + // distinct values of `t.b`. + // + // We apply a very simple heuristic here and take the simple path if `inner` + // contains only maps, filters, projections, and calls to table functions. + // The intuition is that straightforward usage of table functions should + // take the simple path, while everything else should not. (In theory we + // think this transformation is valid as long as `inner` does not contain a + // Reduce, Distinct, or TopK node, but it is not always an optimization in + // the general case.) + // + // TODO(benesch): this should all be handled by a proper optimizer, but + // detecting the moment of decorrelation in the optimizer right now is too + // hard. + let mut is_simple = true; + inner.visit(&mut |expr| match expr { + RelationExpr::Constant { .. } + | RelationExpr::Project { .. } + | RelationExpr::Map { .. } + | RelationExpr::Filter { .. } + | RelationExpr::CallTable { .. } => (), + _ => is_simple = false, + }); + if is_simple { + let new_col_map = col_map.enter_scope(outer.arity() - col_map.len()); + return outer.let_in(id_gen, |id_gen, get_outer| { + apply(id_gen, inner, get_outer, &new_col_map) + }); + } + // The key consists of the columns from the outer expression upon which the // inner relation depends. We discover these dependencies by walking the // inner relation expression and looking for column references whose level diff --git a/src/sql/src/plan/explain.rs b/src/sql/src/plan/explain.rs index 3ff835e286f2..07bcf1a9b266 100644 --- a/src/sql/src/plan/explain.rs +++ b/src/sql/src/plan/explain.rs @@ -133,7 +133,7 @@ impl RelationExpr { Some(parent_expr) => match parent_expr { Project { .. } | Map { .. } - | FlatMap { .. } + | CallTable { .. } | Filter { .. } | Reduce { .. } | TopK { .. } @@ -163,7 +163,7 @@ impl RelationExpr { | TopK { .. } => (), Map { scalars, .. } => scalar_exprs.extend(scalars), Filter { predicates, .. } => scalar_exprs.extend(predicates), - FlatMap { exprs, .. } => scalar_exprs.extend(exprs), + CallTable { exprs, .. } => scalar_exprs.extend(exprs), Join { on, .. } => scalar_exprs.push(on), Reduce { aggregates, .. } => { scalar_exprs.extend(aggregates.iter().map(|a| &*a.expr)) @@ -241,10 +241,10 @@ impl RelationExpr { ) .unwrap(); } - FlatMap { func, exprs, .. } => { + CallTable { func, exprs } => { write!( pretty, - "FlatMap {}({})", + "CallTable {}({})", func, Separated( ", ", @@ -445,8 +445,10 @@ impl std::fmt::Display for JoinKind { f, "{}", match self { - JoinKind::Inner => "Inner", - JoinKind::LeftOuter => "LeftOuter", + JoinKind::Inner { lateral: false } => "Inner", + JoinKind::Inner { lateral: true } => "InnerLateral", + JoinKind::LeftOuter { lateral: false } => "LeftOuter", + JoinKind::LeftOuter { lateral: true } => "LeftOuterLateral", JoinKind::RightOuter => "RightOuter", JoinKind::FullOuter => "FullOuter", } diff --git a/src/sql/src/plan/expr.rs b/src/sql/src/plan/expr.rs index bdcdd0c6c112..a389bd785915 100644 --- a/src/sql/src/plan/expr.rs +++ b/src/sql/src/plan/expr.rs @@ -12,6 +12,7 @@ //! similar to that file, with some differences which are noted below. It gets turned into that //! representation via a call to decorrelate(). +use std::borrow::Cow; use std::collections::BTreeMap; use std::mem; @@ -54,8 +55,7 @@ pub enum RelationExpr { input: Box, scalars: Vec, }, - FlatMap { - input: Box, + CallTable { func: TableFunc, exprs: Vec, }, @@ -269,12 +269,21 @@ pub struct ColumnRef { #[derive(Debug, Clone, PartialEq, Eq)] pub enum JoinKind { - Inner, - LeftOuter, + Inner { lateral: bool }, + LeftOuter { lateral: bool }, RightOuter, FullOuter, } +impl JoinKind { + pub fn is_lateral(&self) -> bool { + match self { + JoinKind::Inner { lateral } | JoinKind::LeftOuter { lateral } => *lateral, + JoinKind::RightOuter | JoinKind::FullOuter => false, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct AggregateExpr { pub func: AggregateFunc, @@ -307,32 +316,35 @@ impl RelationExpr { } typ } - RelationExpr::FlatMap { - input, - func, - exprs: _, - } => { - let mut typ = input.typ(outers, params); - typ.column_types.extend(func.output_type().column_types); - // FlatMap can add duplicate rows, so input keys are no longer valid - RelationType::new(typ.column_types) - } + RelationExpr::CallTable { func, exprs: _ } => func.output_type(), RelationExpr::Filter { input, .. } | RelationExpr::TopK { input, .. } => { input.typ(outers, params) } RelationExpr::Join { left, right, kind, .. } => { - let left_nullable = *kind == JoinKind::RightOuter || *kind == JoinKind::FullOuter; - let right_nullable = *kind == JoinKind::LeftOuter || *kind == JoinKind::FullOuter; + let left_nullable = matches!(kind, JoinKind::RightOuter | JoinKind::FullOuter); + let right_nullable = + matches!(kind, JoinKind::LeftOuter { .. } | JoinKind::FullOuter); let lt = left.typ(outers, params).column_types.into_iter().map(|t| { let nullable = t.nullable || left_nullable; t.nullable(nullable) }); - let rt = right.typ(outers, params).column_types.into_iter().map(|t| { - let nullable = t.nullable || right_nullable; - t.nullable(nullable) - }); + let outers = if kind.is_lateral() { + let mut outers = outers.to_vec(); + outers.push(RelationType::new(lt.clone().collect())); + Cow::Owned(outers) + } else { + Cow::Borrowed(outers) + }; + let rt = right + .typ(&outers, params) + .column_types + .into_iter() + .map(|t| { + let nullable = t.nullable || right_nullable; + t.nullable(nullable) + }); RelationType::new(lt.chain(rt).collect()) } RelationExpr::Reduce { @@ -378,7 +390,7 @@ impl RelationExpr { RelationExpr::Get { typ, .. } => typ.column_types.len(), RelationExpr::Project { outputs, .. } => outputs.len(), RelationExpr::Map { input, scalars } => input.arity() + scalars.len(), - RelationExpr::FlatMap { input, func, .. } => input.arity() + func.output_arity(), + RelationExpr::CallTable { func, .. } => func.output_arity(), RelationExpr::Filter { input, .. } | RelationExpr::TopK { input, .. } | RelationExpr::Distinct { input } @@ -521,16 +533,15 @@ impl RelationExpr { F: FnMut(&'a Self), { match self { - RelationExpr::Constant { .. } | RelationExpr::Get { .. } => (), + RelationExpr::Constant { .. } + | RelationExpr::Get { .. } + | RelationExpr::CallTable { .. } => (), RelationExpr::Project { input, .. } => { f(input); } RelationExpr::Map { input, .. } => { f(input); } - RelationExpr::FlatMap { input, .. } => { - f(input); - } RelationExpr::Filter { input, .. } => { f(input); } @@ -573,16 +584,15 @@ impl RelationExpr { F: FnMut(&'a mut Self), { match self { - RelationExpr::Constant { .. } | RelationExpr::Get { .. } => (), + RelationExpr::Constant { .. } + | RelationExpr::Get { .. } + | RelationExpr::CallTable { .. } => (), RelationExpr::Project { input, .. } => { f(input); } RelationExpr::Map { input, .. } => { f(input); } - RelationExpr::FlatMap { input, .. } => { - f(input); - } RelationExpr::Filter { input, .. } => { f(input); } @@ -623,14 +633,15 @@ impl RelationExpr { { match self { RelationExpr::Join { - kind: _, + kind, on, left, right, } => { - on.visit_columns(depth, f); left.visit_columns(depth, f); + let depth = if kind.is_lateral() { depth + 1 } else { depth }; right.visit_columns(depth, f); + on.visit_columns(depth, f); } RelationExpr::Map { scalars, input } => { for scalar in scalars { @@ -638,11 +649,10 @@ impl RelationExpr { } input.visit_columns(depth, f); } - RelationExpr::FlatMap { exprs, input, .. } => { + RelationExpr::CallTable { exprs, .. } => { for expr in exprs { expr.visit_columns(depth, f); } - input.visit_columns(depth, f); } RelationExpr::Filter { predicates, input } => { for predicate in predicates { @@ -683,7 +693,7 @@ impl RelationExpr { scalar.bind_parameters(parameters); } } - RelationExpr::FlatMap { exprs, .. } => { + RelationExpr::CallTable { exprs, .. } => { for expr in exprs { expr.bind_parameters(parameters); } diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index f424a98fe0e5..895a64750601 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -915,14 +915,25 @@ fn plan_table_with_joins<'a>( Ok((left, left_scope)) } -fn plan_table_factor<'a>( +fn plan_table_factor( qcx: &QueryContext, left: RelationExpr, left_scope: Scope, join_operator: &JoinOperator, - table_factor: &'a TableFactor, + table_factor: &TableFactor, ) -> Result<(RelationExpr, Scope), anyhow::Error> { - match table_factor { + let lateral = matches!( + table_factor, + TableFactor::Function { .. } | TableFactor::Derived { lateral: true, .. } + ); + + let qcx = if lateral { + Cow::Owned(qcx.derived_context(left_scope.clone(), &qcx.relation_type(&left))) + } else { + Cow::Borrowed(qcx) + }; + + let (expr, scope) = match table_factor { TableFactor::Table { name, alias } => { let name = qcx.scx.resolve_item(name.clone())?; let item = qcx.scx.catalog.get_item(&name); @@ -931,51 +942,51 @@ fn plan_table_factor<'a>( typ: item.desc()?.typ().clone(), }; let column_names = item.desc()?.iter_names().map(|n| n.cloned()).collect(); - let scope = plan_table_alias(qcx, alias.as_ref(), Some(name.into()), column_names)?; - plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope) + let scope = plan_table_alias(&qcx, alias.as_ref(), Some(name.into()), column_names)?; + (expr, scope) } + TableFactor::Function { name, args, alias } => { let ecx = &ExprContext { - qcx, + qcx: &qcx, name: "FROM table function", - scope: &left_scope, - relation_type: &qcx.relation_type(&left), + scope: &Scope::empty(Some(qcx.outer_scope.clone())), + relation_type: &RelationType::empty(), allow_aggregates: false, allow_subqueries: true, }; - plan_table_function(ecx, left, &name, alias.as_ref(), args) + plan_table_function(ecx, &name, alias.as_ref(), args)? } + TableFactor::Derived { - lateral, + lateral: _, subquery, alias, } => { - if *lateral { - unsupported!(3111, "LATERAL derived tables"); - } let (expr, scope) = plan_subquery(&qcx, &subquery)?; let table_name = None; let column_names = scope.column_names().map(|n| n.cloned()).collect(); - let scope = plan_table_alias(qcx, alias.as_ref(), table_name, column_names)?; - plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope) + let scope = plan_table_alias(&qcx, alias.as_ref(), table_name, column_names)?; + (expr, scope) } + TableFactor::NestedJoin(table_with_joins) => { - let (identity, identity_scope) = plan_join_identity(qcx); - let (expr, scope) = plan_table_with_joins( - qcx, + let (identity, identity_scope) = plan_join_identity(&qcx); + plan_table_with_joins( + &qcx, identity, identity_scope, &JoinOperator::CrossJoin, table_with_joins, - )?; - plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope) + )? } - } + }; + + plan_join_operator(&qcx, &join_operator, left, left_scope, expr, scope, lateral) } fn plan_table_function( ecx: &ExprContext, - left: RelationExpr, name: &ObjectName, alias: Option<&TableAlias>, args: &FunctionArgs, @@ -986,8 +997,7 @@ fn plan_table_function( FunctionArgs::Args(args) => args, }; let tf = func::select_table_func(ecx, &*ident, args)?; - let call = RelationExpr::FlatMap { - input: Box::new(left), + let call = RelationExpr::CallTable { func: tf.func, exprs: tf.exprs, }; @@ -997,7 +1007,7 @@ fn plan_table_function( item: ident, }; let scope = plan_table_alias(ecx.qcx, alias, Some(name), tf.column_names)?; - Ok((call, ecx.scope.clone().product(scope))) + Ok((call, scope)) } fn plan_table_alias( @@ -1160,6 +1170,7 @@ fn plan_join_operator( left_scope: Scope, right: RelationExpr, right_scope: Scope, + lateral: bool, ) -> Result<(RelationExpr, Scope), anyhow::Error> { match operator { JoinOperator::Inner(constraint) => plan_join_constraint( @@ -1169,7 +1180,7 @@ fn plan_join_operator( left_scope, right, right_scope, - JoinKind::Inner, + JoinKind::Inner { lateral }, ), JoinOperator::LeftOuter(constraint) => plan_join_constraint( qcx, @@ -1178,26 +1189,36 @@ fn plan_join_operator( left_scope, right, right_scope, - JoinKind::LeftOuter, - ), - JoinOperator::RightOuter(constraint) => plan_join_constraint( - qcx, - &constraint, - left, - left_scope, - right, - right_scope, - JoinKind::RightOuter, - ), - JoinOperator::FullOuter(constraint) => plan_join_constraint( - qcx, - &constraint, - left, - left_scope, - right, - right_scope, - JoinKind::FullOuter, + JoinKind::LeftOuter { lateral }, ), + JoinOperator::RightOuter(constraint) => { + if lateral { + bail!("the combining JOIN type must be INNER or LEFT for a LATERAL reference"); + } + plan_join_constraint( + qcx, + &constraint, + left, + left_scope, + right, + right_scope, + JoinKind::RightOuter, + ) + } + JoinOperator::FullOuter(constraint) => { + if lateral { + bail!("the combining JOIN type must be INNER or LEFT for a LATERAL reference"); + } + plan_join_constraint( + qcx, + &constraint, + left, + left_scope, + right, + right_scope, + JoinKind::FullOuter, + ) + } JoinOperator::CrossJoin => { let join = if left.is_join_identity() { right @@ -1208,7 +1229,7 @@ fn plan_join_operator( left: Box::new(left), right: Box::new(right), on: ScalarExpr::literal_true(), - kind: JoinKind::Inner, + kind: JoinKind::Inner { lateral }, } }; Ok((join, left_scope.product(right_scope))) @@ -1244,7 +1265,7 @@ fn plan_join_constraint<'a>( allow_subqueries: true, }; let on = plan_expr(ecx, expr)?.type_as(ecx, ScalarType::Bool)?; - if kind == JoinKind::Inner { + if let JoinKind::Inner { .. } = kind { for (l, r) in find_trivial_column_equivalences(&on) { // When we can statically prove that two columns are // equivalent after a join, the right column becomes @@ -2105,7 +2126,7 @@ pub enum QueryLifetime { } /// The state required when planning a `Query`. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct QueryContext<'a> { /// The context for the containing `Statement`. pub scx: &'a StatementContext<'a>, @@ -2139,7 +2160,7 @@ impl<'a> QueryContext<'a> { expr.typ(&self.outer_relation_types, &self.param_types.borrow()) } - fn derived_context(&self, scope: Scope, relation_type: &RelationType) -> QueryContext { + fn derived_context(&self, scope: Scope, relation_type: &RelationType) -> QueryContext<'a> { QueryContext { scx: self.scx, lifetime: self.lifetime, diff --git a/src/sql/src/plan/scope.rs b/src/sql/src/plan/scope.rs index aefa2b80848e..62324894db9f 100644 --- a/src/sql/src/plan/scope.rs +++ b/src/sql/src/plan/scope.rs @@ -266,7 +266,6 @@ impl Scope { } pub fn product(self, right: Self) -> Self { - assert!(self.outer_scope == right.outer_scope); Scope { items: self .items diff --git a/src/sql/src/plan/transform_expr.rs b/src/sql/src/plan/transform_expr.rs index 408e2b4de892..04b7eb234b7f 100644 --- a/src/sql/src/plan/transform_expr.rs +++ b/src/sql/src/plan/transform_expr.rs @@ -58,7 +58,7 @@ pub fn split_subquery_predicates(expr: &mut RelationExpr) { walk_scalar(scalar); } } - RelationExpr::FlatMap { exprs, .. } => { + RelationExpr::CallTable { exprs, .. } => { for expr in exprs { walk_scalar(expr); } @@ -155,12 +155,7 @@ pub fn split_subquery_predicates(expr: &mut RelationExpr) { pub fn try_simplify_quantified_comparisons(expr: &mut RelationExpr) { fn walk_relation(expr: &mut RelationExpr, outers: &[RelationType]) { expr.visit_mut(&mut |expr| match expr { - RelationExpr::Map { scalars, input } - | RelationExpr::FlatMap { - exprs: scalars, - input, - .. - } => { + RelationExpr::Map { scalars, input } => { let mut outers = outers.to_vec(); outers.push(input.typ(&outers, &NO_PARAMS)); for scalar in scalars { @@ -174,6 +169,11 @@ pub fn try_simplify_quantified_comparisons(expr: &mut RelationExpr) { walk_scalar(pred, &outers, true); } } + RelationExpr::CallTable { exprs, .. } => { + for scalar in exprs { + walk_scalar(scalar, &outers, false); + } + } _ => (), }) } diff --git a/test/sqllogictest/cockroach/subquery_correlated.slt b/test/sqllogictest/cockroach/subquery_correlated.slt index 087564966faf..dc37d0595aac 100644 --- a/test/sqllogictest/cockroach/subquery_correlated.slt +++ b/test/sqllogictest/cockroach/subquery_correlated.slt @@ -878,23 +878,38 @@ SELECT (SELECT string_agg(DISTINCT ship, ', ') WHERE o_c_id=c.c_id) FROM c ORDER BY c_id -# #3111 -statement error supported +query ITI SELECT * FROM - (SELECT c_id AS c_c_id, bill FROM c), - LATERAL (SELECT row_number() OVER () AS rownum FROM o WHERE c_id = c_c_id) -ORDER BY c_c_id, bill, rownum + (SELECT c_id AS c_c_id, bill FROM c) s1, + LATERAL (SELECT o_id FROM o WHERE c_id = c_c_id) s2 +ORDER BY c_c_id, bill, o_id +---- +1 CA 10 +1 CA 20 +1 CA 30 +2 TX 40 +2 TX 50 +2 TX 60 +4 TX 70 +4 TX 80 +6 FL 90 -# #3111 -statement error supported +query TI SELECT * FROM - (SELECT bill FROM c), - LATERAL (SELECT row_number() OVER (PARTITION BY bill) AS rownum FROM o WHERE ship = bill) -ORDER BY bill, rownum + (SELECT bill FROM c) s1, + LATERAL (SELECT o_id FROM o WHERE ship = bill) s2 +ORDER BY bill, o_id +---- +CA 10 +CA 20 +CA 30 +CA 40 +TX 50 +TX 50 # ------------------------------------------------------------------------------ # Subqueries in other interesting locations. diff --git a/test/sqllogictest/postgres/join-lateral.slt b/test/sqllogictest/postgres/join-lateral.slt new file mode 100644 index 000000000000..2d13e2947158 --- /dev/null +++ b/test/sqllogictest/postgres/join-lateral.slt @@ -0,0 +1,648 @@ +# Copyright 1994, Regents of the University of California. +# Copyright 1996-2019 PostgreSQL Global Development Group. +# Copyright Materialize, Inc. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. +# +# This file is derived from the regression test suite in PostgreSQL. +# The original file was retrieved on July 23, 2020 from: +# +# https://github.com/postgres/postgres/blob/5940ffb221316ab73e6fdc780dfe9a07d4221ebb/src/test/regress/expected/join.out +# +# The original source code is subject to the terms of the PostgreSQL +# license, a copy of which can be found in the LICENSE file at the +# root of this repository. + +mode cockroach + +statement ok +CREATE TABLE int2_tbl (f1 smallint) + +statement ok +INSERT INTO int2_tbl (f1) VALUES (0), (1234), (-1234), (32767), (-32767); + +statement ok +CREATE TABLE int4_tbl (f1 int) + +statement ok +INSERT INTO int4_tbl (f1) VALUES (0), (123456), (-123456), (2147483647), (-2147483647) + +statement ok +CREATE TABLE int8_tbl (q1 bigint, q2 bigint) + +statement ok +INSERT INTO int8_tbl VALUES + (123, 456), + (123, 4567890123456789), + (4567890123456789, 123), + (4567890123456789, 4567890123456789), + (4567890123456789, -4567890123456789) + +statement ok +CREATE TABLE tenk1 ( + unique1 int, + unique2 int, + two int, +) + +statement ok +INSERT INTO tenk1 VALUES + (2, 0, 0), + (1, 3, 1), + (6, 9, 0), + (5, 8, 0), + (0, 4, 0), + (7, 7, 1), + (9, 2, 1), + (4, 6, 1), + (8, 1, 0), + (3, 5, 1) + +# NOTE(benesch): The tenk1 table is named as such because it is meant to contain +# 10k rows. We include only 10 rows here because 10k rows causes Materialize to +# absolutely fall over since the plans for a lot of these lateral joins are +# absolutely horrible. + +# statement ok +# CREATE TABLE onerow () + +# statement ok +# INSERT INTO onerow DEFAULT VALUES + +query II colnames +select unique2, x.* +from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; +---- +unique2 f1 +4 0 + +query II colnames +select unique2, x.* +from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; +---- +unique2 f1 +4 0 + +query II colnames,rowsort +select unique2, x.* +from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; +---- +unique2 f1 +4 0 +NULL -123456 +NULL -2147483647 +NULL 123456 +NULL 2147483647 + +# check scoping of lateral versus parent references +# the first of these should return int8_tbl.q2, the second int8_tbl.q1 +# TODO(benesch): col name should be "r", not "?column?" +query III colnames,rowsort +select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl +---- +q1 q2 ?column? +123 456 456 +123 4567890123456789 4567890123456789 +4567890123456789 123 123 +4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 -4567890123456789 + +# TODO(benesch): col name should be "r", not "?column?" +query III colnames,rowsort +select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; +---- +q1 q2 ?column? +123 456 123 +123 4567890123456789 123 +4567890123456789 123 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 + +# lateral with function in FROM +query I colnames +select count(*) from tenk1 a, lateral generate_series(1,two) g; +---- +count +5 + +query III colnames,rowsort +select * from generate_series(100,200) g (g), + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss +---- +g q1 q2 +123 123 456 +123 123 4567890123456789 +123 4567890123456789 123 + +query I colnames +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; +---- +count +10 + +query I colnames +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; +---- +count +10 + +# lateral injecting a strange outer join condition +query IIIII colnames +select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; +---- +q1 q2 q1 q2 z +123 456 123 456 NULL +123 456 123 4567890123456789 NULL +123 456 4567890123456789 -4567890123456789 NULL +123 456 4567890123456789 123 123 +123 456 4567890123456789 123 123 +123 456 4567890123456789 123 123 +123 456 4567890123456789 123 123 +123 456 4567890123456789 123 123 +123 456 4567890123456789 4567890123456789 NULL +123 4567890123456789 123 456 NULL +123 4567890123456789 123 4567890123456789 NULL +123 4567890123456789 4567890123456789 -4567890123456789 NULL +123 4567890123456789 4567890123456789 123 123 +123 4567890123456789 4567890123456789 123 123 +123 4567890123456789 4567890123456789 123 123 +123 4567890123456789 4567890123456789 123 123 +123 4567890123456789 4567890123456789 123 123 +123 4567890123456789 4567890123456789 4567890123456789 NULL +4567890123456789 -4567890123456789 123 456 NULL +4567890123456789 -4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 -4567890123456789 NULL +4567890123456789 -4567890123456789 4567890123456789 123 NULL +4567890123456789 -4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 123 123 456 NULL +4567890123456789 123 123 4567890123456789 4567890123456789 +4567890123456789 123 123 4567890123456789 4567890123456789 +4567890123456789 123 123 4567890123456789 4567890123456789 +4567890123456789 123 123 4567890123456789 4567890123456789 +4567890123456789 123 123 4567890123456789 4567890123456789 +4567890123456789 123 4567890123456789 -4567890123456789 NULL +4567890123456789 123 4567890123456789 123 NULL +4567890123456789 123 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 123 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 123 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 123 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 123 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 123 456 NULL +4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 -4567890123456789 NULL +4567890123456789 4567890123456789 4567890123456789 123 NULL +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 + +# lateral reference to a join alias variable +query III colnames +select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (select x) ss2(y); +---- +x f1 y +0 0 0 + +query III colnames,rowsort +select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (values(x)) ss2(y); +---- +x f1 y + 0 0 0 + 123456 123456 123456 + -123456 -123456 -123456 + 2147483647 2147483647 2147483647 +-2147483647 -2147483647 -2147483647 + +# TODO(benesch): parse failure. +# +# query III colnames +# select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, +# lateral (select x) ss2(y); +# ---- +# x f1 y +# 0 0 0 + +# lateral references requiring pullup +query II rowsort +select * from (values(1)) x(lb), + lateral generate_series(lb,4) x4; +---- +1 1 +1 2 +1 3 +1 4 + +query II rowsort +select * from (select f1/1000000000 from int4_tbl) x(lb), + lateral generate_series(lb,4) x4; +---- +-2 0 +-2 1 +-2 2 +-2 3 +-2 4 +-2 -1 +-2 -2 +0 0 +0 0 +0 0 +0 1 +0 1 +0 1 +0 2 +0 2 +0 2 +0 3 +0 3 +0 3 +0 4 +0 4 +0 4 +2 2 +2 3 +2 4 + +query II colnames +select * from (values(1)) x(lb), + lateral (values(lb)) y(lbcopy) +---- +lb lbcopy +1 1 + +query II colnames +select * from (values(1)) x(lb), + lateral (select lb from int4_tbl) y(lbcopy); +---- +lb lbcopy +1 1 +1 1 +1 1 +1 1 +1 1 + +query IIIIIII colnames,rowsort +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); +---- +q1 q2 q1 q2 xq1 yq1 yq2 +4567890123456789 -4567890123456789 NULL NULL 4567890123456789 NULL NULL +4567890123456789 123 123 456 4567890123456789 123 456 +4567890123456789 123 123 4567890123456789 4567890123456789 123 4567890123456789 +123 456 NULL NULL 123 NULL NULL +123 4567890123456789 4567890123456789 123 123 4567890123456789 123 +123 4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +123 4567890123456789 4567890123456789 -4567890123456789 123 4567890123456789 -4567890123456789 +4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 123 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 4567890123456789 -4567890123456789 + +query IIIIIII colnames,rowsort +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +---- +q1 q2 q1 q2 xq1 yq1 yq2 +4567890123456789 -4567890123456789 NULL NULL 4567890123456789 NULL NULL +4567890123456789 123 123 456 4567890123456789 123 456 +4567890123456789 123 123 4567890123456789 4567890123456789 123 4567890123456789 +123 456 NULL NULL 123 NULL NULL +123 4567890123456789 4567890123456789 123 123 4567890123456789 123 +123 4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 +123 4567890123456789 4567890123456789 -4567890123456789 123 4567890123456789 -4567890123456789 +4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 4567890123456789 123 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 4567890123456789 -4567890123456789 + +query II colnames,rowsort +select x.* from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +---- +q1 q2 +123 456 +123 4567890123456789 +123 4567890123456789 +123 4567890123456789 +4567890123456789 123 +4567890123456789 123 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 + +query II colnames,rowsort +select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); +---- +vx vy +123 NULL +456 NULL +123 4567890123456789 +4567890123456789 -4567890123456789 +123 4567890123456789 +4567890123456789 4567890123456789 +123 4567890123456789 +4567890123456789 123 +4567890123456789 123 +123 4567890123456789 +4567890123456789 123 +123 456 +4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 123 +4567890123456789 NULL +-4567890123456789 NULL + +query II colnames,rowsort +select v.* from + (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); +---- +vx vy +4567890123456789 NULL +-4567890123456789 NULL +4567890123456789 123 +123 456 +4567890123456789 123 +123 4567890123456789 +123 NULL +456 NULL +123 4567890123456789 +4567890123456789 123 +123 4567890123456789 +4567890123456789 4567890123456789 +123 4567890123456789 +4567890123456789 -4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 123 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 + +# TODO(benesch): cannot construct onerow table. +# +# query II +# select v.* from +# (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) +# left join int4_tbl z on z.f1 = x.q2, +# lateral (select x.q1,y.q1 from onerow union all select x.q2,y.q2 from onerow) v(vx,vy); +# ---- +# vx vy +# 4567890123456789 123 +# 123 456 +# 4567890123456789 123 +# 123 4567890123456789 +# 4567890123456789 4567890123456789 +# 4567890123456789 123 +# 123 4567890123456789 +# 4567890123456789 123 +# 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 +# 123 4567890123456789 +# 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 +# 4567890123456789 -4567890123456789 +# 123 4567890123456789 +# 4567890123456789 -4567890123456789 +# 123 NULL +# 456 NULL +# 4567890123456789 NULL +# -4567890123456789 NULL + +query IIIII rowsort +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; +---- +123 456 NULL NULL NULL +123 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 +123 4567890123456789 4567890123456789 123 4567890123456789 +123 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 NULL NULL NULL +4567890123456789 123 123 456 123 +4567890123456789 123 123 4567890123456789 123 +4567890123456789 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 + +query IIIII colnames,rowsort +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; +---- +q1 q2 q1 q2 x +123 456 NULL NULL NULL +123 4567890123456789 4567890123456789 123 4567890123456789 +123 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +123 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 +4567890123456789 123 123 456 123 +4567890123456789 123 123 4567890123456789 123 +4567890123456789 4567890123456789 4567890123456789 123 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +4567890123456789 4567890123456789 4567890123456789 -4567890123456789 4567890123456789 +4567890123456789 -4567890123456789 NULL NULL NULL + +# lateral can result in join conditions appearing below their +# real semantic level +query II colnames,rowsort +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; +---- +f1 f1 +0 0 +123456 NULL +-123456 NULL +2147483647 NULL +-2147483647 NULL + +# TODO(benesch): correlated table reference. +# +# query II colnames,rowsort +# select * from int4_tbl i left join +# lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; +# ---- +# f1 coalesce +# 0 (0) +# 123456 NULL +# -123456 NULL +# 2147483647 NULL +# -2147483647 NULL + +query IIII colnames,rowsort +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; +---- +f1 f1 q1 q2 +0 0 NULL NULL +0 123456 NULL NULL +0 -123456 NULL NULL +0 2147483647 NULL NULL +0 -2147483647 NULL NULL +123456 0 NULL NULL +123456 123456 NULL NULL +123456 -123456 NULL NULL +123456 2147483647 NULL NULL +123456 -2147483647 NULL NULL +-123456 0 NULL NULL +-123456 123456 NULL NULL +-123456 -123456 NULL NULL +-123456 2147483647 NULL NULL +-123456 -2147483647 NULL NULL +2147483647 0 NULL NULL +2147483647 123456 NULL NULL +2147483647 -123456 NULL NULL +2147483647 2147483647 NULL NULL +2147483647 -2147483647 NULL NULL +-2147483647 0 NULL NULL +-2147483647 123456 NULL NULL +-2147483647 -123456 NULL NULL +-2147483647 2147483647 NULL NULL +-2147483647 -2147483647 NULL NULL + +# TODO(benesch): least function is unsupported. +# +# lateral reference in a PlaceHolderVar evaluated at join level +# query IIIII colnames,rowsort +# select * from +# int8_tbl a left join lateral +# (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from +# int8_tbl b cross join int8_tbl c) ss +# on a.q2 = ss.bq1; +# ---- +# q1 q2 bq1 cq1 least +# 123 456 NULL NULL NULL +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 123 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 123 4567890123456789 4567890123456789 4567890123456789 123 +# 4567890123456789 123 123 123 123 +# 4567890123456789 123 123 123 123 +# 4567890123456789 123 123 123 123 +# 4567890123456789 123 123 123 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 123 123 4567890123456789 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 123 123 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 4567890123456789 4567890123456789 4567890123456789 4567890123456789 +# 4567890123456789 -4567890123456789 NULL NULL NULL + +# check handling of nested appendrels inside LATERAL +query II colnames,rowsort +select * from + ((select 2 as v) union all (select 3 as v)) as q1 + cross join lateral + ((select * from + ((select 4 as v) union all (select 5 as v)) as q3) + union all + (select q1.v) + ) as q2; +---- +v v +2 4 +2 5 +2 2 +3 4 +3 5 +3 3 + +# check we don't try to do a unique-ified semijoin with LATERAL +query III colnames +select * from + (values (0,4), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; +---- +id x f1 +0 4 0 + +query error column "f1" does not exist +select f1,g from int4_tbl a, (select f1 as g) ss; + +query error column "a.f1" does not exist +select f1,g from int4_tbl a, (select a.f1 as g) ss; + +query error column "f1" does not exist +select f1,g from int4_tbl a cross join (select f1 as g) ss; + +query error column "a.f1" does not exist +select f1,g from int4_tbl a cross join (select a.f1 as g) ss; + +# SQL:2008 says the left table is in scope but illegal to access here +query error the combining JOIN type must be INNER or LEFT for a LATERAL reference +select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; + +query error the combining JOIN type must be INNER or LEFT for a LATERAL reference +select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; + +# check we complain about ambiguous table references +# query error table reference "x" is ambiguous +# select * from +# int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); + +# LATERAL can be used to put an aggregate into the FROM clause of its query +# query error aggregate functions are not allowed in FROM clause of their own query level +# select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; diff --git a/test/sqllogictest/table_func.slt b/test/sqllogictest/table_func.slt index 7b1b5a5a59c0..e61916db315a 100644 --- a/test/sqllogictest/table_func.slt +++ b/test/sqllogictest/table_func.slt @@ -81,8 +81,7 @@ query T multiline EXPLAIN RAW PLAN FOR SELECT generate_series FROM generate_series(-2, 2) ---- %0 = -| Constant () -| FlatMap generate_series(-(2), 2) +| CallTable generate_series(-(2), 2) EOF @@ -167,7 +166,12 @@ EXPLAIN RAW PLAN FOR SELECT * FROM x, generate_series(1, a) ---- %0 = | Get materialize.public.x (u5) -| FlatMap generate_series(1, #0) + +%1 = +| CallTable generate_series(1, #^0) + +%2 = +| InnerLateralJoin %0 %1 on true EOF @@ -176,7 +180,22 @@ EXPLAIN RAW PLAN FOR SELECT * FROM x, generate_series(100::bigint, a) ---- %0 = | Get materialize.public.x (u5) -| FlatMap generate_series(i32toi64(100), i32toi64(#0)) + +%1 = +| CallTable generate_series(i32toi64(100), i32toi64(#^0)) + +%2 = +| InnerLateralJoin %0 %1 on true + +EOF + +query T multiline +EXPLAIN PLAN FOR SELECT * FROM x, generate_series(1, 10) +---- +%0 = +| Get materialize.public.x (u5) +| FlatMap generate_series(1, 10) +| | demand = (#0..#2) EOF