PostgreSQL Source Code git master
Loading...
Searching...
No Matches
execCurrent.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * execCurrent.c
4 * executor support for WHERE CURRENT OF cursor
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * src/backend/executor/execCurrent.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include "access/genam.h"
16#include "access/relscan.h"
17#include "access/sysattr.h"
18#include "catalog/pg_type.h"
19#include "executor/executor.h"
20#include "utils/builtins.h"
21#include "utils/lsyscache.h"
22#include "utils/portal.h"
23#include "utils/rel.h"
24
25
26static char *fetch_cursor_param_value(ExprContext *econtext, int paramId);
28 bool *pending_rescan);
29
30
31/*
32 * execCurrentOf
33 *
34 * Given a CURRENT OF expression and the OID of a table, determine which row
35 * of the table is currently being scanned by the cursor named by CURRENT OF,
36 * and return the row's TID into *current_tid.
37 *
38 * Returns true if a row was identified. Returns false if the cursor is valid
39 * for the table but is not currently scanning a row of the table (this is a
40 * legal situation in inheritance cases). Raises error if cursor is not a
41 * valid updatable scan of the specified table.
42 */
43bool
45 ExprContext *econtext,
48{
49 char *cursor_name;
50 char *table_name;
51 Portal portal;
52 QueryDesc *queryDesc;
53
54 /* Get the cursor name --- may have to look up a parameter reference */
55 if (cexpr->cursor_name)
56 cursor_name = cexpr->cursor_name;
57 else
58 cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
59
60 /* Fetch table name for possible use in error messages */
61 table_name = get_rel_name(table_oid);
62 if (table_name == NULL)
63 elog(ERROR, "cache lookup failed for relation %u", table_oid);
64
65 /* Find the cursor's portal */
66 portal = GetPortalByName(cursor_name);
67 if (!PortalIsValid(portal))
70 errmsg("cursor \"%s\" does not exist", cursor_name)));
71
72 /*
73 * We have to watch out for non-SELECT queries as well as held cursors,
74 * both of which may have null queryDesc.
75 */
76 if (portal->strategy != PORTAL_ONE_SELECT)
79 errmsg("cursor \"%s\" is not a SELECT query",
80 cursor_name)));
81 queryDesc = portal->queryDesc;
82 if (queryDesc == NULL || queryDesc->estate == NULL)
85 errmsg("cursor \"%s\" is held from a previous transaction",
86 cursor_name)));
87
88 /*
89 * We have two different strategies depending on whether the cursor uses
90 * FOR UPDATE/SHARE or not. The reason for supporting both is that the
91 * FOR UPDATE code is able to identify a target table in many cases where
92 * the other code can't, while the non-FOR-UPDATE case allows use of WHERE
93 * CURRENT OF with an insensitive cursor.
94 */
95 if (queryDesc->estate->es_rowmarks)
96 {
98
99 /*
100 * Here, the query must have exactly one FOR UPDATE/SHARE reference to
101 * the target table, and we dig the ctid info out of that.
102 */
103 erm = NULL;
104 for (int i = 0; i < queryDesc->estate->es_range_table_size; i++)
105 {
106 ExecRowMark *thiserm = queryDesc->estate->es_rowmarks[i];
107
108 if (thiserm == NULL ||
110 continue; /* ignore non-FOR UPDATE/SHARE items */
111
112 if (thiserm->relid == table_oid)
113 {
114 if (erm)
117 errmsg("cursor \"%s\" has multiple FOR UPDATE/SHARE references to table \"%s\"",
118 cursor_name, table_name)));
119 erm = thiserm;
120 }
121 }
122
123 if (erm == NULL)
126 errmsg("cursor \"%s\" does not have a FOR UPDATE/SHARE reference to table \"%s\"",
127 cursor_name, table_name)));
128
129 /*
130 * The cursor must have a current result row: per the SQL spec, it's
131 * an error if not.
132 */
133 if (portal->atStart || portal->atEnd)
136 errmsg("cursor \"%s\" is not positioned on a row",
137 cursor_name)));
138
139 /* Return the currently scanned TID, if there is one */
140 if (ItemPointerIsValid(&(erm->curCtid)))
141 {
142 *current_tid = erm->curCtid;
143 return true;
144 }
145
146 /*
147 * This table didn't produce the cursor's current row; some other
148 * inheritance child of the same parent must have. Signal caller to
149 * do nothing on this table.
150 */
151 return false;
152 }
153 else
154 {
155 /*
156 * Without FOR UPDATE, we dig through the cursor's plan to find the
157 * scan node. Fail if it's not there or buried underneath
158 * aggregation.
159 */
161 bool pending_rescan = false;
162
165 if (!scanstate)
168 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
169 cursor_name, table_name)));
170
171 /*
172 * The cursor must have a current result row: per the SQL spec, it's
173 * an error if not. We test this at the top level, rather than at the
174 * scan node level, because in inheritance cases any one table scan
175 * could easily not be on a row. We want to return false, not raise
176 * error, if the passed-in table OID is for one of the inactive scans.
177 */
178 if (portal->atStart || portal->atEnd)
181 errmsg("cursor \"%s\" is not positioned on a row",
182 cursor_name)));
183
184 /*
185 * Now OK to return false if we found an inactive scan. It is
186 * inactive either if it's not positioned on a row, or there's a
187 * rescan pending for it.
188 */
189 if (TupIsNull(scanstate->ss_ScanTupleSlot) || pending_rescan)
190 return false;
191
192 /*
193 * Extract TID of the scan's current row. The mechanism for this is
194 * in principle scan-type-dependent, but for most scan types, we can
195 * just dig the TID out of the physical scan tuple.
196 */
198 {
199 /*
200 * For IndexOnlyScan, the tuple stored in ss_ScanTupleSlot may be
201 * a virtual tuple that does not have the ctid column, so we have
202 * to get the TID from xs_heaptid.
203 */
204 IndexScanDesc scan = ((IndexOnlyScanState *) scanstate)->ioss_ScanDesc;
205
206 *current_tid = scan->xs_heaptid;
207 }
208 else
209 {
210 /*
211 * Default case: try to fetch TID from the scan node's current
212 * tuple. As an extra cross-check, verify tableoid in the current
213 * tuple. If the scan hasn't provided a physical tuple, we have
214 * to fail.
215 */
216 Datum ldatum;
217 bool lisnull;
219
220#ifdef USE_ASSERT_CHECKING
221 ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
223 &lisnull);
224 if (lisnull)
227 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
228 cursor_name, table_name)));
230#endif
231
232 ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
234 &lisnull);
235 if (lisnull)
238 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
239 cursor_name, table_name)));
241
243 }
244
246
247 return true;
248 }
249}
250
251/*
252 * fetch_cursor_param_value
253 *
254 * Fetch the string value of a param, verifying it is of type REFCURSOR.
255 */
256static char *
258{
260
261 if (paramInfo &&
262 paramId > 0 && paramId <= paramInfo->numParams)
263 {
266
267 /* give hook a chance in case parameter is dynamic */
268 if (paramInfo->paramFetch != NULL)
269 prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
270 else
271 prm = &paramInfo->params[paramId - 1];
272
273 if (OidIsValid(prm->ptype) && !prm->isnull)
274 {
275 /* safety check in case hook did something unexpected */
276 if (prm->ptype != REFCURSOROID)
279 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
280 paramId,
281 format_type_be(prm->ptype),
283
284 /* We know that refcursor uses text's I/O routines */
285 return TextDatumGetCString(prm->value);
286 }
287 }
288
291 errmsg("no value found for parameter %d", paramId)));
292 return NULL;
293}
294
295/*
296 * search_plan_tree
297 *
298 * Search through a PlanState tree for a scan node on the specified table.
299 * Return NULL if not found or multiple candidates.
300 *
301 * CAUTION: this function is not charged simply with finding some candidate
302 * scan, but with ensuring that that scan returned the plan tree's current
303 * output row. That's why we must reject multiple-match cases.
304 *
305 * If a candidate is found, set *pending_rescan to true if that candidate
306 * or any node above it has a pending rescan action, i.e. chgParam != NULL.
307 * That indicates that we shouldn't consider the node to be positioned on a
308 * valid tuple, even if its own state would indicate that it is. (Caller
309 * must initialize *pending_rescan to false, and should not trust its state
310 * if multiple candidates are found.)
311 */
312static ScanState *
314 bool *pending_rescan)
315{
317
318 if (node == NULL)
319 return NULL;
320 switch (nodeTag(node))
321 {
322 /*
323 * Relation scan nodes can all be treated alike: check to see if
324 * they are scanning the specified table.
325 *
326 * ForeignScan and CustomScan might not have a currentRelation, in
327 * which case we just ignore them. (We dare not descend to any
328 * child plan nodes they might have, since we do not know the
329 * relationship of such a node's current output tuple to the
330 * children's current outputs.)
331 */
332 case T_SeqScanState:
334 case T_IndexScanState:
337 case T_TidScanState:
341 {
342 ScanState *sstate = (ScanState *) node;
343
344 if (sstate->ss_currentRelation &&
346 result = sstate;
347 break;
348 }
349
350 /*
351 * For Append, we can check each input node. It is safe to
352 * descend to the inputs because only the input that resulted in
353 * the Append's current output node could be positioned on a tuple
354 * at all; the other inputs are either at EOF or not yet started.
355 * Hence, if the desired table is scanned by some
356 * currently-inactive input node, we will find that node but then
357 * our caller will realize that it didn't emit the tuple of
358 * interest.
359 *
360 * We do need to watch out for multiple matches (possible if
361 * Append was from UNION ALL rather than an inheritance tree).
362 *
363 * Note: we can NOT descend through MergeAppend similarly, since
364 * its inputs are likely all active, and we don't know which one
365 * returned the current output tuple. (Perhaps that could be
366 * fixed if we were to let this code know more about MergeAppend's
367 * internal state, but it does not seem worth the trouble. Users
368 * should not expect plans for ORDER BY queries to be considered
369 * simply-updatable, since they won't be if the sorting is
370 * implemented by a Sort node.)
371 */
372 case T_AppendState:
373 {
374 AppendState *astate = (AppendState *) node;
375 int i;
376
377 for (i = 0; i < astate->as_nplans; i++)
378 {
379 ScanState *elem = search_plan_tree(astate->appendplans[i],
380 table_oid,
382
383 if (!elem)
384 continue;
385 if (result)
386 return NULL; /* multiple matches */
387 result = elem;
388 }
389 break;
390 }
391
392 /*
393 * Result and Limit can be descended through (these are safe
394 * because they always return their input's current row)
395 */
396 case T_ResultState:
397 case T_LimitState:
399 table_oid,
401 break;
402
403 /*
404 * SubqueryScan too, but it keeps the child in a different place
405 */
407 result = search_plan_tree(((SubqueryScanState *) node)->subplan,
408 table_oid,
410 break;
411
412 default:
413 /* Otherwise, assume we can't descend through it */
414 break;
415 }
416
417 /*
418 * If we found a candidate at or below this node, then this node's
419 * chgParam indicates a pending rescan that will affect the candidate.
420 */
421 if (result && node->chgParam != NULL)
422 *pending_rescan = true;
423
424 return result;
425}
#define TextDatumGetCString(d)
Definition builtins.h:99
#define Assert(condition)
Definition c.h:1002
#define OidIsValid(objectId)
Definition c.h:917
uint32 result
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
static ScanState * search_plan_tree(PlanState *node, Oid table_oid, bool *pending_rescan)
static char * fetch_cursor_param_value(ExprContext *econtext, int paramId)
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Oid table_oid, ItemPointer current_tid)
Definition execCurrent.c:44
#define outerPlanState(node)
Definition execnodes.h:1300
char * format_type_be(Oid type_oid)
int i
Definition isn.c:77
ItemPointerData * ItemPointer
Definition itemptr.h:49
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
#define IsA(nodeptr, _type_)
Definition nodes.h:162
#define nodeTag(nodeptr)
Definition nodes.h:137
static char * errmsg
#define RowMarkRequiresRowShareLock(marktype)
Definition plannodes.h:1567
@ PORTAL_ONE_SELECT
Definition portal.h:91
#define PortalIsValid(p)
Definition portal.h:211
Portal GetPortalByName(const char *name)
Definition portalmem.c:132
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
unsigned int Oid
static int fb(int x)
#define RelationGetRelid(relation)
Definition rel.h:516
PlanState ** appendplans
Definition execnodes.h:1538
char * cursor_name
Definition primnodes.h:2121
ExecRowMark ** es_rowmarks
Definition execnodes.h:703
Index es_range_table_size
Definition execnodes.h:700
ParamListInfo ecxt_param_list_info
Definition execnodes.h:299
ItemPointerData xs_heaptid
Definition relscan.h:185
Bitmapset * chgParam
Definition execnodes.h:1236
QueryDesc * queryDesc
Definition portal.h:156
bool atEnd
Definition portal.h:199
bool atStart
Definition portal.h:198
PortalStrategy strategy
Definition portal.h:146
EState * estate
Definition execdesc.h:50
PlanState * planstate
Definition execdesc.h:51
Relation ss_currentRelation
Definition execnodes.h:1663
#define TableOidAttributeNumber
Definition sysattr.h:26
#define SelfItemPointerAttributeNumber
Definition sysattr.h:21
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:438
#define TupIsNull(slot)
Definition tuptable.h:325