aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoravevad <avevad@yandex-team.com>2024-12-09 11:48:43 +0300
committeravevad <avevad@yandex-team.com>2024-12-09 12:07:33 +0300
commit365b065ca32ba05d72f86723169f846d0ee12189 (patch)
tree5e038eb3e468aa71927441b964e326a0cbbb2e1c
parentbc7efd59c3940f609c01a8af3ea6c6d69cbc456c (diff)
downloadydb-365b065ca32ba05d72f86723169f846d0ee12189.tar.gz
YQL-19123 More SQL features for Variant type
More SQL features for Variant type commit_hash:51c15343e2d24190ec59085888dfa3fd008cafcc
-rw-r--r--yql/essentials/core/type_ann/type_ann_core.cpp196
-rw-r--r--yql/essentials/sql/v1/builtin.cpp49
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json36
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_builtins_opt_/formatted.sql30
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_struct_builtins_/formatted.sql46
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_tuple_builtins_/formatted.sql46
-rw-r--r--yql/essentials/tests/sql/suites/expr/variant_builtins_opt.sql26
-rw-r--r--yql/essentials/tests/sql/suites/expr/variant_struct_builtins.sql30
-rw-r--r--yql/essentials/tests/sql/suites/expr/variant_tuple_builtins.sql30
9 files changed, 477 insertions, 12 deletions
diff --git a/yql/essentials/core/type_ann/type_ann_core.cpp b/yql/essentials/core/type_ann/type_ann_core.cpp
index 2474f046ae..8dd928d4aa 100644
--- a/yql/essentials/core/type_ann/type_ann_core.cpp
+++ b/yql/essentials/core/type_ann/type_ann_core.cpp
@@ -946,6 +946,30 @@ namespace NTypeAnnImpl {
return result;
}
+ TMaybe<ui32> FindOrReportMissingMember(TStringBuf memberName, const TTupleExprType& tupleType, TString& errStr) {
+ ui32 index = 0;
+ if (!TryFromString(memberName, index)) {
+ errStr = TStringBuilder() << "Failed to convert to integer: " << memberName;
+ return Nothing();
+ }
+ if (index >= tupleType.GetSize()) {
+ errStr = TStringBuilder()
+ << "Index out of range. Index: "
+ << index << ", size: " << tupleType.GetSize();
+ return Nothing();
+ }
+ return index;
+ }
+
+ TMaybe<ui32> FindOrReportMissingMember(TStringBuf memberName, TPositionHandle pos, const TTupleExprType& tupleType, TExprContext& ctx) {
+ TString errStr;
+ auto result = FindOrReportMissingMember(memberName, tupleType, errStr);
+ if (!result) {
+ ctx.AddError(TIssue(ctx.GetPosition(pos), errStr));
+ }
+ return result;
+ }
+
IGraphTransformer::TStatus MemberWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
if (!EnsureArgsCount(*input, 2, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
@@ -8534,6 +8558,50 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
return IGraphTransformer::TStatus::Ok;
}
+ IGraphTransformer::TStatus SqlVariantItemWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ if (!EnsureArgsCount(*input, 1, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ if (IsNull(input->Head())) {
+ output = input->HeadPtr();
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
+ if (input->Head().GetTypeAnn() && input->Head().GetTypeAnn()->GetKind() == ETypeAnnotationKind::Optional) {
+ auto optType = input->Head().GetTypeAnn()->Cast<TOptionalExprType>();
+ if (!EnsureVariantType(input->Head().Pos(), *optType->GetItemType(), ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ auto varType = optType->GetItemType()->Cast<TVariantExprType>();
+ auto isOptionalItem = false;
+ if (varType->GetUnderlyingType()->GetKind() == ETypeAnnotationKind::Tuple) {
+ auto tupType = varType->GetUnderlyingType()->Cast<TTupleExprType>();
+ if (tupType->GetSize() > 0 && tupType->GetItems()[0]->GetKind() == ETypeAnnotationKind::Optional) {
+ isOptionalItem = true;
+ }
+ } else {
+ auto strType = varType->GetUnderlyingType()->Cast<TStructExprType>();
+ if (strType->GetSize() > 0 && strType->GetItems()[0]->GetItemType()->GetKind() == ETypeAnnotationKind::Optional) {
+ isOptionalItem = true;
+ }
+ }
+ output = ctx.Expr.Builder(input->Pos())
+ .Callable(isOptionalItem ? "FlatMap" : "Map")
+ .Add(0, input->HeadPtr())
+ .Lambda(1)
+ .Param("var")
+ .Callable("VariantItem")
+ .Arg(0, "var")
+ .Seal()
+ .Seal()
+ .Seal().Build();
+ } else {
+ output = ctx.Expr.RenameNode(*input, "VariantItem");
+ }
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
IGraphTransformer::TStatus VariantItemWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
Y_UNUSED(output);
if (!EnsureArgsCount(*input, 1, ctx.Expr)) {
@@ -8591,6 +8659,112 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
return IGraphTransformer::TStatus::Ok;
}
+ IGraphTransformer::TStatus SqlVisitWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ if (!EnsureMinArgsCount(*input, 2, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ if (IsNull(input->Head())) {
+ output = input->HeadPtr();
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
+ if (input->Head().GetTypeAnn() && input->Head().GetTypeAnn()->GetKind() == ETypeAnnotationKind::Optional) {
+ auto appendOtherArgs = [&] (auto &builder) -> auto {
+ for (size_t pos = 1; pos < input->ChildrenSize(); pos++) {
+ builder.Add(pos, input->ChildPtr(pos));
+ }
+ return builder;
+ };
+
+ auto optType = input->Head().GetTypeAnn()->Cast<TOptionalExprType>();
+ if (!EnsureVariantType(input->Pos(), *optType->GetItemType(), ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ auto varType = optType->GetItemType()->Cast<TVariantExprType>();
+ const TTupleExprType* tupType = nullptr;
+ const TStructExprType* strType = nullptr;
+ if (varType->GetUnderlyingType()->GetKind() == ETypeAnnotationKind::Tuple) {
+ tupType = varType->GetUnderlyingType()->Cast<TTupleExprType>();
+ } else {
+ strType = varType->GetUnderlyingType()->Cast<TStructExprType>();
+ }
+
+ bool isOptionalResult = false;
+ bool repeat = false;
+ for (size_t idx = 1; idx < input->ChildrenSize(); idx++) {
+ auto child = input->ChildPtr(idx);
+ if (child->IsAtom()) {
+ const TTypeAnnotationNode* itemType;
+ if (tupType) {
+ auto pos = FindOrReportMissingMember(child->Content(), child->Pos(), *tupType, ctx.Expr);
+ if (!pos) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ itemType = tupType->GetItems()[*pos];
+ } else {
+ auto pos = FindOrReportMissingMember(child->Content(), child->Pos(), *strType, ctx.Expr);
+ if (!pos) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ itemType = strType->GetItems()[*pos]->GetItemType();
+ }
+
+ idx++;
+ if (idx == input->ChildrenSize()) {
+ ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(child->Pos()), "Expected lambda after this argument"));
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ auto status = ConvertToLambda(input->ChildRef(idx), ctx.Expr, 1);
+ if (status.Level != IGraphTransformer::TStatus::Ok) {
+ return status;
+ }
+
+ auto& lambda = input->ChildRef(idx);
+ if (!UpdateLambdaAllArgumentsTypes(lambda, {itemType}, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+
+ if (!lambda->GetTypeAnn()) {
+ repeat = true;
+ continue;
+ }
+
+ if (lambda->GetTypeAnn()->GetKind() == ETypeAnnotationKind::Optional) {
+ isOptionalResult = true;
+ }
+ } else {
+ if (!EnsureComputable(*child, ctx.Expr)) {
+ return IGraphTransformer::TStatus::Error;
+ }
+ if (child->GetTypeAnn()->GetKind() == ETypeAnnotationKind::Optional) {
+ isOptionalResult = true;
+ }
+ }
+ }
+
+ if (repeat) {
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
+ output = appendOtherArgs(ctx.Expr.Builder(input->Pos())
+ .Callable(isOptionalResult ? "FlatMap" : "Map")
+ .Add(0, input->HeadPtr())
+ .Lambda(1)
+ .Param("var")
+ .Callable("Visit")
+ .Arg(0, "var"))
+ .Seal()
+ .Seal()
+ .Seal().Build();
+ } else {
+ output = ctx.Expr.RenameNode(*input, "Visit");
+ }
+
+ return IGraphTransformer::TStatus::Repeat;
+ }
+
IGraphTransformer::TStatus VisitWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
Y_UNUSED(output);
if (!EnsureMinArgsCount(*input, 2, ctx.Expr)) {
@@ -8625,21 +8799,13 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
const TTypeAnnotationNode* itemType;
ui32 itemIndex;
if (tupleType) {
- ui32 index = 0;
- if (!TryFromString(child->Content(), index)) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(child->Pos()), TStringBuilder() << "Failed to convert to integer: " << child->Content()));
- return IGraphTransformer::TStatus::Error;
- }
-
- if (index >= tupleType->GetSize()) {
- ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(child->Pos()), TStringBuilder()
- << "Index out of range. Index: "
- << index << ", size: " << tupleType->GetSize()));
+ auto pos = FindOrReportMissingMember(child->Content(), child->Pos(), *tupleType, ctx.Expr);
+ if (!pos) {
return IGraphTransformer::TStatus::Error;
}
- itemType = tupleType->GetItems()[index];
- itemIndex = index;
+ itemType = tupleType->GetItems()[*pos];
+ itemIndex = *pos;
} else {
auto pos = FindOrReportMissingMember(child->Content(), child->Pos(), *structType, ctx.Expr);
if (!pos) {
@@ -8763,6 +8929,10 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
variantType = itemType->Cast<TVariantExprType>();
}
+ else if (IsNull(input->Head())) {
+ output = input->HeadPtr();
+ return IGraphTransformer::TStatus::Repeat;
+ }
else {
if (!EnsureVariantType(input->Head(), ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
@@ -12421,7 +12591,9 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["FlattenMembersType"] = &TypeArgWrapper<ETypeArgument::FlattenMembers>;
Functions["VariantUnderlyingType"] = &TypeArgWrapper<ETypeArgument::VariantUnderlying>;
Functions["Guess"] = &GuessWrapper;
+ Functions["SqlVariantItem"] = &SqlVariantItemWrapper;
Functions["VariantItem"] = &VariantItemWrapper;
+ Functions["SqlVisit"] = &SqlVisitWrapper;
Functions["Visit"] = &VisitWrapper;
Functions["Way"] = &WayWrapper;
Functions["SqlAccess"] = &SqlAccessWrapper;
diff --git a/yql/essentials/sql/v1/builtin.cpp b/yql/essentials/sql/v1/builtin.cpp
index 5d33b2061f..a4bec63066 100644
--- a/yql/essentials/sql/v1/builtin.cpp
+++ b/yql/essentials/sql/v1/builtin.cpp
@@ -3010,6 +3010,7 @@ struct TBuiltinFuncData {
{"callableresulttype", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("CallableResultType", 1, 1) },
{"callableargumenttype", BuildSimpleBuiltinFactoryCallback<TYqlCallableArgumentType>() },
{"variantunderlyingtype", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("VariantUnderlyingType", 1, 1) },
+ {"variantitem", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("SqlVariantItem", 1, 1) },
{"fromysonsimpletype", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromYsonSimpleType", 2, 2) },
{"currentutcdate", BuildNamedDepsArgcBuiltinFactoryCallback<TCallNodeDepArgs>(0, "CurrentUtcDate", 0, -1) },
{"currentutcdatetime", BuildNamedDepsArgcBuiltinFactoryCallback<TCallNodeDepArgs>(0, "CurrentUtcDatetime", 0, -1) },
@@ -3705,6 +3706,54 @@ TNodePtr BuildBuiltinFunc(TContext& ctx, TPosition pos, TString name, const TVec
BuildTuple(pos, {BuildQuotedAtom(pos, ""), args[1]}),
};
return new TCallNodeImpl(pos, "FlattenMembers", 2, 2, flattenMembersArgs);
+ } else if (normalizedName == "visit" || normalizedName == "visitordefault") {
+ bool withDefault = normalizedName == "visitordefault";
+ TNodePtr variant;
+ TVector<TNodePtr> labels, handlers;
+ TMaybe<TNodePtr> dflt;
+ if (mustUseNamed && *mustUseNamed) {
+ *mustUseNamed = false;
+ auto &positional = *args[0]->GetTupleNode();
+ if (positional.GetTupleSize() != (withDefault ? 2 : 1)) {
+ return new TInvalidBuiltin(pos, TStringBuilder() << name
+ << " requires exactly " << (withDefault ? 2 : 1) << " positional arguments when named args are used");
+ }
+ auto &named = *args[1]->GetStructNode();
+ variant = positional.GetTupleElement(0);
+ auto &namedExprs = named.GetExprs();
+ labels.reserve(namedExprs.size());
+ handlers.reserve(namedExprs.size());
+ for (size_t idx = 0; idx < namedExprs.size(); idx++) {
+ labels.push_back(BuildQuotedAtom(pos, namedExprs[idx]->GetLabel()));
+ handlers.push_back(namedExprs[idx]);
+ }
+ if (withDefault) {
+ dflt = positional.GetTupleElement(positional.GetTupleSize() - 1);
+ }
+ } else {
+ variant = args[0];
+ size_t defaultSuffix = withDefault ? 1 : 0;
+ labels.reserve(args.size() - 1 - defaultSuffix);
+ handlers.reserve(args.size() - 1 - defaultSuffix);
+ for (size_t idx = 0; idx + 1 < args.size() - defaultSuffix; idx++) {
+ labels.push_back(BuildQuotedAtom(pos, ToString(idx)));
+ handlers.push_back(args[idx + 1]);
+ }
+ if (withDefault) {
+ dflt = args.back();
+ }
+ }
+ TVector<TNodePtr> resultArgs;
+ resultArgs.reserve(1 + labels.size() + handlers.size());
+ resultArgs.emplace_back(std::move(variant));
+ for (size_t idx = 0; idx < labels.size(); idx++) {
+ resultArgs.emplace_back(std::move(labels[idx]));
+ resultArgs.emplace_back(std::move(handlers[idx]));
+ }
+ if (dflt.Defined()) {
+ resultArgs.emplace_back(std::move(dflt->Get()));
+ }
+ return new TCallNodeImpl(pos, "SqlVisit", 1, -1, resultArgs);
} else if (normalizedName == "sqlexternalfunction") {
return new TCallNodeImpl(pos, "SqlExternalFunction", args);
} else {
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json
index 553fd35c78..ee6df683bf 100644
--- a/yql/essentials/tests/sql/sql2yql/canondata/result.json
+++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json
@@ -6908,6 +6908,13 @@
"uri": "https://{canondata_backend}/1937429/434276f26b2857be3c5ad3fdbbf877d2bf775ac5/resource.tar.gz#test_sql2yql.test_expr-uuid_order_/sql.yql"
}
],
+ "test_sql2yql.test[expr-variant_builtins_opt]": [
+ {
+ "checksum": "00f7f7ba82bd2474f7b1bfd7771323f9",
+ "size": 3847,
+ "uri": "https://{canondata_backend}/1936842/4ad1682e9f564ef555f5e0180b972a1adad2fc43/resource.tar.gz#test_sql2yql.test_expr-variant_builtins_opt_/sql.yql"
+ }
+ ],
"test_sql2yql.test[expr-variant_list_sort]": [
{
"checksum": "abd93bfb65a1cb17235be843d4cbd2c3",
@@ -6915,6 +6922,13 @@
"uri": "https://{canondata_backend}/1936273/4a1b39013e1bae40e722cff8ccef8829784964e2/resource.tar.gz#test_sql2yql.test_expr-variant_list_sort_/sql.yql"
}
],
+ "test_sql2yql.test[expr-variant_struct_builtins]": [
+ {
+ "checksum": "cb90540556b74bdbb9a95959c62a3cbf",
+ "size": 5780,
+ "uri": "https://{canondata_backend}/1936842/4ad1682e9f564ef555f5e0180b972a1adad2fc43/resource.tar.gz#test_sql2yql.test_expr-variant_struct_builtins_/sql.yql"
+ }
+ ],
"test_sql2yql.test[expr-variant_struct_comp]": [
{
"checksum": "3f85119441280a8729ef912910ac7745",
@@ -6922,6 +6936,13 @@
"uri": "https://{canondata_backend}/1936273/4a1b39013e1bae40e722cff8ccef8829784964e2/resource.tar.gz#test_sql2yql.test_expr-variant_struct_comp_/sql.yql"
}
],
+ "test_sql2yql.test[expr-variant_tuple_builtins]": [
+ {
+ "checksum": "41d01afdf58dcca513a44186a279efcb",
+ "size": 5682,
+ "uri": "https://{canondata_backend}/1936842/4ad1682e9f564ef555f5e0180b972a1adad2fc43/resource.tar.gz#test_sql2yql.test_expr-variant_tuple_builtins_/sql.yql"
+ }
+ ],
"test_sql2yql.test[expr-variant_tuple_comp]": [
{
"checksum": "62f3d0d5c99c037db94a4f8f3c3c9d1d",
@@ -24821,16 +24842,31 @@
"uri": "file://test_sql_format.test_expr-uuid_order_/formatted.sql"
}
],
+ "test_sql_format.test[expr-variant_builtins_opt]": [
+ {
+ "uri": "file://test_sql_format.test_expr-variant_builtins_opt_/formatted.sql"
+ }
+ ],
"test_sql_format.test[expr-variant_list_sort]": [
{
"uri": "file://test_sql_format.test_expr-variant_list_sort_/formatted.sql"
}
],
+ "test_sql_format.test[expr-variant_struct_builtins]": [
+ {
+ "uri": "file://test_sql_format.test_expr-variant_struct_builtins_/formatted.sql"
+ }
+ ],
"test_sql_format.test[expr-variant_struct_comp]": [
{
"uri": "file://test_sql_format.test_expr-variant_struct_comp_/formatted.sql"
}
],
+ "test_sql_format.test[expr-variant_tuple_builtins]": [
+ {
+ "uri": "file://test_sql_format.test_expr-variant_tuple_builtins_/formatted.sql"
+ }
+ ],
"test_sql_format.test[expr-variant_tuple_comp]": [
{
"uri": "file://test_sql_format.test_expr-variant_tuple_comp_/formatted.sql"
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_builtins_opt_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_builtins_opt_/formatted.sql
new file mode 100644
index 0000000000..3023dcf5c2
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_builtins_opt_/formatted.sql
@@ -0,0 +1,30 @@
+$vartype = Variant<a: Optional<String>, b: Optional<String>>;
+
+$handle_a = ($x) -> {
+ RETURN CAST(($x || "1") AS Uint32);
+};
+
+$handle_b = ($x) -> {
+ RETURN CAST(($x || "2") AS Uint32);
+};
+
+$var_a = VARIANT ("5", "a", $vartype);
+$var_b = VARIANT ("6", "b", $vartype);
+
+SELECT
+ Visit(Just($var_a), $handle_a AS a, $handle_b AS b),
+ Visit(Just($var_b), $handle_a AS a, $handle_b AS b),
+ VisitOrDefault(Just($var_b), Just(777u), $handle_a AS a),
+ VariantItem(Just($var_b))
+;
+
+$vartype_t = Variant<Optional<String>, Optional<String>>;
+$var_1 = VARIANT ("7", "0", $vartype_t);
+$var_2 = VARIANT ("8", "1", $vartype_t);
+
+SELECT
+ Visit(Just($var_1), $handle_a, $handle_b),
+ Visit(Just($var_2), $handle_a, $handle_b),
+ VisitOrDefault(Just($var_2), $handle_a, Just(777u)),
+ VariantItem(Just($var_b))
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_struct_builtins_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_struct_builtins_/formatted.sql
new file mode 100644
index 0000000000..1192a90d9b
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_struct_builtins_/formatted.sql
@@ -0,0 +1,46 @@
+$vartype = Variant<num: Int32, flag: Bool, str: String>;
+
+$handle_num = ($x) -> {
+ RETURN 2 * $x;
+};
+
+$handle_flag = ($x) -> {
+ RETURN If($x, 200, 10);
+};
+
+$handle_str = ($x) -> {
+ RETURN Unwrap(CAST(LENGTH($x) AS Int32));
+};
+
+$visitor = ($var) -> {
+ RETURN Visit($var, $handle_num AS num, $handle_flag AS flag, $handle_str AS str);
+};
+
+SELECT
+ $visitor(VARIANT (5, "num", $vartype)),
+ $visitor(Just(VARIANT (TRUE, "flag", $vartype))),
+ $visitor(Just(VARIANT ("somestr", "str", $vartype))),
+ $visitor(Nothing(OptionalType($vartype))),
+ $visitor(NULL)
+;
+
+$visitor_def = ($var) -> {
+ RETURN VisitOrDefault($var, 999, $handle_num AS num, $handle_flag AS flag);
+};
+
+SELECT
+ $visitor_def(VARIANT (5, "num", $vartype)),
+ $visitor_def(Just(VARIANT (TRUE, "flag", $vartype))),
+ $visitor_def(Just(VARIANT ("somestr", "str", $vartype))),
+ $visitor_def(Nothing(OptionalType($vartype))),
+ $visitor_def(NULL)
+;
+
+$vartype1 = Variant<num1: Int32, num2: Int32, num3: Int32>;
+
+SELECT
+ VariantItem(VARIANT (7, "num2", $vartype1)),
+ VariantItem(Just(VARIANT (5, "num1", $vartype1))),
+ VariantItem(Nothing(OptionalType($vartype1))),
+ VariantItem(NULL)
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_tuple_builtins_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_tuple_builtins_/formatted.sql
new file mode 100644
index 0000000000..b1e21cda10
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-variant_tuple_builtins_/formatted.sql
@@ -0,0 +1,46 @@
+$vartype = Variant<Int32, Bool, String>;
+
+$handle_num = ($x) -> {
+ RETURN 2 * $x;
+};
+
+$handle_flag = ($x) -> {
+ RETURN If($x, 200, 10);
+};
+
+$handle_str = ($x) -> {
+ RETURN Unwrap(CAST(LENGTH($x) AS Int32));
+};
+
+$visitor = ($var) -> {
+ RETURN Visit($var, $handle_num, $handle_flag, $handle_str);
+};
+
+SELECT
+ $visitor(VARIANT (5, "0", $vartype)),
+ $visitor(Just(VARIANT (TRUE, "1", $vartype))),
+ $visitor(Just(VARIANT ("somestr", "2", $vartype))),
+ $visitor(Nothing(OptionalType($vartype))),
+ $visitor(NULL)
+;
+
+$visitor_def = ($var) -> {
+ RETURN VisitOrDefault($var, $handle_num, $handle_flag, 999);
+};
+
+SELECT
+ $visitor_def(VARIANT (5, "0", $vartype)),
+ $visitor_def(Just(VARIANT (TRUE, "1", $vartype))),
+ $visitor_def(Just(VARIANT ("somestr", "2", $vartype))),
+ $visitor_def(Nothing(OptionalType($vartype))),
+ $visitor_def(NULL)
+;
+
+$vartype1 = Variant<Int32, Int32, Int32>;
+
+SELECT
+ VariantItem(VARIANT (7, "1", $vartype1)),
+ VariantItem(Just(VARIANT (5, "0", $vartype1))),
+ VariantItem(Nothing(OptionalType($vartype1))),
+ VariantItem(NULL)
+;
diff --git a/yql/essentials/tests/sql/suites/expr/variant_builtins_opt.sql b/yql/essentials/tests/sql/suites/expr/variant_builtins_opt.sql
new file mode 100644
index 0000000000..443802fe56
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/variant_builtins_opt.sql
@@ -0,0 +1,26 @@
+$vartype = Variant<a: Optional<String>, b: Optional<String>>;
+
+$handle_a = ($x) -> { return CAST(($x || "1") AS Uint32); };
+$handle_b = ($x) -> { return CAST(($x || "2") AS Uint32); };
+
+$var_a = Variant("5", "a", $vartype);
+$var_b = Variant("6", "b", $vartype);
+
+SELECT
+ Visit(Just($var_a), $handle_a AS a, $handle_b AS b),
+ Visit(Just($var_b), $handle_a AS a, $handle_b AS b),
+ VisitOrDefault(Just($var_b), Just(777u), $handle_a AS a),
+ VariantItem(Just($var_b))
+;
+
+$vartype_t = Variant<Optional<String>, Optional<String>>;
+
+$var_1 = Variant("7", "0", $vartype_t);
+$var_2 = Variant("8", "1", $vartype_t);
+
+SELECT
+ Visit(Just($var_1), $handle_a, $handle_b),
+ Visit(Just($var_2), $handle_a, $handle_b),
+ VisitOrDefault(Just($var_2), $handle_a, Just(777u)),
+ VariantItem(Just($var_b))
+;
diff --git a/yql/essentials/tests/sql/suites/expr/variant_struct_builtins.sql b/yql/essentials/tests/sql/suites/expr/variant_struct_builtins.sql
new file mode 100644
index 0000000000..71a09810da
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/variant_struct_builtins.sql
@@ -0,0 +1,30 @@
+$vartype = Variant<num: Int32, flag: Bool, str: String>;
+$handle_num = ($x) -> { return 2 * $x; };
+$handle_flag = ($x) -> { return If($x, 200, 10); };
+$handle_str = ($x) -> { return Unwrap(CAST(LENGTH($x) AS Int32)); };
+
+$visitor = ($var) -> { return Visit($var, $handle_num AS num, $handle_flag AS flag, $handle_str AS str); };
+SELECT
+ $visitor(Variant(5, "num", $vartype)),
+ $visitor(Just(Variant(True, "flag", $vartype))),
+ $visitor(Just(Variant("somestr", "str", $vartype))),
+ $visitor(Nothing(OptionalType($vartype))),
+ $visitor(NULL)
+;
+
+$visitor_def = ($var) -> { return VisitOrDefault($var, 999, $handle_num AS num, $handle_flag AS flag); };
+SELECT
+ $visitor_def(Variant(5, "num", $vartype)),
+ $visitor_def(Just(Variant(True, "flag", $vartype))),
+ $visitor_def(Just(Variant("somestr", "str", $vartype))),
+ $visitor_def(Nothing(OptionalType($vartype))),
+ $visitor_def(NULL)
+;
+
+$vartype1 = Variant<num1: Int32, num2: Int32, num3: Int32>;
+SELECT
+ VariantItem(Variant(7, "num2", $vartype1)),
+ VariantItem(Just(Variant(5, "num1", $vartype1))),
+ VariantItem(Nothing(OptionalType($vartype1))),
+ VariantItem(NULL)
+; \ No newline at end of file
diff --git a/yql/essentials/tests/sql/suites/expr/variant_tuple_builtins.sql b/yql/essentials/tests/sql/suites/expr/variant_tuple_builtins.sql
new file mode 100644
index 0000000000..35d1775708
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/variant_tuple_builtins.sql
@@ -0,0 +1,30 @@
+$vartype = Variant<Int32, Bool, String>;
+$handle_num = ($x) -> { return 2 * $x; };
+$handle_flag = ($x) -> { return If($x, 200, 10); };
+$handle_str = ($x) -> { return Unwrap(CAST(LENGTH($x) AS Int32)); };
+
+$visitor = ($var) -> { return Visit($var, $handle_num, $handle_flag, $handle_str); };
+SELECT
+ $visitor(Variant(5, "0", $vartype)),
+ $visitor(Just(Variant(True, "1", $vartype))),
+ $visitor(Just(Variant("somestr", "2", $vartype))),
+ $visitor(Nothing(OptionalType($vartype))),
+ $visitor(NULL)
+;
+
+$visitor_def = ($var) -> { return VisitOrDefault($var, $handle_num, $handle_flag, 999); };
+SELECT
+ $visitor_def(Variant(5, "0", $vartype)),
+ $visitor_def(Just(Variant(True, "1", $vartype))),
+ $visitor_def(Just(Variant("somestr", "2", $vartype))),
+ $visitor_def(Nothing(OptionalType($vartype))),
+ $visitor_def(NULL)
+;
+
+$vartype1 = Variant<Int32, Int32, Int32>;
+SELECT
+ VariantItem(Variant(7, "1", $vartype1)),
+ VariantItem(Just(Variant(5, "0", $vartype1))),
+ VariantItem(Nothing(OptionalType($vartype1))),
+ VariantItem(NULL)
+; \ No newline at end of file