Process derived table which can be recursive CTE. If it is non-recursive return input node unchanged. If it is recursive return new derived table which is an union of union of anchor (non-recursive) queries and union of recursive queries. Check recursive queries to satisfy various criteria. Note that our parser is right-to-left therefore nested list linked as first node in parent list and second n
| 742 | // Also, we should not change layout of original parse tree to allow it to be parsed again if |
| 743 | // needed. Therefore recursive part is built using newly allocated list nodes. |
| 744 | SelectExprNode* DsqlCompilerScratch::pass1RecursiveCte(SelectExprNode* input) |
| 745 | { |
| 746 | RecordSourceNode* const query = input->querySpec; |
| 747 | UnionSourceNode* unionQuery = nodeAs<UnionSourceNode>(query); |
| 748 | |
| 749 | if (!unionQuery) |
| 750 | { |
| 751 | if (!pass1RseIsRecursive(nodeAs<RseNode>(query))) |
| 752 | return input; |
| 753 | |
| 754 | ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << |
| 755 | // Recursive CTE (%s) must be an UNION |
| 756 | Arg::Gds(isc_dsql_cte_not_a_union) << input->alias); |
| 757 | } |
| 758 | |
| 759 | // Split queries list on two parts: anchor and recursive. |
| 760 | // In turn, the anchor part consists of: |
| 761 | // - a number of rightmost nodes combined by UNION ALL |
| 762 | // - the leftmost node representing a union of all other nodes |
| 763 | |
| 764 | MemoryPool& pool = getPool(); |
| 765 | |
| 766 | unsigned anchors = 0; |
| 767 | RecordSourceNode* anchorRse = NULL; |
| 768 | Stack<RseNode*> anchorStack(pool); |
| 769 | Stack<RseNode*> recursiveStack(pool); |
| 770 | |
| 771 | NestConst<RecordSourceNode>* iter = unionQuery->dsqlClauses->items.end(); |
| 772 | bool dsqlAll = unionQuery->dsqlAll; |
| 773 | |
| 774 | while (unionQuery) |
| 775 | { |
| 776 | RecordSourceNode* clause = *--iter; |
| 777 | |
| 778 | if (iter == unionQuery->dsqlClauses->items.begin()) |
| 779 | { |
| 780 | unionQuery = nodeAs<UnionSourceNode>(clause); |
| 781 | |
| 782 | if (unionQuery) |
| 783 | { |
| 784 | iter = unionQuery->dsqlClauses->items.end(); |
| 785 | dsqlAll = unionQuery->dsqlAll; |
| 786 | |
| 787 | clause = *--iter; |
| 788 | |
| 789 | if (!anchorRse && !dsqlAll) |
| 790 | anchorRse = unionQuery; |
| 791 | } |
| 792 | } |
| 793 | |
| 794 | RseNode* const rse = nodeAs<RseNode>(clause); |
| 795 | fb_assert(rse); |
| 796 | RseNode* const newRse = pass1RseIsRecursive(rse); |
| 797 | |
| 798 | if (newRse) // rse is recursive |
| 799 | { |
| 800 | if (anchors) |
| 801 | { |
nothing calls this directly
no test coverage detected