PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
execCurrent.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/portal.h"
#include "utils/rel.h"
Include dependency graph for execCurrent.c:

Go to the source code of this file.

Functions

static char * fetch_cursor_param_value (ExprContext *econtext, int paramId)
 
static ScanStatesearch_plan_tree (PlanState *node, Oid table_oid, bool *pending_rescan)
 
bool execCurrentOf (CurrentOfExpr *cexpr, ExprContext *econtext, Oid table_oid, ItemPointer current_tid)
 

Function Documentation

◆ execCurrentOf()

bool execCurrentOf ( CurrentOfExpr cexpr,
ExprContext econtext,
Oid  table_oid,
ItemPointer  current_tid 
)

Definition at line 44 of file execCurrent.c.

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))
69 (errcode(ERRCODE_UNDEFINED_CURSOR),
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)
78 (errcode(ERRCODE_INVALID_CURSOR_STATE),
79 errmsg("cursor \"%s\" is not a SELECT query",
80 cursor_name)));
81 queryDesc = portal->queryDesc;
82 if (queryDesc == NULL || queryDesc->estate == NULL)
84 (errcode(ERRCODE_INVALID_CURSOR_STATE),
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 {
97 ExecRowMark *erm;
98 Index i;
99
100 /*
101 * Here, the query must have exactly one FOR UPDATE/SHARE reference to
102 * the target table, and we dig the ctid info out of that.
103 */
104 erm = NULL;
105 for (i = 0; i < queryDesc->estate->es_range_table_size; i++)
106 {
107 ExecRowMark *thiserm = queryDesc->estate->es_rowmarks[i];
108
109 if (thiserm == NULL ||
111 continue; /* ignore non-FOR UPDATE/SHARE items */
112
113 if (thiserm->relid == table_oid)
114 {
115 if (erm)
117 (errcode(ERRCODE_INVALID_CURSOR_STATE),
118 errmsg("cursor \"%s\" has multiple FOR UPDATE/SHARE references to table \"%s\"",
119 cursor_name, table_name)));
120 erm = thiserm;
121 }
122 }
123
124 if (erm == NULL)
126 (errcode(ERRCODE_INVALID_CURSOR_STATE),
127 errmsg("cursor \"%s\" does not have a FOR UPDATE/SHARE reference to table \"%s\"",
128 cursor_name, table_name)));
129
130 /*
131 * The cursor must have a current result row: per the SQL spec, it's
132 * an error if not.
133 */
134 if (portal->atStart || portal->atEnd)
136 (errcode(ERRCODE_INVALID_CURSOR_STATE),
137 errmsg("cursor \"%s\" is not positioned on a row",
138 cursor_name)));
139
140 /* Return the currently scanned TID, if there is one */
141 if (ItemPointerIsValid(&(erm->curCtid)))
142 {
143 *current_tid = erm->curCtid;
144 return true;
145 }
146
147 /*
148 * This table didn't produce the cursor's current row; some other
149 * inheritance child of the same parent must have. Signal caller to
150 * do nothing on this table.
151 */
152 return false;
153 }
154 else
155 {
156 /*
157 * Without FOR UPDATE, we dig through the cursor's plan to find the
158 * scan node. Fail if it's not there or buried underneath
159 * aggregation.
160 */
161 ScanState *scanstate;
162 bool pending_rescan = false;
163
164 scanstate = search_plan_tree(queryDesc->planstate, table_oid,
165 &pending_rescan);
166 if (!scanstate)
168 (errcode(ERRCODE_INVALID_CURSOR_STATE),
169 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
170 cursor_name, table_name)));
171
172 /*
173 * The cursor must have a current result row: per the SQL spec, it's
174 * an error if not. We test this at the top level, rather than at the
175 * scan node level, because in inheritance cases any one table scan
176 * could easily not be on a row. We want to return false, not raise
177 * error, if the passed-in table OID is for one of the inactive scans.
178 */
179 if (portal->atStart || portal->atEnd)
181 (errcode(ERRCODE_INVALID_CURSOR_STATE),
182 errmsg("cursor \"%s\" is not positioned on a row",
183 cursor_name)));
184
185 /*
186 * Now OK to return false if we found an inactive scan. It is
187 * inactive either if it's not positioned on a row, or there's a
188 * rescan pending for it.
189 */
190 if (TupIsNull(scanstate->ss_ScanTupleSlot) || pending_rescan)
191 return false;
192
193 /*
194 * Extract TID of the scan's current row. The mechanism for this is
195 * in principle scan-type-dependent, but for most scan types, we can
196 * just dig the TID out of the physical scan tuple.
197 */
198 if (IsA(scanstate, IndexOnlyScanState))
199 {
200 /*
201 * For IndexOnlyScan, the tuple stored in ss_ScanTupleSlot may be
202 * a virtual tuple that does not have the ctid column, so we have
203 * to get the TID from xs_heaptid.
204 */
205 IndexScanDesc scan = ((IndexOnlyScanState *) scanstate)->ioss_ScanDesc;
206
207 *current_tid = scan->xs_heaptid;
208 }
209 else
210 {
211 /*
212 * Default case: try to fetch TID from the scan node's current
213 * tuple. As an extra cross-check, verify tableoid in the current
214 * tuple. If the scan hasn't provided a physical tuple, we have
215 * to fail.
216 */
217 Datum ldatum;
218 bool lisnull;
219 ItemPointer tuple_tid;
220
221#ifdef USE_ASSERT_CHECKING
222 ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
224 &lisnull);
225 if (lisnull)
227 (errcode(ERRCODE_INVALID_CURSOR_STATE),
228 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
229 cursor_name, table_name)));
230 Assert(DatumGetObjectId(ldatum) == table_oid);
231#endif
232
233 ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
235 &lisnull);
236 if (lisnull)
238 (errcode(ERRCODE_INVALID_CURSOR_STATE),
239 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
240 cursor_name, table_name)));
241 tuple_tid = (ItemPointer) DatumGetPointer(ldatum);
242
243 *current_tid = *tuple_tid;
244 }
245
246 Assert(ItemPointerIsValid(current_tid));
247
248 return true;
249 }
250}
#define Assert(condition)
Definition: c.h:812
unsigned int Index
Definition: c.h:568
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
static ScanState * search_plan_tree(PlanState *node, Oid table_oid, bool *pending_rescan)
Definition: execCurrent.c:314
static char * fetch_cursor_param_value(ExprContext *econtext, int paramId)
Definition: execCurrent.c:258
int i
Definition: isn.c:72
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:1928
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:1335
@ PORTAL_ONE_SELECT
Definition: portal.h:91
#define PortalIsValid(p)
Definition: portal.h:211
Portal GetPortalByName(const char *name)
Definition: portalmem.c:130
uintptr_t Datum
Definition: postgres.h:64
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
char * cursor_name
Definition: primnodes.h:2098
struct ExecRowMark ** es_rowmarks
Definition: execnodes.h:638
Index es_range_table_size
Definition: execnodes.h:635
ItemPointerData curCtid
Definition: execnodes.h:771
RowMarkType markType
Definition: execnodes.h:767
ItemPointerData xs_heaptid
Definition: relscan.h:164
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:48
PlanState * planstate
Definition: execdesc.h:49
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1576
#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:416
#define TupIsNull(slot)
Definition: tuptable.h:306

