summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoratarasov5 <[email protected]>2026-06-26 11:29:42 +0300
committeratarasov5 <[email protected]>2026-06-26 12:00:47 +0300
commit2db9f0c19f62105ce3a9ea4a4322946c5c19f594 (patch)
tree03c4586301b2ee597e5fb920559bed0e105572d3
parentdfe850681cebf59f120a571d85c493aea97d0700 (diff)
YQL-21315: Implement block guess
#### Implement BlockGuess functionality for variant type extraction ✎ - Introduces BlockGuess functionality to extract specific alternatives from variant types, supporting both tuple and struct variants - Adds type annotation and runtime implementation for BlockGuess operations with proper validation and error handling - Implements peephole optimization support for BlockGuess operations to improve query execution plans - Includes comprehensive unit tests and SQL format validation for various BlockGuess scenarios - Updates runtime version to 79 to enable new BlockGuess features and ensures backward compatibility <a href="https://nda.ya.ru/t/qa0kX64r7DqvtN"><font size="2">Autodescription by Yandex Code Assistant</font></a> commit_hash:e8f7fc2859cc1986ee0d5fba8b3e25bbf9ba4c41
-rw-r--r--yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp3
-rw-r--r--yql/essentials/core/type_ann/type_ann_blocks.cpp55
-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_guess.cpp134
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_block_guess.h10
-rw-r--r--yql/essentials/minikql/comp_nodes/mkql_factory.cpp2
-rw-r--r--yql/essentials/minikql/comp_nodes/ut/mkql_block_guess_ut.cpp226
-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.cpp40
-rw-r--r--yql/essentials/minikql/mkql_program_builder.h2
-rw-r--r--yql/essentials/minikql/mkql_runtime_version.h2
-rw-r--r--yql/essentials/providers/common/mkql/yql_provider_mkql.cpp15
-rw-r--r--yql/essentials/tests/sql/minirun/part2/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part5/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part6/canondata/result.json21
-rw-r--r--yql/essentials/tests/sql/minirun/part7/canondata/result.json42
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json60
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_/formatted.sql14
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_/formatted.sql14
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_member_/formatted.sql15
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_/formatted.sql14
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_optional_/formatted.sql14
-rw-r--r--yql/essentials/tests/sql/suites/blocks/guess_struct.yql8
-rw-r--r--yql/essentials/tests/sql/suites/blocks/guess_struct_optional.yql9
-rw-r--r--yql/essentials/tests/sql/suites/blocks/guess_struct_optional_member.yql10
-rw-r--r--yql/essentials/tests/sql/suites/blocks/guess_tuple.yql9
-rw-r--r--yql/essentials/tests/sql/suites/blocks/guess_tuple_optional.yql9
29 files changed, 772 insertions, 2 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 1edff421068..1e935d1912d 100644
--- a/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
+++ b/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
@@ -6450,7 +6450,8 @@ private:
TExprNode::TListType funcArgs;
std::string_view arrowFunctionName;
const bool rewriteAsIs = node->IsCallable({"AssumeStrict", "AssumeNonStrict", "NoPush", "Likely"});
- if (node->IsList() || rewriteAsIs ||
+ bool isSuitableGuess = NKikimr::NMiniKQL::RuntimeVersion >= 79 && node->IsCallable("Guess");
+ if (node->IsList() || rewriteAsIs || isSuitableGuess ||
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_, 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 64bab47ae2d..34094b449f3 100644
--- a/yql/essentials/core/type_ann/type_ann_blocks.cpp
+++ b/yql/essentials/core/type_ann/type_ann_blocks.cpp
@@ -352,6 +352,61 @@ IGraphTransformer::TStatus BlockDecimalBinaryWrapper(const TExprNode::TPtr& inpu
return DecimalBinaryWrapperBase(input, output, ctx, /*blocks=*/ true);
}
+IGraphTransformer::TStatus BlockGuessWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ Y_UNUSED(output);
+ if (!EnsureArgsCount(*input, 2, 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);
+
+ const TTypeAnnotationNode* innerItemType = itemType;
+ if (innerItemType->GetKind() == ETypeAnnotationKind::Optional) {
+ innerItemType = innerItemType->Cast<TOptionalExprType>()->GetItemType();
+ }
+ if (!EnsureVariantType(variantNode->Pos(), *innerItemType, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ auto variantType = innerItemType->Cast<TVariantExprType>();
+
+ if (!EnsureAtom(*input->Child(1), ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ const TTypeAnnotationNode* alternativeType = nullptr;
+ if (variantType->GetUnderlyingType()->GetKind() == ETypeAnnotationKind::Tuple) {
+ auto tupleType = variantType->GetUnderlyingType()->Cast<TTupleExprType>();
+ ui32 index = 0;
+ if (!TryFromString(input->Child(1)->Content(), index)) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()),
+ TStringBuilder() << "Failed to convert to integer: " << input->Child(1)->Content()));
+ return IGraphTransformer::TStatus::Error;
+ }
+ if (index >= tupleType->GetSize()) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()),
+ TStringBuilder() << "Index out of range. Index: " << index << ", size: " << tupleType->GetSize()));
+ return IGraphTransformer::TStatus::Error;
+ }
+ alternativeType = tupleType->GetItems()[index];
+ } else {
+ auto structType = variantType->GetUnderlyingType()->Cast<TStructExprType>();
+ auto pos = FindOrReportMissingMember(input->Child(1)->Content(), input->Pos(), *structType, ctx.Expr);
+ if (!pos) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ alternativeType = structType->GetItems()[*pos]->GetItemType();
+ }
+
+ auto optionalAlternativeType = ctx.Expr.MakeType<TOptionalExprType>(alternativeType);
+ input->SetTypeAnn(MakeBlockOrScalarType(optionalAlternativeType, 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 6050893b73c..4fdaad8f1ec 100644
--- a/yql/essentials/core/type_ann/type_ann_blocks.h
+++ b/yql/essentials/core/type_ann/type_ann_blocks.h
@@ -16,6 +16,7 @@ namespace NYql::NTypeAnnImpl {
IGraphTransformer::TStatus BlockValidUnwrapWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockCoalesceWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus BlockLogicalWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
+ IGraphTransformer::TStatus BlockGuessWrapper(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 1b8c731d814..d95673e3979 100644
--- a/yql/essentials/core/type_ann/type_ann_core.cpp
+++ b/yql/essentials/core/type_ann/type_ann_core.cpp
@@ -16460,6 +16460,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["BlockOr"] = &BlockLogicalWrapper;
Functions["BlockXor"] = &BlockLogicalWrapper;
Functions["BlockNot"] = &BlockLogicalWrapper;
+ Functions["BlockGuess"] = &BlockGuessWrapper;
Functions["BlockIf"] = &BlockIfWrapper;
Functions["BlockJust"] = &BlockJustWrapper;
Functions["BlockAsStruct"] = &BlockAsStructWrapper;
diff --git a/yql/essentials/minikql/comp_nodes/mkql_block_guess.cpp b/yql/essentials/minikql/comp_nodes/mkql_block_guess.cpp
new file mode 100644
index 00000000000..d13e839f5c0
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/mkql_block_guess.cpp
@@ -0,0 +1,134 @@
+#include "mkql_block_guess.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 {
+namespace NMiniKQL {
+
+namespace {
+
+template <bool IsOptional>
+class TGuessBlockExec {
+public:
+ class TGuessKernelState: public arrow::compute::KernelState {
+ public:
+ explicit TGuessKernelState(TType* inputItemType)
+ : Reader_(MakeBlockReader(TTypeInfoHelper(), inputItemType))
+ {
+ }
+
+ IBlockReader& GetReader() {
+ return *Reader_;
+ }
+
+ private:
+ std::unique_ptr<IBlockReader> Reader_;
+ };
+
+ explicit TGuessBlockExec(TType* inputItemType, ui32 alternativeIndex, TType* resultItemType)
+ : InputItemType_(inputItemType)
+ , AlternativeIndex_(alternativeIndex)
+ , ResultItemType_(resultItemType)
+ {
+ }
+
+ arrow::Status Exec(arrow::compute::KernelContext* ctx, const arrow::compute::ExecBatch& batch, arrow::Datum* res) const {
+ auto& reader = static_cast<TGuessKernelState&>(*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.GetVariantIndex() == AlternativeIndex_
+ ? blockItem.GetVariantItem().MakeOptional()
+ : TBlockItem{};
+ }
+
+ TType* const InputItemType_;
+ const ui32 AlternativeIndex_;
+ TType* const ResultItemType_;
+};
+
+template <bool IsOptional>
+std::shared_ptr<arrow::compute::ScalarKernel> MakeBlockGuessKernel(const TVector<TType*>& argTypes,
+ TType* resultType,
+ TType* inputItemType,
+ ui32 alternativeIndex) {
+ using TExec = TGuessBlockExec<IsOptional>;
+ auto exec = std::make_shared<TExec>(
+ inputItemType,
+ alternativeIndex,
+ 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::TGuessKernelState>(inputItemType));
+ };
+ return kernel;
+}
+
+} // namespace
+
+IComputationNode* WrapBlockGuess(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
+ MKQL_ENSURE(callable.GetInputsCount() == 2, "Expected 2 arguments");
+
+ auto blockType = AS_TYPE(TBlockType, callable.GetInput(0).GetStaticType());
+ auto inputItemType = blockType->GetItemType();
+
+ bool isOptional;
+ auto variantItemType = UnpackOptional(inputItemType, isOptional);
+ auto variantType = AS_TYPE(TVariantType, variantItemType);
+
+ const ui32 alternativeIndex = AS_VALUE(TDataLiteral, callable.GetInput(1))->AsValue().Get<ui32>();
+ MKQL_ENSURE(alternativeIndex < variantType->GetAlternativesCount(), "Bad alternative index");
+
+ auto variantCompute = LocateNode(ctx.NodeLocator, callable, 0);
+ TComputationNodePtrVector argsNodes = {variantCompute};
+ TVector<TType*> argsTypes = {blockType};
+
+ auto resultType = callable.GetType()->GetReturnType();
+
+ auto kernel = isOptional
+ ? MakeBlockGuessKernel<true>(argsTypes, resultType, inputItemType, alternativeIndex)
+ : MakeBlockGuessKernel<false>(argsTypes, resultType, inputItemType, alternativeIndex);
+
+ return new TBlockFuncNode(ctx.Mutables, ctx.RuntimeSettings->DatumValidation.Get(),
+ callable.GetType()->GetName(), std::move(argsNodes), argsTypes, resultType, *kernel, kernel);
+}
+
+} // namespace NMiniKQL
+} // namespace NKikimr
diff --git a/yql/essentials/minikql/comp_nodes/mkql_block_guess.h b/yql/essentials/minikql/comp_nodes/mkql_block_guess.h
new file mode 100644
index 00000000000..124929ecd51
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/mkql_block_guess.h
@@ -0,0 +1,10 @@
+#pragma once
+#include <yql/essentials/minikql/computation/mkql_computation_node.h>
+
+namespace NKikimr {
+namespace NMiniKQL {
+
+IComputationNode* WrapBlockGuess(TCallable& callable, const TComputationNodeFactoryContext& ctx);
+
+} // namespace NMiniKQL
+} // namespace NKikimr
diff --git a/yql/essentials/minikql/comp_nodes/mkql_factory.cpp b/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
index ce7637b32d8..75faa470c6c 100644
--- a/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
+++ b/yql/essentials/minikql/comp_nodes/mkql_factory.cpp
@@ -12,6 +12,7 @@
#include "mkql_block_decimal.h"
#include "mkql_block_exists.h"
#include "mkql_block_getelem.h"
+#include "mkql_block_guess.h"
#include "mkql_block_if.h"
#include "mkql_block_just.h"
#include "mkql_block_logical.h"
@@ -308,6 +309,7 @@ struct TCallableComputationNodeBuilderFuncMapFiller {
{"ReplicateScalar", &WrapReplicateScalar},
{"BlockCoalesce", &WrapBlockCoalesce},
{"BlockExists", &WrapBlockExists},
+ {"BlockGuess", &WrapBlockGuess},
{"BlockIf", &WrapBlockIf},
{"BlockAnd", &WrapBlockAnd},
{"BlockOr", &WrapBlockOr},
diff --git a/yql/essentials/minikql/comp_nodes/ut/mkql_block_guess_ut.cpp b/yql/essentials/minikql/comp_nodes/ut/mkql_block_guess_ut.cpp
new file mode 100644
index 00000000000..e90dfbe8869
--- /dev/null
+++ b/yql/essentials/minikql/comp_nodes/ut/mkql_block_guess_ut.cpp
@@ -0,0 +1,226 @@
+#include <yql/essentials/minikql/comp_nodes/mkql_block_guess.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>
+
+namespace NKikimr::NMiniKQL {
+
+using namespace NTest;
+
+namespace {
+
+template <typename TInputData, typename TExpectedData>
+void TestBlockGuess(const TVector<TInputData>& data, const TVector<TExpectedData>& expected, ui32 tupleIndex) {
+ TBlockHelper().TestKernelFuzzied(data, expected, [tupleIndex](TSetup<false>& setup, TRuntimeNode variantValue) {
+ return setup.PgmBuilder->BlockGuess(variantValue, tupleIndex);
+ });
+}
+
+} // namespace
+
+Y_UNIT_TEST_SUITE(TMiniKQLBlockGuessTest) {
+
+Y_UNIT_TEST(TupleVariant_Ui32Ui64_Index0) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TVariant> data = {TVariant{ui32{1}}, TVariant{ui64{2}}, TVariant{ui32{3}}};
+ TVector<TMaybe<ui32>> expected = {ui32{1}, Nothing(), ui32{3}};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_Ui32Ui64_Index1) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TVariant> data = {TVariant{ui32{1}}, TVariant{ui64{2}}, TVariant{ui32{3}}};
+ TVector<TMaybe<ui64>> expected = {Nothing(), ui64{2}, Nothing()};
+ TestBlockGuess(data, expected, 1u);
+}
+
+Y_UNIT_TEST(TupleVariant_AllSameAlternative) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TVariant> data = {TVariant{ui32{10}}, TVariant{ui32{20}}, TVariant{ui32{30}}};
+ TVector<TMaybe<ui32>> expected = {ui32{10}, ui32{20}, ui32{30}};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_NoneMatchAlternative) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TVariant> data = {TVariant{ui32{1}}, TVariant{ui32{2}}, TVariant{ui32{3}}};
+ TVector<TMaybe<ui64>> expected = {Nothing(), Nothing(), Nothing()};
+ TestBlockGuess(data, expected, 1u);
+}
+
+Y_UNIT_TEST(TupleVariant_StringAlternative) {
+ using TVariant = std::variant<ui32, TString>;
+ TVector<TVariant> data = {TVariant{ui32{42}}, TVariant{TString{"hello"}}, TVariant{ui32{7}}};
+ TVector<TMaybe<TString>> expected = {Nothing(), TMaybe<TString>{"hello"}, Nothing()};
+ TestBlockGuess(data, expected, 1u);
+}
+
+Y_UNIT_TEST(TupleVariant_TupleAlternative) {
+ using TTupleAlternative = std::tuple<ui32, TString>;
+ using TVariant = std::variant<TTupleAlternative, ui64>;
+ TVector<TVariant> data = {
+ TVariant{TTupleAlternative{ui32{1}, TString{"a"}}},
+ TVariant{ui64{2}},
+ TVariant{TTupleAlternative{ui32{3}, TString{"b"}}},
+ };
+ TVector<TMaybe<TTupleAlternative>> expected = {
+ TMaybe<TTupleAlternative>{TTupleAlternative{ui32{1}, TString{"a"}}},
+ Nothing(),
+ TMaybe<TTupleAlternative>{TTupleAlternative{ui32{3}, TString{"b"}}},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_PgIntAlternative) {
+ using TVariant = std::variant<TPgInt, ui64>;
+ TVector<TVariant> data = {TVariant{TPgInt{10}}, TVariant{ui64{20}}, TVariant{TPgInt{30}}};
+ TVector<TMaybe<TPgInt>> expected = {TMaybe<TPgInt>{TPgInt{10}}, Nothing(), TMaybe<TPgInt>{TPgInt{30}}};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalAlternative_DoubleOptional) {
+ using TVariant = std::variant<TMaybe<ui32>, ui64>;
+ TVector<TVariant> data = {
+ TVariant{TMaybe<ui32>{10u}},
+ TVariant{ui64{20u}},
+ TVariant{TMaybe<ui32>{}},
+ };
+ TVector<TMaybe<TMaybe<ui32>>> expected = {
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{10u}},
+ TMaybe<TMaybe<ui32>>{},
+ TMaybe<TMaybe<ui32>>{TMaybe<ui32>{}},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_NullRows) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{ui32{1}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{ui32{3}}},
+ };
+ TVector<TMaybe<ui32>> expected = {ui32{1}, Nothing(), ui32{3}};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_AllNull) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TMaybe<TVariant>> data = {TMaybe<TVariant>{}, TMaybe<TVariant>{}, TMaybe<TVariant>{}};
+ TVector<TMaybe<ui32>> expected = {Nothing(), Nothing(), Nothing()};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_NoNull) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{ui32{1}}},
+ TMaybe<TVariant>{TVariant{ui64{2}}},
+ TMaybe<TVariant>{TVariant{ui32{3}}},
+ };
+ TVector<TMaybe<ui32>> expected = {ui32{1}, Nothing(), ui32{3}};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_MixedNull) {
+ using TVariant = std::variant<ui32, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{ui64{10}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{ui64{20}}},
+ };
+ TVector<TMaybe<ui64>> expected = {ui64{10}, Nothing(), ui64{20}};
+ TestBlockGuess(data, expected, 1u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_SomeFieldsSet_StringAlternative) {
+ using TVariant = std::variant<TString, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{TString{"hello"}}},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{TVariant{ui64{42}}},
+ TMaybe<TVariant>{TVariant{TString{"world"}}},
+ };
+ TVector<TMaybe<TString>> expected = {
+ TMaybe<TString>{"hello"},
+ Nothing(),
+ Nothing(),
+ TMaybe<TString>{"world"},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_AllFieldsSet_StringAlternative) {
+ using TVariant = std::variant<TString, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{TVariant{TString{"foo"}}},
+ TMaybe<TVariant>{TVariant{TString{"bar"}}},
+ TMaybe<TVariant>{TVariant{TString{"baz"}}},
+ };
+ TVector<TMaybe<TString>> expected = {
+ TMaybe<TString>{"foo"},
+ TMaybe<TString>{"bar"},
+ TMaybe<TString>{"baz"},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalString_AllStringsSet) {
+ using TVariant = std::variant<TMaybe<TString>, ui64>;
+ TVector<TVariant> data = {
+ TVariant{TMaybe<TString>{"foo"}},
+ TVariant{TMaybe<TString>{"bar"}},
+ TVariant{TMaybe<TString>{"baz"}},
+ };
+ TVector<TMaybe<TMaybe<TString>>> expected = {
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{"foo"}},
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{"bar"}},
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{"baz"}},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalDouble_AllDoublesSet) {
+ using TVariant = std::variant<TMaybe<double>, ui64>;
+ TVector<TVariant> data = {
+ TVariant{TMaybe<double>{1.5}},
+ TVariant{TMaybe<double>{2.5}},
+ TVariant{TMaybe<double>{3.5}},
+ };
+ TVector<TMaybe<TMaybe<double>>> expected = {
+ TMaybe<TMaybe<double>>{TMaybe<double>{1.5}},
+ TMaybe<TMaybe<double>>{TMaybe<double>{2.5}},
+ TMaybe<TMaybe<double>>{TMaybe<double>{3.5}},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(OptionalTupleVariant_AllVariantsOmitted_StringAlternative) {
+ using TVariant = std::variant<TString, ui64>;
+ TVector<TMaybe<TVariant>> data = {
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{},
+ TMaybe<TVariant>{},
+ };
+ TVector<TMaybe<TString>> expected = {Nothing(), Nothing(), Nothing()};
+ TestBlockGuess(data, expected, 0u);
+}
+
+Y_UNIT_TEST(TupleVariant_OptionalString_AllStringsOmitted) {
+ using TVariant = std::variant<TMaybe<TString>, ui64>;
+ TVector<TVariant> data = {
+ TVariant{TMaybe<TString>{}},
+ TVariant{TMaybe<TString>{}},
+ TVariant{TMaybe<TString>{}},
+ };
+ TVector<TMaybe<TMaybe<TString>>> expected = {
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{}},
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{}},
+ TMaybe<TMaybe<TString>>{TMaybe<TString>{}},
+ };
+ TestBlockGuess(data, expected, 0u);
+}
+
+} // Y_UNIT_TEST_SUITE(TMiniKQLBlockGuessTest)
+
+} // namespace NKikimr::NMiniKQL
diff --git a/yql/essentials/minikql/comp_nodes/ut/ya.make.inc b/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
index 642b0afcdb6..bed3bd8f9e9 100644
--- a/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
+++ b/yql/essentials/minikql/comp_nodes/ut/ya.make.inc
@@ -35,6 +35,7 @@ SET(ORIG_SOURCES
mkql_block_test_helper.cpp
mkql_block_fuzzer.cpp
mkql_block_getelem_ut.cpp
+ mkql_block_guess_ut.cpp
mkql_block_just_ut.cpp
mkql_block_top_sort_ut.cpp
mkql_block_variant_ut.cpp
diff --git a/yql/essentials/minikql/comp_nodes/ya.make.inc b/yql/essentials/minikql/comp_nodes/ya.make.inc
index e26c8420d2a..50b7ba84f2e 100644
--- a/yql/essentials/minikql/comp_nodes/ya.make.inc
+++ b/yql/essentials/minikql/comp_nodes/ya.make.inc
@@ -19,6 +19,7 @@ SET(ORIG_SOURCES
mkql_block_decimal.cpp
mkql_block_exists.cpp
mkql_block_getelem.cpp
+ mkql_block_guess.cpp
mkql_block_if.cpp
mkql_block_just.cpp
mkql_block_logical.cpp
diff --git a/yql/essentials/minikql/mkql_program_builder.cpp b/yql/essentials/minikql/mkql_program_builder.cpp
index 47cd5be577e..4f3bb43b487 100644
--- a/yql/essentials/minikql/mkql_program_builder.cpp
+++ b/yql/essentials/minikql/mkql_program_builder.cpp
@@ -6292,6 +6292,46 @@ TRuntimeNode TProgramBuilder::PgInternal0(TType* returnType) {
return TRuntimeNode(callableBuilder.Build(), false);
}
+TRuntimeNode TProgramBuilder::BlockGuess(TRuntimeNode variant, ui32 tupleIndex) {
+ if constexpr (RuntimeVersion < 79) {
+ 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 underlying = AS_TYPE(TTupleType, variantType->GetUnderlyingType());
+ MKQL_ENSURE(tupleIndex < underlying->GetElementsCount(), "Wrong tuple index");
+ auto alternativeType = underlying->GetElementType(tupleIndex);
+ auto returnType = NewBlockType(NewOptionalType(alternativeType), blockType->GetShape());
+
+ TCallableBuilder callableBuilder(Env_, __func__, returnType);
+ callableBuilder.Add(variant);
+ callableBuilder.Add(NewDataLiteral<ui32>(tupleIndex));
+ return TRuntimeNode(callableBuilder.Build(), false);
+}
+
+TRuntimeNode TProgramBuilder::BlockGuess(TRuntimeNode variant, const std::string_view& memberName) {
+ if constexpr (RuntimeVersion < 79) {
+ 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 underlying = AS_TYPE(TStructType, variantType->GetUnderlyingType());
+ auto structIndex = underlying->GetMemberIndex(memberName);
+ auto alternativeType = underlying->GetMemberType(structIndex);
+ auto returnType = NewBlockType(NewOptionalType(alternativeType), blockType->GetShape());
+
+ TCallableBuilder callableBuilder(Env_, __func__, returnType);
+ callableBuilder.Add(variant);
+ callableBuilder.Add(NewDataLiteral<ui32>(structIndex));
+ return TRuntimeNode(callableBuilder.Build(), 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 b6372693cf7..569bc0fe06c 100644
--- a/yql/essentials/minikql/mkql_program_builder.h
+++ b/yql/essentials/minikql/mkql_program_builder.h
@@ -302,6 +302,8 @@ public:
TRuntimeNode BlockOr(TRuntimeNode first, TRuntimeNode second);
TRuntimeNode BlockXor(TRuntimeNode first, TRuntimeNode second);
+ TRuntimeNode BlockGuess(TRuntimeNode variant, ui32 tupleIndex);
+ TRuntimeNode BlockGuess(TRuntimeNode variant, const std::string_view& memberName);
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 590cba7b6cb..4addfc8a820 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 78U
+ #define MKQL_RUNTIME_VERSION 79U
#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 f1ecb973506..c302604faf4 100644
--- a/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp
+++ b/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp
@@ -990,6 +990,21 @@ TMkqlCommonCallableCompiler::TShared::TShared() {
}
});
+ AddCallable("BlockGuess", [](const TExprNode& node, TMkqlBuildContext& ctx) {
+ const auto blockVariantValue = MkqlBuildExpr(*node.Child(0), ctx);
+ bool isScalar;
+ const TTypeAnnotationNode* blockItemType = GetBlockItemType(*node.Child(0)->GetTypeAnn(), isScalar);
+ const TTypeAnnotationNode* variantItemType = blockItemType->GetKind() == ETypeAnnotationKind::Optional
+ ? blockItemType->Cast<TOptionalExprType>()->GetItemType()
+ : blockItemType;
+ const auto* variantType = variantItemType->Cast<TVariantExprType>();
+ if (variantType->GetUnderlyingType()->GetKind() == ETypeAnnotationKind::Tuple) {
+ const auto alternativeIndex = FromString<ui32>(node.Child(1)->Content());
+ return ctx.ProgramBuilder.BlockGuess(blockVariantValue, alternativeIndex);
+ }
+ return ctx.ProgramBuilder.BlockGuess(blockVariantValue, node.Child(1)->Content());
+ });
+
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/part2/canondata/result.json b/yql/essentials/tests/sql/minirun/part2/canondata/result.json
index 9d71b171b1f..1a20928220f 100644
--- a/yql/essentials/tests/sql/minirun/part2/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part2/canondata/result.json
@@ -212,6 +212,27 @@
"uri": "https://{canondata_backend}/1937150/e249b7faff24c68591c4c61cf9ecf7e069660ddc/resource.tar.gz#test.test_blocks-coalesce_pg_types-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-guess_tuple_optional-default.txt-Debug]": [
+ {
+ "checksum": "019652f450b6e2938d893a1bf3b5a068",
+ "size": 673,
+ "uri": "https://{canondata_backend}/1937429/4b1f75db514670d5b5e84a8a90fff94480086825/resource.tar.gz#test.test_blocks-guess_tuple_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_tuple_optional-default.txt-Peephole]": [
+ {
+ "checksum": "caa828e834e5c7ebfb1f9c981d65153a",
+ "size": 798,
+ "uri": "https://{canondata_backend}/1937429/4b1f75db514670d5b5e84a8a90fff94480086825/resource.tar.gz#test.test_blocks-guess_tuple_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_tuple_optional-default.txt-Results]": [
+ {
+ "checksum": "6c183c541d7d1675dec1a471edc070c5",
+ "size": 1587,
+ "uri": "https://{canondata_backend}/1937429/4b1f75db514670d5b5e84a8a90fff94480086825/resource.tar.gz#test.test_blocks-guess_tuple_optional-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-sort-default.txt-Debug]": [
{
"checksum": "f5d0c3ad8b655c3a6684a13ff19885e6",
diff --git a/yql/essentials/tests/sql/minirun/part5/canondata/result.json b/yql/essentials/tests/sql/minirun/part5/canondata/result.json
index 17daba1efad..59611798116 100644
--- a/yql/essentials/tests/sql/minirun/part5/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part5/canondata/result.json
@@ -251,6 +251,27 @@
"uri": "https://{canondata_backend}/1881367/57bfc4a1435f10934d7710509072254b7f260723/resource.tar.gz#test.test_bigdate-compare_big_small-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-guess_struct_optional_member-default.txt-Debug]": [
+ {
+ "checksum": "e3a46fdda791183fc7d8dcec71eca1b6",
+ "size": 785,
+ "uri": "https://{canondata_backend}/1925821/60da0c39c5d2a728756bea3b0baa9e7db3d4268e/resource.tar.gz#test.test_blocks-guess_struct_optional_member-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct_optional_member-default.txt-Peephole]": [
+ {
+ "checksum": "377957b7502936a51a97d6112bda318e",
+ "size": 912,
+ "uri": "https://{canondata_backend}/1925821/60da0c39c5d2a728756bea3b0baa9e7db3d4268e/resource.tar.gz#test.test_blocks-guess_struct_optional_member-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct_optional_member-default.txt-Results]": [
+ {
+ "checksum": "e1ffea58b75a78a03c7d9862ee39dc8d",
+ "size": 1950,
+ "uri": "https://{canondata_backend}/1925821/60da0c39c5d2a728756bea3b0baa9e7db3d4268e/resource.tar.gz#test.test_blocks-guess_struct_optional_member-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-just-default.txt-Debug]": [
{
"checksum": "248412653c63c6892f76b6ed3c91a2ae",
diff --git a/yql/essentials/tests/sql/minirun/part6/canondata/result.json b/yql/essentials/tests/sql/minirun/part6/canondata/result.json
index 022d99e40c0..2852aec51f8 100644
--- a/yql/essentials/tests/sql/minirun/part6/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part6/canondata/result.json
@@ -300,6 +300,27 @@
"uri": "https://{canondata_backend}/1917492/53531106e44e755ea77008ec27c01bc41d81b31c/resource.tar.gz#test.test_blocks-agg_singular_type_value_optional-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-guess_tuple-default.txt-Debug]": [
+ {
+ "checksum": "b3484c97deecd85485035f71becb6ed3",
+ "size": 702,
+ "uri": "https://{canondata_backend}/1889210/a52c86a2a53b24445efdf84bb4f5f2e22189f80b/resource.tar.gz#test.test_blocks-guess_tuple-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_tuple-default.txt-Peephole]": [
+ {
+ "checksum": "fb9a56b5f63ceeae3437f0c5eb32c5ce",
+ "size": 831,
+ "uri": "https://{canondata_backend}/1889210/a52c86a2a53b24445efdf84bb4f5f2e22189f80b/resource.tar.gz#test.test_blocks-guess_tuple-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_tuple-default.txt-Results]": [
+ {
+ "checksum": "57b4a9b2ade4fa471979336dc2f30bbf",
+ "size": 1646,
+ "uri": "https://{canondata_backend}/1889210/a52c86a2a53b24445efdf84bb4f5f2e22189f80b/resource.tar.gz#test.test_blocks-guess_tuple-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-ifpresent_nonstrict-default.txt-Debug]": [
{
"checksum": "3408c8b5f15c5ad2bf6d3b498eaee9aa",
diff --git a/yql/essentials/tests/sql/minirun/part7/canondata/result.json b/yql/essentials/tests/sql/minirun/part7/canondata/result.json
index ee25c925aa4..5d50b870083 100644
--- a/yql/essentials/tests/sql/minirun/part7/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part7/canondata/result.json
@@ -293,6 +293,48 @@
"uri": "https://{canondata_backend}/1903280/44f4f1690f38bce1fadfbce889480224fbb8b527/resource.tar.gz#test.test_blocks-extend-default.txt-Results_/results.txt"
}
],
+ "test.test[blocks-guess_struct-default.txt-Debug]": [
+ {
+ "checksum": "dc695ef63c0f88a81910b94520d24e95",
+ "size": 672,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct-default.txt-Peephole]": [
+ {
+ "checksum": "503506e157eefe241ea710f344a9a4d3",
+ "size": 857,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct-default.txt-Results]": [
+ {
+ "checksum": "28718b5e1be812f03ffc37b36f5a484f",
+ "size": 2907,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct-default.txt-Results_/results.txt"
+ }
+ ],
+ "test.test[blocks-guess_struct_optional-default.txt-Debug]": [
+ {
+ "checksum": "a39f5af6d371ab3cd348088dfc555581",
+ "size": 682,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct_optional-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct_optional-default.txt-Peephole]": [
+ {
+ "checksum": "04b061f56cff50b6e5e047373ea6d77d",
+ "size": 807,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct_optional-default.txt-Peephole_/opt.yql"
+ }
+ ],
+ "test.test[blocks-guess_struct_optional-default.txt-Results]": [
+ {
+ "checksum": "ab943064d130561ae2d2eff39b483c0f",
+ "size": 1583,
+ "uri": "https://{canondata_backend}/1814674/af6048e526ca90118fc48e8a1171c55d8d01599c/resource.tar.gz#test.test_blocks-guess_struct_optional-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[blocks-if_scalar-default.txt-Debug]": [
{
"checksum": "b1316cc011f1941633e8e78e1297ac0b",
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json
index 3b36e102020..f0e87dec7c7 100644
--- a/yql/essentials/tests/sql/sql2yql/canondata/result.json
+++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json
@@ -2330,6 +2330,41 @@
"uri": "https://{canondata_backend}/1942100/698d95eec1cbd4e4ada1a8da105c203a7a12bb85/resource.tar.gz#test_sql2yql.test_blocks-frompg_/sql.yql"
}
],
+ "test_sql2yql.test[blocks-guess_struct]": [
+ {
+ "checksum": "84d8c16ade2df9cbf4fafa084f925616",
+ "size": 1768,
+ "uri": "https://{canondata_backend}/1942100/59faa31c8c62f57bd9d23c377a9ecd8a3fc1fc84/resource.tar.gz#test_sql2yql.test_blocks-guess_struct_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-guess_struct_optional]": [
+ {
+ "checksum": "dabbcbab4b66442b84a804df6ae216dd",
+ "size": 1678,
+ "uri": "https://{canondata_backend}/1942100/59faa31c8c62f57bd9d23c377a9ecd8a3fc1fc84/resource.tar.gz#test_sql2yql.test_blocks-guess_struct_optional_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-guess_struct_optional_member]": [
+ {
+ "checksum": "54ce491fdb17455668bb6f5bba1b8d16",
+ "size": 1811,
+ "uri": "https://{canondata_backend}/1942100/59faa31c8c62f57bd9d23c377a9ecd8a3fc1fc84/resource.tar.gz#test_sql2yql.test_blocks-guess_struct_optional_member_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-guess_tuple]": [
+ {
+ "checksum": "f9e25ddbfed1d8d6de147d4567142216",
+ "size": 1659,
+ "uri": "https://{canondata_backend}/1899731/617f799adb700d0123a74da1da91c1c62797b1e0/resource.tar.gz#test_sql2yql.test_blocks-guess_tuple_/sql.yql"
+ }
+ ],
+ "test_sql2yql.test[blocks-guess_tuple_optional]": [
+ {
+ "checksum": "b1eab2d46db98c837f877f5dad37478b",
+ "size": 1669,
+ "uri": "https://{canondata_backend}/1899731/617f799adb700d0123a74da1da91c1c62797b1e0/resource.tar.gz#test_sql2yql.test_blocks-guess_tuple_optional_/sql.yql"
+ }
+ ],
"test_sql2yql.test[blocks-if]": [
{
"checksum": "2b573fa3534410c28b1273b496c93231",
@@ -14019,6 +14054,31 @@
"uri": "file://test_sql_format.test_blocks-frompg_/formatted.sql"
}
],
+ "test_sql_format.test[blocks-guess_struct]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-guess_struct_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-guess_struct_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-guess_struct_optional_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-guess_struct_optional_member]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-guess_struct_optional_member_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-guess_tuple]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-guess_tuple_/formatted.sql"
+ }
+ ],
+ "test_sql_format.test[blocks-guess_tuple_optional]": [
+ {
+ "uri": "file://test_sql_format.test_blocks-guess_tuple_optional_/formatted.sql"
+ }
+ ],
"test_sql_format.test[blocks-if]": [
{
"uri": "file://test_sql_format.test_blocks-if_/formatted.sql"
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_/formatted.sql
new file mode 100644
index 00000000000..63d841c814f
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_/formatted.sql
@@ -0,0 +1,14 @@
+$structVariantType = Variant<a: Int32, b: String>;
+
+$data = [
+ <|variantValue: Variant(1, 'a', $structVariantType)|>,
+ <|variantValue: Variant('world', 'b', $structVariantType)|>,
+];
+
+SELECT
+ variantValue,
+ variantValue.a AS aAlternative,
+ variantValue.b AS bAlternative
+FROM
+ as_table($data)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_/formatted.sql
new file mode 100644
index 00000000000..dd3e6d4a6d4
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_/formatted.sql
@@ -0,0 +1,14 @@
+$structVariantType = Variant<a: Int32, b: String>;
+
+$optionalVariantData = [
+ <|variantValue: Just(Variant(1, 'a', $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant('hello', 'b', $structVariantType))|>,
+];
+
+SELECT
+ variantValue.a AS aAlternative,
+ variantValue.b AS bAlternative
+FROM
+ as_table($optionalVariantData)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_member_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_member_/formatted.sql
new file mode 100644
index 00000000000..ef9a6a154d9
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_struct_optional_member_/formatted.sql
@@ -0,0 +1,15 @@
+$variantWithOptionalMemberType = Variant<a: Optional<Int32>, b: String>;
+
+$optionalVariantWithOptionalMemberData = [
+ <|variantValue: Just(Variant(Just(10), 'a', $variantWithOptionalMemberType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), 'a', $variantWithOptionalMemberType))|>,
+ <|variantValue: Nothing(OptionalType($variantWithOptionalMemberType))|>,
+ <|variantValue: Just(Variant('world', 'b', $variantWithOptionalMemberType))|>,
+];
+
+SELECT
+ variantValue.a AS aAlternative,
+ variantValue.b AS bAlternative
+FROM
+ as_table($optionalVariantWithOptionalMemberData)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_/formatted.sql
new file mode 100644
index 00000000000..d8117621bda
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_/formatted.sql
@@ -0,0 +1,14 @@
+$tupleVariantType = Variant<Int32, String>;
+
+$data = [
+ <|variantValue: Variant(1, '0', $tupleVariantType)|>,
+ <|variantValue: Variant('hello', '1', $tupleVariantType)|>,
+ <|variantValue: Variant(42, '0', $tupleVariantType)|>,
+];
+
+SELECT
+ variantValue.0 AS intAlternative,
+ variantValue.1 AS strAlternative
+FROM
+ as_table($data)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_optional_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_optional_/formatted.sql
new file mode 100644
index 00000000000..277f91ebf23
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_blocks-guess_tuple_optional_/formatted.sql
@@ -0,0 +1,14 @@
+$tupleVariantType = Variant<Int32, String>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, '0', $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant('hello', '1', $tupleVariantType))|>,
+];
+
+SELECT
+ variantValue.0 AS intAlternative,
+ variantValue.1 AS strAlternative
+FROM
+ as_table($optionalData)
+;
diff --git a/yql/essentials/tests/sql/suites/blocks/guess_struct.yql b/yql/essentials/tests/sql/suites/blocks/guess_struct.yql
new file mode 100644
index 00000000000..7bc063649e5
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/guess_struct.yql
@@ -0,0 +1,8 @@
+$structVariantType = Variant<a: Int32, b: String>;
+
+$data = [
+ <|variantValue: Variant(1, "a", $structVariantType)|>,
+ <|variantValue: Variant("world", "b", $structVariantType)|>,
+];
+
+SELECT variantValue, variantValue.a AS aAlternative, variantValue.b AS bAlternative FROM as_table($data);
diff --git a/yql/essentials/tests/sql/suites/blocks/guess_struct_optional.yql b/yql/essentials/tests/sql/suites/blocks/guess_struct_optional.yql
new file mode 100644
index 00000000000..91217f50c54
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/guess_struct_optional.yql
@@ -0,0 +1,9 @@
+$structVariantType = Variant<a: Int32, b: String>;
+
+$optionalVariantData = [
+ <|variantValue: Just(Variant(1, "a", $structVariantType))|>,
+ <|variantValue: Nothing(OptionalType($structVariantType))|>,
+ <|variantValue: Just(Variant("hello", "b", $structVariantType))|>,
+];
+
+SELECT variantValue.a AS aAlternative, variantValue.b AS bAlternative FROM as_table($optionalVariantData);
diff --git a/yql/essentials/tests/sql/suites/blocks/guess_struct_optional_member.yql b/yql/essentials/tests/sql/suites/blocks/guess_struct_optional_member.yql
new file mode 100644
index 00000000000..a6c9baf79a1
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/guess_struct_optional_member.yql
@@ -0,0 +1,10 @@
+$variantWithOptionalMemberType = Variant<a: Optional<Int32>, b: String>;
+
+$optionalVariantWithOptionalMemberData = [
+ <|variantValue: Just(Variant(Just(10), "a", $variantWithOptionalMemberType))|>,
+ <|variantValue: Just(Variant(Nothing(Int32?), "a", $variantWithOptionalMemberType))|>,
+ <|variantValue: Nothing(OptionalType($variantWithOptionalMemberType))|>,
+ <|variantValue: Just(Variant("world", "b", $variantWithOptionalMemberType))|>,
+];
+
+SELECT variantValue.a AS aAlternative, variantValue.b AS bAlternative FROM as_table($optionalVariantWithOptionalMemberData);
diff --git a/yql/essentials/tests/sql/suites/blocks/guess_tuple.yql b/yql/essentials/tests/sql/suites/blocks/guess_tuple.yql
new file mode 100644
index 00000000000..f112e186783
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/guess_tuple.yql
@@ -0,0 +1,9 @@
+$tupleVariantType = Variant<Int32, String>;
+
+$data = [
+ <|variantValue: Variant(1, "0", $tupleVariantType)|>,
+ <|variantValue: Variant("hello", "1", $tupleVariantType)|>,
+ <|variantValue: Variant(42, "0", $tupleVariantType)|>,
+];
+
+SELECT variantValue.0 AS intAlternative, variantValue.1 AS strAlternative FROM as_table($data);
diff --git a/yql/essentials/tests/sql/suites/blocks/guess_tuple_optional.yql b/yql/essentials/tests/sql/suites/blocks/guess_tuple_optional.yql
new file mode 100644
index 00000000000..12bdf873829
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/blocks/guess_tuple_optional.yql
@@ -0,0 +1,9 @@
+$tupleVariantType = Variant<Int32, String>;
+
+$optionalData = [
+ <|variantValue: Just(Variant(1, "0", $tupleVariantType))|>,
+ <|variantValue: Nothing(OptionalType($tupleVariantType))|>,
+ <|variantValue: Just(Variant("hello", "1", $tupleVariantType))|>,
+];
+
+SELECT variantValue.0 AS intAlternative, variantValue.1 AS strAlternative FROM as_table($optionalData);