From eb8df675fa239b47ab05e10dfa4b502ac3ebc8a2 Mon Sep 17 00:00:00 2001 From: vvvv Date: Fri, 26 Sep 2025 18:00:55 +0300 Subject: YQL-20339 better error pos for dynamic checks init commit_hash:11d97d8b3cbe39684d0813f20fdba59085a2e265 --- yql/essentials/ast/yql_expr.h | 12 ++++++ yql/essentials/core/type_ann/type_ann_dict.cpp | 46 +++++++++++++--------- yql/essentials/core/yql_expr_type_annotation.cpp | 33 +++++++++++----- yql/essentials/core/yql_expr_type_annotation.h | 3 +- .../providers/common/mkql/yql_provider_mkql.cpp | 46 ++++++++++++++-------- .../tests/sql/minirun/part3/canondata/result.json | 6 +-- 6 files changed, 98 insertions(+), 48 deletions(-) diff --git a/yql/essentials/ast/yql_expr.h b/yql/essentials/ast/yql_expr.h index 426900071ee..6a77fdaabc6 100644 --- a/yql/essentials/ast/yql_expr.h +++ b/yql/essentials/ast/yql_expr.h @@ -308,6 +308,18 @@ public: return GetKind() == ETypeAnnotationKind::Scalar; } + bool IsLinearOrDynamicLinear() const { + return IsLinear() || IsDynamicLinear(); + } + + bool IsLinear() const { + return GetKind() == ETypeAnnotationKind::Linear; + } + + bool IsDynamicLinear() const { + return GetKind() == ETypeAnnotationKind::DynamicLinear; + } + bool HasFixedSizeRepr() const { return (GetFlags() & (TypeHasDynamicSize | TypeNonPersistable | TypeNonComputable)) == 0; } diff --git a/yql/essentials/core/type_ann/type_ann_dict.cpp b/yql/essentials/core/type_ann/type_ann_dict.cpp index 5afdffcdb4c..0ecec612743 100644 --- a/yql/essentials/core/type_ann/type_ann_dict.cpp +++ b/yql/essentials/core/type_ann/type_ann_dict.cpp @@ -17,7 +17,8 @@ const TTypeAnnotationNode* ConvertDictTypeToMutDictType(const TDictExprType* dic bool ParseMutDictType(TPositionHandle pos, const TTypeAnnotationNode* type, const TDictExprType*& dictType, TExprContext& ctx, TTypeAnnotationContext& typeCtx) { - auto innerType = type->Cast()->GetItemType(); + bool isDynamic; + auto innerType = GetLinearItemType(*type, isDynamic); auto resType = innerType->UserCast(ctx.GetPosition(pos), ctx); if (!resType) { return false; @@ -113,7 +114,8 @@ IGraphTransformer::TStatus FromMutDictWrapper(const TExprNode::TPtr& input, TExp return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -138,7 +140,8 @@ IGraphTransformer::TStatus MutDictBlindOpWrapper(const TExprNode::TPtr& input, T return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -160,7 +163,7 @@ IGraphTransformer::TStatus MutDictBlindOpWrapper(const TExprNode::TPtr& input, T } } - input->SetTypeAnn(input->Head().GetTypeAnn()); + input->SetTypeAnn(linType); return IGraphTransformer::TStatus::Ok; } @@ -174,7 +177,8 @@ IGraphTransformer::TStatus MutDictPopWrapper(const TExprNode::TPtr& input, TExpr return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -190,7 +194,7 @@ IGraphTransformer::TStatus MutDictPopWrapper(const TExprNode::TPtr& input, TExpr } auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, ctx.Expr.MakeType(dictType->GetPayloadType())}); input->SetTypeAnn(pair); @@ -207,7 +211,8 @@ IGraphTransformer::TStatus MutDictContainsWrapper(const TExprNode::TPtr& input, return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -223,7 +228,7 @@ IGraphTransformer::TStatus MutDictContainsWrapper(const TExprNode::TPtr& input, } auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, ctx.Expr.MakeType(NUdf::EDataSlot::Bool)}); input->SetTypeAnn(pair); @@ -240,7 +245,8 @@ IGraphTransformer::TStatus MutDictHasItemsWrapper(const TExprNode::TPtr& input, return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -251,7 +257,7 @@ IGraphTransformer::TStatus MutDictHasItemsWrapper(const TExprNode::TPtr& input, } auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, ctx.Expr.MakeType(NUdf::EDataSlot::Bool)}); input->SetTypeAnn(pair); @@ -268,7 +274,8 @@ IGraphTransformer::TStatus MutDictLengthWrapper(const TExprNode::TPtr& input, TE return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -279,7 +286,7 @@ IGraphTransformer::TStatus MutDictLengthWrapper(const TExprNode::TPtr& input, TE } auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, ctx.Expr.MakeType(NUdf::EDataSlot::Uint64)}); input->SetTypeAnn(pair); @@ -296,7 +303,8 @@ IGraphTransformer::TStatus MutDictItemsWrapper(const TExprNode::TPtr& input, TEx return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -313,7 +321,7 @@ IGraphTransformer::TStatus MutDictItemsWrapper(const TExprNode::TPtr& input, TEx ); auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, list}); input->SetTypeAnn(pair); @@ -330,7 +338,8 @@ IGraphTransformer::TStatus MutDictKeysWrapper(const TExprNode::TPtr& input, TExp return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -345,7 +354,7 @@ IGraphTransformer::TStatus MutDictKeysWrapper(const TExprNode::TPtr& input, TExp ); auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, list}); input->SetTypeAnn(pair); @@ -362,7 +371,8 @@ IGraphTransformer::TStatus MutDictPayloadsWrapper(const TExprNode::TPtr& input, return IGraphTransformer::TStatus::Error; } - auto status = ConvertToLinearType(input->ChildRef(0), ctx.Expr); + const TLinearExprType* linType; + auto status = ConvertToLinearType(*input->Child(0), ctx.Expr, linType); if (status != IGraphTransformer::TStatus::Ok) { return status; } @@ -377,7 +387,7 @@ IGraphTransformer::TStatus MutDictPayloadsWrapper(const TExprNode::TPtr& input, ); auto pair = ctx.Expr.MakeType(TTypeAnnotationNode::TListType{ - input->Head().GetTypeAnn(), + linType, list}); input->SetTypeAnn(pair); diff --git a/yql/essentials/core/yql_expr_type_annotation.cpp b/yql/essentials/core/yql_expr_type_annotation.cpp index b6f6969e468..3d907e6151f 100644 --- a/yql/essentials/core/yql_expr_type_annotation.cpp +++ b/yql/essentials/core/yql_expr_type_annotation.cpp @@ -3881,28 +3881,29 @@ bool EnsureDataOrOptionalOfData(TPositionHandle position, const TTypeAnnotationN return true; } -IGraphTransformer::TStatus ConvertToLinearType(TExprNode::TPtr& node, TExprContext& ctx) { - if (HasError(node->GetTypeAnn(), ctx)) { +IGraphTransformer::TStatus ConvertToLinearType(const TExprNode& node, TExprContext& ctx, const TLinearExprType*& retType) { + if (HasError(node.GetTypeAnn(), ctx)) { return IGraphTransformer::TStatus::Error; } - if (!node->GetTypeAnn()) { - ctx.AddError(TIssue(ctx.GetPosition(node->Pos()), TStringBuilder() << + if (!node.GetTypeAnn()) { + ctx.AddError(TIssue(ctx.GetPosition(node.Pos()), TStringBuilder() << "Expected (dynamic) linear type, but got lambda")); return IGraphTransformer::TStatus::Error; } - if (node->GetTypeAnn()->GetKind() == ETypeAnnotationKind::Linear) { + if (node.GetTypeAnn()->GetKind() == ETypeAnnotationKind::Linear) { + retType = node.GetTypeAnn()->Cast(); return IGraphTransformer::TStatus::Ok; } - if (node->GetTypeAnn()->GetKind() == ETypeAnnotationKind::DynamicLinear) { - node = ctx.NewCallable(node->Pos(), "FromDynamicLinear", { node }); - return IGraphTransformer::TStatus::Repeat; + if (node.GetTypeAnn()->GetKind() == ETypeAnnotationKind::DynamicLinear) { + retType = ctx.MakeType(node.GetTypeAnn()->Cast()->GetItemType()); + return IGraphTransformer::TStatus::Ok; } - ctx.AddError(TIssue(ctx.GetPosition(node->Pos()), TStringBuilder() << - "Expected (dynamic) linear type, but got: " << *node->GetTypeAnn())); + ctx.AddError(TIssue(ctx.GetPosition(node.Pos()), TStringBuilder() << + "Expected (dynamic) linear type, but got: " << *node.GetTypeAnn())); return IGraphTransformer::TStatus::Error; } @@ -6568,6 +6569,18 @@ const TTypeAnnotationNode* UnpackOptionalBlockItemType(const TTypeAnnotationNode } } +const TTypeAnnotationNode* GetLinearItemType(const TTypeAnnotationNode& type, bool& isDynamic) { + YQL_ENSURE(type.IsLinearOrDynamicLinear()); + const auto kind = type.GetKind(); + if (kind == ETypeAnnotationKind::Linear) { + isDynamic = false; + return type.Cast()->GetItemType(); + } + + isDynamic = true; + return type.Cast()->GetItemType(); +} + const TTypeAnnotationNode* AggApplySerializedStateType(const TExprNode::TPtr& input, TExprContext& ctx) { auto name = input->Child(0)->Content(); if (name == "count" || name == "count_all" || name == "min" || name == "max" || name == "some") { diff --git a/yql/essentials/core/yql_expr_type_annotation.h b/yql/essentials/core/yql_expr_type_annotation.h index d781a3d7fca..03762afa815 100644 --- a/yql/essentials/core/yql_expr_type_annotation.h +++ b/yql/essentials/core/yql_expr_type_annotation.h @@ -172,7 +172,7 @@ bool EnsureEquatableKey(TPositionHandle position, const TTypeAnnotationNode* key bool EnsureHashableKey(TPositionHandle position, const TTypeAnnotationNode* keyType, TExprContext& ctx); bool EnsureDataOrOptionalOfData(const TExprNode& node, bool& isOptional, const TDataExprType*& dataType, TExprContext& ctx); bool EnsureDataOrOptionalOfData(TPositionHandle position, const TTypeAnnotationNode* type, bool& isOptional, const TDataExprType*& dataType, TExprContext& ctx); -IGraphTransformer::TStatus ConvertToLinearType(TExprNode::TPtr& node, TExprContext& ctx); +IGraphTransformer::TStatus ConvertToLinearType(const TExprNode& node, TExprContext& ctx, const TLinearExprType*& retType); bool EnsureLinearType(const TExprNode& node, TExprContext& ctx); bool EnsureDynamicLinearType(const TExprNode& node, TExprContext& ctx); bool EnsurePersistable(const TExprNode& node, TExprContext& ctx); @@ -372,6 +372,7 @@ bool EnsureScalarType(const TExprNode& node, TExprContext& ctx); bool EnsureScalarType(TPositionHandle position, const TTypeAnnotationNode& type, TExprContext& ctx); const TTypeAnnotationNode* GetBlockItemType(const TTypeAnnotationNode& type, bool& isScalar); const TTypeAnnotationNode* UnpackOptionalBlockItemType(const TTypeAnnotationNode& type, TExprContext& ctx, bool convertToScalar); +const TTypeAnnotationNode* GetLinearItemType(const TTypeAnnotationNode& type, bool& isDynamic); const TTypeAnnotationNode* AggApplySerializedStateType(const TExprNode::TPtr& input, TExprContext& ctx); bool GetSumResultType(const TPositionHandle& pos, const TTypeAnnotationNode& inputType, const TTypeAnnotationNode*& retType, TExprContext& ctx); diff --git a/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp b/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp index 66b2ea967a7..5ac4ba19294 100644 --- a/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp +++ b/yql/essentials/providers/common/mkql/yql_provider_mkql.cpp @@ -31,10 +31,22 @@ namespace NYql { namespace NCommon { TType* ExtractDictTypeFromMutDic(const TExprNode& node, TMkqlBuildContext& ctx) { - auto tag = node.GetTypeAnn()->Cast()->GetItemType()->Cast()->GetTag(); + bool isDynamic; + auto innerType = GetLinearItemType(*node.GetTypeAnn(), isDynamic); + auto tag = innerType->Cast()->GetTag(); return ctx.BuildType(node, *NTypeAnnImpl::GetCachedMutDictType(tag, ctx.ExprCtx)); } +TRuntimeNode EnsureLinear(TRuntimeNode value, const TPositionHandle posHandle, TMkqlBuildContext& ctx) { + auto linType = AS_TYPE(TLinearType, value); + if (!linType->IsDynamic()) { + return value; + } + + auto pos = ctx.ExprCtx.GetPosition(posHandle); + return ctx.ProgramBuilder.FromDynamicLinear(value, pos.File, pos.Row, pos.Column); +} + TRuntimeNode WideTopImpl(const TExprNode& node, TMkqlBuildContext& ctx, TRuntimeNode(TProgramBuilder::*func)(TRuntimeNode, TRuntimeNode, const std::vector>&)) { const auto flow = MkqlBuildExpr(node.Head(), ctx); @@ -2460,12 +2472,14 @@ TMkqlCommonCallableCompiler::TShared::TShared() { }); AddCallable("FromMutDict", [](const TExprNode& node, TMkqlBuildContext& ctx) { + auto type = node.Head().GetTypeAnn(); + bool isDynamic; + auto innerType = GetLinearItemType(*type, isDynamic); auto dictType = NTypeAnnImpl::GetCachedMutDictType( - node.Head().GetTypeAnn()->Cast()->GetItemType()->Cast()->GetTag(), - ctx.ExprCtx); + innerType->Cast()->GetTag(), ctx.ExprCtx); return ctx.ProgramBuilder.FromMutDict( ctx.BuildType(node, *dictType), - MkqlBuildExpr(node.Head(), ctx)); + EnsureLinear(MkqlBuildExpr(node.Head(), ctx), node.Pos(), ctx)); }); AddCallable("MutDictCreate", [](const TExprNode& node, TMkqlBuildContext& ctx) { @@ -2495,7 +2509,7 @@ TMkqlCommonCallableCompiler::TShared::TShared() { auto key = MkqlBuildExpr(*node.Child(1), ctx); auto payload = MkqlBuildExpr(*node.Child(2), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictInsert(dictType, dict, key, payload); + return ctx.ProgramBuilder.MutDictInsert(dictType, EnsureLinear(dict, node.Pos(), ctx), key, payload); }); AddCallable("MutDictUpsert", [](const TExprNode& node, TMkqlBuildContext& ctx) { @@ -2503,7 +2517,7 @@ TMkqlCommonCallableCompiler::TShared::TShared() { auto key = MkqlBuildExpr(*node.Child(1), ctx); auto payload = MkqlBuildExpr(*node.Child(2), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictUpsert(dictType, dict, key, payload); + return ctx.ProgramBuilder.MutDictUpsert(dictType, EnsureLinear(dict, node.Pos(), ctx), key, payload); }); AddCallable("MutDictUpdate", [](const TExprNode& node, TMkqlBuildContext& ctx) { @@ -2511,65 +2525,65 @@ TMkqlCommonCallableCompiler::TShared::TShared() { auto key = MkqlBuildExpr(*node.Child(1), ctx); auto payload = MkqlBuildExpr(*node.Child(2), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictUpdate(dictType, dict, key, payload); + return ctx.ProgramBuilder.MutDictUpdate(dictType, EnsureLinear(dict, node.Pos(), ctx), key, payload); }); AddCallable("MutDictRemove", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto key = MkqlBuildExpr(*node.Child(1), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictRemove(dictType, dict, key); + return ctx.ProgramBuilder.MutDictRemove(dictType, EnsureLinear(dict, node.Pos(), ctx), key); }); AddCallable("MutDictPop", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto key = MkqlBuildExpr(*node.Child(1), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictPop(dictType, dict, key); + return ctx.ProgramBuilder.MutDictPop(dictType, EnsureLinear(dict, node.Pos(), ctx), key); }); AddCallable("MutDictContains", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto key = MkqlBuildExpr(*node.Child(1), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictContains(dictType, dict, key); + return ctx.ProgramBuilder.MutDictContains(dictType, EnsureLinear(dict, node.Pos(), ctx), key); }); AddCallable("MutDictLookup", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto key = MkqlBuildExpr(*node.Child(1), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictLookup(dictType, dict, key); + return ctx.ProgramBuilder.MutDictLookup(dictType, EnsureLinear(dict, node.Pos(), ctx), key); }); AddCallable("MutDictHasItems", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictHasItems(dictType, dict); + return ctx.ProgramBuilder.MutDictHasItems(dictType, EnsureLinear(dict, node.Pos(), ctx)); }); AddCallable("MutDictLength", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictLength(dictType, dict); + return ctx.ProgramBuilder.MutDictLength(dictType, EnsureLinear(dict, node.Pos(), ctx)); }); AddCallable("MutDictItems", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictItems(dictType, dict); + return ctx.ProgramBuilder.MutDictItems(dictType, EnsureLinear(dict, node.Pos(), ctx)); }); AddCallable("MutDictKeys", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictKeys(dictType, dict); + return ctx.ProgramBuilder.MutDictKeys(dictType, EnsureLinear(dict, node.Pos(), ctx)); }); AddCallable("MutDictPayloads", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto dict = MkqlBuildExpr(*node.Child(0), ctx); auto dictType = ExtractDictTypeFromMutDic(node.Head(), ctx); - return ctx.ProgramBuilder.MutDictPayloads(dictType, dict); + return ctx.ProgramBuilder.MutDictPayloads(dictType, EnsureLinear(dict, node.Pos(), ctx)); }); AddCallable("Error", [](const TExprNode& node, TMkqlBuildContext& ctx)->NKikimr::NMiniKQL::TRuntimeNode { diff --git a/yql/essentials/tests/sql/minirun/part3/canondata/result.json b/yql/essentials/tests/sql/minirun/part3/canondata/result.json index 4be32269fe2..5ba7d0d0936 100644 --- a/yql/essentials/tests/sql/minirun/part3/canondata/result.json +++ b/yql/essentials/tests/sql/minirun/part3/canondata/result.json @@ -874,9 +874,9 @@ ], "test.test[linear-mutdict_empty_dyn-default.txt-Debug]": [ { - "checksum": "9d8dcea623975c3b8e65ae32be9b26f8", - "size": 364, - "uri": "https://{canondata_backend}/212715/7e4d6e7e6e77f1e937efd3a6c5fbc32ec1d1c0e7/resource.tar.gz#test.test_linear-mutdict_empty_dyn-default.txt-Debug_/opt.yql" + "checksum": "2964cf602ea96f9e2c6ffb8f37b5b575", + "size": 344, + "uri": "https://{canondata_backend}/1130705/8fa2a861588fa16ec2224a74b617badd67af634e/resource.tar.gz#test.test_linear-mutdict_empty_dyn-default.txt-Debug_/opt.yql" } ], "test.test[linear-mutdict_empty_dyn-default.txt-Results]": [ -- cgit v1.3