1/*-------------------------------------------------------------------------
4 * Utility commands affecting portals (that is, SQL cursor commands)
6 * Note: see also tcop/pquery.c, which implements portal operations for
7 * the FE/BE protocol. This module uses pquery.c for some operations.
8 * And both modules depend on utils/mmgr/portalmem.c, which controls
9 * storage management for portals (but doesn't run any queries in them).
12 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
17 * src/backend/commands/portalcmds.c
19 *-------------------------------------------------------------------------
42 * Execute SQL DECLARE CURSOR command.
57 * Disallow empty-string cursor name (conflicts with protocol-level
62 (
errcode(ERRCODE_INVALID_CURSOR_NAME),
63 errmsg(
"invalid cursor name: must not be empty")));
66 * If this is a non-holdable cursor, we require that this statement has
67 * been executed inside a transaction block (or else, it would have no
68 * user-visible effect).
74 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
75 errmsg(
"cannot create a cursor WITH HOLD within security-restricted operation")));
77 /* Query contained by DeclareCursor needs to be jumbled if requested */
82 (*post_parse_analyze_hook) (pstate, query, jstate);
85 * Parse analysis was done already, but we still have to run the rule
86 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
87 * came straight from the parser, or suitable locks were acquired by
92 /* SELECT should never rewrite to more or less than one query */
94 elog(
ERROR,
"non-SELECT statement in DECLARE CURSOR");
99 elog(
ERROR,
"non-SELECT statement in DECLARE CURSOR");
101 /* Plan the query, applying the specified options */
105 * Create a portal and copy the plan and query string into its memory.
118 CMDTAG_SELECT,
/* cursor's query is always a SELECT */
123 * Also copy the outer portal's parameter list into the inner portal's
124 * memory context. We want to pass down the parameter values in case we
126 * DECLARE c CURSOR FOR SELECT ... WHERE foo = 1ドル
127 * This will have been parsed using the outer parameter set and the
128 * parameter value needs to be preserved for use when the cursor is
137 * Set up options for portal.
139 * If the user didn't specify a SCROLL type, allow or disallow scrolling
140 * based on whether it would require any additional runtime overhead to do
141 * so. Also, we disallow scrolling for FOR UPDATE cursors.
154 * Start execution, inserting parameters if any.
161 * We're done; the query won't actually be run until PerformPortalFetch is
168 * Execute SQL FETCH or MOVE command.
170 * stmt: parsetree node for command
171 * dest: where to send results
172 * qc: where to store a command completion status data.
174 * qc may be NULL if caller doesn't want status data.
185 * Disallow empty-string cursor name (conflicts with protocol-level
188 if (!
stmt->portalname ||
stmt->portalname[0] ==
'0円')
190 (
errcode(ERRCODE_INVALID_CURSOR_NAME),
191 errmsg(
"invalid cursor name: must not be empty")));
193 /* get the portal from the portal name */
198 (
errcode(ERRCODE_UNDEFINED_CURSOR),
199 errmsg(
"cursor \"%s\" does not exist",
stmt->portalname)));
200 return;
/* keep compiler happy */
203 /* Adjust dest if needed. MOVE wants destination DestNone */
213 /* Return command status if wanted */
228 /* NULL means CLOSE ALL */
236 * Disallow empty-string cursor name (conflicts with protocol-level
241 (
errcode(ERRCODE_INVALID_CURSOR_NAME),
242 errmsg(
"invalid cursor name: must not be empty")));
245 * get the portal from the portal name
251 (
errcode(ERRCODE_UNDEFINED_CURSOR),
252 errmsg(
"cursor \"%s\" does not exist",
name)));
253 return;
/* keep compiler happy */
257 * Note: PortalCleanup is called as a side-effect, if not already done.
265 * Clean up a portal when it's dropped. This is the standard cleanup hook
268 * Note: if portal->status is PORTAL_FAILED, we are probably being called
269 * during error abort, and must be careful to avoid doing anything that
270 * is likely to fail again.
284 * Shut down executor, if still running. We skip this during error abort,
285 * since other mechanisms will take care of releasing executor resources,
286 * and we can't be sure that ExecutorEnd itself wouldn't fail.
292 * Reset the queryDesc before anything else. This prevents us from
293 * trying to shut down the executor twice, in case of an error below.
294 * The transaction abort mechanisms will take care of resource cleanup
303 /* We must make the portal's resource owner current */
318 * PersistHoldablePortal
320 * Prepare the specified Portal for access outside of the current
321 * transaction. When this function returns, all future accesses to the
322 * portal must be done via the Tuplestore (not by invoking the
335 * If we're preserving a holdable portal, we had better be inside the
336 * transaction that originally created it.
339 Assert(queryDesc != NULL);
342 * Caller must have created the tuplestore already ... but not a snapshot.
349 * Before closing down the executor, we must copy the tupdesc into
350 * long-term memory, since it was created in executor memory.
359 * Check for improper portal use, and mark portal active.
364 * Set up global portal context pointers.
383 * If the portal is marked scrollable, we need to store the entire
384 * result set in the tuplestore, so that subsequent backward FETCHs
385 * can be processed. Otherwise, store only the not-yet-fetched rows.
386 * (The latter is not only more efficient, but avoids semantic
387 * problems if the query's output isn't stable.)
389 * In the no-scroll case, tuple indexes in the tuplestore will not
390 * match the cursor's nominal position (portalPos). Currently this
391 * causes no difficulty because we only navigate in the tuplestore by
392 * relative position, except for the tuplestore_skiptuples call below
393 * and the tuplestore_rescan call in DoPortalRewind, both of which are
394 * disabled for no-scroll cursors. But someday we might need to track
395 * the offset between the holdStore and the cursor's nominal position
405 * If we already reached end-of-query, set the direction to
406 * NoMovement to avoid trying to fetch any tuples. (This check
407 * exists because not all plan node types are robust about being
408 * called again if they've already returned NULL once.) We'll
409 * still set up an empty tuplestore, though, to keep this from
410 * being a special case later.
417 * Change the destination to output to the tuplestore. Note we tell
418 * the tuplestore receiver to detoast all data passed through it; this
419 * makes it safe to not keep a snapshot associated with the data.
429 /* Fetch the result set into the tuplestore */
433 queryDesc->
dest = NULL;
436 * Now shut down the inner executor.
438 portal->
queryDesc = NULL;
/* prevent double shutdown */
444 * Set the position in the result set.
451 * Just force the tuplestore forward to its end. The size of the
452 * skip request here is arbitrary.
462 * In the no-scroll case, the start of the tuplestore is exactly
463 * where we want to be, so no repositioning is wanted.
470 elog(
ERROR,
"unexpected end of tuple stream");
476 /* Uncaught error while executing portal: mark it dead */
479 /* Restore global vars and propagate error */
490 /* Mark portal not active */
500 * We can now release any subsidiary memory of the portal's context; we'll
501 * never use it again. The executor already dropped its context, but this
502 * will clean up anything that glommed onto the portal's context via
#define InvalidSubTransactionId
static void SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag, uint64 nprocessed)
DestReceiver * CreateDestReceiver(CommandDest dest)
DestReceiver * None_Receiver
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
bool ExecSupportsBackwardScan(Plan *node)
void ExecutorEnd(QueryDesc *queryDesc)
void ExecutorFinish(QueryDesc *queryDesc)
void ExecutorRewind(QueryDesc *queryDesc)
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Assert(PointerIsAligned(start, uint64))
char * pstrdup(const char *in)
void MemoryContextDeleteChildren(MemoryContext context)
MemoryContext PortalContext
bool InSecurityRestrictedOperation(void)
#define castNode(_type_, nodeptr)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
ParamListInfo copyParamList(ParamListInfo from)
#define CURSOR_OPT_SCROLL
#define CURSOR_OPT_NO_SCROLL
post_parse_analyze_hook_type post_parse_analyze_hook
static int list_length(const List *l)
#define linitial_node(type, l)
void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo params, bool isTopLevel)
void PerformPortalClose(const char *name)
void PortalCleanup(Portal portal)
void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, QueryCompletion *qc)
void PersistHoldablePortal(Portal portal)
void MarkPortalFailed(Portal portal)
void MarkPortalActive(Portal portal)
void PortalDrop(Portal portal, bool isTopCommit)
Portal GetPortalByName(const char *name)
void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, CommandTag commandTag, List *stmts, CachedPlan *cplan)
void PortalHashTableDeleteAll(void)
Portal CreatePortal(const char *name, bool allowDup, bool dupSilent)
PlannedStmt * pg_plan_query(Query *querytree, const char *query_string, int cursorOptions, ParamListInfo boundParams)
void FreeQueryDesc(QueryDesc *qdesc)
void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot)
uint64 PortalRunFetch(Portal portal, FetchDirection fdirection, long count, DestReceiver *dest)
static bool IsQueryIdEnabled(void)
JumbleState * JumbleQuery(Query *query)
ResourceOwner CurrentResourceOwner
List * QueryRewrite(Query *parsetree)
@ NoMovementScanDirection
void PushActiveSnapshot(Snapshot snapshot)
void PopActiveSnapshot(void)
Snapshot GetActiveSnapshot(void)
const char * p_sourcetext
SubTransactionId createSubid
MemoryContext holdContext
MemoryContext portalContext
Tuplestorestate * holdStore
void(* cleanup)(Portal portal)
void(* rDestroy)(DestReceiver *self)
void SetTuplestoreDestReceiverParams(DestReceiver *self, Tuplestorestate *tStore, MemoryContext tContext, bool detoast, TupleDesc target_tupdesc, const char *map_failure_msg)
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
void tuplestore_rescan(Tuplestorestate *state)
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
void RequireTransactionBlock(bool isTopLevel, const char *stmtType)