@@ -3848,8 +3848,11 @@ IGraphTransformer::TStatus PgSetItemWrapper(const TExprNode::TPtr& input, TExprN
38483848 inputStructType = ctx.Expr .MakeType <TStructExprType>(items);
38493849 columnOrder = TColumnOrder ({ TString (memberName) });
38503850 }
3851- }
3852- else {
3851+ } else {
3852+ if (p->Head ().IsCallable (" PgSelf" )) {
3853+ input->SetTypeAnn (ctx.Expr .MakeType <TUnitExprType>());
3854+ return IGraphTransformer::TStatus::Ok;
3855+ }
38533856 if (!EnsureListType (p->Head (), ctx.Expr )) {
38543857 return IGraphTransformer::TStatus::Error;
38553858 }
@@ -4869,6 +4872,7 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
48694872 TExprNode* setItems = nullptr ;
48704873 TExprNode* setOps = nullptr ;
48714874 bool hasSort = false ;
4875+ bool hasLimit = false ;
48724876
48734877 for (ui32 pass = 0 ; pass < 2 ; ++pass) {
48744878 for (auto & option : options.Children ()) {
@@ -4928,6 +4932,7 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
49284932 setItems = &option->Tail ();
49294933 }
49304934 } else if (optionName == " limit" || optionName == " offset" ) {
4935+ hasLimit = true ;
49314936 if (pass != 0 ) {
49324937 continue ;
49334938 }
@@ -5024,6 +5029,90 @@ IGraphTransformer::TStatus PgSelectWrapper(const TExprNode::TPtr& input, TExprNo
50245029 return IGraphTransformer::TStatus::Error;
50255030 }
50265031
5032+ const bool hasRecursive = AnyOf (setItems->Children (), [](const auto & n) {
5033+ return n->GetTypeAnn ()->GetKind () == ETypeAnnotationKind::Unit;
5034+ });
5035+
5036+ if (hasRecursive) {
5037+ bool error = false ;
5038+ if (setItems->ChildrenSize () != 2 ) {
5039+ error = true ;
5040+ } else if (setItems->Child (0 )->GetTypeAnn ()->GetKind () == ETypeAnnotationKind::Unit) {
5041+ error = true ;
5042+ } else if (setOps->Child (2 )->Content () != " union" && setOps->Child (2 )->Content () != " union_all" ) {
5043+ error = true ;
5044+ } else if (hasSort || hasLimit) {
5045+ error = true ;
5046+ }
5047+
5048+ if (error) {
5049+ ctx.Expr .AddError (TIssue (ctx.Expr .GetPosition (input->Pos ()),
5050+ " Recursive query does not have the form non-recursive-term UNION [ALL] recursive-term" ));
5051+ return IGraphTransformer::TStatus::Error;
5052+ }
5053+
5054+ auto nonRecursivePart = setItems->ChildPtr (0 );
5055+ auto recursivePart = setItems->ChildPtr (1 );
5056+ auto tableArg = ctx.Expr .NewArgument (input->Pos (), " table" );
5057+ auto order = *ctx.Types .LookupColumnOrder (*nonRecursivePart);
5058+ auto withColumnOrder = KeepColumnOrder (order, tableArg, ctx.Expr );
5059+ auto status = OptimizeExpr (recursivePart, recursivePart, [&](const TExprNode::TPtr& node, TExprContext& ctx) -> TExprNode::TPtr {
5060+ Y_UNUSED (ctx);
5061+ if (node->IsCallable (" PgSelf" )) {
5062+ return withColumnOrder;
5063+ }
5064+
5065+ return node;
5066+ }, ctx.Expr , TOptimizeExprSettings (&ctx.Types ));
5067+
5068+ YQL_ENSURE (status.Level != IGraphTransformer::TStatus::Error);
5069+
5070+ auto lambdaBody = ctx.Expr .Builder (input->Pos ())
5071+ .Callable (" PgSelect" )
5072+ .List (0 )
5073+ .List (0 )
5074+ .Atom (0 , " set_items" )
5075+ .List (1 )
5076+ .Add (0 , recursivePart)
5077+ .Seal ()
5078+ .Seal ()
5079+ .List (1 )
5080+ .Atom (0 , " set_ops" )
5081+ .List (1 )
5082+ .Atom (0 , " push" )
5083+ .Seal ()
5084+ .Seal ()
5085+ .Seal ()
5086+ .Seal ()
5087+ .Build ();
5088+
5089+ auto lambda = ctx.Expr .NewLambda (input->Pos (), ctx.Expr .NewArguments (input->Pos (), { tableArg }), std::move (lambdaBody));
5090+
5091+ output = ctx.Expr .Builder (input->Pos ())
5092+ .Callable (ToString (" PgIterate" ) + (setOps->Child (2 )->Content () == " union_all" ? " All" : " " ))
5093+ .Callable (0 , " PgSelect" )
5094+ .List (0 )
5095+ .List (0 )
5096+ .Atom (0 , " set_items" )
5097+ .List (1 )
5098+ .Add (0 , nonRecursivePart)
5099+ .Seal ()
5100+ .Seal ()
5101+ .List (1 )
5102+ .Atom (0 , " set_ops" )
5103+ .List (1 )
5104+ .Atom (0 , " push" )
5105+ .Seal ()
5106+ .Seal ()
5107+ .Seal ()
5108+ .Seal ()
5109+ .Add (1 , lambda)
5110+ .Seal ()
5111+ .Build ();
5112+
5113+ return IGraphTransformer::TStatus::Repeat;
5114+ }
5115+
50275116 TColumnOrder resultColumnOrder;
50285117 const TStructExprType* resultStructType = nullptr ;
50295118
@@ -5815,5 +5904,40 @@ IGraphTransformer::TStatus PgToRecordWrapper(const TExprNode::TPtr& input, TExpr
58155904 return IGraphTransformer::TStatus::Ok;
58165905}
58175906
5907+ IGraphTransformer::TStatus PgIterateWrapper (const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
5908+ Y_UNUSED (output);
5909+ if (!EnsureArgsCount (*input, 2 , ctx.Expr )) {
5910+ return IGraphTransformer::TStatus::Error;
5911+ }
5912+
5913+ if (!EnsureListType (input->Head (), ctx.Expr )) {
5914+ return IGraphTransformer::TStatus::Error;
5915+ }
5916+
5917+ auto & lambda = input->ChildRef (1 );
5918+ const auto status = ConvertToLambda (lambda, ctx.Expr , 1 );
5919+ if (status.Level != IGraphTransformer::TStatus::Ok) {
5920+ return status;
5921+ }
5922+
5923+ if (!UpdateLambdaAllArgumentsTypes (lambda, { input->Head ().GetTypeAnn () }, ctx.Expr )) {
5924+ return IGraphTransformer::TStatus::Error;
5925+ }
5926+
5927+ if (!lambda->GetTypeAnn ()) {
5928+ return IGraphTransformer::TStatus::Repeat;
5929+ }
5930+
5931+ if (!IsSameAnnotation (*lambda->GetTypeAnn (), *input->Head ().GetTypeAnn ())) {
5932+ ctx.Expr .AddError (TIssue (ctx.Expr .GetPosition (lambda->Pos ()), TStringBuilder () <<
5933+ " Mismatch of transform lambda return type and input type: " <<
5934+ *lambda->GetTypeAnn () << " != " << *input->Head ().GetTypeAnn ()));
5935+ return IGraphTransformer::TStatus::Error;
5936+ }
5937+
5938+ input->SetTypeAnn (input->Head ().GetTypeAnn ());
5939+ return IGraphTransformer::TStatus::Ok;
5940+ }
5941+
58185942} // namespace NTypeAnnImpl
58195943}
0 commit comments