/* Enumeration of contexts in which a self-reference is disallowed */ typedefenum
{
RECURSION_OK,
RECURSION_NONRECURSIVETERM, /* inside the left-hand term */
RECURSION_SUBLINK, /* inside a sublink */
RECURSION_OUTERJOIN, /* inside nullable side of an outer join */
RECURSION_INTERSECT, /* underneath INTERSECT (ALL) */
RECURSION_EXCEPT, /* underneath EXCEPT (ALL) */
} RecursionContext;
/* Associated error messages --- each must have one %s for CTE name */ staticconstchar *const recursion_errormsgs[] = { /* RECURSION_OK */
NULL, /* RECURSION_NONRECURSIVETERM */
gettext_noop("recursive reference to query \"%s\" must not appear within its non-recursive term"), /* RECURSION_SUBLINK */
gettext_noop("recursive reference to query \"%s\" must not appear within a subquery"), /* RECURSION_OUTERJOIN */
gettext_noop("recursive reference to query \"%s\" must not appear within an outer join"), /* RECURSION_INTERSECT */
gettext_noop("recursive reference to query \"%s\" must not appear within INTERSECT"), /* RECURSION_EXCEPT */
gettext_noop("recursive reference to query \"%s\" must not appear within EXCEPT")
};
/* *ForWITHRECURSIVE,wehavetofindanorderingoftheclausemembers *withnoforwardreferences,anddeterminewhichmembersarerecursive *(i.e.,self-referential).Itisconvenienttodothiswithanarray *ofCteItemsinsteadofalistofCommonTableExprs.
*/ typedefstruct CteItem
{
CommonTableExpr *cte; /* One CTE to examine */ int id; /* Its ID number for dependencies */
Bitmapset *depends_on; /* CTEs depended on (not including self) */
} CteItem;
/* CteState is what we need to pass around in the tree walkers */ typedefstruct CteState
{ /* global state: */
ParseState *pstate; /* global parse state */
CteItem *items; /* array of CTEs and extra data */ int numitems; /* number of CTEs */ /* working state during a tree walk: */ int curitem; /* index of item currently being examined */
List *innerwiths; /* list of lists of CommonTableExpr */ /* working state for checkWellFormedRecursion walk only: */ int selfrefcount; /* number of self-references detected */
RecursionContext context; /* context to allow or disallow self-ref */
} CteState;
if (strcmp(cte->ctename, cte2->ctename) == 0)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("WITH query name \"%s\" specified more than once",
cte2->ctename),
parser_errposition(pstate, cte2->location)));
}
cte->cterecursive = false;
cte->cterefcount = 0;
if (!IsA(cte->ctequery, SelectStmt))
{ /* must be a data-modifying statement */
Assert(IsA(cte->ctequery, InsertStmt) ||
IsA(cte->ctequery, UpdateStmt) ||
IsA(cte->ctequery, DeleteStmt) ||
IsA(cte->ctequery, MergeStmt));
pstate->p_hasModifyingCTE = true;
}
}
if (withClause->recursive)
{ /* *ForWITHRECURSIVE,werearrangethelistelementsifneededto *eliminateforwardreferences.First,buildaworkarrayandsetup *thedatastructureneededbythetreewalkers.
*/
CteState cstate; int i;
/* Might as well look up the relevant <> operator while we are at it */
typentry = lookup_type_cache(cycle_clause->cycle_mark_type,
TYPECACHE_EQ_OPR); if (!OidIsValid(typentry->eq_opr))
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an equality operator for type %s",
format_type_be(cycle_clause->cycle_mark_type)));
op = get_negator(typentry->eq_opr); if (!OidIsValid(op))
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an inequality operator for type %s",
format_type_be(cycle_clause->cycle_mark_type)));
cycle_clause->cycle_mark_neop = op;
}
/* Now we can get on with analyzing the CTE's query */
query = parse_sub_analyze(cte->ctequery, pstate, cte, false, true);
cte->ctequery = (Node *) query;
/* *Checkthatwegotsomethingreasonable.Thesefirsttwocasesshould *bepreventedbythegrammar.
*/ if (!IsA(query, Query))
elog(ERROR, "unexpected non-Query statement in WITH"); if (query->utilityStmt != NULL)
elog(ERROR, "unexpected utility statement in WITH");
/* *Wedisallowdata-modifyingWITHexceptatthetoplevelofaquery, *becauseit'snotclearwhensuchamodificationshouldbeexecuted.
*/ if (query->commandType != CMD_SELECT &&
pstate->parentParseState != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WITH clause containing a data-modifying statement must be at the top level"),
parser_errposition(pstate, cte->location)));
if (te->resjunk) continue;
varattno++;
Assert(varattno == te->resno); if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */
elog(ERROR, "wrong number of output columns in WITH");
texpr = (Node *) te->expr; if (exprType(texpr) != lfirst_oid(lctyp) ||
exprTypmod(texpr) != lfirst_int(lctypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("recursive query \"%s\" column %d has type %s in non-recursive term but type %s overall",
cte->ctename, varattno,
format_type_with_typemod(lfirst_oid(lctyp),
lfirst_int(lctypmod)),
format_type_with_typemod(exprType(texpr),
exprTypmod(texpr))),
errhint("Cast the output of the non-recursive term to the correct type."),
parser_errposition(pstate, exprLocation(texpr)))); if (exprCollation(texpr) != lfirst_oid(lccoll))
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall",
cte->ctename, varattno,
get_collation_name(lfirst_oid(lccoll)),
get_collation_name(exprCollation(texpr))),
errhint("Use the COLLATE clause to set the collation of the non-recursive term."),
parser_errposition(pstate, exprLocation(texpr))));
lctyp = lnext(cte->ctecoltypes, lctyp);
lctypmod = lnext(cte->ctecoltypmods, lctypmod);
lccoll = lnext(cte->ctecolcollations, lccoll);
} if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */
elog(ERROR, "wrong number of output columns in WITH");
}
if (!cte->cterecursive)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("WITH query is not recursive"),
parser_errposition(pstate, cte->location)));
ctequery = castNode(Query, cte->ctequery);
Assert(ctequery->setOperations);
sos = castNode(SetOperationStmt, ctequery->setOperations);
/* *Thisleftsidecheckisnotrequiredforexpandability,but *rewriteSearchAndCycle()doesn'tcurrentlyhavesupportforit,so *wecatchithere.
*/ if (!IsA(sos->larg, RangeTblRef))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT")));
if (!IsA(sos->rarg, RangeTblRef))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT")));
}
if (search_clause)
{
ListCell *lc;
List *seen = NIL;
if (!list_member(cte->ctecolnames, colname))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("search column \"%s\" not in WITH query column list",
strVal(colname)),
parser_errposition(pstate, search_clause->location)));
if (list_member(seen, colname))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("search column \"%s\" specified more than once",
strVal(colname)),
parser_errposition(pstate, search_clause->location)));
seen = lappend(seen, colname);
}
if (list_member(cte->ctecolnames, makeString(search_clause->search_seq_column)))
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("search sequence column name \"%s\" already used in WITH query column list",
search_clause->search_seq_column),
parser_errposition(pstate, search_clause->location));
}
if (cycle_clause)
{
ListCell *lc;
List *seen = NIL;
if (!list_member(cte->ctecolnames, colname))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cycle column \"%s\" not in WITH query column list",
strVal(colname)),
parser_errposition(pstate, cycle_clause->location)));
if (list_member(seen, colname))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("cycle column \"%s\" specified more than once",
strVal(colname)),
parser_errposition(pstate, cycle_clause->location)));
seen = lappend(seen, colname);
}
if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_mark_column)))
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cycle mark column name \"%s\" already used in WITH query column list",
cycle_clause->cycle_mark_column),
parser_errposition(pstate, cycle_clause->location));
if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_path_column)))
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cycle path column name \"%s\" already used in WITH query column list",
cycle_clause->cycle_path_column),
parser_errposition(pstate, cycle_clause->location));
if (strcmp(cycle_clause->cycle_mark_column,
cycle_clause->cycle_path_column) == 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cycle mark column name and cycle path column name are the same"),
parser_errposition(pstate, cycle_clause->location));
}
if (search_clause && cycle_clause)
{ if (strcmp(search_clause->search_seq_column,
cycle_clause->cycle_mark_column) == 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("search sequence column name and cycle mark column name are the same"),
parser_errposition(pstate, search_clause->location));
if (strcmp(search_clause->search_seq_column,
cycle_clause->cycle_path_column) == 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("search sequence column name and cycle path column name are the same"),
parser_errposition(pstate, search_clause->location));
}
}
/* *ComputederivedfieldsofaCTE,giventhetransformedoutputtargetlist * *ForanonrecursiveCTE,thisiscalledaftertransformingtheCTE'squery. *ForarecursiveCTE,wecallitaftertransformingthenon-recursiveterm, *andpassthetargetlistemittedbythenon-recursivetermonly. * *Note:intherecursivecase,thepassedpstateisactuallytheonebeing *usedtoanalyzetheCTE'squery,soitisonelevellowerdownthanin *thenonrecursivecase.Thisdoesn'tmattersinceweonlyuseitfor *errormessagecontextanyway.
*/ void
analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
{ int numaliases; int varattno;
ListCell *tlistitem;
/* Not done already ... */
Assert(cte->ctecolnames == NIL);
if (strcmp(rv->relname, cte->ctename) == 0) returnfalse; /* yes, so bail out */
}
}
/* No, could be a reference to the query level we are working on */ for (i = 0; i < cstate->numitems; i++)
{
CommonTableExpr *cte = cstate->items[i].cte;
if (strcmp(rv->relname, cte->ctename) == 0)
{ int myindex = cstate->curitem;
if (i != myindex)
{ /* Add cross-item dependency */
cstate->items[myindex].depends_on =
bms_add_member(cstate->items[myindex].depends_on,
cstate->items[i].id);
} else
{ /* Found out this one is self-referential */
cte->cterecursive = true;
} break;
}
}
} returnfalse;
} if (IsA(node, SelectStmt))
{
SelectStmt *stmt = (SelectStmt *) node;
if (stmt->withClause)
{ /* Examine the WITH clause and the SelectStmt */
WalkInnerWith(node, stmt->withClause, cstate); /* We're done examining the SelectStmt */ returnfalse;
} /* if no WITH clause, just fall through for normal processing */
} elseif (IsA(node, InsertStmt))
{
InsertStmt *stmt = (InsertStmt *) node;
if (stmt->withClause)
{ /* Examine the WITH clause and the InsertStmt */
WalkInnerWith(node, stmt->withClause, cstate); /* We're done examining the InsertStmt */ returnfalse;
} /* if no WITH clause, just fall through for normal processing */
} elseif (IsA(node, DeleteStmt))
{
DeleteStmt *stmt = (DeleteStmt *) node;
if (stmt->withClause)
{ /* Examine the WITH clause and the DeleteStmt */
WalkInnerWith(node, stmt->withClause, cstate); /* We're done examining the DeleteStmt */ returnfalse;
} /* if no WITH clause, just fall through for normal processing */
} elseif (IsA(node, UpdateStmt))
{
UpdateStmt *stmt = (UpdateStmt *) node;
if (stmt->withClause)
{ /* Examine the WITH clause and the UpdateStmt */
WalkInnerWith(node, stmt->withClause, cstate); /* We're done examining the UpdateStmt */ returnfalse;
} /* if no WITH clause, just fall through for normal processing */
} elseif (IsA(node, MergeStmt))
{
MergeStmt *stmt = (MergeStmt *) node;
if (stmt->withClause)
{ /* Examine the WITH clause and the MergeStmt */
WalkInnerWith(node, stmt->withClause, cstate); /* We're done examining the MergeStmt */ returnfalse;
} /* if no WITH clause, just fall through for normal processing */
} elseif (IsA(node, WithClause))
{ /* *Preventraw_expression_tree_walkerfromrecursingdirectlyintoa *WITHclause.Weneedthattohappenonlyunderthecontrolofthe *codeabove.
*/ returnfalse;
} return raw_expression_tree_walker(node,
makeDependencyGraphWalker,
cstate);
}
/* *Sortbydependencies,usingastandardtopologicalsortoperation
*/ staticvoid
TopologicalSort(ParseState *pstate, CteItem *items, int numitems)
{ int i,
j;
/* for each position in sequence ... */ for (i = 0; i < numitems; i++)
{ /* ... scan the remaining items to find one that has no dependencies */ for (j = i; j < numitems; j++)
{ if (bms_is_empty(items[j].depends_on)) break;
}
/* if we didn't find one, the dependency graph has a cycle */ if (j >= numitems)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("mutual recursion between WITH items is not implemented"),
parser_errposition(pstate, items[i].cte->location)));
/* *Foundone.Moveittofrontandremoveitfromeveryotheritem's *dependencies.
*/ if (i != j)
{
CteItem tmp;
/* *Checkthatrecursivequeriesarewell-formed.
*/ staticvoid
checkWellFormedRecursion(CteState *cstate)
{ int i;
for (i = 0; i < cstate->numitems; i++)
{
CommonTableExpr *cte = cstate->items[i].cte;
SelectStmt *stmt = (SelectStmt *) cte->ctequery;
Assert(!IsA(stmt, Query)); /* not analyzed yet */
/* Ignore items that weren't found to be recursive */ if (!cte->cterecursive) continue;
/* Must be a SELECT statement */ if (!IsA(stmt, SelectStmt))
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg("recursive query \"%s\" must not contain data-modifying statements",
cte->ctename),
parser_errposition(cstate->pstate, cte->location)));
/* Must have top-level UNION */ if (stmt->op != SETOP_UNION)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg("recursive query \"%s\" does not have the form non-recursive-term UNION [ALL] recursive-term",
cte->ctename),
parser_errposition(cstate->pstate, cte->location)));
/* *DisallowORDERBYandsimilardecorationatoptheUNION.These *don'tmakesensebecauseit'simpossibletofigureoutwhatthey *meanwhenwehaveonlypartoftherecursivequery'sresults.(If *wedidallowthem,we'dhavetocheckforrecursivereferences *insidethesesubtrees.AsforWITH,wehavetodothisbefore *examiningtheUNIONarms,toavoidissuingconfusingerrorsif *thereisarecursivereferencehere.)
*/ if (stmt->sortClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ORDER BY in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation((Node *) stmt->sortClause)))); if (stmt->limitOffset)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("OFFSET in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation(stmt->limitOffset)))); if (stmt->limitCount)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("LIMIT in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation(stmt->limitCount)))); if (stmt->lockingClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation((Node *) stmt->lockingClause))));
if (strcmp(rv->relname, cte->ctename) == 0) returnfalse; /* yes, so bail out */
}
}
/* No, could be a reference to the query level we are working on */
mycte = cstate->items[cstate->curitem].cte; if (strcmp(rv->relname, mycte->ctename) == 0)
{ /* Found a recursive reference to the active query */ if (cstate->context != RECURSION_OK)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg(recursion_errormsgs[cstate->context],
mycte->ctename),
parser_errposition(cstate->pstate,
rv->location))); /* Count references */ if (++(cstate->selfrefcount) > 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg("recursive reference to query \"%s\" must not appear more than once",
mycte->ctename),
parser_errposition(cstate->pstate,
rv->location)));
}
} returnfalse;
} if (IsA(node, SelectStmt))
{
SelectStmt *stmt = (SelectStmt *) node;
ListCell *lc;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.