summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
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 /yql/essentials/sql
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
Diffstat (limited to 'yql/essentials/sql')
-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
4 files changed, 22 insertions, 15 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> ||