diff options
author | vitya-smirnov <[email protected]> | 2025-07-13 10:57:03 +0300 |
---|---|---|
committer | vitya-smirnov <[email protected]> | 2025-07-13 11:13:18 +0300 |
commit | 50fb38c344d3976f79badaef61cb8040ecdc404f (patch) | |
tree | d802f56c25b770c8c42ca181aadc678957f08183 | |
parent | d28a870061b49d7c985cd453eb01954173e4ffd0 (diff) |
YQL-17269: Fix UNION/EXCEPT/INTERSECT precedence
There was a mistake, because actually EXCEPT
has the same precedence as UNION. INTERSECT has
higher precedence than.
commit_hash:20375ef498861c6704571161fa3c4eebf54e895c
-rw-r--r-- | yql/essentials/sql/v1/SQLv1.g.in | 4 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1Antlr4.g.in | 4 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_select.cpp | 25 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_select.h | 4 | ||||
-rw-r--r-- | yql/essentials/tests/sql/minirun/part4/canondata/result.json | 12 | ||||
-rw-r--r-- | yql/essentials/tests/sql/minirun/part9/canondata/result.json | 12 | ||||
-rw-r--r-- | yql/essentials/tests/sql/sql2yql/canondata/result.json | 10 | ||||
-rw-r--r-- | yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql | 12 |
8 files changed, 45 insertions, 38 deletions
diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in index d9fd70f8ab4..c70730210a5 100644 --- a/yql/essentials/sql/v1/SQLv1.g.in +++ b/yql/essentials/sql/v1/SQLv1.g.in @@ -375,9 +375,9 @@ select_unparenthesized_stmt_intersect: select_kind_partial (intersect_op select_ select_kind_parenthesis: select_kind_partial | LPAREN select_kind_partial RPAREN; -union_op: UNION (DISTINCT | ALL)?; +union_op: (UNION | EXCEPT) (DISTINCT | ALL)?; -intersect_op: (INTERSECT | EXCEPT) (DISTINCT | ALL)?; +intersect_op: INTERSECT (DISTINCT | ALL)?; select_kind_partial: select_kind (LIMIT expr ((OFFSET | COMMA) expr)?)? diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in index 95db5bbc5f6..5ef063d226e 100644 --- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in +++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in @@ -374,9 +374,9 @@ select_unparenthesized_stmt_intersect: select_kind_partial (intersect_op select_ select_kind_parenthesis: select_kind_partial | LPAREN select_kind_partial RPAREN; -union_op: UNION (DISTINCT | ALL)?; +union_op: (UNION | EXCEPT) (DISTINCT | ALL)?; -intersect_op: (INTERSECT | EXCEPT) (DISTINCT | ALL)?; +intersect_op: INTERSECT (DISTINCT | ALL)?; select_kind_partial: select_kind (LIMIT expr ((OFFSET | COMMA) expr)?)? diff --git a/yql/essentials/sql/v1/sql_select.cpp b/yql/essentials/sql/v1/sql_select.cpp index fd5aa8d96c2..b2ae4916924 100644 --- a/yql/essentials/sql/v1/sql_select.cpp +++ b/yql/essentials/sql/v1/sql_select.cpp @@ -1374,7 +1374,7 @@ template <typename TRule> std::same_as<TRule, TRule_select_unparenthesized_stmt> TSourcePtr TSqlSelect::BuildStmt(const TRule& node, TPosition& pos) { TBuildExtra extra; - auto result = BuildUnion(node, pos, extra); + auto result = BuildUnionException(node, pos, extra); pos = extra.FirstPos; if (!result) { return nullptr; @@ -1423,7 +1423,7 @@ TSourcePtr TSqlSelect::BuildStmt(const TRule& node, TPosition& pos) { template <typename TRule> requires std::same_as<TRule, TRule_select_stmt> || std::same_as<TRule, TRule_select_unparenthesized_stmt> -TSourcePtr TSqlSelect::BuildUnion(const TRule& node, TPosition& pos, TSqlSelect::TBuildExtra& extra) { +TSourcePtr TSqlSelect::BuildUnionException(const TRule& node, TPosition& pos, TSqlSelect::TBuildExtra& extra) { const TSelectKindPlacement firstPlacement = { .IsFirstInSelectOp = true, .IsLastInSelectOp = node.GetBlock2().empty(), @@ -1431,9 +1431,9 @@ TSourcePtr TSqlSelect::BuildUnion(const TRule& node, TPosition& pos, TSqlSelect: TSourcePtr first; if constexpr (std::is_same_v<TRule, TRule_select_stmt>) { - first = BuildExceptionIntersection(node.GetRule_select_stmt_intersect1(), pos, firstPlacement, extra); + first = BuildIntersection(node.GetRule_select_stmt_intersect1(), pos, firstPlacement, extra); } else if constexpr (std::is_same_v<TRule, TRule_select_unparenthesized_stmt>) { - first = BuildExceptionIntersection(node.GetRule_select_unparenthesized_stmt_intersect1(), pos, firstPlacement, extra); + first = BuildIntersection(node.GetRule_select_unparenthesized_stmt_intersect1(), pos, firstPlacement, extra); } else { static_assert(false, "Change implementation according to grammar changes."); } @@ -1443,25 +1443,31 @@ TSourcePtr TSqlSelect::BuildUnion(const TRule& node, TPosition& pos, TSqlSelect: } TVector<TSourcePtr> sources = {std::move(first)}; + TString lastOp = ""; bool isLastAllQualified = false; const auto& tail = node.GetBlock2(); for (int i = 0; i < tail.size(); ++i) { const auto& nextBlock = tail[i]; + TString nextOp = ToLowerUTF8(Token(nextBlock.GetRule_union_op1().GetToken1())); bool isNextAllQualified = IsAllQualifiedOp(nextBlock.GetRule_union_op1()); TSelectKindPlacement nextPlacement = { .IsFirstInSelectOp = false, .IsLastInSelectOp = (i + 1 == tail.size()), }; - TSourcePtr next = BuildExceptionIntersection(nextBlock.GetRule_select_stmt_intersect2(), pos, nextPlacement, extra); + TSourcePtr next = BuildIntersection(nextBlock.GetRule_select_stmt_intersect2(), pos, nextPlacement, extra); if (!next) { return nullptr; } - if (i != 0 && isLastAllQualified != isNextAllQualified) { - auto source = BuildSelectOp(pos, std::move(sources), "union", isLastAllQualified, /* settings = */ {}); + bool areArgsInflattable = ((isLastAllQualified != isNextAllQualified) || + (lastOp != nextOp) || + (nextOp != "union")); + + if ((i != 0) && areArgsInflattable) { + auto source = BuildSelectOp(pos, std::move(sources), lastOp, isLastAllQualified, /* settings = */ {}); Y_ENSURE(source); sources.clear(); @@ -1469,6 +1475,7 @@ TSourcePtr TSqlSelect::BuildUnion(const TRule& node, TPosition& pos, TSqlSelect: } sources.emplace_back(std::move(next)); + lastOp = std::move(nextOp); isLastAllQualified = isNextAllQualified; } @@ -1483,13 +1490,13 @@ TSourcePtr TSqlSelect::BuildUnion(const TRule& node, TPosition& pos, TSqlSelect: outermostSettings.Label = extra.Last.Settings.Label; } - return BuildSelectOp(pos, std::move(sources), "union", isLastAllQualified, outermostSettings); + return BuildSelectOp(pos, std::move(sources), lastOp, isLastAllQualified, outermostSettings); } template <typename TRule> requires std::same_as<TRule, TRule_select_stmt_intersect> || std::same_as<TRule, TRule_select_unparenthesized_stmt_intersect> -TSourcePtr TSqlSelect::BuildExceptionIntersection( +TSourcePtr TSqlSelect::BuildIntersection( const TRule& node, TPosition& pos, TSelectKindPlacement placement, diff --git a/yql/essentials/sql/v1/sql_select.h b/yql/essentials/sql/v1/sql_select.h index 3224a3ca3b3..f4097396745 100644 --- a/yql/essentials/sql/v1/sql_select.h +++ b/yql/essentials/sql/v1/sql_select.h @@ -81,12 +81,12 @@ private: template <typename TRule> requires std::same_as<TRule, TRule_select_stmt> || std::same_as<TRule, TRule_select_unparenthesized_stmt> - TSourcePtr BuildUnion(const TRule& node, TPosition& pos, TBuildExtra& extra); + TSourcePtr BuildUnionException(const TRule& node, TPosition& pos, TBuildExtra& extra); template <typename TRule> requires std::same_as<TRule, TRule_select_stmt_intersect> || std::same_as<TRule, TRule_select_unparenthesized_stmt_intersect> - TSourcePtr BuildExceptionIntersection(const TRule& node, TPosition& pos, TSelectKindPlacement placement, TBuildExtra& extra); + TSourcePtr BuildIntersection(const TRule& node, TPosition& pos, TSelectKindPlacement placement, TBuildExtra& extra); template <typename TRule> requires std::same_as<TRule, TRule_select_kind_parenthesis> || diff --git a/yql/essentials/tests/sql/minirun/part4/canondata/result.json b/yql/essentials/tests/sql/minirun/part4/canondata/result.json index 28b0265e7f5..e6d2cfd6553 100644 --- a/yql/essentials/tests/sql/minirun/part4/canondata/result.json +++ b/yql/essentials/tests/sql/minirun/part4/canondata/result.json @@ -1613,16 +1613,16 @@ ], "test.test[select_op-select_op_precedence-default.txt-Debug]": [ { - "checksum": "d988a916cc8cf62b2b0a209dac05e6ad", - "size": 2449, - "uri": "https://{canondata_backend}/1937424/ee95b3be0f5d76ab6152d1ca5aedc6f278ad320b/resource.tar.gz#test.test_select_op-select_op_precedence-default.txt-Debug_/opt.yql" + "checksum": "3cc5644e63d6d4d4601132f97c177a71", + "size": 2350, + "uri": "https://{canondata_backend}/1871182/4957464f17f4b05ffc05eeebd894c6e979082da5/resource.tar.gz#test.test_select_op-select_op_precedence-default.txt-Debug_/opt.yql" } ], "test.test[select_op-select_op_precedence-default.txt-Results]": [ { - "checksum": "355b6ce9dc8117b59c91a73b5a309c1e", - "size": 2428, - "uri": "https://{canondata_backend}/1937424/ee95b3be0f5d76ab6152d1ca5aedc6f278ad320b/resource.tar.gz#test.test_select_op-select_op_precedence-default.txt-Results_/results.txt" + "checksum": "c0efe7c81a642f898ef8a9edaa9744eb", + "size": 2355, + "uri": "https://{canondata_backend}/1871182/4957464f17f4b05ffc05eeebd894c6e979082da5/resource.tar.gz#test.test_select_op-select_op_precedence-default.txt-Results_/results.txt" } ], "test.test[udf-same_udf_modules--Debug]": [ diff --git a/yql/essentials/tests/sql/minirun/part9/canondata/result.json b/yql/essentials/tests/sql/minirun/part9/canondata/result.json index 5748430323b..935388a50fc 100644 --- a/yql/essentials/tests/sql/minirun/part9/canondata/result.json +++ b/yql/essentials/tests/sql/minirun/part9/canondata/result.json @@ -1428,16 +1428,16 @@ ], "test.test[select_op-select_op_order_by-default.txt-Debug]": [ { - "checksum": "91caa30e8db750c1ec76d151fca78754", - "size": 1330, - "uri": "https://{canondata_backend}/1937429/9e4129b6ff2ea8916fb065ef6e1c352f09062635/resource.tar.gz#test.test_select_op-select_op_order_by-default.txt-Debug_/opt.yql" + "checksum": "a4d6a392dd1a49d7f8ca0ff624c50d84", + "size": 1362, + "uri": "https://{canondata_backend}/1937429/18d53b6e51c2d67a4139b1541925383ccf907eb1/resource.tar.gz#test.test_select_op-select_op_order_by-default.txt-Debug_/opt.yql" } ], "test.test[select_op-select_op_order_by-default.txt-Results]": [ { - "checksum": "d60baea9173f30a2120442ad48fbe6f1", - "size": 1608, - "uri": "https://{canondata_backend}/995452/5090a24ed2603cf2d46948bbefc60f465212e8eb/resource.tar.gz#test.test_select_op-select_op_order_by-default.txt-Results_/results.txt" + "checksum": "39d3126f3afa785bad90779dcbef0d66", + "size": 1462, + "uri": "https://{canondata_backend}/1937429/18d53b6e51c2d67a4139b1541925383ccf907eb1/resource.tar.gz#test.test_select_op-select_op_order_by-default.txt-Results_/results.txt" } ], "test.test[udf-udaf_default-default.txt-Debug]": [ diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json index 3181fd5b022..069f12459ea 100644 --- a/yql/essentials/tests/sql/sql2yql/canondata/result.json +++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json @@ -7428,16 +7428,16 @@ ], "test_sql2yql.test[select_op-select_op_order_by]": [ { - "checksum": "c29c29e19e0a2dc2ec0b18e5f514d257", + "checksum": "5bb387570099b20bc348a61fb9aca5cf", "size": 5911, - "uri": "https://{canondata_backend}/1600758/b6a6fb350f1fdfbb2cdd5ef84cd0c97f27aa4e1a/resource.tar.gz#test_sql2yql.test_select_op-select_op_order_by_/sql.yql" + "uri": "https://{canondata_backend}/1871182/dae39f0cfd95af470ed2892346cc8716e27e027e/resource.tar.gz#test_sql2yql.test_select_op-select_op_order_by_/sql.yql" } ], "test_sql2yql.test[select_op-select_op_precedence]": [ { - "checksum": "1aa020ec98a803bed27d3e9d69bd1cc8", - "size": 7832, - "uri": "https://{canondata_backend}/1937424/ff4229d081f7de79cc74b919a7e5f6c33b478b7f/resource.tar.gz#test_sql2yql.test_select_op-select_op_precedence_/sql.yql" + "checksum": "97a131fbcb318306e7a59d28174a3861", + "size": 7840, + "uri": "https://{canondata_backend}/1871182/dae39f0cfd95af470ed2892346cc8716e27e027e/resource.tar.gz#test_sql2yql.test_select_op-select_op_precedence_/sql.yql" } ], "test_sql2yql.test[seq_mode-shared_named_expr]": [ diff --git a/yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql b/yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql index f8f08006e26..7212b21855c 100644 --- a/yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql +++ b/yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql @@ -5,11 +5,11 @@ UNION (SELECT * FROM (VALUES (3)) AS t (x)); - (SELECT * FROM (VALUES (3)) AS t (x)) +(SELECT * FROM (VALUES (3)) AS t (x)) UNION - (SELECT * FROM (VALUES (2)) AS t (x)) - EXCEPT - (SELECT * FROM (VALUES (3)) AS t (x)); +(SELECT * FROM (VALUES (2)) AS t (x)) +EXCEPT +(SELECT * FROM (VALUES (3)) AS t (x)); (SELECT * FROM (VALUES (1)) AS t (x)) @@ -17,9 +17,9 @@ UNION (SELECT * FROM (VALUES (2)) AS t (x)) INTERSECT (SELECT * FROM (VALUES (2), (3)) AS t (x)) - EXCEPT +EXCEPT (SELECT * FROM (VALUES (3)) AS t (x)) UNION (SELECT * FROM (VALUES (4), (3)) AS t (x)) - EXCEPT +EXCEPT (SELECT * FROM (VALUES (4)) AS t (x)); |