References Assert, PortalData::atEnd, PortalData::atStart, ExecRowMark::curCtid, CurrentOfExpr::cursor_name, CurrentOfExpr::cursor_param, DatumGetObjectId(), DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, EState::es_range_table_size, EState::es_rowmarks, QueryDesc::estate, fetch_cursor_param_value(), get_rel_name(), GetPortalByName(), i, IsA, ItemPointerIsValid(), ExecRowMark::markType, QueryDesc::planstate, PORTAL_ONE_SELECT, PortalIsValid, PortalData::queryDesc, ExecRowMark::relid, RowMarkRequiresRowShareLock, search_plan_tree(), SelfItemPointerAttributeNumber, slot_getsysattr(), ScanState::ss_ScanTupleSlot, PortalData::strategy, TableOidAttributeNumber, TupIsNull, and IndexScanDescData::xs_heaptid.

Referenced by TidListEval().

◆ fetch_cursor_param_value()

static char * fetch_cursor_param_value ( ExprContext econtext,
int  paramId 
)
static

Definition at line 258 of file execCurrent.c.

259{
260 ParamListInfo paramInfo = econtext->ecxt_param_list_info;
261
262 if (paramInfo &&
263 paramId > 0 && paramId <= paramInfo->numParams)
264 {
265 ParamExternData *prm;
266 ParamExternData prmdata;
267
268 /* give hook a chance in case parameter is dynamic */
269 if (paramInfo->paramFetch != NULL)
270 prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
271 else
272 prm = &paramInfo->params[paramId - 1];
273
274 if (OidIsValid(prm->ptype) && !prm->isnull)
275 {
276 /* safety check in case hook did something unexpected */
277 if (prm->ptype != REFCURSOROID)
279 (errcode(ERRCODE_DATATYPE_MISMATCH),
280 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
281 paramId,
282 format_type_be(prm->ptype),
283 format_type_be(REFCURSOROID))));
284
285 /* We know that refcursor uses text's I/O routines */
286 return TextDatumGetCString(prm->value);
287 }
288 }
289
291 (errcode(ERRCODE_UNDEFINED_OBJECT),
292 errmsg("no value found for parameter %d", paramId)));
293 return NULL;
294}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define OidIsValid(objectId)
Definition: c.h:729
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:270
bool isnull
Definition: params.h:93
Datum value
Definition: params.h:92
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
Definition: params.h:125
ParamFetchHook paramFetch
Definition: params.h:112

