PostgreSQL Source Code  git master
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.

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().

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))
68  ereport(ERROR,
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)
77  ereport(ERROR,
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)
83  ereport(ERROR,
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)
116  ereport(ERROR,
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)
125  ereport(ERROR,
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)
135  ereport(ERROR,
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)
167  ereport(ERROR,
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)
180  ereport(ERROR,
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_ctup.t_self.
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)
226  ereport(ERROR,
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)
237  ereport(ERROR,
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 ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
static ScanState * search_plan_tree(PlanState *node, Oid table_oid, bool *pending_rescan)
Definition: execCurrent.c:310
bool atEnd
Definition: portal.h:189
EState * estate
Definition: execdesc.h:48
Portal GetPortalByName(const char *name)
Definition: portalmem.c:130
#define DatumGetObjectId(X)
Definition: postgres.h:500
int errcode(int sqlerrcode)
Definition: elog.c:570
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1334
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:1013
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ItemPointerData curCtid
Definition: execnodes.h:631
struct ExecRowMark ** es_rowmarks
Definition: execnodes.h:509
#define ERROR
Definition: elog.h:43
PlanState * planstate
Definition: execdesc.h:49
ItemPointerData xs_heaptid
Definition: relscan.h:132
#define TupIsNull(slot)
Definition: tuptable.h:293
#define TableOidAttributeNumber
Definition: sysattr.h:26
char * cursor_name
Definition: primnodes.h:1303
PortalStrategy strategy
Definition: portal.h:143
#define ereport(elevel, rest)
Definition: elog.h:141
QueryDesc * queryDesc
Definition: portal.h:154
#define PortalIsValid(p)
Definition: portal.h:201
uintptr_t Datum
Definition: postgres.h:367
unsigned int Index
Definition: c.h:475
#define Assert(condition)
Definition: c.h:732
RowMarkType markType
Definition: execnodes.h:627
Index es_range_table_size
Definition: execnodes.h:506
#define DatumGetPointer(X)
Definition: postgres.h:549
bool atStart
Definition: portal.h:188
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
int i
static char * fetch_cursor_param_value(ExprContext *econtext, int paramId)
Definition: execCurrent.c:258
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:403
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730

◆ fetch_cursor_param_value()

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

Definition at line 258 of file execCurrent.c.

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().

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)
278  ereport(ERROR,
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 
290  ereport(ERROR,
291  (errcode(ERRCODE_UNDEFINED_OBJECT),
292  errmsg("no value found for parameter %d", paramId)));
293  return NULL;
294 }
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
Definition: params.h:124
Datum value
Definition: params.h:92
int errcode(int sqlerrcode)
Definition: elog.c:570
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
#define OidIsValid(objectId)
Definition: c.h:638
ParamFetchHook paramFetch
Definition: params.h:112
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:141
#define TextDatumGetCString(d)
Definition: builtins.h:84
int errmsg(const char *fmt,...)
Definition: elog.c:784
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:236
bool isnull
Definition: params.h:93

◆ search_plan_tree()

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

Definition at line 310 of file execCurrent.c.

References AppendState::appendplans, AppendState::as_nplans, PlanState::chgParam, i, PlanState::lefttree, MergeAppendState::mergeplans, MergeAppendState::ms_nplans, nodeTag, RelationGetRelid, ScanState::ss_currentRelation, T_AppendState, T_BitmapHeapScanState, T_CustomScanState, T_ForeignScanState, T_IndexOnlyScanState, T_IndexScanState, T_LimitState, T_MergeAppendState, T_ResultState, T_SampleScanState, T_SeqScanState, T_SubqueryScanState, and T_TidScanState.

Referenced by execCurrentOf().

312 {
313  ScanState *result = NULL;
314 
315  if (node == NULL)
316  return NULL;
317  switch (nodeTag(node))
318  {
319  /*
320  * Relation scan nodes can all be treated alike
321  */
322  case T_SeqScanState:
323  case T_SampleScanState:
324  case T_IndexScanState:
327  case T_TidScanState:
328  case T_ForeignScanState:
329  case T_CustomScanState:
330  {
331  ScanState *sstate = (ScanState *) node;
332 
333  if (RelationGetRelid(sstate->ss_currentRelation) == table_oid)
334  result = sstate;
335  break;
336  }
337 
338  /*
339  * For Append, we must look through the members; watch out for
340  * multiple matches (possible if it was from UNION ALL)
341  */
342  case T_AppendState:
343  {
344  AppendState *astate = (AppendState *) node;
345  int i;
346 
347  for (i = 0; i < astate->as_nplans; i++)
348  {
349  ScanState *elem = search_plan_tree(astate->appendplans[i],
350  table_oid,
351  pending_rescan);
352 
353  if (!elem)
354  continue;
355  if (result)
356  return NULL; /* multiple matches */
357  result = elem;
358  }
359  break;
360  }
361 
362  /*
363  * Similarly for MergeAppend
364  */
365  case T_MergeAppendState:
366  {
367  MergeAppendState *mstate = (MergeAppendState *) node;
368  int i;
369 
370  for (i = 0; i < mstate->ms_nplans; i++)
371  {
372  ScanState *elem = search_plan_tree(mstate->mergeplans[i],
373  table_oid,
374  pending_rescan);
375 
376  if (!elem)
377  continue;
378  if (result)
379  return NULL; /* multiple matches */
380  result = elem;
381  }
382  break;
383  }
384 
385  /*
386  * Result and Limit can be descended through (these are safe
387  * because they always return their input's current row)
388  */
389  case T_ResultState:
390  case T_LimitState:
391  result = search_plan_tree(node->lefttree,
392  table_oid,
393  pending_rescan);
394  break;
395 
396  /*
397  * SubqueryScan too, but it keeps the child in a different place
398  */
399  case T_SubqueryScanState:
400  result = search_plan_tree(((SubqueryScanState *) node)->subplan,
401  table_oid,
402  pending_rescan);
403  break;
404 
405  default:
406  /* Otherwise, assume we can't descend through it */
407  break;
408  }
409 
410  /*
411  * If we found a candidate at or below this node, then this node's
412  * chgParam indicates a pending rescan that will affect the candidate.
413  */
414  if (result && node->chgParam != NULL)
415  *pending_rescan = true;
416 
417  return result;
418 }
static ScanState * search_plan_tree(PlanState *node, Oid table_oid, bool *pending_rescan)
Definition: execCurrent.c:310
Relation ss_currentRelation
Definition: execnodes.h:1332
struct PlanState * lefttree
Definition: execnodes.h:962
PlanState ** mergeplans
Definition: execnodes.h:1250
Bitmapset * chgParam
Definition: execnodes.h:972
#define nodeTag(nodeptr)
Definition: nodes.h:530
PlanState ** appendplans
Definition: execnodes.h:1218
int i
#define RelationGetRelid(relation)
Definition: rel.h:419