summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-10-14 09:30:10 +0300
committervitya-smirnov <[email protected]>2025-10-14 09:48:40 +0300
commite06bac75417cb26b5603d104e58a15aeda178d5e (patch)
tree5a3cc5430cd0bc4fff262e5d37f41291914d6e70
parent6afc58f5f8644b9e12b407cfa1341dacc1aaf5e2 (diff)
YQL-20436: Translate `SELECT .. FROM VALUES ..` to `YqlSelect`
Current `SQLv1` translation produces relatively low level `YQLs` constructions that are hard to match during later optimizations, for example, subqueries unnesting. Also it assumes that expressions are only depend on corresponding source row, which is not true for correlated subqueries. Both limitations blocks currelated subqueries implementation. Although the problem exists for `SQLv1`, it is already solved for `PG` syntax. There PostgreSQL-produced AST is converted to special `YQLs` "bulk select" node called `PgSelect`. It is more declarative and expanded later than translation. This fact helped to support correlated subqueries, which with `PgSelect` are type-checkable and decorrelatable (not generally, but heuristically). This patch is the first step forward to "bulk select" translation for `SQLv1`. As there are a lot of code already written for `PgSelect` and `PG` and `SQLv1` relatively similar, I decided to rebrand `PgSelect` into more general `SqlSelect` node. It seems that for some near future goals `PgSelect` should be enough. There was 2 problems solved: 1. `PgSelect` comes with `OrderedColumns` by default and its implementation tightly coupled with it. 2. `PgSelect` does Pg type casts. This patch contains following changes: - Added pragma `YqlSelect = 'disable' | 'auto' | 'force'` - Added `YqlSelect` (`PgSelect` alias) translation - Changed `PgSelect` wrappers to support `YqlSelect` - Changed `PgSelect` expanders to support `YqlSelect` commit_hash:8a55d63e06c22592b2029dd260bbd259194e92dc
-rw-r--r--yql/essentials/core/common_opt/yql_co_pgselect.cpp159
-rw-r--r--yql/essentials/core/common_opt/yql_co_simple1.cpp3
-rw-r--r--yql/essentials/core/expr_nodes/yql_expr_nodes.json26
-rw-r--r--yql/essentials/core/type_ann/type_ann_core.cpp11
-rw-r--r--yql/essentials/core/type_ann/type_ann_list.cpp81
-rw-r--r--yql/essentials/core/type_ann/type_ann_list.h8
-rw-r--r--yql/essentials/core/type_ann/type_ann_pg.cpp79
-rw-r--r--yql/essentials/sql/v1/context.h8
-rw-r--r--yql/essentials/sql/v1/node.cpp24
-rw-r--r--yql/essentials/sql/v1/node.h3
-rw-r--r--yql/essentials/sql/v1/select_yql.cpp254
-rw-r--r--yql/essentials/sql/v1/select_yql.h30
-rw-r--r--yql/essentials/sql/v1/sql_expression.cpp11
-rw-r--r--yql/essentials/sql/v1/sql_expression.h5
-rw-r--r--yql/essentials/sql/v1/sql_query.cpp44
-rw-r--r--yql/essentials/sql/v1/sql_select_yql.cpp359
-rw-r--r--yql/essentials/sql/v1/sql_select_yql.h25
-rw-r--r--yql/essentials/sql/v1/sql_ut_common.h109
-rw-r--r--yql/essentials/sql/v1/ya.make2
-rw-r--r--yql/essentials/tests/sql/minirun/part0/canondata/result.json14
-rw-r--r--yql/essentials/tests/sql/minirun/part1/canondata/result.json28
-rw-r--r--yql/essentials/tests/sql/minirun/part4/canondata/result.json14
-rw-r--r--yql/essentials/tests/sql/minirun/part7/canondata/result.json28
-rw-r--r--yql/essentials/tests/sql/minirun/part9/canondata/result.json14
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json84
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-alias_/formatted.sql21
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-expr_/formatted.sql7
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_anon_values_/formatted.sql8
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_/formatted.sql14
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_ordered_columns_/formatted.sql15
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_/formatted.sql5
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_ordered_columns_/formatted.sql6
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/alias.yql21
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/default.cfg1
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/expr.yql7
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/from_anon_values.yql8
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/from_values.yql14
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/from_values_ordered_columns.yql15
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/minimal.yql5
-rw-r--r--yql/essentials/tests/sql/suites/select_yql/minimal_ordered_columns.yql6
40 files changed, 1478 insertions, 98 deletions
diff --git a/yql/essentials/core/common_opt/yql_co_pgselect.cpp b/yql/essentials/core/common_opt/yql_co_pgselect.cpp
index db7b6d31676..0da20daf26e 100644
--- a/yql/essentials/core/common_opt/yql_co_pgselect.cpp
+++ b/yql/essentials/core/common_opt/yql_co_pgselect.cpp
@@ -1945,15 +1945,21 @@ TExprNode::TPtr BuildCrossJoinsBetweenGroups(TPositionHandle pos, const TExprNod
return ctx.NewCallable(pos, "EquiJoin", std::move(args));
}
-TExprNode::TPtr BuildProjectionLambda(TPositionHandle pos, const TExprNode::TPtr& result, const TStructExprType* finalType,
- const TColumnOrder& nodeColumnOrder, const TColumnOrder& setItemColumnOrder,
- bool subLink, bool emitPgStar, TExprContext& ctx) {
-
- YQL_ENSURE(nodeColumnOrder.Size() == setItemColumnOrder.Size());
-
+TExprNode::TPtr BuildProjectionLambda(
+ TPositionHandle pos,
+ const TExprNode::TPtr& result,
+ const TStructExprType* finalType,
+ const TColumnOrder* nodeColumnOrder,
+ const TColumnOrder* setItemColumnOrder,
+ bool subLink,
+ bool emitPgStar,
+ bool isYql,
+ TExprContext& ctx)
+{
TMap<TStringBuf, TStringBuf> columnNamesMap;
- for (size_t i = 0; i < nodeColumnOrder.Size(); ++i) {
- columnNamesMap[setItemColumnOrder[i].PhysicalName] = nodeColumnOrder[i].PhysicalName;
+ for (size_t i = 0; nodeColumnOrder && i < nodeColumnOrder->Size(); ++i) {
+ YQL_ENSURE(nodeColumnOrder->Size() == setItemColumnOrder->Size());
+ columnNamesMap[setItemColumnOrder->at(i).PhysicalName] = nodeColumnOrder->at(i).PhysicalName;
}
TColumnOrder order;
@@ -1976,8 +1982,18 @@ TExprNode::TPtr BuildProjectionLambda(TPositionHandle pos, const TExprNode::TPtr
.Seal();
};
- auto addPgCast = [&finalType, &columnNamesMap, &pos, &ctx] (TExprNodeBuilder& builder, ui32 idx, const TStringBuf& columnName,
- const TTypeAnnotationNode* actualTypeNode, const std::function<void(TExprNodeBuilder&, ui32)>& buildCore) {
+ auto addPgCast = [&finalType, &columnNamesMap, &pos, isYql, &ctx](
+ TExprNodeBuilder& builder,
+ ui32 idx,
+ const TStringBuf& columnName,
+ const TTypeAnnotationNode* actualTypeNode,
+ const std::function<void(TExprNodeBuilder&, ui32)>& buildCore)
+ {
+ if (isYql) {
+ buildCore(builder, idx);
+ builder.Seal();
+ return;
+ }
const auto expectedTypeNode = finalType->FindItemType(columnNamesMap[columnName]);
Y_ENSURE(expectedTypeNode);
@@ -3674,25 +3690,43 @@ TExprNode::TPtr CombineSetItems(TPositionHandle pos, const TExprNode::TPtr& left
TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ctx, TOptimizeContext& optCtx,
TMaybe<ui32> subLinkId, const TExprNode::TListType& outerInputs, const TVector<TString>& outerInputAliases) {
- auto order = optCtx.Types->LookupColumnOrder(*node);
- YQL_ENSURE(order);
- TExprNode::TListType columnsItems;
- for (const auto& [x, gen_x] : *order) {
- columnsItems.push_back(ctx.NewAtom(node->Pos(), x));
- }
+ struct TColumnOrderInfo {
+ TColumnOrder Node;
+ TExprNode::TListType Items;
+ TMap<TString, ui32> External;
+ TColumnOrder Target;
+ TExprNode::TPtr Assumption;
+ TVector<TColumnOrder> BySetItems;
+ };
+
+ YQL_ENSURE(node->IsCallable({"YqlSelect", "PgSelect"}));
+ const bool isYql = node->IsCallable("YqlSelect");
+ const bool isOrderedColumns = !isYql || optCtx.Types->OrderedColumns;
+
+ TMaybe<TColumnOrderInfo> columnOrder;
+ if (auto order = optCtx.Types->LookupColumnOrder(*node)) {
+ columnOrder.ConstructInPlace();
+ columnOrder->Node = std::move(*order);
+ columnOrder->Target = columnOrder->Node;
+
+ for (const auto& [x, gen_x] : columnOrder->Node) {
+ columnOrder->Items.push_back(ctx.NewAtom(node->Pos(), x));
+ }
- TMap<TString, ui32> extColumns;
- TColumnOrder targetOrder = *order;
- if (subLinkId) {
- extColumns = NTypeAnnImpl::ExtractExternalColumns(*node);
- for (const auto& x : extColumns) {
- auto name = TString("_yql_join_sublink_") + ToString(*subLinkId) + "_" + x.first;
- columnsItems.push_back(ctx.NewAtom(node->Pos(), name));
- targetOrder.AddColumn(name);
+ if (subLinkId) {
+ columnOrder->External = NTypeAnnImpl::ExtractExternalColumns(*node);
+ for (const auto& x : columnOrder->External) {
+ auto name = TString("_yql_join_sublink_") + ToString(*subLinkId) + "_" + x.first;
+ columnOrder->Items.push_back(ctx.NewAtom(node->Pos(), name));
+ columnOrder->Target.AddColumn(name);
+ }
}
+
+ columnOrder->Assumption = ctx.NewList(node->Pos(), std::move(columnOrder->Items));
}
- auto columns = ctx.NewList(node->Pos(), std::move(columnsItems));
+ YQL_ENSURE(!isOrderedColumns || columnOrder, "Column order was required, but absent");
+
auto setItems = GetSetting(node->Head(), "set_items");
auto setOps = GetSetting(node->Head(), "set_ops");
YQL_ENSURE(setItems);
@@ -3703,15 +3737,20 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
TExprNode::TPtr finalSortDirections;
TExprNode::TListType setItemNodes;
- TVector<TColumnOrder> columnOrders;
for (auto setItem : setItems->Tail().Children()) {
- auto childOrder = optCtx.Types->LookupColumnOrder(*setItem);
- YQL_ENSURE(childOrder);
- columnOrders.push_back(*childOrder);
- if (subLinkId) {
- auto& setOrder = columnOrders.back();
- for (size_t i = targetOrder.Size() - extColumns.size(); i < targetOrder.Size(); ++i) {
- setOrder.AddColumn(targetOrder[i].LogicalName);
+ TMaybe<TColumnOrder> childOrder = optCtx.Types->LookupColumnOrder(*setItem);
+ if (columnOrder) {
+ YQL_ENSURE(childOrder);
+
+ columnOrder->BySetItems.push_back(*childOrder);
+ if (subLinkId) {
+ auto& setOrder = columnOrder->BySetItems.back();
+ for (size_t i = columnOrder->Target.Size() - columnOrder->External.size();
+ i < columnOrder->Target.Size();
+ ++i)
+ {
+ setOrder.AddColumn(columnOrder->Target.at(i).LogicalName);
+ }
}
}
@@ -3750,9 +3789,21 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
YQL_ENSURE(result);
auto finalType = node->GetTypeAnn()->Cast<TListExprType>()->GetItemType()->Cast<TStructExprType>();
Y_ENSURE(finalType);
- TExprNode::TPtr projectionLambda = BuildProjectionLambda(node->Pos(), result, finalType, *order, *childOrder, subLinkId.Defined(), emitPgStar, ctx);
+
+ TExprNode::TPtr projectionLambda = BuildProjectionLambda(
+ node->Pos(),
+ result,
+ finalType,
+ columnOrder ? &columnOrder->Node : nullptr,
+ childOrder.Get(),
+ subLinkId.Defined(),
+ emitPgStar,
+ isYql,
+ ctx);
+
TExprNode::TPtr projectionArg = projectionLambda->Head().HeadPtr();
TExprNode::TPtr projectionRoot = projectionLambda->TailPtr();
+
TVector<TString> inputAliases;
TExprNode::TListType cleanedInputs;
TWindowsCtx winCtx;
@@ -3928,7 +3979,17 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
for (const auto& x : setOps->Tail().Children()) {
if (x->Content() == "push") {
YQL_ENSURE(inputIndex < setItemNodes.size());
- stack.push_back(NormalizeColumnOrder(setItemNodes[inputIndex], columnOrders[inputIndex], targetOrder, ctx));
+
+ TExprNode::TPtr expr = setItemNodes[inputIndex];
+ if (columnOrder) {
+ expr = NormalizeColumnOrder(
+ std::move(expr),
+ columnOrder->BySetItems[inputIndex],
+ columnOrder->Target,
+ ctx);
+ }
+
+ stack.push_back(std::move(expr));
++inputIndex;
continue;
}
@@ -3943,17 +4004,23 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
}
YQL_ENSURE(stack.size() == 1);
- list = KeepColumnOrder(targetOrder, stack.front(), ctx);
+
+ list = stack.front();
+ if (columnOrder) {
+ list = KeepColumnOrder(columnOrder->Target, list, ctx);
+ }
} else {
TExprNode::TListType children = setItemNodes;
- for (ui32 childIndex = 0; childIndex < children.size(); ++childIndex) {
- const auto& childColumnOrder = columnOrders[childIndex];
+ for (ui32 childIndex = 0; columnOrder && childIndex < children.size(); ++childIndex) {
+ const auto& childColumnOrder = columnOrder->BySetItems[childIndex];
auto& child = children[childIndex];
- child = NormalizeColumnOrder(child, childColumnOrder, targetOrder, ctx);
+ child = NormalizeColumnOrder(child, childColumnOrder, columnOrder->Target, ctx);
}
- auto res = ctx.NewCallable(node->Pos(), "UnionAll", std::move(children));
- list = KeepColumnOrder(targetOrder, res, ctx);
+ list = ctx.NewCallable(node->Pos(), "UnionAll", std::move(children));
+ if (columnOrder) {
+ list = KeepColumnOrder(columnOrder->Target, list, ctx);
+ }
}
}
@@ -3966,7 +4033,7 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
finalSortDirections = BuildSortDirections(node->Pos(), finalSort, ctx);
}
- if (extColumns.empty()) {
+ if (!columnOrder || columnOrder->External.empty()) {
if (finalSortLambda) {
list = BuildSort(node->Pos(), list, finalSortDirections, finalSortLambda->TailPtr(), finalSortLambda->HeadPtr(), ctx);
}
@@ -3985,17 +4052,21 @@ TExprNode::TPtr ExpandPgSelectImpl(const TExprNode::TPtr& node, TExprContext& ct
TExprNode::TPtr limitValue = limit ? WrapWithNonNegativeCheck(node->Pos(), limit->ChildPtr(1), ctx, "LIMIT must not be negative") : nullptr;
// enumerate all rows for each external key
- auto enumerated = EnumerateForExtColumns(node->Pos(), list, *subLinkId, extColumns, finalSortDirections, finalSortLambda, ctx);
+ auto enumerated = EnumerateForExtColumns(node->Pos(), list, *subLinkId, columnOrder->External, finalSortDirections, finalSortLambda, ctx);
// apply limit & offset and drop a column used for enumeration
list = ApplyOffsetLimitOverEnumerated(node->Pos(), enumerated, offsetValue, limitValue, ctx);
}
}
+ if (!columnOrder) {
+ return list;
+ }
+
return ctx.Builder(node->Pos())
.Callable("AssumeColumnOrder")
.Add(0, list)
- .Add(1, columns)
+ .Add(1, columnOrder->Assumption)
.Seal()
.Build();
}
diff --git a/yql/essentials/core/common_opt/yql_co_simple1.cpp b/yql/essentials/core/common_opt/yql_co_simple1.cpp
index 000d63b54c6..98a1d4c6545 100644
--- a/yql/essentials/core/common_opt/yql_co_simple1.cpp
+++ b/yql/essentials/core/common_opt/yql_co_simple1.cpp
@@ -7095,6 +7095,9 @@ void RegisterCoSimpleCallables1(TCallableOptimizerMap& map) {
map["PgIterate"] = &ExpandPgIterate;
map["PgIterateAll"] = &ExpandPgIterate;
+ map["YqlSelect"] = &ExpandPgSelect;
+ map["YqlIterate"] = &ExpandPgIterate;
+
map["PgLike"] = &ExpandPgLike;
map["PgILike"] = &ExpandPgLike;
diff --git a/yql/essentials/core/expr_nodes/yql_expr_nodes.json b/yql/essentials/core/expr_nodes/yql_expr_nodes.json
index 7bdbd8837fd..65bdb39dbe2 100644
--- a/yql/essentials/core/expr_nodes/yql_expr_nodes.json
+++ b/yql/essentials/core/expr_nodes/yql_expr_nodes.json
@@ -2657,6 +2657,32 @@
]
},
{
+ "Name": "TCoYqlSelect",
+ "Base": "TCallable",
+ "Match": {"Type": "Callable", "Name": "YqlSelect"},
+ "Children": [
+ {"Index": 0, "Name": "SelectOptions", "Type": "TCoNameValueTupleList"}
+ ]
+ },
+ {
+ "Name": "TCoYqlSetItem",
+ "Base": "TCallable",
+ "Match": {"Type": "Callable", "Name": "YqlSetItem"},
+ "Children": [
+ {"Index": 0, "Name": "SetItemOptions", "Type": "TExprList"}
+ ]
+ },
+ {
+ "Name": "TCoYqlResultItem",
+ "Base": "TCallable",
+ "Match": {"Type": "Callable", "Name": "YqlResultItem"},
+ "Children": [
+ {"Index": 0, "Name": "ExpandedColumns", "Type": "TExprBase"},
+ {"Index": 1, "Name": "Type", "Type": "TCallable"},
+ {"Index": 2, "Name": "Lambda", "Type": "TCoLambda"}
+ ]
+ },
+ {
"Name": "TCoPickle",
"Base": "TCallable",
"Match": {"Type": "Callable", "Name": "Pickle"},
diff --git a/yql/essentials/core/type_ann/type_ann_core.cpp b/yql/essentials/core/type_ann/type_ann_core.cpp
index b0f71624bfc..077aa32e909 100644
--- a/yql/essentials/core/type_ann/type_ann_core.cpp
+++ b/yql/essentials/core/type_ann/type_ann_core.cpp
@@ -13697,6 +13697,11 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
ColumnOrderFunctions["PgSetItem"] = &OrderForPgSetItem;
ColumnOrderFunctions["PgIterate"] = &OrderFromFirst;
ColumnOrderFunctions["PgIterateAll"] = &OrderFromFirst;
+
+ ColumnOrderFunctions["YqlSetItem"] = &OrderForPgSetItem;
+ ColumnOrderFunctions["YqlIterate"] = &OrderFromFirst;
+ ColumnOrderFunctions["YqlIterateAll"] = &OrderFromFirst;
+
ColumnOrderFunctions["AssumeColumnOrder"] = &OrderForAssumeColumnOrder;
ColumnOrderFunctions["SqlProject"] = ColumnOrderFunctions["OrderedSqlProject"] = &OrderForSqlProject;
@@ -13715,6 +13720,12 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
ColumnOrderFunctions["AssumeSorted"] = ColumnOrderFunctions["Unordered"] =
ColumnOrderFunctions["UnorderedSubquery"] = ColumnOrderFunctions["AssumeUniq"] = &OrderFromFirst;
+ ExtFunctions["YqlSelect"] = &PgSelectWrapper;
+ ExtFunctions["YqlSetItem"] = &PgSetItemWrapper;
+ ExtFunctions["YqlResultItem"] = &PgResultItemWrapper;
+ ExtFunctions["YqlValuesList"] = &PgValuesListWrapper;
+ Functions["YqlColumnRef"] = &PgColumnRefWrapper;
+
for (ui32 i = 0; i < NKikimr::NUdf::DataSlotCount; ++i) {
auto name = TString(NKikimr::NUdf::GetDataTypeInfo((EDataSlot)i).Name);
Functions[name] = &DataConstructorWrapper;
diff --git a/yql/essentials/core/type_ann/type_ann_list.cpp b/yql/essentials/core/type_ann/type_ann_list.cpp
index 6dc2fa87fdf..2d9a497cac1 100644
--- a/yql/essentials/core/type_ann/type_ann_list.cpp
+++ b/yql/essentials/core/type_ann/type_ann_list.cpp
@@ -2778,9 +2778,49 @@ namespace {
}
}
+ const TStructExprType* structType = nullptr;
+ if (auto status = InferUnionType(input->Pos(), input->ChildrenList(), structType, ctx, checkHashes);
+ status != IGraphTransformer::TStatus::Ok) {
+ return status;
+ }
+
+ input->SetTypeAnn(ctx.Expr.MakeType<TListExprType>(structType));
+ return IGraphTransformer::TStatus::Ok;
+ }
+
+ IGraphTransformer::TStatus SelectOpPositionalWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
+ Y_UNUSED(output);
+ if (!ctx.Types.OrderedColumns) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), TStringBuilder()
+ << "Unable to handle positional UNION ALL with column order disabled"));
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ if (!EnsureMinArgsCount(*input, 1, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ TColumnOrder resultColumnOrder;
+ const TStructExprType* resultStructType = nullptr;
+ auto status = InferPositionalUnionType(input->Pos(), input->ChildrenList(), resultColumnOrder, resultStructType, ctx);
+ if (status != IGraphTransformer::TStatus::Ok) {
+ return status;
+ }
+
+ input->SetTypeAnn(ctx.Expr.MakeType<TListExprType>(resultStructType));
+ return ctx.Types.SetColumnOrder(*input, resultColumnOrder, ctx.Expr);
+ }
+
+ IGraphTransformer::TStatus InferUnionType(
+ TPositionHandle pos,
+ const TExprNode::TListType& children,
+ const TStructExprType*& resultStructType,
+ TContext& ctx,
+ const bool areHashesChecked)
+ {
std::unordered_map<std::string_view, std::pair<const TTypeAnnotationNode*, ui32>> members;
- const auto inputsCount = input->ChildrenSize();
- const auto checkStructType = [&members, &ctx, inputsCount, pos = input->Pos()](TExprNode& input) -> const TStructExprType* {
+ const auto inputsCount = children.size();
+ const auto checkStructType = [&members, &ctx, inputsCount, pos](TExprNode& input) -> const TStructExprType* {
if (!EnsureListType(input, ctx.Expr)) {
return nullptr;
}
@@ -2822,7 +2862,7 @@ namespace {
};
TVector<const TStructExprType*> structTypes;
- for (auto child : input->Children()) {
+ for (auto child : children) {
auto structType = checkStructType(*child);
if (!structType) {
return IGraphTransformer::TStatus::Error;
@@ -2858,48 +2898,25 @@ namespace {
addResultItems(structType);
}
- if (checkHashes) {
+ if (areHashesChecked) {
for (const auto& r : resultItems) {
if (!r->GetItemType()->IsHashable() || !r->GetItemType()->IsEquatable()) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), TStringBuilder() << "Expected hashable and equatable type for column: " <<
- r->GetName() << ", but got: " << *r->GetItemType()));
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(pos), TStringBuilder()
+ << "Expected hashable and equatable type for column: "
+ << r->GetName() << ", but got: " << *r->GetItemType()));
return IGraphTransformer::TStatus::Error;
}
}
}
- auto structType = ctx.Expr.MakeType<TStructExprType>(resultItems);
- if (!structType->Validate(input->Pos(), ctx.Expr)) {
+ resultStructType = ctx.Expr.MakeType<TStructExprType>(resultItems);
+ if (!resultStructType->Validate(pos, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
}
- input->SetTypeAnn(ctx.Expr.MakeType<TListExprType>(structType));
return IGraphTransformer::TStatus::Ok;
}
- IGraphTransformer::TStatus SelectOpPositionalWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
- Y_UNUSED(output);
- if (!ctx.Types.OrderedColumns) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), TStringBuilder()
- << "Unable to handle positional UNION ALL with column order disabled"));
- return IGraphTransformer::TStatus::Error;
- }
-
- if (!EnsureMinArgsCount(*input, 1, ctx.Expr)) {
- return IGraphTransformer::TStatus::Error;
- }
-
- TColumnOrder resultColumnOrder;
- const TStructExprType* resultStructType = nullptr;
- auto status = InferPositionalUnionType(input->Pos(), input->ChildrenList(), resultColumnOrder, resultStructType, ctx);
- if (status != IGraphTransformer::TStatus::Ok) {
- return status;
- }
-
- input->SetTypeAnn(ctx.Expr.MakeType<TListExprType>(resultStructType));
- return ctx.Types.SetColumnOrder(*input, resultColumnOrder, ctx.Expr);
- }
-
IGraphTransformer::TStatus InferPositionalUnionType(TPositionHandle pos, const TExprNode::TListType& children,
TColumnOrder& resultColumnOrder, const TStructExprType*& resultStructType, TExtContext& ctx) {
TTypeAnnotationNode::TListType resultTypes;
diff --git a/yql/essentials/core/type_ann/type_ann_list.h b/yql/essentials/core/type_ann/type_ann_list.h
index 485b9c265a2..22c4e032c9b 100644
--- a/yql/essentials/core/type_ann/type_ann_list.h
+++ b/yql/essentials/core/type_ann/type_ann_list.h
@@ -8,6 +8,14 @@
namespace NYql {
namespace NTypeAnnImpl {
+
+ IGraphTransformer::TStatus InferUnionType(
+ TPositionHandle pos,
+ const TExprNode::TListType& children,
+ const TStructExprType*& resultStructType,
+ TContext& ctx,
+ bool areHashesChecked);
+
IGraphTransformer::TStatus InferPositionalUnionType(TPositionHandle pos, const TExprNode::TListType& children,
TColumnOrder& resultColumnOrder, const TStructExprType*& resultStructType, TExtContext& ctx);
TExprNode::TPtr ExpandToWindowTraits(const TExprNode& input, TExprContext& ctx);
diff --git a/yql/essentials/core/type_ann/type_ann_pg.cpp b/yql/essentials/core/type_ann/type_ann_pg.cpp
index d269fa0ed28..d51e81e0c8e 100644
--- a/yql/essentials/core/type_ann/type_ann_pg.cpp
+++ b/yql/essentials/core/type_ann/type_ann_pg.cpp
@@ -1793,7 +1793,7 @@ bool ScanColumns(TExprNode::TPtr root, TInputs& inputs, const THashSet<TString>&
break;
}
}
- } else if (node->IsCallable("PgColumnRef")) {
+ } else if (node->IsCallable({"YqlColumnRef", "PgColumnRef"})) {
if (hasStar && *hasStar && !hasEmitPgStar) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(node->Pos()), "Star is incompatible to column reference"));
isError = true;
@@ -2264,7 +2264,7 @@ IGraphTransformer::TStatus RebuildLambdaColumns(const TExprNode::TPtr& root, con
return argNode;
}
- if (node->IsCallable("PgColumnRef")) {
+ if (node->IsCallable({"YqlColumnRef", "PgColumnRef"})) {
const TInput* matchedAliasInput = nullptr;
const TInput* matchedAliasInputI = nullptr;
if (node->ChildrenSize() == 1 && usedInUsing.contains(node->Tail().Content())) {
@@ -3448,6 +3448,11 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
return IGraphTransformer::TStatus::Error;
}
+ YQL_ENSURE(input->IsCallable({"YqlSetItem", "PgSetItem"}));
+ const bool isYql = input->IsCallable("YqlSetItem");
+ const TStringBuf sqlResultItem = isYql ? "YqlResultItem" : "PgResultItem";
+ const bool isOrderedColumns = !isYql || ctx.Types.OrderedColumns;
+
auto& options = input->Head();
if (!EnsureTuple(options, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
@@ -3617,8 +3622,9 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
for (ui32 index = 0; index < data.ChildrenSize(); ++index) {
const auto& column = data.Child(index);
- if (!column->IsCallable("PgResultItem")) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(column->Pos()), "Expected PgResultItem"));
+ if (!column->IsCallable(sqlResultItem)) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(column->Pos()),
+ TStringBuilder() << "Expected " << sqlResultItem));
return IGraphTransformer::TStatus::Error;
}
}
@@ -3785,7 +3791,7 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
}
if (hasChanges) {
- auto newColumn = ctx.Expr.NewCallable(column->Pos(), "PgResultItem", std::move(newColumnChildren));
+ auto newColumn = ctx.Expr.NewCallable(column->Pos(), sqlResultItem, std::move(newColumnChildren));
newResult.push_back(newColumn);
hasNewResult = true;
} else {
@@ -4077,19 +4083,30 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
return IGraphTransformer::TStatus::Error;
}
- if (!columnOrder) {
+ if (isOrderedColumns && !columnOrder) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(option->Head().Pos()),
"No column order at source"));
return IGraphTransformer::TStatus::Error;
}
TVector<const TItemExprType*> newStructItems;
- TColumnOrder newOrder;
+
+ TMaybe<TColumnOrder> newOrder;
+ if (isOrderedColumns) {
+ newOrder.ConstructInPlace();
+ }
+
for (ui32 i = 0; i < p->Child(2)->ChildrenSize(); ++i) {
- auto pos = inputStructType->FindItemI(columnOrder->at(i).PhysicalName, nullptr);
- YQL_ENSURE(pos);
- auto type = inputStructType->GetItems()[*pos]->GetItemType();
- newOrder.AddColumn(TString(p->Child(2)->Child(i)->Content()));
+ const TTypeAnnotationNode* type = nullptr;
+ if (columnOrder) {
+ auto pos = inputStructType->FindItemI(columnOrder->at(i).PhysicalName, nullptr);
+ YQL_ENSURE(pos);
+ type = inputStructType->GetItems()[*pos]->GetItemType();
+ newOrder->AddColumn(TString(p->Child(2)->Child(i)->Content()));
+ } else {
+ type = inputStructType->GetItems()[i]->GetItemType();
+ }
+
newStructItems.push_back(ctx.Expr.MakeType<TItemExprType>(p->Child(2)->Child(i)->Content(), type));
}
@@ -5000,6 +5017,9 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
}
IGraphTransformer::TStatus PgValuesListWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
+ YQL_ENSURE(input->IsCallable({"YqlValuesList", "PgValuesList"}));
+ const bool isYql = input->IsCallable("YqlValuesList");
+
if (!EnsureMinArgsCount(*input, 1, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
}
@@ -5030,7 +5050,15 @@ IGraphTransformer::TStatus PgValuesListWrapper(const TExprNode::TPtr& input, TEx
TStringBuilder() << "VALUES lists must all be the same length"));
return IGraphTransformer::TStatus::Error;
}
+ }
+ if (isYql) {
+ output = ctx.Expr.NewCallable(input->Pos(), "AsListStrict", std::move(input->ChildrenList()));
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
+ for (size_t i = 0; i < input->ChildrenSize(); ++i) {
+ auto* value = input->Child(i);
for (size_t j = 0; j < tupleSize; ++j) {
auto* item = value->Child(j);
auto type = item->GetTypeAnn();
@@ -5102,6 +5130,13 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
return IGraphTransformer::TStatus::Error;
}
+ YQL_ENSURE(input->IsCallable({"YqlSelect", "PgSelect"}));
+ const bool isYql = input->IsCallable("YqlSelect");
+ const TStringBuf sqlSelect = isYql ? "YqlSelect" : "PgSelect";
+ const TStringBuf sqlSetItem = isYql ? "YqlSetItem" : "PgSetItem";
+ const TStringBuf sqlIterate = isYql ? "YqlIterate" : "PgIterate";
+ const bool isOrderedColumns = !isYql || ctx.Types.OrderedColumns;
+
TExprNode* setItems = nullptr;
TExprNode* setOps = nullptr;
bool hasSort = false;
@@ -5156,8 +5191,9 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
}
for (const auto& child : option->Tail().Children()) {
- if (!child->IsCallable("PgSetItem")) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(child->Pos()), "Expected PgSetItem"));
+ if (!child->IsCallable(sqlSetItem)) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(child->Pos()),
+ TStringBuilder() << "Expected " << sqlSetItem));
return IGraphTransformer::TStatus::Error;
}
}
@@ -5301,7 +5337,7 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
YQL_ENSURE(status.Level != IGraphTransformer::TStatus::Error);
auto lambdaBody = ctx.Expr.Builder(input->Pos())
- .Callable("PgSelect")
+ .Callable(sqlSelect)
.List(0)
.List(0)
.Atom(0, "set_items")
@@ -5322,8 +5358,8 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
auto lambda = ctx.Expr.NewLambda(input->Pos(), ctx.Expr.NewArguments(input->Pos(), { tableArg }), std::move(lambdaBody));
output = ctx.Expr.Builder(input->Pos())
- .Callable(ToString("PgIterate") + (setOps->Child(2)->Content() == "union_all" ? "All" : ""))
- .Callable(0, "PgSelect")
+ .Callable(ToString(sqlIterate) + (setOps->Child(2)->Content() == "union_all" ? "All" : ""))
+ .Callable(0, sqlSelect)
.List(0)
.List(0)
.Atom(0, "set_items")
@@ -5349,9 +5385,14 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
TColumnOrder resultColumnOrder;
const TStructExprType* resultStructType = nullptr;
- auto status = (1 == setItems->ChildrenSize() && HasSetting(*setItems->Child(0)->Child(0), "unknowns_allowed"))
- ? InferPositionalUnionType(input->Pos(), setItems->ChildrenList(), resultColumnOrder, resultStructType, ctx)
- : InferPgCommonType(input->Pos(), setItems, setOps, resultColumnOrder, resultStructType, ctx);
+ IGraphTransformer::TStatus status = IGraphTransformer::TStatus::Error;
+ if (isYql && !isOrderedColumns) {
+ status = InferUnionType(input->Pos(), setItems->ChildrenList(), resultStructType, ctx, /* areHashesChecked = */ false);
+ } else if (isYql || (1 == setItems->ChildrenSize() && HasSetting(*setItems->Child(0)->Child(0), "unknowns_allowed"))) {
+ status = InferPositionalUnionType(input->Pos(), setItems->ChildrenList(), resultColumnOrder, resultStructType, ctx);
+ } else {
+ status = InferPgCommonType(input->Pos(), setItems, setOps, resultColumnOrder, resultStructType, ctx);
+ }
if (status != IGraphTransformer::TStatus::Ok) {
return status;
diff --git a/yql/essentials/sql/v1/context.h b/yql/essentials/sql/v1/context.h
index 49041cd11b2..b7db8ab358e 100644
--- a/yql/essentials/sql/v1/context.h
+++ b/yql/essentials/sql/v1/context.h
@@ -86,6 +86,12 @@ enum class EColumnRefState {
MatchRecognizeDefineAggregate,
};
+enum class EYqlSelectMode {
+ Disable,
+ Auto,
+ Force,
+};
+
class TContext {
public:
TContext(const TLexers& lexers,
@@ -393,8 +399,8 @@ public:
bool StrictWarningAsError = false;
TMaybe<bool> DirectRowDependsOn;
TVector<size_t> ForAllStatementsParts;
-
TMaybe<TString> Engine;
+ EYqlSelectMode YqlSelectMode = EYqlSelectMode::Disable;
};
class TColumnRefScope {
diff --git a/yql/essentials/sql/v1/node.cpp b/yql/essentials/sql/v1/node.cpp
index e4105c0064a..50dc8a47089 100644
--- a/yql/essentials/sql/v1/node.cpp
+++ b/yql/essentials/sql/v1/node.cpp
@@ -1589,7 +1589,17 @@ bool TColumnNode::DoInit(TContext& ctx, ISource* src) {
// TODO: consider replacing Member -> SqlPlainColumn
callable = Reliable_ && !UseSource_ ? "Member" : "SqlColumn";
}
- Node_ = Y(callable, "row", ColumnExpr_ ? Y("EvaluateAtom", ColumnExpr_) : BuildQuotedAtom(Pos_, *GetColumnName()));
+
+ TNodePtr ref = ColumnExpr_
+ ? Y("EvaluateAtom", ColumnExpr_)
+ : BuildQuotedAtom(Pos_, *GetColumnName());
+
+ if (IsYqlRef_) {
+ Node_ = Y("YqlColumnRef", ref);
+ } else {
+ Node_ = Y(callable, "row", ref);
+ }
+
if (UseSource_) {
YQL_ENSURE(Source_);
Node_ = L(Node_, BuildQuotedAtom(Pos_, Source_));
@@ -1612,6 +1622,10 @@ void TColumnNode::SetAsNotReliable() {
Reliable_ = false;
}
+void TColumnNode::SetAsYqlRef() {
+ IsYqlRef_ = true;
+}
+
void TColumnNode::SetUseSource() {
UseSource_ = true;
}
@@ -1695,6 +1709,14 @@ TNodePtr BuildColumnOrType(TPosition pos, const TString& column) {
return new TColumnNode(pos, column, source, maybeType);
}
+TNodePtr BuildYqlColumnRef(TPosition pos) {
+ TString source = "";
+ bool maybeType = true;
+ auto* node = new TColumnNode(pos, /* column = */ "", source, maybeType);
+ node->SetAsYqlRef();
+ return node;
+}
+
ITableKeys::ITableKeys(TPosition pos)
: INode(pos)
{
diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h
index a37d58a16be..c690404561d 100644
--- a/yql/essentials/sql/v1/node.h
+++ b/yql/essentials/sql/v1/node.h
@@ -878,6 +878,7 @@ public:
void SetUseSource();
void ResetAsReliable();
void SetAsNotReliable();
+ void SetAsYqlRef();
bool IsReliable() const;
bool IsUseSourceAsColumn() const;
bool IsUseSource() const;
@@ -901,6 +902,7 @@ private:
bool UseSource_ = false;
bool UseSourceAsColumn_ = false;
bool MaybeType_ = false;
+ bool IsYqlRef_ = false;
};
class TArgPlaceholderNode final: public INode {
@@ -1523,6 +1525,7 @@ TNodePtr BuildColumn(TPosition pos, const TString& column = TString(), const TSt
TNodePtr BuildColumn(TPosition pos, const TNodePtr& column, const TString& source = TString());
TNodePtr BuildColumn(TPosition pos, const TDeferredAtom& column, const TString& source = TString());
TNodePtr BuildColumnOrType(TPosition pos, const TString& column = TString());
+TNodePtr BuildYqlColumnRef(TPosition pos);
TNodePtr BuildAccess(TPosition pos, const TVector<INode::TIdPart>& ids, bool isLookup);
TNodePtr BuildBind(TPosition pos, const TString& module, const TString& alias);
TNodePtr BuildLambda(TPosition pos, TNodePtr params, TNodePtr body, const TString& resName = TString());
diff --git a/yql/essentials/sql/v1/select_yql.cpp b/yql/essentials/sql/v1/select_yql.cpp
new file mode 100644
index 00000000000..3a7d091b7d3
--- /dev/null
+++ b/yql/essentials/sql/v1/select_yql.cpp
@@ -0,0 +1,254 @@
+#include "select_yql.h"
+
+#include "context.h"
+
+namespace NSQLTranslationV1 {
+
+bool Init(TContext& ctx, ISource* src, const TVector<TNodePtr>& nodes) {
+ for (const TNodePtr& node : nodes) {
+ if (!node->Init(ctx, src)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+class TYqlValuesNode final: public INode, private TYqlValuesArgs {
+public:
+ TYqlValuesNode(TPosition position, TYqlValuesArgs&& args)
+ : INode(std::move(position))
+ , TYqlValuesArgs(std::move(args))
+ {
+ }
+
+ bool DoInit(TContext& ctx, ISource* src) override {
+ for (const auto& row : Rows) {
+ if (!::NSQLTranslationV1::Init(ctx, src, row)) {
+ return false;
+ }
+ }
+
+ if (TMaybe<size_t> width = Width(Rows, ctx)) {
+ Width_ = *width;
+ } else {
+ return false;
+ }
+
+ Values_ = BuildValueList(Rows);
+ if (!Values_) {
+ return false;
+ }
+
+ return true;
+ }
+
+ TAstNode* Translate(TContext& ctx) const override {
+ TNodePtr node =
+ Y("YqlSelect",
+ Q(Y(Q(Y(Q("set_items"),
+ Q(Y(Y("YqlSetItem",
+ Q(Y(Q(Y(Q("values"),
+ Q(BuildColumnList()),
+ Values_))))))))),
+ Q(Y(Q("set_ops"), Q(Y(Q("push"))))))));
+ return node->Translate(ctx);
+ }
+
+ TNodePtr DoClone() const override {
+ return new TYqlValuesNode(*this);
+ }
+
+ bool SetColumns(TVector<TString> columns, TContext& ctx) {
+ if (columns.empty()) {
+ return true;
+ }
+
+ if (columns.size() != Width_) {
+ ctx.Error() << "VALUES statement width is " << Width_
+ << ", but got " << columns.size()
+ << " column aliases";
+ return false;
+ }
+
+ Columns_ = std::move(columns);
+ return true;
+ }
+
+private:
+ TNodePtr BuildColumnList() const {
+ TNodePtr columns = Y();
+ for (size_t i = 0; i < Width_; ++i) {
+ TString name;
+ if (!Columns_) {
+ name = TStringBuilder() << "column" << i;
+ } else {
+ name = Columns_->at(i);
+ }
+
+ columns->Add(Q(std::move(name)));
+ }
+ return columns;
+ }
+
+ TNodePtr BuildValueList(const TVector<TVector<TNodePtr>>& rows) const {
+ TNodePtr values = Y("YqlValuesList");
+ for (const auto& row : rows) {
+ TNodePtr value = Y();
+ for (const TNodePtr& column : row) {
+ value->Add(column);
+ }
+
+ values->Add(Q(std::move(value)));
+ }
+ return values;
+ }
+
+ TMaybe<size_t> Width(const TVector<TVector<TNodePtr>>& rows, TContext& ctx) const {
+ size_t width = std::numeric_limits<size_t>::max();
+ for (const auto& row : rows) {
+ if (width == std::numeric_limits<size_t>::max()) {
+ width = row.size();
+ } else if (width != row.size()) {
+ ctx.Error() << "VALUES lists must all be the same length. "
+ << "Expected width is " << width << ", "
+ << "but got " << row.size();
+ return Nothing();
+ }
+ }
+ return width;
+ }
+
+ TNodePtr Values_;
+ size_t Width_ = 0;
+ TMaybe<TVector<TString>> Columns_;
+};
+
+class TYqlSelectNode final: public INode, private TYqlSelectArgs {
+public:
+ TYqlSelectNode(TPosition position, TYqlSelectArgs&& args)
+ : INode(std::move(position))
+ , TYqlSelectArgs(std::move(args))
+ {
+ }
+
+ bool DoInit(TContext& ctx, ISource* src) override {
+ if (!InitTerms(ctx, src) || (Source && !Source->Node->Init(ctx, src))) {
+ return false;
+ }
+
+ TNodePtr item = Y();
+ {
+ TNodePtr items = BuildYqlResultItems(Terms);
+ if (!items) {
+ return false;
+ }
+
+ item->Add(Q(Y(Q("result"), Q(std::move(items)))));
+ }
+
+ if (Source) {
+ TNodePtr& node = Source->Node;
+
+ TString sourceName;
+ if (auto& alias = Source->Alias) {
+ sourceName = std::move(alias->Name);
+
+ if (auto& columns = alias->Columns) {
+ if (auto* values = dynamic_cast<TYqlValuesNode*>(node.Get())) {
+ if (!values->SetColumns(std::move(columns), ctx)) {
+ return false;
+ }
+ } else {
+ ctx.Error() << "Qualified by column names source alias "
+ << "is viable only for VALUES statement";
+ return false;
+ }
+ }
+ }
+
+ item->Add(Q(Y(Q("from"),
+ Q(Y(Q(Y(
+ std::move(node),
+ Q(std::move(sourceName)),
+ Q(Y(/* Columns are passed through SetColumns */)))))))));
+
+ item->Add(Q(Y(Q("join_ops"), Q(Y(Q(Y(Q(Y(Q("push"))))))))));
+ }
+
+ TNodePtr output =
+ Y("YqlSelect",
+ Q(Y(Q(Y(Q("set_items"),
+ Q(Y(Y("YqlSetItem", Q(std::move(item))))))),
+ Q(Y(Q("set_ops"), Q(Y(Q("push"))))))));
+
+ TNodePtr block = Y();
+ {
+ block->Add(Y("let", "output", std::move(output)));
+
+ block->Add(Y("let", "result_sink",
+ Y("DataSink", Q("result"))));
+
+ block->Add(Y("let", "world",
+ Y("Write!", "world", "result_sink", Y("Key"), "output",
+ Q(Y(Q(Y(Q("type"))), Q(Y(Q("autoref"))), Q(Y(Q("unordered"))))))));
+
+ block->Add(Y("return", Y("Commit!", "world", "result_sink")));
+ }
+
+ Node_ = Y("block", Q(std::move(block)));
+ return true;
+ }
+
+ TAstNode* Translate(TContext& ctx) const override {
+ return Node_->Translate(ctx);
+ }
+
+ TNodePtr DoClone() const override {
+ return new TYqlSelectNode(*this);
+ }
+
+private:
+ bool InitTerms(TContext& ctx, ISource* src) {
+ for (size_t i = 0; i < Terms.size(); ++i) {
+ const TNodePtr& term = Terms[i];
+ term->SetLabel(TermAlias(term, i));
+ }
+
+ return ::NSQLTranslationV1::Init(ctx, src, Terms);
+ }
+
+ TString TermAlias(const TNodePtr& term, size_t i) const {
+ const TString& label = term->GetLabel();
+ if (!label.empty()) {
+ return label;
+ }
+ if (const TString* column = term->GetColumnName()) {
+ return *column;
+ }
+ return TStringBuilder() << "column" << i;
+ }
+
+ TNodePtr BuildYqlResultItems(const TVector<TNodePtr>& terms) const {
+ TNodePtr items = Y();
+ for (const TNodePtr& term : terms) {
+ items->Add(BuildYqlResultItem(term->GetLabel(), term));
+ }
+ return items;
+ }
+
+ TNodePtr BuildYqlResultItem(const TString& name, const TNodePtr& term) const {
+ return Y("YqlResultItem", Q(name), Y("Void"), Y("lambda", Q(Y()), term));
+ }
+
+ TNodePtr Node_;
+};
+
+TNodePtr BuildYqlValues(TPosition position, TYqlValuesArgs&& args) {
+ return new TYqlValuesNode(std::move(position), std::move(args));
+}
+
+TNodePtr BuildYqlSelect(TPosition position, TYqlSelectArgs&& args) {
+ return new TYqlSelectNode(std::move(position), std::move(args));
+}
+
+} // namespace NSQLTranslationV1
diff --git a/yql/essentials/sql/v1/select_yql.h b/yql/essentials/sql/v1/select_yql.h
new file mode 100644
index 00000000000..7c819e36ced
--- /dev/null
+++ b/yql/essentials/sql/v1/select_yql.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "node.h"
+
+namespace NSQLTranslationV1 {
+
+struct TYqlSourceAlias {
+ TString Name;
+ TVector<TString> Columns;
+};
+
+struct TYqlSource {
+ TNodePtr Node;
+ TMaybe<TYqlSourceAlias> Alias;
+};
+
+struct TYqlValuesArgs {
+ TVector<TVector<TNodePtr>> Rows;
+};
+
+struct TYqlSelectArgs {
+ TVector<TNodePtr> Terms;
+ TMaybe<TYqlSource> Source;
+};
+
+TNodePtr BuildYqlValues(TPosition position, TYqlValuesArgs&& args);
+
+TNodePtr BuildYqlSelect(TPosition position, TYqlSelectArgs&& args);
+
+} // namespace NSQLTranslationV1
diff --git a/yql/essentials/sql/v1/sql_expression.cpp b/yql/essentials/sql/v1/sql_expression.cpp
index 7ece4db8fca..dc311c66fb6 100644
--- a/yql/essentials/sql/v1/sql_expression.cpp
+++ b/yql/essentials/sql/v1/sql_expression.cpp
@@ -1068,7 +1068,16 @@ TNodePtr TSqlExpression::UnaryCasualExpr(const TUnaryCasualExprRule& node, const
return nullptr;
}
- ids.push_back(columnOrType ? BuildColumnOrType(Ctx_.Pos()) : BuildColumn(Ctx_.Pos()));
+ TNodePtr id;
+ if (IsYqlColumnRefProduced_) {
+ id = BuildYqlColumnRef(Ctx_.Pos());
+ } else if (columnOrType) {
+ id = BuildColumnOrType(Ctx_.Pos());
+ } else {
+ id = BuildColumn(Ctx_.Pos());
+ }
+ ids.emplace_back(std::move(id));
+
ids.push_back(name);
}
diff --git a/yql/essentials/sql/v1/sql_expression.h b/yql/essentials/sql/v1/sql_expression.h
index 66025de97c1..bd4e5e5409d 100644
--- a/yql/essentials/sql/v1/sql_expression.h
+++ b/yql/essentials/sql/v1/sql_expression.h
@@ -35,6 +35,10 @@ public:
MaybeUnnamedSmartParenOnTop_ = false;
}
+ void ProduceYqlColumnRef() {
+ IsYqlColumnRefProduced_ = true;
+ }
+
TMaybe<TExprOrIdent> LiteralExpr(const TRule_literal_value& node);
private:
@@ -142,6 +146,7 @@ private:
ESmartParenthesis SmartParenthesisMode_ = ESmartParenthesis::Default;
bool MaybeUnnamedSmartParenOnTop_ = true;
bool IsSourceAllowed_ = true;
+ bool IsYqlColumnRefProduced_ = false;
THashMap<TString, TNodePtr> ExprShortcuts_;
};
diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp
index 757389b9f7f..5c2c9024d66 100644
--- a/yql/essentials/sql/v1/sql_query.cpp
+++ b/yql/essentials/sql/v1/sql_query.cpp
@@ -1,6 +1,7 @@
#include "sql_query.h"
#include "sql_expression.h"
#include "sql_select.h"
+#include "sql_select_yql.h"
#include "sql_into_tables.h"
#include "sql_values.h"
#include "node.h"
@@ -266,6 +267,26 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core&
return false;
}
+ if (Ctx_.YqlSelectMode != EYqlSelectMode::Disable) {
+ if (!IsBackwardCompatibleFeatureAvailable(MakeLangVersion(2025, 04))) {
+ Error() << "YqlSelect is not available before 2025.04";
+ return false;
+ }
+
+ const auto stmt = core.GetAlt_sql_stmt_core2().GetRule_select_stmt1();
+ if (auto result = BuildYqlSelect(Ctx_, Mode_, stmt)) {
+ blocks.emplace_back(std::move(*result));
+ break;
+ } else if (
+ result.error() == EYqlSelectError::Unsupported &&
+ Ctx_.YqlSelectMode == EYqlSelectMode::Force)
+ {
+ Error() << "Translation of the statement "
+ << "to YqlSelect was forced, but unsupported";
+ return false;
+ }
+ }
+
Ctx_.BodyPart();
TSqlSelect select(Ctx_, Mode_);
TPosition pos;
@@ -3451,6 +3472,29 @@ THashMap<TString, TPragmaDescr> PragmaDescrs{
}
return BuildPragma(ctx.Pos(), TString(ConfigProviderName), "Layer", values, false);
}),
+ TableElemExt("YqlSelect", [](CB_SIG) -> TMaybe<TNodePtr> {
+ auto& ctx = query.Context();
+
+ const TString* literal = values.size() == 1 ? values[0].GetLiteral() : nullptr;
+ if (!literal) {
+ query.Error() << "Expected an only literal argument for: " << pragma;
+ return Nothing();
+ }
+
+ if (*literal == "disable") {
+ ctx.YqlSelectMode = EYqlSelectMode::Disable;
+ } else if (*literal == "auto") {
+ ctx.YqlSelectMode = EYqlSelectMode::Auto;
+ } else if (*literal == "force") {
+ ctx.YqlSelectMode = EYqlSelectMode::Force;
+ } else {
+ query.Error() << "Unexpected literal '" << *literal << "' for: " << pragma
+ << ", expected 'disable', 'auto' or 'force'";
+ return Nothing();
+ }
+
+ return TNodePtr();
+ }),
// TMaybe<bool> fields.
PAIRED_TABLE_ELEM("AnsiInForEmptyOrNullableItemsCollections", AnsiInForEmptyOrNullableItemsCollections),
diff --git a/yql/essentials/sql/v1/sql_select_yql.cpp b/yql/essentials/sql/v1/sql_select_yql.cpp
new file mode 100644
index 00000000000..e1a4da6250e
--- /dev/null
+++ b/yql/essentials/sql/v1/sql_select_yql.cpp
@@ -0,0 +1,359 @@
+#include "sql_select_yql.h"
+
+#include "sql_expression.h"
+#include "select_yql.h"
+
+namespace NSQLTranslationV1 {
+
+using namespace NSQLv1Generated;
+
+class TYqlSelect final: public TSqlTranslation {
+public:
+ template <class T>
+ using TResult = std::expected<T, EYqlSelectError>;
+
+ TYqlSelect(TContext& ctx, NSQLTranslation::ESqlMode mode)
+ : TSqlTranslation(ctx, mode)
+ {
+ }
+
+ TResult<TNodePtr> Build(const TRule_select_stmt& rule) {
+ const auto& intersect = rule.GetRule_select_stmt_intersect1();
+ if (!rule.GetBlock2().empty()) {
+ return Unsupported("(union_op select_stmt_intersect)*");
+ }
+
+ const auto& partial = Unpack(intersect.GetRule_select_kind_parenthesis1());
+ if (!intersect.GetBlock2().empty()) {
+ return Unsupported("(intersect_op select_kind_parenthesis)*");
+ }
+
+ return Build(partial);
+ }
+
+private:
+ TResult<TNodePtr> Build(const TRule_select_kind_partial& rule) {
+ if (rule.HasBlock2()) {
+ return Unsupported("(LIMIT expr ((OFFSET | COMMA) expr)?)");
+ }
+
+ return Build(rule.GetRule_select_kind1());
+ }
+
+ TResult<TNodePtr> Build(const TRule_select_kind& rule) {
+ if (rule.HasBlock1()) {
+ return Unsupported("DISCARD");
+ }
+
+ if (rule.HasBlock3()) {
+ return Unsupported("INTO RESULT pure_column_or_named");
+ }
+
+ switch (const auto& block = rule.GetBlock2(); block.GetAltCase()) {
+ case NSQLv1Generated::TRule_select_kind_TBlock2::kAlt1:
+ return Unsupported("process_core");
+ case NSQLv1Generated::TRule_select_kind_TBlock2::kAlt2:
+ return Unsupported("reduce_core");
+ case NSQLv1Generated::TRule_select_kind_TBlock2::kAlt3:
+ return Build(block.GetAlt3().GetRule_select_core1());
+ case NSQLv1Generated::TRule_select_kind_TBlock2::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ TResult<TNodePtr> Build(const TRule_select_core& rule) {
+ TYqlSelectArgs select;
+
+ if (rule.HasBlock1()) {
+ Token(rule.GetBlock1().GetToken1());
+
+ if (auto result = Build(rule.GetBlock1().GetRule_join_source2())) {
+ select.Source = std::move(*result);
+ } else {
+ return std::unexpected(result.error());
+ }
+ }
+
+ Token(rule.GetToken2());
+
+ if (Mode_ != NSQLTranslation::ESqlMode::QUERY) {
+ return Unsupported("ESqlMode != QUERY");
+ }
+
+ if (rule.HasBlock3()) {
+ Token(rule.GetBlock3().GetToken1());
+ return Unsupported("STREAM");
+ }
+
+ if (rule.GetRule_opt_set_quantifier4().HasBlock1()) {
+ return Unsupported("opt_set_quantifier");
+ }
+
+ if (auto result = BuildTerms(rule)) {
+ select.Terms = std::move(*result);
+ } else {
+ return std::unexpected(result.error());
+ }
+
+ if (rule.HasBlock8()) {
+ Token(rule.GetBlock8().GetToken1());
+ return Unsupported("WITHOUT (IF EXISTS)? without_column_list");
+ }
+
+ if (rule.HasBlock9()) {
+ Token(rule.GetBlock9().GetToken1());
+
+ if (rule.HasBlock1()) {
+ Ctx_.IncrementMonCounter("sql_errors", "DoubleFrom");
+ Ctx_.Error() << "Only one FROM clause is allowed";
+ return std::unexpected(EYqlSelectError::Error);
+ }
+
+ if (auto result = Build(rule.GetBlock9().GetRule_join_source2())) {
+ select.Source = std::move(*result);
+ } else {
+ return std::unexpected(result.error());
+ }
+ }
+
+ if (rule.HasBlock10()) {
+ Token(rule.GetBlock10().GetToken1());
+ return Unsupported("WHERE expr");
+ }
+
+ if (rule.HasBlock11()) {
+ return Unsupported("group_by_clause");
+ }
+
+ if (rule.HasBlock12()) {
+ Token(rule.GetBlock12().GetToken1());
+ return Unsupported("HAVING expr");
+ }
+
+ if (rule.HasBlock13()) {
+ return Unsupported("window_clause");
+ }
+
+ if (rule.HasBlock14()) {
+ return Unsupported("ext_order_by_clause");
+ }
+
+ if (auto node = BuildYqlSelect(Ctx_.Pos(), std::move(select))) {
+ return node;
+ } else {
+ return std::unexpected(EYqlSelectError::Error);
+ }
+ }
+
+ TResult<TVector<TNodePtr>> BuildTerms(const TRule_select_core& rule) {
+ TVector<TNodePtr> terms;
+
+ if (auto result = Build(rule.GetRule_result_column5())) {
+ terms.emplace_back(std::move(*result));
+ } else {
+ return std::unexpected(result.error());
+ }
+
+ for (const auto& block : rule.GetBlock6()) {
+ if (auto result = Build(block.GetRule_result_column2())) {
+ terms.emplace_back(std::move(*result));
+ } else {
+ return std::unexpected(result.error());
+ }
+ }
+
+ return terms;
+ }
+
+ TResult<TNodePtr> Build(const TRule_result_column& rule) {
+ switch (rule.GetAltCase()) {
+ case NSQLv1Generated::TRule_result_column::kAltResultColumn1:
+ return Unsupported("opt_id_prefix ASTERISK");
+ case NSQLv1Generated::TRule_result_column::kAltResultColumn2: {
+ const auto& alt = rule.GetAlt_result_column2();
+
+ TColumnRefScope scope(Ctx_, EColumnRefState::Allow);
+ TSqlExpression sqlExpr(Ctx_, Mode_);
+ sqlExpr.ProduceYqlColumnRef();
+
+ TNodePtr expr = sqlExpr.Build(alt.GetRule_expr1());
+ if (!expr) {
+ return std::unexpected(EYqlSelectError::Error);
+ }
+
+ if (const auto label = Label(alt)) {
+ expr->SetLabel(*label);
+ }
+
+ return expr;
+ } break;
+ case NSQLv1Generated::TRule_result_column::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ TResult<TYqlSource> Build(const TRule_join_source& rule) {
+ if (rule.HasBlock1()) {
+ Token(rule.GetBlock1().GetToken1());
+ return Unsupported<TYqlSource>("ANY");
+ }
+
+ if (!rule.GetBlock3().empty()) {
+ return Unsupported<TYqlSource>("join_op ANY? flatten_source join_constraint?");
+ }
+
+ return Build(rule.GetRule_flatten_source2());
+ }
+
+ TResult<TYqlSource> Build(const TRule_flatten_source& rule) {
+ if (rule.HasBlock2()) {
+ Token(rule.GetBlock2().GetToken1());
+ return Unsupported<TYqlSource>("FLATTEN ((OPTIONAL|LIST|DICT)? BY flatten_by_arg | COLUMNS)");
+ }
+
+ return Build(rule.GetRule_named_single_source1());
+ }
+
+ TResult<TYqlSource> Build(const TRule_named_single_source& rule) {
+ TYqlSource source;
+
+ if (auto result = Build(rule.GetRule_single_source1())) {
+ source.Node = std::move(*result);
+ } else {
+ return std::unexpected(result.error());
+ }
+
+ if (rule.HasBlock2()) {
+ return Unsupported<TYqlSource>("row_pattern_recognition_clause");
+ }
+
+ if (rule.HasBlock3()) {
+ const auto& block = rule.GetBlock3();
+
+ source.Alias.ConstructInPlace();
+ source.Alias->Name = TableAlias(block.GetBlock1());
+ if (block.HasBlock2()) {
+ source.Alias->Columns = TableColumns(block.GetBlock2().GetRule_pure_column_list1());
+ }
+ }
+
+ if (rule.HasBlock4()) {
+ return Unsupported<TYqlSource>("sample_clause | tablesample_clause");
+ }
+
+ return source;
+ }
+
+ TResult<TNodePtr> Build(const TRule_single_source& rule) {
+ switch (rule.GetAltCase()) {
+ case NSQLv1Generated::TRule_single_source::kAltSingleSource1:
+ return Unsupported("table_ref");
+ case NSQLv1Generated::TRule_single_source::kAltSingleSource2:
+ return Unsupported("select_stmt");
+ case NSQLv1Generated::TRule_single_source::kAltSingleSource3:
+ return Build(rule.GetAlt_single_source3().GetRule_values_stmt2());
+ case NSQLv1Generated::TRule_single_source::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ TResult<TNodePtr> Build(const TRule_values_stmt& rule) {
+ TYqlValuesArgs values;
+
+ Token(rule.GetToken1());
+ const auto& rows = rule.GetRule_values_source_row_list2();
+
+ if (auto result = Build(rows.GetRule_values_source_row1())) {
+ values.Rows.emplace_back(std::move(*result));
+ } else {
+ return std::unexpected(result.error());
+ }
+
+ for (const auto& row : rows.GetBlock2()) {
+ if (auto result = Build(row.GetRule_values_source_row2())) {
+ values.Rows.emplace_back(std::move(*result));
+ } else {
+ return std::unexpected(result.error());
+ }
+ }
+
+ return BuildYqlValues(Ctx_.Pos(), std::move(values));
+ }
+
+ TResult<TVector<TNodePtr>> Build(const TRule_values_source_row& rule) {
+ TVector<TNodePtr> columns;
+
+ TSqlExpression sqlExpr(Ctx_, Mode_);
+ if (!ExprList(sqlExpr, columns, rule.GetRule_expr_list2())) {
+ return std::unexpected(EYqlSelectError::Error);
+ }
+
+ return columns;
+ }
+
+ TMaybe<TString> Label(const TRule_result_column::TAlt2& rule) {
+ if (!rule.HasBlock2()) {
+ return Nothing();
+ }
+
+ const auto& block = rule.GetBlock2();
+ switch (block.Alt_case()) {
+ case TRule_result_column_TAlt2_TBlock2::kAlt1:
+ return Id(block.GetAlt1().GetRule_an_id_or_type2(), *this);
+ case TRule_result_column_TAlt2_TBlock2::kAlt2:
+ return Id(block.GetAlt2().GetRule_an_id_as_compat1(), *this);
+ case TRule_result_column_TAlt2_TBlock2::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ const TRule_select_kind_partial& Unpack(const TRule_select_kind_parenthesis& parenthesis) {
+ switch (parenthesis.GetAltCase()) {
+ case NSQLv1Generated::TRule_select_kind_parenthesis::kAltSelectKindParenthesis1:
+ return parenthesis.GetAlt_select_kind_parenthesis1().GetRule_select_kind_partial1();
+ case NSQLv1Generated::TRule_select_kind_parenthesis::kAltSelectKindParenthesis2:
+ return parenthesis.GetAlt_select_kind_parenthesis2().GetRule_select_kind_partial2();
+ case NSQLv1Generated::TRule_select_kind_parenthesis::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ TString TableAlias(const TRule_named_single_source::TBlock3::TBlock1& block) {
+ switch (block.GetAltCase()) {
+ case TRule_named_single_source_TBlock3_TBlock1::kAlt1:
+ return Id(block.GetAlt1().GetRule_an_id2(), *this);
+ case TRule_named_single_source_TBlock3_TBlock1::kAlt2:
+ return Id(block.GetAlt2().GetRule_an_id_as_compat1(), *this);
+ case TRule_named_single_source_TBlock3_TBlock1::ALT_NOT_SET:
+ Y_ABORT("You should change implementation according to grammar changes");
+ }
+ }
+
+ TVector<TString> TableColumns(const TRule_pure_column_list& rule) {
+ TVector<TString> columns;
+ columns.emplace_back(Id(rule.GetRule_an_id2(), *this));
+ for (const auto& id : rule.GetBlock3()) {
+ columns.emplace_back(Id(id.GetRule_an_id2(), *this));
+ }
+ return columns;
+ }
+
+ template <class T = TNodePtr>
+ TResult<T> Unsupported(TStringBuf message) {
+ if (Ctx_.YqlSelectMode == EYqlSelectMode::Force) {
+ Error() << "YqlSelect unsupported: " << message;
+ }
+
+ return std::unexpected(EYqlSelectError::Error);
+ }
+};
+
+TYqlSelectResult BuildYqlSelect(
+ TContext& ctx,
+ NSQLTranslation::ESqlMode mode,
+ const NSQLv1Generated::TRule_select_stmt& rule)
+{
+ return TYqlSelect(ctx, mode).Build(rule);
+}
+
+} // namespace NSQLTranslationV1
diff --git a/yql/essentials/sql/v1/sql_select_yql.h b/yql/essentials/sql/v1/sql_select_yql.h
new file mode 100644
index 00000000000..367473707ed
--- /dev/null
+++ b/yql/essentials/sql/v1/sql_select_yql.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "node.h"
+
+#include <yql/essentials/sql/settings/translation_settings.h>
+
+#include <yql/essentials/parser/proto_ast/gen/v1_proto_split/SQLv1Parser.pb.main.h>
+
+#include <expected>
+
+namespace NSQLTranslationV1 {
+
+enum class EYqlSelectError {
+ Error,
+ Unsupported,
+};
+
+using TYqlSelectResult = std::expected<TNodePtr, EYqlSelectError>;
+
+TYqlSelectResult BuildYqlSelect(
+ TContext& ctx,
+ NSQLTranslation::ESqlMode mode,
+ const NSQLv1Generated::TRule_select_stmt& rule);
+
+} // namespace NSQLTranslationV1
diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h
index 49c32fd450c..30f01e1c061 100644
--- a/yql/essentials/sql/v1/sql_ut_common.h
+++ b/yql/essentials/sql/v1/sql_ut_common.h
@@ -10695,3 +10695,112 @@ Y_UNIT_TEST(NamedNode) {
UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
}
} // Y_UNIT_TEST_SUITE(InlineUncorrelatedSubquery)
+
+Y_UNIT_TEST_SUITE(YqlSelect) {
+
+Y_UNIT_TEST(LangVer) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 3);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT 1;
+ )sql", settings);
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(
+ res.Issues.ToOneLineString(),
+ "YqlSelect is not available before 2025.04");
+}
+
+Y_UNIT_TEST(Minimal) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 4);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT 1;
+ )sql", settings);
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
+
+ TWordCountHive stat = {{TString("YqlSelect"), 0}};
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlSelect"], 1);
+}
+
+Y_UNIT_TEST(Expr) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 4);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT 2 + 2 * 2;
+ )sql", settings);
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
+
+ TWordCountHive stat = {
+ {TString("YqlSelect"), 0},
+ {TString("+MayWarn"), 0},
+ {TString("*MayWarn"), 0},
+ };
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlSelect"], 1);
+ UNIT_ASSERT_VALUES_EQUAL(stat["+MayWarn"], 1);
+ UNIT_ASSERT_VALUES_EQUAL(stat["*MayWarn"], 1);
+}
+
+Y_UNIT_TEST(ResultColumnLabel) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 4);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT 1 AS x;
+ )sql", settings);
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
+
+ TWordCountHive stat = {{TString("YqlSelect"), 0}};
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlSelect"], 1);
+}
+
+Y_UNIT_TEST(FromAnonValues) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 4);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT 1 FROM (VALUES (1));
+ )sql", settings);
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
+
+ TWordCountHive stat = {
+ {TString("YqlSelect"), 0},
+ {TString("YqlValuesList"), 0},
+ };
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlSelect"], 2);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlValuesList"], 1);
+}
+
+Y_UNIT_TEST(FromValues) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.LangVer = NYql::MakeLangVersion(2025, 4);
+
+ NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
+ PRAGMA YqlSelect = 'force';
+ SELECT a FROM (VALUES (1)) AS x (a);
+ )sql", settings);
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToOneLineString());
+
+ TWordCountHive stat = {
+ {TString("YqlSelect"), 0},
+ {TString("YqlValuesList"), 0},
+ {TString("YqlColumnRef"), 0},
+ };
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlSelect"], 2);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlValuesList"], 1);
+ UNIT_ASSERT_VALUES_EQUAL(stat["YqlColumnRef"], 1);
+}
+
+} // Y_UNIT_TEST_SUITE(YqlSelect)
diff --git a/yql/essentials/sql/v1/ya.make b/yql/essentials/sql/v1/ya.make
index 0e89d150bf8..1c26bcede72 100644
--- a/yql/essentials/sql/v1/ya.make
+++ b/yql/essentials/sql/v1/ya.make
@@ -31,6 +31,7 @@ SRCS(
list_builtin.cpp
match_recognize.cpp
node.cpp
+ select_yql.cpp
select.cpp
source.cpp
sql.cpp
@@ -40,6 +41,7 @@ SRCS(
sql_match_recognize.cpp
sql_into_tables.cpp
sql_query.cpp
+ sql_select_yql.cpp
sql_select.cpp
sql_translation.cpp
sql_values.cpp
diff --git a/yql/essentials/tests/sql/minirun/part0/canondata/result.json b/yql/essentials/tests/sql/minirun/part0/canondata/result.json
index 92663561e1b..8caca3ea29b 100644
--- a/yql/essentials/tests/sql/minirun/part0/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part0/canondata/result.json
@@ -1597,6 +1597,20 @@
"uri": "https://{canondata_backend}/1809005/3d9ba2209e199d535bc6bbb3c259c9d955a3f50c/resource.tar.gz#test.test_select-without_if_exists-default.txt-Results_/results.txt"
}
],
+ "test.test[select_yql-alias-default.txt-Debug]": [
+ {
+ "checksum": "b4f5e21b6c671e6a24f2baa0042ba37b",
+ "size": 565,
+ "uri": "https://{canondata_backend}/1936997/93c36d1901fec2752db63bc4b0dfd0c1e64ffbb5/resource.tar.gz#test.test_select_yql-alias-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-alias-default.txt-Results]": [
+ {
+ "checksum": "c527907580bb46abec85b44a8782ae9d",
+ "size": 3922,
+ "uri": "https://{canondata_backend}/1936997/93c36d1901fec2752db63bc4b0dfd0c1e64ffbb5/resource.tar.gz#test.test_select_yql-alias-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[side_effects-logical_bools_skipped-default.txt-Debug]": [
{
"checksum": "cd3d05b2d54e913992265ba01a0f15a0",
diff --git a/yql/essentials/tests/sql/minirun/part1/canondata/result.json b/yql/essentials/tests/sql/minirun/part1/canondata/result.json
index 6994021845f..bf94623311a 100644
--- a/yql/essentials/tests/sql/minirun/part1/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part1/canondata/result.json
@@ -1545,6 +1545,34 @@
"uri": "https://{canondata_backend}/1775319/60b6d8d1eea951b7a0bb833ba189ada82576641b/resource.tar.gz#test.test_select_op-select_op_precedence_named_node-default.txt-Results_/results.txt"
}
],
+ "test.test[select_yql-from_anon_values-default.txt-Debug]": [
+ {
+ "checksum": "9ff57f1dd3133170993092071c716b7d",
+ "size": 195,
+ "uri": "https://{canondata_backend}/1903885/834859b8d90cc65c8e15ca2c68dd48f148d46edf/resource.tar.gz#test.test_select_yql-from_anon_values-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-from_anon_values-default.txt-Results]": [
+ {
+ "checksum": "3432be66794780aaeaad6d43b71e08e5",
+ "size": 695,
+ "uri": "https://{canondata_backend}/1903885/834859b8d90cc65c8e15ca2c68dd48f148d46edf/resource.tar.gz#test.test_select_yql-from_anon_values-default.txt-Results_/results.txt"
+ }
+ ],
+ "test.test[select_yql-minimal-default.txt-Debug]": [
+ {
+ "checksum": "09ab2e8655b34c4968d5d114d5e4de52",
+ "size": 197,
+ "uri": "https://{canondata_backend}/1903885/834859b8d90cc65c8e15ca2c68dd48f148d46edf/resource.tar.gz#test.test_select_yql-minimal-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-minimal-default.txt-Results]": [
+ {
+ "checksum": "3432be66794780aaeaad6d43b71e08e5",
+ "size": 695,
+ "uri": "https://{canondata_backend}/1903885/834859b8d90cc65c8e15ca2c68dd48f148d46edf/resource.tar.gz#test.test_select_yql-minimal-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[seq_mode-shared_named_expr-default.txt-Debug]": [
{
"checksum": "6de9752b20a85eb7d5c4b1508dfd607d",
diff --git a/yql/essentials/tests/sql/minirun/part4/canondata/result.json b/yql/essentials/tests/sql/minirun/part4/canondata/result.json
index 994d4c20629..474ac7fc4fe 100644
--- a/yql/essentials/tests/sql/minirun/part4/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part4/canondata/result.json
@@ -1849,6 +1849,20 @@
"uri": "https://{canondata_backend}/1871182/4957464f17f4b05ffc05eeebd894c6e979082da5/resource.tar.gz#test.test_select_op-select_op_precedence-default.txt-Results_/results.txt"
}
],
+ "test.test[select_yql-expr-default.txt-Debug]": [
+ {
+ "checksum": "1bfdf1d6b54ea92d685b5879b915f3db",
+ "size": 272,
+ "uri": "https://{canondata_backend}/1881367/0d65113c859922c39754b7a5431118dd49b482da/resource.tar.gz#test.test_select_yql-expr-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-expr-default.txt-Results]": [
+ {
+ "checksum": "acbd7a57e6a5fbb2346504902c8e88e5",
+ "size": 1267,
+ "uri": "https://{canondata_backend}/1881367/0d65113c859922c39754b7a5431118dd49b482da/resource.tar.gz#test.test_select_yql-expr-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[udf-same_udf_modules--Debug]": [
{
"checksum": "f90e4ed7c7d964c319a4155dda443d04",
diff --git a/yql/essentials/tests/sql/minirun/part7/canondata/result.json b/yql/essentials/tests/sql/minirun/part7/canondata/result.json
index aa432ef0181..cb4317d9462 100644
--- a/yql/essentials/tests/sql/minirun/part7/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part7/canondata/result.json
@@ -1426,6 +1426,34 @@
"uri": "https://{canondata_backend}/212715/392992a262a39acb9e6e49f104e5800a3e731eb6/resource.tar.gz#test.test_select-prune_keys-default.txt-Results_/results.txt"
}
],
+ "test.test[select_yql-from_values-default.txt-Debug]": [
+ {
+ "checksum": "03d0266f96fc7980722973edc10e8335",
+ "size": 355,
+ "uri": "https://{canondata_backend}/1881367/8372278e853d5b34e19d2aff34e1df1e19102bd3/resource.tar.gz#test.test_select_yql-from_values-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-from_values-default.txt-Results]": [
+ {
+ "checksum": "8b4564d53149a88c92f3cf18c412ab92",
+ "size": 1181,
+ "uri": "https://{canondata_backend}/1881367/8372278e853d5b34e19d2aff34e1df1e19102bd3/resource.tar.gz#test.test_select_yql-from_values-default.txt-Results_/results.txt"
+ }
+ ],
+ "test.test[select_yql-from_values_ordered_columns-default.txt-Debug]": [
+ {
+ "checksum": "912f93e350a4f0f1f2fc10a20ae0621c",
+ "size": 468,
+ "uri": "https://{canondata_backend}/1942278/c9207b6ddde3382e4a3ca049e51133ef7ad47ee0/resource.tar.gz#test.test_select_yql-from_values_ordered_columns-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-from_values_ordered_columns-default.txt-Results]": [
+ {
+ "checksum": "8b4564d53149a88c92f3cf18c412ab92",
+ "size": 1181,
+ "uri": "https://{canondata_backend}/1942278/c9207b6ddde3382e4a3ca049e51133ef7ad47ee0/resource.tar.gz#test.test_select_yql-from_values_ordered_columns-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[side_effects-logical_bools_unreachable-default.txt-Debug]": [
{
"checksum": "2299b8c92f2489fa453539c267109e68",
diff --git a/yql/essentials/tests/sql/minirun/part9/canondata/result.json b/yql/essentials/tests/sql/minirun/part9/canondata/result.json
index 9557d452799..49baae7f93a 100644
--- a/yql/essentials/tests/sql/minirun/part9/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part9/canondata/result.json
@@ -1552,6 +1552,20 @@
"uri": "https://{canondata_backend}/1937429/18d53b6e51c2d67a4139b1541925383ccf907eb1/resource.tar.gz#test.test_select_op-select_op_order_by-default.txt-Results_/results.txt"
}
],
+ "test.test[select_yql-minimal_ordered_columns-default.txt-Debug]": [
+ {
+ "checksum": "c327584809dd6bef576a108f4cee6e54",
+ "size": 311,
+ "uri": "https://{canondata_backend}/1942100/4c788a4446adc09f2f36b907fe8324cbf42f7287/resource.tar.gz#test.test_select_yql-minimal_ordered_columns-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[select_yql-minimal_ordered_columns-default.txt-Results]": [
+ {
+ "checksum": "3432be66794780aaeaad6d43b71e08e5",
+ "size": 695,
+ "uri": "https://{canondata_backend}/1942100/4c788a4446adc09f2f36b907fe8324cbf42f7287/resource.tar.gz#test.test_select_yql-minimal_ordered_columns-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[subselect-offset-default.txt-Debug]": [
{
"checksum": "407648bc04bab290eacbdbee1b1eeb91",
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json
index 4646fb26ee0..c3a4b4510ab 100644
--- a/yql/essentials/tests/sql/sql2yql/canondata/result.json
+++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json
@@ -8154,6 +8154,55 @@
"uri": "https://{canondata_backend}/1942671/82ed7d067ef141d1fcef204dc3b89bf4e1c70c57/resource.tar.gz#test_sql2yql.test_select_op-select_op_precedence_named_node_/sql.yql"
}
],
+ "test_sql2yql.test[select_yql-alias]": [
+ {
+ "checksum": "33dc55c0790d7112a27e081a48a228ce",
+ "size": 1849,
+ "uri": "https://{canondata_backend}/1881367/bd3634d7029be128790526e9f6936e9d4e62611f/resource.tar.gz#test_sql2yql.test_select_yql-alias_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-expr]": [
+ {
+ "checksum": "c3970c80cdc82b4a685c8639427d3f0a",
+ "size": 751,
+ "uri": "https://{canondata_backend}/1881367/bd3634d7029be128790526e9f6936e9d4e62611f/resource.tar.gz#test_sql2yql.test_select_yql-expr_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-from_anon_values]": [
+ {
+ "checksum": "e5e204d08da35e20051a79a5476d7dcb",
+ "size": 763,
+ "uri": "https://{canondata_backend}/1881367/bd3634d7029be128790526e9f6936e9d4e62611f/resource.tar.gz#test_sql2yql.test_select_yql-from_anon_values_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-from_values]": [
+ {
+ "checksum": "0c824a5c49577fd20991e97f58b8f20f",
+ "size": 893,
+ "uri": "https://{canondata_backend}/1946324/49df8639d304dfe2c977bad20e3580e113ec0014/resource.tar.gz#test_sql2yql.test_select_yql-from_values_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-from_values_ordered_columns]": [
+ {
+ "checksum": "5126b24f43de5eb7717bef58a6a7d4e0",
+ "size": 965,
+ "uri": "https://{canondata_backend}/1946324/49df8639d304dfe2c977bad20e3580e113ec0014/resource.tar.gz#test_sql2yql.test_select_yql-from_values_ordered_columns_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-minimal]": [
+ {
+ "checksum": "45a1f9a2ca783e3861f51241f57039b0",
+ "size": 588,
+ "uri": "https://{canondata_backend}/1881367/bd3634d7029be128790526e9f6936e9d4e62611f/resource.tar.gz#test_sql2yql.test_select_yql-minimal_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[select_yql-minimal_ordered_columns]": [
+ {
+ "checksum": "3f8a6c6437c3acb562c0a0adabc9aa0d",
+ "size": 660,
+ "uri": "https://{canondata_backend}/1130705/d43abb2de178f94a1126902afd1e77e6bd8e8734/resource.tar.gz#test_sql2yql.test_select_yql-minimal_ordered_columns_/sql.yql"
+ }
+ ],
"test_sql2yql.test[seq_mode-shared_named_expr]": [
{
"checksum": "1bc276a9cb08e31273e24da9ad90582d",
@@ -13154,6 +13203,41 @@
"uri": "file://test_sql_format.test_select_op-select_op_precedence_named_node_/formatted.sql"
}
],
+ "test_sql_format.test[select_yql-alias]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-alias_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-expr]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-expr_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-from_anon_values]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-from_anon_values_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-from_values]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-from_values_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-from_values_ordered_columns]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-from_values_ordered_columns_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-minimal]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-minimal_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[select_yql-minimal_ordered_columns]": [
+ {
+ "uri": "file://test_sql_format.test_select_yql-minimal_ordered_columns_/formatted.sql"
+ }
+ ],
"test_sql_format.test[seq_mode-shared_named_expr]": [
{
"uri": "file://test_sql_format.test_seq_mode-shared_named_expr_/formatted.sql"
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-alias_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-alias_/formatted.sql
new file mode 100644
index 00000000000..3959ed02ac0
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-alias_/formatted.sql
@@ -0,0 +1,21 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1,
+ 2
+;
+
+SELECT
+ 1 AS x,
+ 2
+;
+
+SELECT
+ 1,
+ 2 AS y
+;
+
+SELECT
+ 1 AS x,
+ 2 AS y
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-expr_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-expr_/formatted.sql
new file mode 100644
index 00000000000..f1c2ef44266
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-expr_/formatted.sql
@@ -0,0 +1,7 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1 AS c1,
+ 2 * 2 - 2 AS c2,
+ Length('333') AS c3
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_anon_values_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_anon_values_/formatted.sql
new file mode 100644
index 00000000000..cd0cfa7ecda
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_anon_values_/formatted.sql
@@ -0,0 +1,8 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1
+FROM (
+ VALUES
+ (1)
+);
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_/formatted.sql
new file mode 100644
index 00000000000..53f93c26d72
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_/formatted.sql
@@ -0,0 +1,14 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ a,
+ b
+FROM (
+ VALUES
+ (1, '1'),
+ (2, '2'),
+ (3, '3')
+) AS x (
+ a,
+ b
+);
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_ordered_columns_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_ordered_columns_/formatted.sql
new file mode 100644
index 00000000000..bd2f2188698
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-from_values_ordered_columns_/formatted.sql
@@ -0,0 +1,15 @@
+PRAGMA YqlSelect = 'force';
+PRAGMA OrderedColumns;
+
+SELECT
+ a,
+ b
+FROM (
+ VALUES
+ (1, '1'),
+ (2, '2'),
+ (3, '3')
+) AS x (
+ a,
+ b
+);
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_/formatted.sql
new file mode 100644
index 00000000000..883e0d271da
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_/formatted.sql
@@ -0,0 +1,5 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_ordered_columns_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_ordered_columns_/formatted.sql
new file mode 100644
index 00000000000..b6e3e90e8e1
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_select_yql-minimal_ordered_columns_/formatted.sql
@@ -0,0 +1,6 @@
+PRAGMA YqlSelect = 'force';
+PRAGMA OrderedColumns;
+
+SELECT
+ 1
+;
diff --git a/yql/essentials/tests/sql/suites/select_yql/alias.yql b/yql/essentials/tests/sql/suites/select_yql/alias.yql
new file mode 100644
index 00000000000..3959ed02ac0
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/alias.yql
@@ -0,0 +1,21 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1,
+ 2
+;
+
+SELECT
+ 1 AS x,
+ 2
+;
+
+SELECT
+ 1,
+ 2 AS y
+;
+
+SELECT
+ 1 AS x,
+ 2 AS y
+;
diff --git a/yql/essentials/tests/sql/suites/select_yql/default.cfg b/yql/essentials/tests/sql/suites/select_yql/default.cfg
new file mode 100644
index 00000000000..617474f8d63
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/default.cfg
@@ -0,0 +1 @@
+langver 2025.04
diff --git a/yql/essentials/tests/sql/suites/select_yql/expr.yql b/yql/essentials/tests/sql/suites/select_yql/expr.yql
new file mode 100644
index 00000000000..f1c2ef44266
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/expr.yql
@@ -0,0 +1,7 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1 AS c1,
+ 2 * 2 - 2 AS c2,
+ Length('333') AS c3
+;
diff --git a/yql/essentials/tests/sql/suites/select_yql/from_anon_values.yql b/yql/essentials/tests/sql/suites/select_yql/from_anon_values.yql
new file mode 100644
index 00000000000..cd0cfa7ecda
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/from_anon_values.yql
@@ -0,0 +1,8 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1
+FROM (
+ VALUES
+ (1)
+);
diff --git a/yql/essentials/tests/sql/suites/select_yql/from_values.yql b/yql/essentials/tests/sql/suites/select_yql/from_values.yql
new file mode 100644
index 00000000000..53f93c26d72
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/from_values.yql
@@ -0,0 +1,14 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ a,
+ b
+FROM (
+ VALUES
+ (1, '1'),
+ (2, '2'),
+ (3, '3')
+) AS x (
+ a,
+ b
+);
diff --git a/yql/essentials/tests/sql/suites/select_yql/from_values_ordered_columns.yql b/yql/essentials/tests/sql/suites/select_yql/from_values_ordered_columns.yql
new file mode 100644
index 00000000000..bd2f2188698
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/from_values_ordered_columns.yql
@@ -0,0 +1,15 @@
+PRAGMA YqlSelect = 'force';
+PRAGMA OrderedColumns;
+
+SELECT
+ a,
+ b
+FROM (
+ VALUES
+ (1, '1'),
+ (2, '2'),
+ (3, '3')
+) AS x (
+ a,
+ b
+);
diff --git a/yql/essentials/tests/sql/suites/select_yql/minimal.yql b/yql/essentials/tests/sql/suites/select_yql/minimal.yql
new file mode 100644
index 00000000000..883e0d271da
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/minimal.yql
@@ -0,0 +1,5 @@
+PRAGMA YqlSelect = 'force';
+
+SELECT
+ 1
+;
diff --git a/yql/essentials/tests/sql/suites/select_yql/minimal_ordered_columns.yql b/yql/essentials/tests/sql/suites/select_yql/minimal_ordered_columns.yql
new file mode 100644
index 00000000000..b6e3e90e8e1
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/select_yql/minimal_ordered_columns.yql
@@ -0,0 +1,6 @@
+PRAGMA YqlSelect = 'force';
+PRAGMA OrderedColumns;
+
+SELECT
+ 1
+;