References ExprContext::ecxt_param_list_info, ereport, errcode(), errmsg(), ERROR, format_type_be(), ParamExternData::isnull, OidIsValid, ParamListInfoData::paramFetch, ParamListInfoData::params, ParamExternData::ptype, TextDatumGetCString, and ParamExternData::value.

Referenced by execCurrentOf().

◆ search_plan_tree()

static ScanState * search_plan_tree ( PlanState node,
Oid  table_oid,
bool *  pending_rescan 
)
static

Definition at line 314 of file execCurrent.c.

316{
317 ScanState *result = NULL;
318
319 if (node == NULL)
320 return NULL;
321 switch (nodeTag(node))
322 {
323 /*
324 * Relation scan nodes can all be treated alike: check to see if
325 * they are scanning the specified table.
326 *
327 * ForeignScan and CustomScan might not have a currentRelation, in
328 * which case we just ignore them. (We dare not descend to any
329 * child plan nodes they might have, since we do not know the
330 * relationship of such a node's current output tuple to the
331 * children's current outputs.)
332 */
333 case T_SeqScanState:
334 case T_SampleScanState:
335 case T_IndexScanState:
336 case T_IndexOnlyScanState:
337 case T_BitmapHeapScanState:
338 case T_TidScanState:
339 case T_TidRangeScanState:
340 case T_ForeignScanState:
341 case T_CustomScanState:
342 {
343 ScanState *sstate = (ScanState *) node;
344
345 if (sstate->ss_currentRelation &&
346 RelationGetRelid(sstate->ss_currentRelation) == table_oid)
347 result = sstate;
348 break;
349 }
350
351 /*
352 * For Append, we can check each input node. It is safe to
353 * descend to the inputs because only the input that resulted in
354 * the Append's current output node could be positioned on a tuple
355 * at all; the other inputs are either at EOF or not yet started.
356 * Hence, if the desired table is scanned by some
357 * currently-inactive input node, we will find that node but then
358 * our caller will realize that it didn't emit the tuple of
359 * interest.
360 *
361 * We do need to watch out for multiple matches (possible if
362 * Append was from UNION ALL rather than an inheritance tree).
363 *
364 * Note: we can NOT descend through MergeAppend similarly, since
365 * its inputs are likely all active, and we don't know which one
366 * returned the current output tuple. (Perhaps that could be
367 * fixed if we were to let this code know more about MergeAppend's
368 * internal state, but it does not seem worth the trouble. Users
369 * should not expect plans for ORDER BY queries to be considered
370 * simply-updatable, since they won't be if the sorting is
371 * implemented by a Sort node.)
372 */
373 case T_AppendState:
374 {
375 AppendState *astate = (AppendState *) node;
376 int i;
377
378 for (i = 0; i < astate->as_nplans; i++)
379 {
380 ScanState *elem = search_plan_tree(astate->appendplans[i],
381 table_oid,
382 pending_rescan);
383
384 if (!elem)
385 continue;
386 if (result)
387 return NULL; /* multiple matches */
388 result = elem;
389 }
390 break;
391 }
392
393 /*
394 * Result and Limit can be descended through (these are safe
395 * because they always return their input's current row)
396 */
397 case T_ResultState:
398 case T_LimitState:
399 result = search_plan_tree(outerPlanState(node),
400 table_oid,
401 pending_rescan);
402 break;
403
404 /*
405 * SubqueryScan too, but it keeps the child in a different place
406 */
407 case T_SubqueryScanState:
408 result = search_plan_tree(((SubqueryScanState *) node)->subplan,
409 table_oid,
410 pending_rescan);
411 break;
412
413 default:
414 /* Otherwise, assume we can't descend through it */
415 break;
416 }
417
418 /*
419 * If we found a candidate at or below this node, then this node's
420 * chgParam indicates a pending rescan that will affect the candidate.
421 */
422 if (result && node->chgParam != NULL)
423 *pending_rescan = true;
424
425 return result;
426}
#define outerPlanState(node)
Definition: execnodes.h:1222
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define RelationGetRelid(relation)
Definition: rel.h:505
PlanState ** appendplans
Definition: execnodes.h:1449
Bitmapset * chgParam
Definition: execnodes.h:1158
Relation ss_currentRelation
Definition: execnodes.h:1574

References AppendState::appendplans, AppendState::as_nplans, PlanState::chgParam, i, nodeTag, outerPlanState, RelationGetRelid, search_plan_tree(), and ScanState::ss_currentRelation.

Referenced by execCurrentOf(), and search_plan_tree().