* Check that recursive queries are well-formed. */
| 847 | * Check that recursive queries are well-formed. |
| 848 | */ |
| 849 | static void |
| 850 | checkWellFormedRecursion(CteState *cstate) |
| 851 | { |
| 852 | int i; |
| 853 | |
| 854 | for (i = 0; i < cstate->numitems; i++) |
| 855 | { |
| 856 | CommonTableExpr *cte = cstate->items[i].cte; |
| 857 | SelectStmt *stmt = (SelectStmt *) cte->ctequery; |
| 858 | |
| 859 | Assert(!IsA(stmt, Query)); /* not analyzed yet */ |
| 860 | |
| 861 | /* Ignore items that weren't found to be recursive */ |
| 862 | if (!cte->cterecursive) |
| 863 | continue; |
| 864 | |
| 865 | /* Must be a SELECT statement */ |
| 866 | if (!IsA(stmt, SelectStmt)) |
| 867 | ereport(ERROR, |
| 868 | (errcode(ERRCODE_INVALID_RECURSION), |
| 869 | errmsg("recursive query \"%s\" must not contain data-modifying statements", |
| 870 | cte->ctename), |
| 871 | parser_errposition(cstate->pstate, cte->location))); |
| 872 | |
| 873 | /* Must have top-level UNION */ |
| 874 | if (stmt->op != SETOP_UNION) |
| 875 | ereport(ERROR, |
| 876 | (errcode(ERRCODE_INVALID_RECURSION), |
| 877 | errmsg("recursive query \"%s\" does not have the form non-recursive-term UNION [ALL] recursive-term", |
| 878 | cte->ctename), |
| 879 | parser_errposition(cstate->pstate, cte->location))); |
| 880 | |
| 881 | /* The left-hand operand mustn't contain self-reference at all */ |
| 882 | cstate->curitem = i; |
| 883 | cstate->innerwiths = NIL; |
| 884 | cstate->selfrefcount = 0; |
| 885 | cstate->context = RECURSION_NONRECURSIVETERM; |
| 886 | checkWellFormedRecursionWalker((Node *) stmt->larg, cstate); |
| 887 | Assert(cstate->innerwiths == NIL); |
| 888 | |
| 889 | /* Right-hand operand should contain one reference in a valid place */ |
| 890 | cstate->curitem = i; |
| 891 | cstate->innerwiths = NIL; |
| 892 | cstate->selfrefcount = 0; |
| 893 | cstate->context = RECURSION_OK; |
| 894 | checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate); |
| 895 | Assert(cstate->innerwiths == NIL); |
| 896 | if (cstate->selfrefcount != 1) /* shouldn't happen */ |
| 897 | elog(ERROR, "missing recursive reference"); |
| 898 | |
| 899 | /* WITH mustn't contain self-reference, either */ |
| 900 | if (stmt->withClause) |
| 901 | { |
| 902 | cstate->curitem = i; |
| 903 | cstate->innerwiths = NIL; |
| 904 | cstate->selfrefcount = 0; |
| 905 | cstate->context = RECURSION_SUBLINK; |
| 906 | checkWellFormedRecursionWalker((Node *) stmt->withClause->ctes, |
no test coverage detected