@@ -282,6 +282,7 @@ class TConverter : public IPGParseEvents {
282282 ui32 ReadIndex = 0 ;
283283 TViews Views;
284284 TVector<TViews> CTE;
285+ const TView* CurrentRecursiveView = nullptr ;
285286 TVector<NYql::TPosition> Positions = {NYql::TPosition ()};
286287 THashMap<TString, TString> ParamNameToPgTypeName;
287288 THashMap<TString, Ydb::TypedValue> AutoParamValues;
@@ -450,7 +451,7 @@ class TConverter : public IPGParseEvents {
450451 }
451452 switch (NodeTag (node)) {
452453 case T_SelectStmt:
453- return ParseSelectStmt (CAST_NODE (SelectStmt, node), false ) != nullptr ;
454+ return ParseSelectStmt (CAST_NODE (SelectStmt, node), {. Inner = false } ) != nullptr ;
454455 case T_InsertStmt:
455456 return ParseInsertStmt (CAST_NODE (InsertStmt, node)) != nullptr ;
456457 case T_UpdateStmt:
@@ -759,15 +760,20 @@ class TConverter : public IPGParseEvents {
759760 using TTraverseSelectStack = TStack<std::pair<const SelectStmt*, bool >>;
760761 using TTraverseNodeStack = TStack<std::pair<const Node*, bool >>;
761762
763+ struct TSelectStmtSettings {
764+ bool Inner = true ;
765+ mutable TVector<TAstNode*> TargetColumns;
766+ bool AllowEmptyResSet = false ;
767+ bool EmitPgStar = false ;
768+ bool FillTargetColumns = false ;
769+ bool UnknownsAllowed = false ;
770+ const TView* Recursive = nullptr ;
771+ };
772+
762773 [[nodiscard]]
763774 TAstNode* ParseSelectStmt (
764775 const SelectStmt* value,
765- bool inner,
766- TVector <TAstNode*> targetColumns = {},
767- bool allowEmptyResSet = false ,
768- bool emitPgStar = false ,
769- bool fillTargetColumns = false ,
770- bool unknownsAllowed = false
776+ const TSelectStmtSettings& selectSettings
771777 ) {
772778 if (Settings.Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW) {
773779 if (HasSelectInLimitedView) {
@@ -778,11 +784,14 @@ class TConverter : public IPGParseEvents {
778784 HasSelectInLimitedView = true ;
779785 }
780786
781- bool isValuesClauseOfInsertStmt = fillTargetColumns ;
787+ bool isValuesClauseOfInsertStmt = selectSettings. FillTargetColumns ;
782788
783789 State.CTE .emplace_back ();
790+ auto prevRecursiveView = State.CurrentRecursiveView ;
791+ State.CurrentRecursiveView = selectSettings.Recursive ;
784792 Y_DEFER {
785793 State.CTE .pop_back ();
794+ State.CurrentRecursiveView = prevRecursiveView;
786795 };
787796
788797 if (value->withClause ) {
@@ -904,11 +913,11 @@ class TConverter : public IPGParseEvents {
904913 auto node = ListNodeNth (x->fromClause , i);
905914 if (NodeTag (node) != T_JoinExpr) {
906915 auto p = ParseFromClause (node);
907- if (!p. Source ) {
916+ if (!p) {
908917 return nullptr ;
909918 }
910919
911- AddFrom (p, fromList);
920+ AddFrom (* p, fromList);
912921 joinOps.push_back (QL ());
913922 } else {
914923 TTraverseNodeStack traverseNodeStack;
@@ -920,10 +929,10 @@ class TConverter : public IPGParseEvents {
920929 if (NodeTag (top.first ) != T_JoinExpr) {
921930 // leaf
922931 auto p = ParseFromClause (top.first );
923- if (!p. Source ) {
932+ if (!p) {
924933 return nullptr ;
925934 }
926- AddFrom (p, fromList);
935+ AddFrom (* p, fromList);
927936 traverseNodeStack.pop ();
928937 } else {
929938 auto join = CAST_NODE (JoinExpr, top.first );
@@ -1105,7 +1114,7 @@ class TConverter : public IPGParseEvents {
11051114 return nullptr ;
11061115 }
11071116
1108- if (!allowEmptyResSet && (ListLength (x->valuesLists ) == 0 ) && (ListLength (x->targetList ) == 0 )) {
1117+ if (!selectSettings. AllowEmptyResSet && (ListLength (x->valuesLists ) == 0 ) && (ListLength (x->targetList ) == 0 )) {
11091118 AddError (" SelectStmt: both values_list and target_list are not allowed to be empty" );
11101119 return nullptr ;
11111120 }
@@ -1133,11 +1142,11 @@ class TConverter : public IPGParseEvents {
11331142
11341143 TVector<TAstNode*> res;
11351144 ui32 i = 0 ;
1136- if (emitPgStar && id + 1 == setItems.size ()) {
1145+ if (selectSettings. EmitPgStar && id + 1 == setItems.size ()) {
11371146 res.emplace_back (CreatePgStarResultItem ());
11381147 i++;
11391148 }
1140- bool maybeSelectWithJustSetConfig = !inner && !sort && windowItems.empty () && !having && !groupBy && !whereFilter && !x->distinctClause && ListLength (x->targetList ) == 1 ;
1149+ bool maybeSelectWithJustSetConfig = !selectSettings. Inner && !sort && windowItems.empty () && !having && !groupBy && !whereFilter && !x->distinctClause && ListLength (x->targetList ) == 1 ;
11411150 if (maybeSelectWithJustSetConfig) {
11421151 auto node = ListNodeNth (x->targetList , 0 );
11431152 if (NodeTag (node) != T_ResTarget) {
@@ -1193,13 +1202,13 @@ class TConverter : public IPGParseEvents {
11931202 }
11941203
11951204 TVector<TAstNode*> setItemOptions;
1196- if (emitPgStar ) {
1205+ if (selectSettings. EmitPgStar ) {
11971206 setItemOptions.push_back (QL (QA (" emit_pg_star" )));
11981207 }
1199- if (!targetColumns .empty ()) {
1200- setItemOptions.push_back (QL (QA (" target_columns" ), QVL (targetColumns. data (), targetColumns .size ())));
1208+ if (!selectSettings. TargetColumns .empty ()) {
1209+ setItemOptions.push_back (QL (QA (" target_columns" ), QVL (selectSettings. TargetColumns . data (), selectSettings. TargetColumns .size ())));
12011210 }
1202- if (fillTargetColumns ) {
1211+ if (selectSettings. FillTargetColumns ) {
12031212 setItemOptions.push_back (QL (QA (" fill_target_columns" )));
12041213 }
12051214 if (ListLength (x->targetList ) > 0 ) {
@@ -1247,7 +1256,7 @@ class TConverter : public IPGParseEvents {
12471256 setItemOptions.push_back (QL (QA (" sort" ), sort));
12481257 }
12491258
1250- if (unknownsAllowed || hasCombiningQueries) {
1259+ if (selectSettings. UnknownsAllowed || hasCombiningQueries) {
12511260 setItemOptions.push_back (QL (QA (" unknowns_allowed" )));
12521261 }
12531262
@@ -1312,7 +1321,7 @@ class TConverter : public IPGParseEvents {
13121321
13131322 auto output = L (A (" PgSelect" ), QVL (selectOptions.data (), selectOptions.size ()));
13141323
1315- if (inner ) {
1324+ if (selectSettings. Inner ) {
13161325 return output;
13171326 }
13181327
@@ -1345,19 +1354,14 @@ class TConverter : public IPGParseEvents {
13451354 [[nodiscard]]
13461355 bool ParseWithClause (const WithClause* value) {
13471356 AT_LOCATION (value);
1348- if (value->recursive ) {
1349- AddError (" WithClause: recursion is not supported" );
1350- return false ;
1351- }
1352-
13531357 for (int i = 0 ; i < ListLength (value->ctes ); ++i) {
13541358 auto object = ListNodeNth (value->ctes , i);
13551359 if (NodeTag (object) != T_CommonTableExpr) {
13561360 NodeNotImplemented (value, object);
13571361 return false ;
13581362 }
13591363
1360- if (!ParseCTE (CAST_NODE (CommonTableExpr, object))) {
1364+ if (!ParseCTE (CAST_NODE (CommonTableExpr, object), value-> recursive )) {
13611365 return false ;
13621366 }
13631367 }
@@ -1366,7 +1370,7 @@ class TConverter : public IPGParseEvents {
13661370 }
13671371
13681372 [[nodiscard]]
1369- bool ParseCTE (const CommonTableExpr* value) {
1373+ bool ParseCTE (const CommonTableExpr* value, bool recursive ) {
13701374 AT_LOCATION (value);
13711375 TView view;
13721376 view.Name = value->ctename ;
@@ -1386,7 +1390,11 @@ class TConverter : public IPGParseEvents {
13861390 return false ;
13871391 }
13881392
1389- view.Source = ParseSelectStmt (CAST_NODE (SelectStmt, value->ctequery ), true );
1393+ view.Source = ParseSelectStmt (CAST_NODE (SelectStmt, value->ctequery ), {
1394+ .Inner = true ,
1395+ .Recursive = recursive ? &view : nullptr
1396+ });
1397+
13901398 if (!view.Source ) {
13911399 return false ;
13921400 }
@@ -1531,12 +1539,14 @@ class TConverter : public IPGParseEvents {
15311539 const auto select = (value->selectStmt )
15321540 ? ParseSelectStmt (
15331541 CAST_NODE (SelectStmt, value->selectStmt ),
1534- true ,
1535- targetColumns,
1536- /* allowEmptyResSet=*/ false ,
1537- /* emitPgStar=*/ false ,
1538- /* fillTargetColumns=*/ true ,
1539- /* unknownsAllowed=*/ true )
1542+ {
1543+ .Inner = true ,
1544+ .TargetColumns = targetColumns,
1545+ .AllowEmptyResSet = false ,
1546+ .EmitPgStar = false ,
1547+ .FillTargetColumns = true ,
1548+ .UnknownsAllowed = true
1549+ })
15401550 : L (A (" Void" ));
15411551 if (!select) {
15421552 return nullptr ;
@@ -1572,12 +1582,13 @@ class TConverter : public IPGParseEvents {
15721582 };
15731583 const auto select = ParseSelectStmt (
15741584 &selectStmt,
1575- /* inner */ true ,
1576- /* targetColumns */ {},
1577- /* allowEmptyResSet */ true ,
1578- /* emitPgStar=*/ true ,
1579- /* fillTargetColumns=*/ false ,
1580- /* unknownsAllowed=*/ true
1585+ {
1586+ .Inner = true ,
1587+ .AllowEmptyResSet = true ,
1588+ .EmitPgStar = true ,
1589+ .FillTargetColumns = false ,
1590+ .UnknownsAllowed = true
1591+ }
15811592 );
15821593 if (!select) {
15831594 return nullptr ;
@@ -1675,7 +1686,7 @@ class TConverter : public IPGParseEvents {
16751686 }
16761687
16771688
1678- view.Source = ParseSelectStmt (CAST_NODE (SelectStmt, value->query ), true );
1689+ view.Source = ParseSelectStmt (CAST_NODE (SelectStmt, value->query ), { . Inner = true } );
16791690 if (!view.Source ) {
16801691 return nullptr ;
16811692 }
@@ -2478,10 +2489,10 @@ class TConverter : public IPGParseEvents {
24782489
24792490 TVector<TAstNode*> fromList;
24802491 auto p = ParseRangeVar (value->relation );
2481- if (!p. Source ) {
2492+ if (!p) {
24822493 return nullptr ;
24832494 }
2484- AddFrom (p, fromList);
2495+ AddFrom (* p, fromList);
24852496
24862497 TAstNode* whereFilter = nullptr ;
24872498 if (value->whereClause ) {
@@ -2878,7 +2889,7 @@ class TConverter : public IPGParseEvents {
28782889 return State.Statements .back ();
28792890 }
28802891
2881- TFromDesc ParseFromClause (const Node* node) {
2892+ TMaybe< TFromDesc> ParseFromClause (const Node* node) {
28822893 switch (NodeTag (node)) {
28832894 case T_RangeVar:
28842895 return ParseRangeVar (CAST_NODE (RangeVar, node));
@@ -2907,7 +2918,12 @@ class TConverter : public IPGParseEvents {
29072918 fromList.push_back (QL (L (A (" Right!" ), A (label)), aliasNode, colNamesTuple));
29082919 ++State.ReadIndex ;
29092920 } else {
2910- fromList.push_back (QL (p.Source , aliasNode, colNamesTuple));
2921+ auto source = p.Source ;
2922+ if (!source) {
2923+ source = L (A (" PgSelf" ));
2924+ }
2925+
2926+ fromList.push_back (QL (source, aliasNode, colNamesTuple));
29112927 }
29122928 }
29132929
@@ -3031,7 +3047,7 @@ class TConverter : public IPGParseEvents {
30313047 /* isSink */ true , isScheme);
30323048 }
30333049
3034- TFromDesc ParseRangeVar (const RangeVar* value) {
3050+ TMaybe< TFromDesc> ParseRangeVar (const RangeVar* value) {
30353051 AT_LOCATION (value);
30363052
30373053 const TView* view = nullptr ;
@@ -3043,6 +3059,10 @@ class TConverter : public IPGParseEvents {
30433059 break ;
30443060 }
30453061 }
3062+ if (!view && State.CurrentRecursiveView && State.CurrentRecursiveView ->Name == value->relname ) {
3063+ view = State.CurrentRecursiveView ;
3064+ }
3065+
30463066 if (!view) {
30473067 auto viewIt = State.Views .find (value->relname );
30483068 if (viewIt != State.Views .end ()) {
@@ -3062,7 +3082,7 @@ class TConverter : public IPGParseEvents {
30623082 }
30633083
30643084 if (view) {
3065- return { view->Source , alias, colnames.empty () ? view->ColNames : colnames, false };
3085+ return TFromDesc{ view->Source , alias, colnames.empty () ? view->ColNames : colnames, false };
30663086 }
30673087
30683088 TString schemaname = value->schemaname ;
@@ -3088,7 +3108,7 @@ class TConverter : public IPGParseEvents {
30883108 if (!s) {
30893109 return {};
30903110 }
3091- return { s, alias, colnames, true };
3111+ return TFromDesc { s, alias, colnames, true };
30923112 }
30933113 }
30943114
@@ -3108,7 +3128,7 @@ class TConverter : public IPGParseEvents {
31083128 L (A (" Void" )),
31093129 QL ()
31103130 );
3111- return {
3131+ return TFromDesc {
31123132 readExpr,
31133133 alias,
31143134 colnames,
@@ -3168,7 +3188,7 @@ class TConverter : public IPGParseEvents {
31683188 );
31693189 }
31703190
3171- TFromDesc ParseRangeFunction (const RangeFunction* value) {
3191+ TMaybe< TFromDesc> ParseRangeFunction (const RangeFunction* value) {
31723192 if (value->lateral ) {
31733193 AddError (" RangeFunction: unsupported lateral" );
31743194 return {};
@@ -3229,10 +3249,10 @@ class TConverter : public IPGParseEvents {
32293249 return {};
32303250 }
32313251
3232- return { func, alias, colnames, false };
3252+ return TFromDesc { func, alias, colnames, false };
32333253 }
32343254
3235- TFromDesc ParseRangeSubselect (const RangeSubselect* value) {
3255+ TMaybe< TFromDesc> ParseRangeSubselect (const RangeSubselect* value) {
32363256 if (value->lateral ) {
32373257 AddError (" RangeSubselect: unsupported lateral" );
32383258 return {};
@@ -3259,7 +3279,7 @@ class TConverter : public IPGParseEvents {
32593279 return {};
32603280 }
32613281
3262- return { ParseSelectStmt (CAST_NODE (SelectStmt, value->subquery ), true ), alias, colnames, false };
3282+ return TFromDesc { ParseSelectStmt (CAST_NODE (SelectStmt, value->subquery ), { . Inner = true } ), alias, colnames, false };
32633283 }
32643284
32653285 TAstNode* ParseNullTestExpr (const NullTest* value, const TExprSettings& settings) {
@@ -3811,7 +3831,7 @@ class TConverter : public IPGParseEvents {
38113831 rowTest = L (A (" Void" ));
38123832 }
38133833
3814- auto select = ParseSelectStmt (CAST_NODE (SelectStmt, value->subselect ), true );
3834+ auto select = ParseSelectStmt (CAST_NODE (SelectStmt, value->subselect ), {. Inner = true } );
38153835 if (!select) {
38163836 return nullptr ;
38173837 }
0 commit comments