summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-07-13 10:57:03 +0300
committervitya-smirnov <[email protected]>2025-07-13 11:13:18 +0300
commit50fb38c344d3976f79badaef61cb8040ecdc404f (patch)
treed802f56c25b770c8c42ca181aadc678957f08183
parentd28a870061b49d7c985cd453eb01954173e4ffd0 (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.in4
-rw-r--r--yql/essentials/sql/v1/SQLv1Antlr4.g.in4
-rw-r--r--yql/essentials/sql/v1/sql_select.cpp25
-rw-r--r--yql/essentials/sql/v1/sql_select.h4
-rw-r--r--yql/essentials/tests/sql/minirun/part4/canondata/result.json12
-rw-r--r--yql/essentials/tests/sql/minirun/part9/canondata/result.json12
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json10
-rw-r--r--yql/essentials/tests/sql/suites/select_op/select_op_precedence.sql12
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));