diff options
Diffstat (limited to 'src/backend/commands/portalcmds.c')
-rw-r--r-- | src/backend/commands/portalcmds.c | 50 |
1 files changed, 37 insertions, 13 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 71640b7748f..1d3e39299b9 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -27,7 +27,9 @@ #include "commands/portalcmds.h" #include "executor/executor.h" #include "executor/tstoreReceiver.h" +#include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" +#include "tcop/tcopprot.h" #include "utils/memutils.h" #include "utils/snapmgr.h" @@ -35,21 +37,18 @@ /* * PerformCursorOpen * Execute SQL DECLARE CURSOR command. - * - * The query has already been through parse analysis, rewriting, and planning. - * When it gets here, it looks like a SELECT PlannedStmt, except that the - * utilityStmt field is set. */ void -PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, +PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params, const char *queryString, bool isTopLevel) { - DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; + Query *query = (Query *) cstmt->query; + List *rewritten; + PlannedStmt *plan; Portal portal; MemoryContext oldContext; - if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) - elog(ERROR, "PerformCursorOpen called for non-cursor query"); + Assert(IsA(query, Query)); /* else parse analysis wasn't done */ /* * Disallow empty-string cursor name (conflicts with protocol-level @@ -69,14 +68,39 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); /* + * Parse analysis was done already, but we still have to run the rule + * rewriter. We do not do AcquireRewriteLocks: we assume the query either + * came straight from the parser, or suitable locks were acquired by + * plancache.c. + * + * Because the rewriter and planner tend to scribble on the input, we make + * a preliminary copy of the source querytree. This prevents problems in + * the case that the DECLARE CURSOR is in a portal or plpgsql function and + * is executed repeatedly. (See also the same hack in EXPLAIN and + * PREPARE.) XXX FIXME someday. + */ + rewritten = QueryRewrite((Query *) copyObject(query)); + + /* SELECT should never rewrite to more or less than one query */ + if (list_length(rewritten) != 1) + elog(ERROR, "non-SELECT statement in DECLARE CURSOR"); + + query = (Query *) linitial(rewritten); + + if (query->commandType != CMD_SELECT) + elog(ERROR, "non-SELECT statement in DECLARE CURSOR"); + + /* Plan the query, applying the specified options */ + plan = pg_plan_query(query, cstmt->options, params); + + /* * Create a portal and copy the plan and queryString into its memory. */ portal = CreatePortal(cstmt->portalname, false, false); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - stmt = copyObject(stmt); - stmt->utilityStmt = NULL; /* make it look like plain SELECT */ + plan = copyObject(plan); queryString = pstrdup(queryString); @@ -84,7 +108,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, NULL, queryString, "SELECT", /* cursor's query is always a SELECT */ - list_make1(stmt), + list_make1(plan), NULL); /*---------- @@ -111,8 +135,8 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, portal->cursorOptions = cstmt->options; if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { - if (stmt->rowMarks == NIL && - ExecSupportsBackwardScan(stmt->planTree)) + if (plan->rowMarks == NIL && + ExecSupportsBackwardScan(plan->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; |