summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoratarasov5 <[email protected]>2026-07-17 11:28:08 +0300
committeratarasov5 <[email protected]>2026-07-17 18:57:11 +0300
commit0e52f04caa16030b7e49090548b32fb50be2ef81 (patch)
tree5cb5ee4fdfc60626e08275b6a82bb9c1a98de74a
parent031e2b7b3e7e5e9a45058b929173e2c64384d4d3 (diff)
YQL-21315: Block variant item implementation
BlockVariantItem implementation commit_hash:f35a597dee3dc67ebc5be599ed9d379d7ad57802
-rw-r--r--yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp3
-rw-r--r--yql/essentials/core/type_ann/type_ann_blocks.cpp58
-rw-r--r--yql/essentials/core/type_ann/type_ann_blocks.h1
-rw-r--r--yql/essentials/core/type_ann/type_ann_core.cpp1
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_block_variant_item.cpp125
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_block_variant_item.h9
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_factory.cpp2
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_varitem.cpp23
-rw-r--r--yql/essentials/minikql/comp_nodes/ut/mkql_block_variant_item_ut.cpp312
-rw-r--r--yql/essentials/minikql/comp_nodes/ut/mkql_variant_ut.cpp157
-rw-r--r--yql/essentials/minikql/comp_nodes/ut/ya.make.inc1
-rw-r--r--yql/essentials/minikql/comp_nodes/ya.make.inc1
-rw-r--r--yql/essentials/minikql/mkql_program_builder.cpp18
-rw-r--r--yql/essentials/minikql/mkql_program_builder.h1
-rw-r--r--yql/essentials/minikql/mkql_runtime_version.h2
-rw-r--r--yql/essentials/providers/common/mkql/yql_provider_mkql.cpp5
-rw-r--r--yql/essentials/tests/sql/minirun/part0/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part13/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part15/canondata/result.json42
-rw-r--r--yql/essentials/tests/sql/minirun/part4/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part6/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json72
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_/formatted.sql12
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_double_optional_/formatted.sql15
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_optional_/formatted.sql13
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_/formatted.sql13
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_double_optional_/formatted.sql15
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_optional_/formatted.sql13
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_struct.yql8
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_struct_double_optional.yql11
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_struct_optional.yql9
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_tuple.yql9
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_tuple_double_optional.yql11
-rw-r--r--yql/essentials/tests/sql/suites/blocks/variant_item_tuple_optional.yql9
34 files changed, 1047 insertions, 8 deletions
diff --git a/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp b/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
index e3878dc97f3..b4049e8b405 100644
--- a/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
+++ b/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
@@ -6478,7 +6478,8 @@ private:
bool isSuitableGuess = NKikimr::NMiniKQL::RuntimeVersion >= 79 && node->IsCallable("Guess");
bool isSuitableWay = NKikimr::NMiniKQL::RuntimeVersion >= 80 && node->IsCallable("Way");
bool isSuitableVariant = NKikimr::NMiniKQL::RuntimeVersion >= 81 && node->IsCallable("Variant");
- if (node->IsList() || rewriteAsIs || isSuitableGuess || isSuitableWay || isSuitableVariant ||
+ bool isSuitableVariantItem = NKikimr::NMiniKQL::RuntimeVersion >= 82 && node->IsCallable("VariantItem");
+ if (node->IsList() || rewriteAsIs || isSuitableGuess || isSuitableWay || isSuitableVariant || isSuitableVariantItem ||
node->IsCallable({"DecimalMul", "DecimalDiv", "DecimalMod", "And", "Or", "Xor", "Not", "Coalesce", "Exists", "If", "Just", "AsStruct", "Member", "Nth", "ToPg", "FromPg", "PgResolvedCall", "PgResolvedOp"})) {
if (node->IsCallable() && !IsSupportedAsBlockType(node->Pos(), *node->GetTypeAnn(), Ctx_, Types_, /*reportUnspported=*/true)) {
YQL_CLOG(TRACE, CorePeepHole) << Log(node) << "Type are not supported";
diff --git a/yql/essentials/core/type_ann/type_ann_blocks.cpp b/yql/essentials/core/type_ann/type_ann_blocks.cpp
index dd5e3dae0a4..755c6704afb 100644
--- a/yql/essentials/core/type_ann/type_ann_blocks.cpp
+++ b/yql/essentials/core/type_ann/type_ann_blocks.cpp
@@ -59,6 +59,32 @@ bool EnsurePayloadMatchesAlternative(const TExprNode& payloadNode, const TTypeAn
return false;
}
+const TTypeAnnotationNode* ResolveCommonVariantAlternativeType(const TExprNode& input, const TVariantExprType& variantType, TContext& ctx) {
+ if (variantType.GetUnderlyingType()->GetKind() == ETypeAnnotationKind::Tuple) {
+ auto tupleType = variantType.GetUnderlyingType()->Cast<TTupleExprType>();
+ auto firstType = tupleType->GetItems()[0];
+ for (size_t i = 1; i < tupleType->GetSize(); ++i) {
+ if (firstType != tupleType->GetItems()[i]) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input.Pos()), TStringBuilder()
+ << "All Variant item types should be equal: " << GetTypeDiff(*firstType, *tupleType->GetItems()[i])));
+ return nullptr;
+ }
+ }
+ return firstType;
+ } else {
+ auto structType = variantType.GetUnderlyingType()->Cast<TStructExprType>();
+ auto firstType = structType->GetItems()[0]->GetItemType();
+ for (size_t i = 1; i < structType->GetSize(); ++i) {
+ if (firstType != structType->GetItems()[i]->GetItemType()) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input.Pos()), TStringBuilder()
+ << "All Variant item types should be equal: " << GetTypeDiff(*firstType, *structType->GetItems()[i]->GetItemType())));
+ return nullptr;
+ }
+ }
+ return firstType;
+ }
+}
+
} // namespace
IGraphTransformer::TStatus AsScalarWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
@@ -502,6 +528,38 @@ IGraphTransformer::TStatus BlockVariantWrapper(const TExprNode::TPtr& input, TEx
return IGraphTransformer::TStatus::Ok;
}
+IGraphTransformer::TStatus BlockVariantItemWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ Y_UNUSED(output);
+ if (!EnsureArgsCount(*input, 1, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ auto variantNode = input->Child(0);
+ if (!EnsureBlockOrScalarType(*variantNode, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ bool isScalar;
+ const TTypeAnnotationNode* itemType = GetBlockItemType(*variantNode->GetTypeAnn(), isScalar);
+
+ bool isOptional = itemType->GetKind() == ETypeAnnotationKind::Optional;
+ const TTypeAnnotationNode* innerItemType = isOptional ? itemType->Cast<TOptionalExprType>()->GetItemType() : itemType;
+ if (!EnsureVariantType(variantNode->Pos(), *innerItemType, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ auto variantType = innerItemType->Cast<TVariantExprType>();
+
+ const TTypeAnnotationNode* alternativeType = ResolveCommonVariantAlternativeType(*input, *variantType, ctx);
+ if (!alternativeType) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ if (isOptional) {
+ alternativeType = ctx.Expr.MakeType<TOptionalExprType>(alternativeType);
+ }
+ input->SetTypeAnn(MakeBlockOrScalarType(alternativeType, isScalar, ctx.Expr));
+ return IGraphTransformer::TStatus::Ok;
+}
+
IGraphTransformer::TStatus BlockIfWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
Y_UNUSED(output);
if (!EnsureArgsCount(*input, 3U, ctx.Expr)) {
diff --git a/yql/essentials/core/type_ann/type_ann_blocks.h b/yql/essentials/core/type_ann/type_ann_blocks.h
index 44adc07d30a..781322fc345 100644
--- a/yql/essentials/core/type_ann/type_ann_blocks.h
+++ b/yql/essentials/core/type_ann/type_ann_blocks.h
@@ -19,6 +19,7 @@ namespace NYql::NTypeAnnImpl {
IGraphTransformer::TStatus BlockGuessWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockWayWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockVariantWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
+ IGraphTransformer::TStatus BlockVariantItemWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockIfWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockJustWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockAsStructWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
diff --git a/yql/essentials/core/type_ann/type_ann_core.cpp b/yql/essentials/core/type_ann/type_ann_core.cpp
index 399e1254315..3235cbfb033 100644
--- a/yql/essentials/core/type_ann/type_ann_core.cpp
+++ b/yql/essentials/core/type_ann/type_ann_core.cpp
@@ -16522,6 +16522,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["BlockGuess"] = &BlockGuessWrapper;
Functions["BlockWay"] = &BlockWayWrapper;
Functions["BlockVariant"] = &BlockVariantWrapper;
+ Functions["BlockVariantItem"] = &BlockVariantItemWrapper;
Functions["BlockIf"] = &BlockIfWrapper;
Functions["BlockJust"] = &BlockJustWrapper;
Functions["BlockAsStruct"] = &BlockAsStructWrapper;
diff --git a/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.cpp b/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.cpp
new file mode 100644
index 00000000000..82db82fa17b
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.cpp
@@ -0,0 +1,125 @@
+#include "mkql_block_variant_item.h"
+
+#include <yql/essentials/minikql/computation/mkql_block_impl.h>
+#include <yql/essentials/minikql/computation/mkql_block_reader.h>
+#include <yql/essentials/minikql/computation/mkql_computation_node_holders.h>
+#include <yql/essentials/minikql/mkql_node_builder.h>
+#include <yql/essentials/minikql/mkql_node_cast.h>
+#include <yql/essentials/public/udf/arrow/block_builder.h>
+#include <yql/essentials/public/udf/arrow/block_reader.h>
+
+namespace NKikimr::NMiniKQL {
+
+namespace {
+
+template <bool IsOptional>
+class TVariantItemBlockExec {
+public:
+ class TVariantItemKernelState: public arrow::compute::KernelState {
+ public:
+ explicit TVariantItemKernelState(TType* inputItemType)
+ : Reader_(MakeBlockReader(TTypeInfoHelper(), inputItemType))
+ {
+ }
+
+ IBlockReader& GetReader() {
+ return *Reader_;
+ }
+
+ private:
+ std::unique_ptr<IBlockReader> Reader_;
+ };
+
+ explicit TVariantItemBlockExec(TType* inputItemType, TType* resultItemType)
+ : InputItemType_(inputItemType)
+ , ResultItemType_(resultItemType)
+ {
+ }
+
+ arrow::Status Exec(arrow::compute::KernelContext* ctx, const arrow::compute::ExecBatch& batch, arrow::Datum* res) const {
+ auto& reader = static_cast<TVariantItemKernelState&>(*ctx->state()).GetReader();
+ const arrow::Datum& variantDatum = batch.values[0];
+
+ if (variantDatum.is_scalar()) {
+ *res = ConvertScalar(ResultItemType_,
+ ComputeOutputItem(reader.GetScalarItem(*variantDatum.scalar())),
+ *ctx->memory_pool());
+ return arrow::Status::OK();
+ }
+
+ MKQL_ENSURE(variantDatum.is_array(), "Expected array datum");
+ const auto& variantArrayData = variantDatum.array();
+ const size_t length = static_cast<size_t>(variantArrayData->length);
+ auto builder = NYql::NUdf::MakeArrayBuilder(TTypeInfoHelper(), ResultItemType_, *ctx->memory_pool(), length, /*pgBuilder=*/nullptr);
+ for (size_t i = 0; i < length; ++i) {
+ builder->Add(ComputeOutputItem(reader.GetItem(*variantArrayData, i)));
+ }
+ *res = builder->Build(/*finish=*/true);
+ return arrow::Status::OK();
+ }
+
+private:
+ TBlockItem ComputeOutputItem(TBlockItem blockItem) const {
+ if constexpr (IsOptional) {
+ if (!blockItem) {
+ return TBlockItem{};
+ }
+ return blockItem.GetVariantItem().MakeOptional();
+ } else {
+ return blockItem.GetVariantItem();
+ }
+ }
+
+ TType* const InputItemType_;
+ TType* const ResultItemType_;
+};
+
+template <bool IsOptional>
+std::shared_ptr<arrow::compute::ScalarKernel> MakeBlockVariantItemKernel(const TVector<TType*>& argTypes,
+ TType* resultType,
+ TType* inputItemType) {
+ using TExec = TVariantItemBlockExec<IsOptional>;
+ auto exec = std::make_shared<TExec>(
+ inputItemType,
+ AS_TYPE(TBlockType, resultType)->GetItemType());
+ auto kernel = std::make_shared<arrow::compute::ScalarKernel>(
+ ConvertToInputTypes(argTypes),
+ ConvertToOutputType(resultType),
+ [exec](arrow::compute::KernelContext* ctx, const arrow::compute::ExecBatch& batch, arrow::Datum* res) {
+ return exec->Exec(ctx, batch, res);
+ });
+ kernel->null_handling = arrow::compute::NullHandling::COMPUTED_NO_PREALLOCATE;
+ kernel->mem_allocation = arrow::compute::MemAllocation::NO_PREALLOCATE;
+ kernel->init = [inputItemType](arrow::compute::KernelContext*, const arrow::compute::KernelInitArgs&) {
+ return arrow::Result(std::make_unique<typename TExec::TVariantItemKernelState>(inputItemType));
+ };
+ return kernel;
+}
+
+} // namespace
+
+IComputationNode* WrapBlockVariantItem(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
+ MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 argument");
+
+ auto blockType = AS_TYPE(TBlockType, callable.GetInput(0).GetStaticType());
+ auto inputItemType = blockType->GetItemType();
+
+ bool isOptional;
+ auto variantItemType = UnpackOptional(inputItemType, isOptional);
+ AS_TYPE(TVariantType, variantItemType);
+
+ auto variantCompute = LocateNode(ctx.NodeLocator, callable, 0);
+ TComputationNodePtrVector argsNodes = {variantCompute};
+ TVector<TType*> argsTypes = {blockType};
+
+ auto resultType = callable.GetType()->GetReturnType();
+
+ auto kernel = isOptional
+ ? MakeBlockVariantItemKernel<true>(argsTypes, resultType, inputItemType)
+ : MakeBlockVariantItemKernel<false>(argsTypes, resultType, inputItemType);
+
+ return new TBlockFuncNode(ctx.Mutables, ctx.RuntimeSettings->DatumValidation.Get(),
+ callable.GetType()->GetName(), std::move(argsNodes), argsTypes, resultType, *kernel, kernel);
+}
+
+} // namespace NKikimr::NMiniKQL
diff --git a/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.h b/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.h
new file mode 100644
index 00000000000..78017ada20f
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/mkql_block_variant_item.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <yql/essentials/minikql/computation/mkql_computation_node.h>
+
+namespace NKikimr::NMiniKQL {
+
+IComputationNode* WrapBlockVariantItem(TCallable& callable, const TComputationNodeFactoryContext& ctx);
+
+} // namespace NKikimr::NMiniKQL
diff --git a/yql/essentials/minikql/comp_nodes/mkql_factory.cpp b/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
index f49b942dd7a..2f1fc15cc23 100644
--- a/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
+++ b/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
@@ -14,6 +14,7 @@
#include "mkql_block_getelem.h"
#include "mkql_block_guess.h"
#include "mkql_block_variant.h"
+#include "mkql_block_variant_item.h"
#include "mkql_block_way.h"
#include "mkql_block_if.h"
#include "mkql_block_just.h"
@@ -313,6 +314,7 @@ struct TCallableComputationNodeBuilderFuncMapFiller {
{"BlockExists", &WrapBlockExists},
{"BlockGuess", &WrapBlockGuess},
{"BlockVariant", &WrapBlockVariant},
+ {"BlockVariantItem", &WrapBlockVariantItem},
{"BlockWay", &WrapBlockWay},
{"BlockIf", &WrapBlockIf},
{"BlockAnd", &WrapBlockAnd},
diff --git a/yql/essentials/minikql/comp_nodes/mkql_varitem.cpp b/yql/essentials/minikql/comp_nodes/mkql_varitem.cpp
index 90e0c02bb60..ecde4b30f8a 100644
--- a/yql/essentials/minikql/comp_nodes/mkql_varitem.cpp
+++ b/yql/essentials/minikql/comp_nodes/mkql_varitem.cpp
@@ -24,11 +24,14 @@ public:
NUdf::TUnboxedValuePod DoCalculate(TComputationContext& compCtx) const {
auto var = VarNode->GetValue(compCtx);
- if (IsOptional && !var) {
- return NUdf::TUnboxedValuePod();
+ if constexpr (IsOptional) {
+ if (!var) {
+ return NUdf::TUnboxedValuePod();
+ }
+ return var.Release().GetVariantItem().Release().MakeOptional();
+ } else {
+ return var.Release().GetVariantItem().Release();
}
-
- return var.Release().GetVariantItem().Release();
}
#ifndef MKQL_DISABLE_CODEGEN
@@ -40,7 +43,7 @@ public:
const auto var = GetNodeValue(VarNode, ctx, block);
const auto done = BasicBlock::Create(context, "done", ctx.Func);
- if (IsOptional) {
+ if constexpr (IsOptional) {
const auto good = BasicBlock::Create(context, "good", ctx.Func);
const auto none = BasicBlock::Create(context, "none", ctx.Func);
@@ -66,12 +69,20 @@ public:
const uint64_t init[] = {0xFFFFFFFFFFFFFFFFULL, 0x3FFFFFFFFFFFFFFULL};
const auto mask = ConstantInt::get(valueType, APInt(128, 2, init));
const auto clean = BinaryOperator::CreateAnd(var, mask, "clean", block);
- new StoreInst(clean, pointer, block);
+ if constexpr (IsOptional) {
+ new StoreInst(MakeOptional(context, clean, block), pointer, block);
+ } else {
+ new StoreInst(clean, pointer, block);
+ }
ValueAddRef(this->RepresentationKind_, pointer, ctx, block);
BranchInst::Create(done, block);
block = box;
CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetVariantItem>(pointer, var, ctx.Codegen, block);
+ if constexpr (IsOptional) {
+ const auto load = new LoadInst(valueType, pointer, "load", block);
+ new StoreInst(MakeOptional(context, load, block), pointer, block);
+ }
BranchInst::Create(done, block);
block = done;
diff --git a/yql/essentials/minikql/comp_nodes/ut/mkql_block_variant_item_ut.cpp b/yql/essentials/minikql/comp_nodes/ut/mkql_block_variant_item_ut.cpp
new file mode 100644
index 00000000000..34042224cc4
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/ut/mkql_block_variant_item_ut.cpp
@@ -0,0 +1,312 @@
+#include <yql/essentials/minikql/comp_nodes/mkql_block_variant_item.h>
+
+#include <yql/essentials/minikql/comp_nodes/ut/mkql_block_test_helper.h>
+#include <yql/essentials/minikql/comp_nodes/ut/mkql_computation_node_ut.h>
+#include <yql/essentials/minikql/mkql_type_builder.h>
+#include <yql/essentials/utils/random_data_generator/random_data_generator.h>
+
+#include <library/cpp/random_provider/random_provider.h>
+
+#include <tuple>
+#include <variant>
+
+namespace NKikimr::NMiniKQL {
+
+using namespace NTest;
+
+namespace {
+
+constexpr size_t LargeStringLength = MaxBlockSizeInBytes / 10;
+
+template <typename TInputData, typename TExpectedData>
+void TestBlockVariantItem(const TVector<TInputData>& data, const TVector<TExpectedData>& expected) {
+ TBlockHelper().TestKernelFuzzied(data, expected, [](TSetup<false>& setup, TRuntimeNode variantValue) {
+ return setup.PgmBuilder->BlockVariantItem(variantValue);
+ });
+}
+
+template <typename... Ts>
+auto ExtractCommonAlternative(const std::variant<Ts...>& variant) {
+ return std::visit([](const auto& alternativeValue) { return alternativeValue; }, variant);
+}
+
+template <typename TVariant>
+TVector<decltype(ExtractCommonAlternative(std::declval<TVariant>()))> ExtractCommonAlternatives(const TVector<TVariant>& data) {
+ TVector<decltype(ExtractCommonAlternative(std::declval<TVariant>()))> expected;
+ expected.reserve(data.size());
+ for (const auto& variant : data) {
+ expected.push_back(ExtractCommonAlternative(variant));
+ }
+ return expected;
+}
+
+} // namespace
+
+Y_UNIT_TEST_SUITE(TMiniKQLBlockVariantItemTest) {
+
+Y_UNIT_TEST(TupleVariant_Ui32_TwoAlternatives) {
+ using TVariant = std::variant<ui32, ui32>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, 1u),
+ TVariant(std::in_place_index<1>, 2u),
+ TVariant(std::in_place_index<0>, 3u),
+ };
+ TVector<ui32> expected = {1u, 2u, 3u};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_Ui32_ThreeAlternatives) {
+ using TVariant = std::variant<ui32, ui32, ui32>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, 1u),
+ TVariant(std::in_place_index<1>, 2u),
+ TVariant(std::in_place_index<2>, 3u),
+ TVariant(std::in_place_index<0>, 4u),
+ };
+ TVector<ui32> expected = {1u, 2u, 3u, 4u};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_TString_TwoAlternatives) {
+ using TVariant = std::variant<TString, TString>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TString{"hello"}),
+ TVariant(std::in_place_index<1>, TString{"world"}),
+ TVariant(std::in_place_index<0>, TString{""}),
+ };
+ TVector<TString> expected = {"hello", "world", ""};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_NestedTuple_TwoAlternatives) {
+ using TInner = std::tuple<ui32, TString>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TInner{1u, "a"}),
+ TVariant(std::in_place_index<1>, TInner{2u, "b"}),
+ TVariant(std::in_place_index<0>, TInner{3u, "c"}),
+ };
+ TVector<TInner> expected = {TInner{1u, "a"}, TInner{2u, "b"}, TInner{3u, "c"}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_NestedVariant_TwoAlternatives) {
+ using TInner = std::variant<ui32, ui32>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TInner(std::in_place_index<0>, 1u)),
+ TVariant(std::in_place_index<1>, TInner(std::in_place_index<1>, 2u)),
+ TVariant(std::in_place_index<0>, TInner(std::in_place_index<1>, 3u)),
+ };
+ TVector<TInner> expected = ExtractCommonAlternatives(data);
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalInnerUi32_TwoAlternatives) {
+ using TInner = TMaybe<ui32>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TInner{10u}),
+ TVariant(std::in_place_index<1>, TInner{}),
+ TVariant(std::in_place_index<0>, TInner{20u}),
+ };
+ TVector<TMaybe<ui32>> expected = {TInner{10u}, TInner{}, TInner{20u}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalInnerString_TwoAlternatives) {
+ using TInner = TMaybe<TString>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TInner{"first"}),
+ TVariant(std::in_place_index<1>, TInner{}),
+ TVariant(std::in_place_index<1>, TInner{"second"}),
+ };
+ TVector<TMaybe<TString>> expected = {TInner{"first"}, TInner{}, TInner{"second"}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_Ui32_NullRows) {
+ using TVariant = std::variant<ui32, ui32>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, 1u)},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, 3u)},
+ };
+ TVector<TMaybe<ui32>> expected = {ui32{1u}, Nothing(), ui32{3u}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_TString_MixedNull) {
+ using TVariant = std::variant<TString, TString>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TString{"hello"})},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TString{"world"})},
+ TMaybe<TVariant>{},
+ };
+ TVector<TMaybe<TString>> expected = {TString{"hello"}, Nothing(), TString{"world"}, Nothing()};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_NestedTuple_MixedNull) {
+ using TInner = std::tuple<ui32, TString>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TInner{1u, "a"})},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{2u, "b"})},
+ };
+ TVector<TMaybe<TInner>> expected = {TInner{1u, "a"}, Nothing(), TInner{2u, "b"}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_OptionalInnerUi32_MixedNull) {
+ using TInner = TMaybe<ui32>;
+ using TVariant = std::variant<TInner, TInner>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TInner{10u})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TInner{})},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{20u})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{})},
+ };
+ TVector<TMaybe<TInner>> expected = {
+ TMaybe<TInner>{TInner{10u}},
+ TMaybe<TInner>{TInner{}},
+ TMaybe<TInner>{},
+ TMaybe<TInner>{TInner{20u}},
+ TMaybe<TInner>{TInner{}},
+ };
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(StructVariant_Ui32_TwoMembers) {
+ using TMemberA = NTest::TStructMember<"a", ui32>;
+ using TMemberB = NTest::TStructMember<"b", ui32>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TVariant> data = {TVariant{TMemberA{1}}, TVariant{TMemberB{2}}, TVariant{TMemberA{3}}};
+ TVector<ui32> expected = {1u, 2u, 3u};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(StructVariant_Ui32_ThreeMembers) {
+ using TMemberA = NTest::TStructMember<"a", ui32>;
+ using TMemberB = NTest::TStructMember<"b", ui32>;
+ using TMemberC = NTest::TStructMember<"c", ui32>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB, TMemberC>;
+
+ TVector<TVariant> data = {TVariant{TMemberA{1}}, TVariant{TMemberB{2}}, TVariant{TMemberC{3}}, TVariant{TMemberA{4}}};
+ TVector<ui32> expected = {1u, 2u, 3u, 4u};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(StructVariant_TString_TwoMembers) {
+ using TMemberA = NTest::TStructMember<"a", TString>;
+ using TMemberB = NTest::TStructMember<"b", TString>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TVariant> data = {TVariant{TMemberA{"hello"}}, TVariant{TMemberB{"world"}}, TVariant{TMemberA{""}}};
+ TVector<TString> expected = {"hello", "world", ""};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(StructVariant_NestedTuple_TwoMembers) {
+ using TInner = std::tuple<ui32, TString>;
+ using TMemberA = NTest::TStructMember<"a", TInner>;
+ using TMemberB = NTest::TStructMember<"b", TInner>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TVariant> data = {TVariant{TMemberA{TInner{1u, "a"}}}, TVariant{TMemberB{TInner{2u, "b"}}}};
+ TVector<TInner> expected = {TInner{1u, "a"}, TInner{2u, "b"}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalStructVariant_Ui32_MixedNull) {
+ using TMemberA = NTest::TStructMember<"a", ui32>;
+ using TMemberB = NTest::TStructMember<"b", ui32>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{TMemberA{1}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{TMemberB{2}}},
+ };
+ TVector<TMaybe<ui32>> expected = {ui32{1u}, Nothing(), ui32{2u}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalStructVariant_TString_MixedNull) {
+ using TMemberA = NTest::TStructMember<"a", TString>;
+ using TMemberB = NTest::TStructMember<"b", TString>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{TMemberA{"hello"}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{TMemberB{"world"}}},
+ };
+ TVector<TMaybe<TString>> expected = {TString{"hello"}, Nothing(), TString{"world"}};
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(OptionalStructVariant_OptionalInnerUi32_MixedNull) {
+ using TInner = TMaybe<ui32>;
+ using TMemberA = NTest::TStructMember<"a", TInner>;
+ using TMemberB = NTest::TStructMember<"b", TInner>;
+ using TVariant = NTest::TStructVariant<TMemberA, TMemberB>;
+
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{TMemberA{TInner{10u}}}},
+ TMaybe<TVariant>{TVariant{TMemberA{TInner{}}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{TMemberB{TInner{20u}}}},
+ TMaybe<TVariant>{TVariant{TMemberB{TInner{}}}},
+ };
+ TVector<TMaybe<TInner>> expected = {
+ TMaybe<TInner>{TInner{10u}},
+ TMaybe<TInner>{TInner{}},
+ TMaybe<TInner>{},
+ TMaybe<TInner>{TInner{20u}},
+ TMaybe<TInner>{TInner{}},
+ };
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_Ui32_Fuzzied) {
+ using TVariant = std::variant<ui32, ui32>;
+ auto rng = CreateDeterministicRandomProvider(200);
+ const TVector<TVariant> data = NYql::GenerateRandomData<TVariant>(rng, 7);
+ const TVector<ui32> expected = ExtractCommonAlternatives(data);
+ TestBlockVariantItem(data, expected);
+}
+
+Y_UNIT_TEST(TupleVariant_LargeString_ChunkedOutput) {
+ using TVariant = std::variant<TString, TString>;
+ auto rng = CreateDeterministicRandomProvider(201);
+ TBlockHelper helper;
+
+ helper.WithScopedFuzzers([&]() {
+ const NYql::TGeneratorSettings<TVariant> settings{
+ .Weights = {1.0, 1.0},
+ .InnerSettings = {
+ NYql::TGeneratorSettings<TString>{.MinSize = 0, .MaxSize = LargeStringLength},
+ NYql::TGeneratorSettings<TString>{.MinSize = 0, .MaxSize = LargeStringLength},
+ },
+ };
+ const TVector<TVariant> data = NYql::GenerateRandomData<TVariant>(rng, settings, 50);
+ const TVector<TString> expected = ExtractCommonAlternatives(data);
+
+ helper.RunNodeOverWideStream(
+ expected,
+ [](TSetup<false>& setup, TRuntimeNode variantBlock) {
+ return setup.PgmBuilder->BlockVariantItem(variantBlock);
+ },
+ data);
+ });
+}
+
+} // Y_UNIT_TEST_SUITE(TMiniKQLBlockVariantItemTest)
+
+} // namespace NKikimr::NMiniKQL
diff --git a/yql/essentials/minikql/comp_nodes/ut/mkql_variant_ut.cpp b/yql/essentials/minikql/comp_nodes/ut/mkql_variant_ut.cpp
index 83652eb40d8..81083d6fa72 100644
--- a/yql/essentials/minikql/comp_nodes/ut/mkql_variant_ut.cpp
+++ b/yql/essentials/minikql/comp_nodes/ut/mkql_variant_ut.cpp
@@ -8,6 +8,29 @@
namespace NKikimr {
namespace NMiniKQL {
+namespace {
+
+template <typename T, size_t>
+using TRepeatedAlternative = T;
+
+template <typename T, size_t... Indices>
+auto MakeRepeatedVariant(std::index_sequence<Indices...>) -> std::variant<TRepeatedAlternative<T, Indices>...>;
+
+template <typename T, size_t Count>
+using TRepeatedVariant = decltype(MakeRepeatedVariant<T>(std::make_index_sequence<Count>{}));
+
+template <bool LLVM, typename TInputData, typename TExpectedData>
+void TestVariantItemRowWise(TSetup<LLVM>& setup, const TVector<TInputData>& data, const TVector<TExpectedData>& expected) {
+ TProgramBuilder& pb = *setup.PgmBuilder;
+ const auto list = NTest::ConvertValueToLiteralNode(pb, data);
+ const auto pgmReturn = pb.Map(list, [&](TRuntimeNode item) { return pb.VariantItem(item); });
+
+ const auto graph = setup.BuildGraph(pgmReturn);
+ AssertUnboxedValueElementEqual(graph->GetValue(), expected);
+}
+
+} // namespace
+
Y_UNIT_TEST_SUITE(TMiniKQLVariantTest) {
Y_UNIT_TEST_LLVM(TestGuessTuple) {
TSetup<LLVM> setup;
@@ -365,6 +388,140 @@ Y_UNIT_TEST_LLVM(TestItemInMap) {
UNIT_ASSERT(!iterator.Next(item));
}
+Y_UNIT_TEST_LLVM(TestVariantItemTupleOptionalInnerOptional) {
+ TSetup<LLVM> setup;
+
+ using TInner = TMaybe<ui32>;
+ using TVariant = std::variant<TInner, TInner>;
+ const TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TInner{10U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, TInner{})},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{20U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{})},
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<TMaybe<ui32>>>{
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{10U}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ TMaybe<TMaybe<ui32>>{},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{20U}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ });
+}
+
+Y_UNIT_TEST_LLVM(TestVariantItemTupleOptionalInnerOptional65Alternatives) {
+ TSetup<LLVM> setup;
+
+ using TInner = TMaybe<ui32>;
+ using TVariant = TRepeatedVariant<TInner, 65>;
+ const TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{10U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, TInner{})},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant(std::in_place_index<7>, TInner{20U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<7>, TInner{})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<64>, TInner{30U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<64>, TInner{})},
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<TMaybe<ui32>>>{
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{10U}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ TMaybe<TMaybe<ui32>>{},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{20U}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{30U}},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ });
+}
+
+Y_UNIT_TEST_LLVM(TestVariantItemOptionalVariantTwoAlternatives) {
+ TSetup<LLVM> setup;
+
+ using TVariant = std::variant<ui32, ui32>;
+ const TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<0>, ui32{10U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, ui32{20U})},
+ TMaybe<TVariant>{},
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<ui32>>{
+ TMaybe<ui32>{10U},
+ TMaybe<ui32>{20U},
+ Nothing(),
+ });
+}
+
+Y_UNIT_TEST_LLVM(TestVariantItemOptionalVariant65Alternatives) {
+ TSetup<LLVM> setup;
+
+ using TVariant = TRepeatedVariant<ui32, 65>;
+ const TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant(std::in_place_index<1>, ui32{10U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<7>, ui32{20U})},
+ TMaybe<TVariant>{TVariant(std::in_place_index<64>, ui32{30U})},
+ TMaybe<TVariant>{},
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<ui32>>{
+ TMaybe<ui32>{10U},
+ TMaybe<ui32>{20U},
+ TMaybe<ui32>{30U},
+ Nothing(),
+ });
+}
+
+Y_UNIT_TEST_LLVM(TestVariantItemVariantOptionalItemTwoAlternatives) {
+ TSetup<LLVM> setup;
+
+ using TInner = TMaybe<ui32>;
+ using TVariant = std::variant<TInner, TInner>;
+ const TVector<TVariant> data = {
+ TVariant(std::in_place_index<0>, TInner{10U}),
+ TVariant(std::in_place_index<0>, TInner{}),
+ TVariant(std::in_place_index<1>, TInner{20U}),
+ TVariant(std::in_place_index<1>, TInner{}),
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<ui32>>{
+ TMaybe<ui32>{10U},
+ TMaybe<ui32>{},
+ TMaybe<ui32>{20U},
+ TMaybe<ui32>{},
+ });
+}
+
+Y_UNIT_TEST_LLVM(TestVariantItemVariantOptionalItem65Alternatives) {
+ TSetup<LLVM> setup;
+
+ using TInner = TMaybe<ui32>;
+ using TVariant = TRepeatedVariant<TInner, 65>;
+ const TVector<TVariant> data = {
+ TVariant(std::in_place_index<1>, TInner{10U}),
+ TVariant(std::in_place_index<1>, TInner{}),
+ TVariant(std::in_place_index<7>, TInner{20U}),
+ TVariant(std::in_place_index<7>, TInner{}),
+ TVariant(std::in_place_index<64>, TInner{30U}),
+ TVariant(std::in_place_index<64>, TInner{}),
+ };
+
+ TestVariantItemRowWise(setup, data,
+ TVector<TMaybe<ui32>>{
+ TMaybe<ui32>{10U},
+ TMaybe<ui32>{},
+ TMaybe<ui32>{20U},
+ TMaybe<ui32>{},
+ TMaybe<ui32>{30U},
+ TMaybe<ui32>{},
+ });
+}
+
Y_UNIT_TEST_LLVM(TestGuessInMap) {
TSetup<LLVM> setup;
TProgramBuilder& pb = *setup.PgmBuilder;
diff --git a/yql/essentials/minikql/comp_nodes/ut/ya.make.inc b/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
index 7cacc9f952a..9ff32b04877 100644
--- a/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
+++ b/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
@@ -43,6 +43,7 @@ SET(ORIG_SOURCES
mkql_block_variant_ut.cpp
mkql_block_uuid_ut.cpp
mkql_block_dynumber_ut.cpp
+ mkql_block_variant_item_ut.cpp
mkql_block_variant_node_ut.cpp
mkql_blocks_ut.cpp
mkql_callable_ut.cpp
diff --git a/yql/essentials/minikql/comp_nodes/ya.make.inc b/yql/essentials/minikql/comp_nodes/ya.make.inc
index 1386f6b5866..224e9803b43 100644
--- a/yql/essentials/minikql/comp_nodes/ya.make.inc
+++ b/yql/essentials/minikql/comp_nodes/ya.make.inc
@@ -21,6 +21,7 @@ SET(ORIG_SOURCES
mkql_block_getelem.cpp
mkql_block_guess.cpp
mkql_block_variant.cpp
+ mkql_block_variant_item.cpp
mkql_block_way.cpp
mkql_block_if.cpp
mkql_block_just.cpp
diff --git a/yql/essentials/minikql/mkql_program_builder.cpp b/yql/essentials/minikql/mkql_program_builder.cpp
index 87ed9bbbeb7..68c92cda92c 100644
--- a/yql/essentials/minikql/mkql_program_builder.cpp
+++ b/yql/essentials/minikql/mkql_program_builder.cpp
@@ -6422,6 +6422,24 @@ TRuntimeNode TProgramBuilder::BlockVariant(TRuntimeNode item, const std::string_
return TRuntimeNode(callableBuilder.Build(), /*isImmediate=*/false);
}
+TRuntimeNode TProgramBuilder::BlockVariantItem(TRuntimeNode variant) {
+ if constexpr (RuntimeVersion < 82) {
+ THROW yexception() << "Runtime version (" << RuntimeVersion << ") too old for " << __func__;
+ }
+ auto blockType = AS_TYPE(TBlockType, variant.GetStaticType());
+ auto inputItemType = blockType->GetItemType();
+ bool isOptional;
+ auto unpacked = UnpackOptional(inputItemType, isOptional);
+ auto variantType = AS_TYPE(TVariantType, unpacked);
+ auto alternativeType = variantType->GetAlternativeType(0);
+ auto itemReturnType = isOptional ? NewOptionalType(alternativeType) : alternativeType;
+ auto returnType = NewBlockType(itemReturnType, blockType->GetShape());
+
+ TCallableBuilder callableBuilder(Env_, __func__, returnType);
+ callableBuilder.Add(variant);
+ return TRuntimeNode(callableBuilder.Build(), /*isImmediate=*/false);
+}
+
TRuntimeNode TProgramBuilder::BlockIf(TRuntimeNode condition, TRuntimeNode thenBranch, TRuntimeNode elseBranch) {
const auto conditionType = AS_TYPE(TBlockType, condition.GetStaticType());
MKQL_ENSURE(AS_TYPE(TDataType, conditionType->GetItemType())->GetSchemeType() == NUdf::TDataType<bool>::Id,
diff --git a/yql/essentials/minikql/mkql_program_builder.h b/yql/essentials/minikql/mkql_program_builder.h
index 32fccf7c7c8..79369bf401e 100644
--- a/yql/essentials/minikql/mkql_program_builder.h
+++ b/yql/essentials/minikql/mkql_program_builder.h
@@ -307,6 +307,7 @@ public:
TRuntimeNode BlockWay(TRuntimeNode variant);
TRuntimeNode BlockVariant(TRuntimeNode item, ui32 tupleIndex, TType* variantType);
TRuntimeNode BlockVariant(TRuntimeNode item, const std::string_view& memberName, TType* variantType);
+ TRuntimeNode BlockVariantItem(TRuntimeNode variant);
TRuntimeNode BlockIf(TRuntimeNode condition, TRuntimeNode thenBranch, TRuntimeNode elseBranch);
TRuntimeNode BlockJust(TRuntimeNode data);
diff --git a/yql/essentials/minikql/mkql_runtime_version.h b/yql/essentials/minikql/mkql_runtime_version.h
index c1fec777ada..2f2c454ef19 100644
--- a/yql/essentials/minikql/mkql_runtime_version.h
+++ b/yql/essentials/minikql/mkql_runtime_version.h
@@ -28,7 +28,7 @@ namespace NKikimr::NMiniKQL {
// 1. Bump this version every time incompatible runtime nodes are introduced.
// 2. Make sure you provide runtime node generation for previous runtime versions.
#ifndef MKQL_RUNTIME_VERSION
- #define MKQL_RUNTIME_VERSION 81U
+ #define MKQL_RUNTIME_VERSION 82U
#endif
class TRuntimeVersion {
diff --git a/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp b/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp
index 21b1137e6ef..5761f947da3 100644
--- a/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp
+++ b/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp
@@ -1023,6 +1023,11 @@ TMkqlCommonCallableCompiler::TShared::TShared() {
return ctx.ProgramBuilder.BlockVariant(payloadValue, node.Child(1)->Content(), type);
});
+ AddCallable("BlockVariantItem", [](const TExprNode& node, TMkqlBuildContext& ctx) {
+ const auto blockVariantValue = MkqlBuildExpr(*node.Child(0), ctx);
+ return ctx.ProgramBuilder.BlockVariantItem(blockVariantValue);
+ });
+
AddCallable("Visit", [](const TExprNode& node, TMkqlBuildContext& ctx) {
const auto variantObj = MkqlBuildExpr(node.Head(), ctx);
const auto type = node.Head().GetTypeAnn()->Cast<TVariantExprType>();
diff --git a/yql/essentials/tests/sql/minirun/part0/canondata/result.json b/yql/essentials/tests/sql/minirun/part0/canondata/result.json
index 90221a7ca8b..d8545e051d0 100644
--- a/yql/essentials/tests/sql/minirun/part0/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part0/canondata/result.json
@@ -405,6 +405,27 @@
"uri": "https://{canondata_backend}/1937424/d02bb271c9ab67557c669f52bc80a0a698a8a91e/resource.tar.gz#test.test_blocks-if-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-variant_item_tuple-default.txt-Debug]": [
+ {
+ "checksum": "c51dc11884e895fa059d737ee2c9c045",
+ "size": 580,
+ "uri": "https://{canondata_backend}/1946324/b9fe6a5a945e23db9e4ac84e6edac51b4a9a7c92/resource.tar.gz#test.test_blocks-variant_item_tuple-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple-default.txt-Peephole]": [
+ {
+ "checksum": "13d96e6fdc5acc50cce29fb454ffcffd",
+ "size": 744,
+ "uri": "https://{canondata_backend}/1946324/b9fe6a5a945e23db9e4ac84e6edac51b4a9a7c92/resource.tar.gz#test.test_blocks-variant_item_tuple-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple-default.txt-Results]": [
+ {
+ "checksum": "752787be97605457fe04ca12d9a6ce54",
+ "size": 839,
+ "uri": "https://{canondata_backend}/1946324/b9fe6a5a945e23db9e4ac84e6edac51b4a9a7c92/resource.tar.gz#test.test_blocks-variant_item_tuple-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[column_order-values-default.txt-Debug]": [
{
"checksum": "ec4c1859bead1f9b88e6239fe8d6addb",
diff --git a/yql/essentials/tests/sql/minirun/part13/canondata/result.json b/yql/essentials/tests/sql/minirun/part13/canondata/result.json
index 82cf4f95a1c..1a544f54aa2 100644
--- a/yql/essentials/tests/sql/minirun/part13/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part13/canondata/result.json
@@ -383,6 +383,27 @@
"uri": "https://{canondata_backend}/1871002/12af85b5b27eccd0e3391513f34ad8082ac84843/resource.tar.gz#test.test_blocks-or-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-variant_item_tuple_double_optional-default.txt-Debug]": [
+ {
+ "checksum": "6fcb3db2f3daa7aa700968df1e76ba04",
+ "size": 719,
+ "uri": "https://{canondata_backend}/1936273/854abe42453705136666cc5b313bfc721a48afea/resource.tar.gz#test.test_blocks-variant_item_tuple_double_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple_double_optional-default.txt-Peephole]": [
+ {
+ "checksum": "1b2d155e8eff24e51aae77f266b10790",
+ "size": 881,
+ "uri": "https://{canondata_backend}/1936273/854abe42453705136666cc5b313bfc721a48afea/resource.tar.gz#test.test_blocks-variant_item_tuple_double_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple_double_optional-default.txt-Results]": [
+ {
+ "checksum": "86ee982d1332b88e012416d3bc5d0a88",
+ "size": 1614,
+ "uri": "https://{canondata_backend}/1925821/46269e5add3027e8022118942f0b2ec4a7d413c8/resource.tar.gz#test.test_blocks-variant_item_tuple_double_optional-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-xor_scalar-default.txt-Debug]": [
{
"checksum": "01875c4a7756e30537fbaa02d4b70d91",
diff --git a/yql/essentials/tests/sql/minirun/part15/canondata/result.json b/yql/essentials/tests/sql/minirun/part15/canondata/result.json
index fe0b1cd7794..daaf80a7a77 100644
--- a/yql/essentials/tests/sql/minirun/part15/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part15/canondata/result.json
@@ -272,6 +272,48 @@
"uri": "https://{canondata_backend}/1777230/1de313d944cae10d4384ec6656e7270944aba259/resource.tar.gz#test.test_blocks-or_opt-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-variant_item_struct_double_optional-default.txt-Debug]": [
+ {
+ "checksum": "d7ec18fc97a188f3846ea4756e9709ee",
+ "size": 736,
+ "uri": "https://{canondata_backend}/1817427/438cafea5ac685cff797d9f5e48093ac686b940d/resource.tar.gz#test.test_blocks-variant_item_struct_double_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct_double_optional-default.txt-Peephole]": [
+ {
+ "checksum": "a40b5a9d4491f3bddaf50e8e867d293f",
+ "size": 898,
+ "uri": "https://{canondata_backend}/1817427/438cafea5ac685cff797d9f5e48093ac686b940d/resource.tar.gz#test.test_blocks-variant_item_struct_double_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct_double_optional-default.txt-Results]": [
+ {
+ "checksum": "86ee982d1332b88e012416d3bc5d0a88",
+ "size": 1614,
+ "uri": "https://{canondata_backend}/1871002/3c6c923fa59fd1bab5a053dabe0a04b0e87dc418/resource.tar.gz#test.test_blocks-variant_item_struct_double_optional-default.txt-Results_/results.txt"
+ }
+ ],
+ "test.test[blocks-variant_item_struct_optional-default.txt-Debug]": [
+ {
+ "checksum": "95682f190c7e476bf8b347aed03248f8",
+ "size": 569,
+ "uri": "https://{canondata_backend}/1817427/438cafea5ac685cff797d9f5e48093ac686b940d/resource.tar.gz#test.test_blocks-variant_item_struct_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct_optional-default.txt-Peephole]": [
+ {
+ "checksum": "b37e9c2fd12bd6e6eeaf588db65e2842",
+ "size": 729,
+ "uri": "https://{canondata_backend}/1817427/438cafea5ac685cff797d9f5e48093ac686b940d/resource.tar.gz#test.test_blocks-variant_item_struct_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct_optional-default.txt-Results]": [
+ {
+ "checksum": "576dc6ba6fccf551cbdb2bd0d9a5ffdd",
+ "size": 1084,
+ "uri": "https://{canondata_backend}/1784117/cb5e9e20f085a53076de3c3accaa5636a96c40af/resource.tar.gz#test.test_blocks-variant_item_struct_optional-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[case-case_opt_cond-default.txt-Debug]": [
{
"checksum": "4fd4460b8d2584006fe79cb1cb1e538e",
diff --git a/yql/essentials/tests/sql/minirun/part4/canondata/result.json b/yql/essentials/tests/sql/minirun/part4/canondata/result.json
index ed55d8d0852..05e92a8724e 100644
--- a/yql/essentials/tests/sql/minirun/part4/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part4/canondata/result.json
@@ -412,6 +412,27 @@
"uri": "https://{canondata_backend}/1781765/06cae327ea6c6fc81b5ac8d206f80c5f2f779d8e/resource.tar.gz#test.test_blocks-ifpresent_null_last_argument-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-variant_item_tuple_optional-default.txt-Debug]": [
+ {
+ "checksum": "62919382fcfc0dd997eead6f4e03a932",
+ "size": 552,
+ "uri": "https://{canondata_backend}/1817427/09d302bef9ec38bcb4a935c4b330a4d5cd031a5c/resource.tar.gz#test.test_blocks-variant_item_tuple_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple_optional-default.txt-Peephole]": [
+ {
+ "checksum": "c4bdf988655437c82a6bbe85e4455d85",
+ "size": 712,
+ "uri": "https://{canondata_backend}/1817427/09d302bef9ec38bcb4a935c4b330a4d5cd031a5c/resource.tar.gz#test.test_blocks-variant_item_tuple_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_tuple_optional-default.txt-Results]": [
+ {
+ "checksum": "576dc6ba6fccf551cbdb2bd0d9a5ffdd",
+ "size": 1084,
+ "uri": "https://{canondata_backend}/1942278/1f4ac0fbebe543a2fb8b2b163831240b6d36147c/resource.tar.gz#test.test_blocks-variant_item_tuple_optional-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-xor_opt_scalar-default.txt-Debug]": [
{
"checksum": "27783c2f95862105c305c624babb7dfe",
diff --git a/yql/essentials/tests/sql/minirun/part6/canondata/result.json b/yql/essentials/tests/sql/minirun/part6/canondata/result.json
index 1639fa9c66f..dabd5d061d9 100644
--- a/yql/essentials/tests/sql/minirun/part6/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part6/canondata/result.json
@@ -447,6 +447,27 @@
"uri": "https://{canondata_backend}/1920236/cd7892eb0facff7eb2c6e0b30c741174c37d06f1/resource.tar.gz#test.test_blocks-singular_type_scalar-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-variant_item_struct-default.txt-Debug]": [
+ {
+ "checksum": "f1779ed08401f8a3cb57b7214d5bf541",
+ "size": 509,
+ "uri": "https://{canondata_backend}/1784117/485dcef955bd6d323f9158400aafb28652804f8f/resource.tar.gz#test.test_blocks-variant_item_struct-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct-default.txt-Peephole]": [
+ {
+ "checksum": "8c6098c7d4d4a759a4ac3d1ebff32b05",
+ "size": 685,
+ "uri": "https://{canondata_backend}/1784117/485dcef955bd6d323f9158400aafb28652804f8f/resource.tar.gz#test.test_blocks-variant_item_struct-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-variant_item_struct-default.txt-Results]": [
+ {
+ "checksum": "30d44c52485b3e84ca77e1feace2a4b1",
+ "size": 765,
+ "uri": "https://{canondata_backend}/1784117/485dcef955bd6d323f9158400aafb28652804f8f/resource.tar.gz#test.test_blocks-variant_item_struct-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-variant_struct-default.txt-Debug]": [
{
"checksum": "5b7ccf38526cde333d2b3b92ae8acaba",
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json
index c7586d62951..f7b3d04f180 100644
--- a/yql/essentials/tests/sql/sql2yql/canondata/result.json
+++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json
@@ -2589,6 +2589,48 @@
"uri": "https://{canondata_backend}/1942100/698d95eec1cbd4e4ada1a8da105c203a7a12bb85/resource.tar.gz#test_sql2yql.test_blocks-topg_/sql.yql"
}
],
+ "test_sql2yql.test[blocks-variant_item_struct]": [
+ {
+ "checksum": "5fbb028b27ac5363cf30639b2ac3c772",
+ "size": 1300,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_struct_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-variant_item_struct_double_optional]": [
+ {
+ "checksum": "70bfa15e505250659671b7ad9fa45d5a",
+ "size": 1648,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_struct_double_optional_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-variant_item_struct_optional]": [
+ {
+ "checksum": "42f0f2748a1554da68958abe6efed009",
+ "size": 1384,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_struct_optional_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-variant_item_tuple]": [
+ {
+ "checksum": "7aa768c192d2add04dc4a5fa9076c77f",
+ "size": 1357,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_tuple_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-variant_item_tuple_double_optional]": [
+ {
+ "checksum": "fc8655b0759014f8d37956a28910e980",
+ "size": 1631,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_tuple_double_optional_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-variant_item_tuple_optional]": [
+ {
+ "checksum": "70f2c2b80c03f8ad0f43bff4bdacad54",
+ "size": 1367,
+ "uri": "https://{canondata_backend}/1936273/19a7ee3dffd801646a27a6f23176e8cc2837724d/resource.tar.gz#test_sql2yql.test_blocks-variant_item_tuple_optional_/sql.yql"
+ }
+ ],
"test_sql2yql.test[blocks-variant_struct]": [
{
"checksum": "2ce6349bc3b6a13f53f519351bcd408c",
@@ -14512,6 +14554,36 @@
"uri": "file://test_sql_format.test_blocks-topg_/formatted.sql"
}
],
+ "test_sql_format.test[blocks-variant_item_struct]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_struct_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-variant_item_struct_double_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_struct_double_optional_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-variant_item_struct_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_struct_optional_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-variant_item_tuple]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_tuple_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-variant_item_tuple_double_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_tuple_double_optional_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-variant_item_tuple_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-variant_item_tuple_optional_/formatted.sql"
+ }
+ ],
"test_sql_format.test[blocks-variant_struct]": [
{
"uri": "file://test_sql_format.test_blocks-variant_struct_/formatted.sql"
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_/formatted.sql
new file mode 100644
index 00000000000..5359e1298cf
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_/formatted.sql
@@ -0,0 +1,12 @@
+$structVariantType = Variant<a: Int32, b: Int32>;
+
+$data = [
+ <|variantValue: Variant(1, 'a', $structVariantType)|>,
+ <|variantValue: Variant(2, 'b', $structVariantType)|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($data)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_double_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_double_optional_/formatted.sql
new file mode 100644
index 00000000000..0448dd0b140
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_double_optional_/formatted.sql
@@ -0,0 +1,15 @@
+$structVariantType = Variant<a: Optional<Int32>, b: Optional<Int32>>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(Just(1), 'a', $structVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), 'a', $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant(Just(2), 'b', $structVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), 'b', $structVariantType))|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($optionalData)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_optional_/formatted.sql
new file mode 100644
index 00000000000..c360d4712ec
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_struct_optional_/formatted.sql
@@ -0,0 +1,13 @@
+$structVariantType = Variant<a: Int32, b: Int32>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, 'a', $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant(2, 'b', $structVariantType))|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($optionalData)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_/formatted.sql
new file mode 100644
index 00000000000..333807f89be
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_/formatted.sql
@@ -0,0 +1,13 @@
+$tupleVariantType = Variant<Int32, Int32>;
+
+$data = [
+ <|variantValue: Variant(1, '0', $tupleVariantType)|>,
+ <|variantValue: Variant(2, '1', $tupleVariantType)|>,
+ <|variantValue: Variant(42, '0', $tupleVariantType)|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($data)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_double_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_double_optional_/formatted.sql
new file mode 100644
index 00000000000..5f8d93539d9
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_double_optional_/formatted.sql
@@ -0,0 +1,15 @@
+$tupleVariantType = Variant<Optional<Int32>, Optional<Int32>>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(Just(1), '0', $tupleVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), '0', $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant(Just(2), '1', $tupleVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), '1', $tupleVariantType))|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($optionalData)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_optional_/formatted.sql
new file mode 100644
index 00000000000..259ec274289
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-variant_item_tuple_optional_/formatted.sql
@@ -0,0 +1,13 @@
+$tupleVariantType = Variant<Int32, Int32>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, '0', $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant(2, '1', $tupleVariantType))|>,
+];
+
+SELECT
+ Yql::VariantItem(variantValue) AS item
+FROM
+ as_table($optionalData)
+;
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_struct.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_struct.yql
new file mode 100644
index 00000000000..b825c0c4fec
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_struct.yql
@@ -0,0 +1,8 @@
+$structVariantType = Variant<a: Int32, b: Int32>;
+
+$data = [
+ <|variantValue: Variant(1, "a", $structVariantType)|>,
+ <|variantValue: Variant(2, "b", $structVariantType)|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($data);
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_struct_double_optional.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_struct_double_optional.yql
new file mode 100644
index 00000000000..a81ffba680f
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_struct_double_optional.yql
@@ -0,0 +1,11 @@
+$structVariantType = Variant<a: Optional<Int32>, b: Optional<Int32>>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(Just(1), "a", $structVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), "a", $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant(Just(2), "b", $structVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), "b", $structVariantType))|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($optionalData);
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_struct_optional.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_struct_optional.yql
new file mode 100644
index 00000000000..53a3dfe9a13
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_struct_optional.yql
@@ -0,0 +1,9 @@
+$structVariantType = Variant<a: Int32, b: Int32>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, "a", $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant(2, "b", $structVariantType))|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($optionalData);
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_tuple.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple.yql
new file mode 100644
index 00000000000..f1fc295d091
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple.yql
@@ -0,0 +1,9 @@
+$tupleVariantType = Variant<Int32, Int32>;
+
+$data = [
+ <|variantValue: Variant(1, "0", $tupleVariantType)|>,
+ <|variantValue: Variant(2, "1", $tupleVariantType)|>,
+ <|variantValue: Variant(42, "0", $tupleVariantType)|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($data);
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_double_optional.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_double_optional.yql
new file mode 100644
index 00000000000..324230dd5b0
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_double_optional.yql
@@ -0,0 +1,11 @@
+$tupleVariantType = Variant<Optional<Int32>, Optional<Int32>>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(Just(1), "0", $tupleVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), "0", $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant(Just(2), "1", $tupleVariantType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), "1", $tupleVariantType))|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($optionalData);
diff --git a/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_optional.yql b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_optional.yql
new file mode 100644
index 00000000000..bc9aefe9752
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/variant_item_tuple_optional.yql
@@ -0,0 +1,9 @@
+$tupleVariantType = Variant<Int32, Int32>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, "0", $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant(2, "1", $tupleVariantType))|>,
+];
+
+SELECT Yql::VariantItem(variantValue) AS item FROM as_table($optionalData);