PostgreSQL Source Code  git master
execCurrent.c File Reference
#include "postgres.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 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 42 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_rowMarks, QueryDesc::estate, fetch_cursor_param_value(), get_rel_name(), GetPortalByName(), IsA, ItemPointerIsValid, lfirst, ExecRowMark::markType, QueryDesc::planstate, PORTAL_ONE_SELECT, PortalIsValid, PortalData::queryDesc, ExecRowMark::relid, RowMarkRequiresRowShareLock, search_plan_tree(), SelfItemPointerAttributeNumber, slot_getsysattr(), ScanState::ss_ScanTupleSlot, PortalData::strategy, HeapTupleData::t_self, TableOidAttributeNumber, TupIsNull, and IndexScanDescData::xs_ctup.

Referenced by TidListEval().

46 {
47  char *cursor_name;
48  char *table_name;
49  Portal portal;
50  QueryDesc *queryDesc;
51 
52  /* Get the cursor name --- may have to look up a parameter reference */
53  if (cexpr->cursor_name)
54  cursor_name = cexpr->cursor_name;
55  else
56  cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
57 
58  /* Fetch table name for possible use in error messages */
59  table_name = get_rel_name(table_oid);
60  if (table_name == NULL)
61  elog(ERROR, "cache lookup failed for relation %u", table_oid);
62 
63  /* Find the cursor's portal */
64  portal = GetPortalByName(cursor_name);
65  if (!PortalIsValid(portal))
66  ereport(ERROR,
67  (errcode(ERRCODE_UNDEFINED_CURSOR),
68  errmsg("cursor \"%s\" does not exist", cursor_name)));
69 
70  /*
71  * We have to watch out for non-SELECT queries as well as held cursors,
72  * both of which may have null queryDesc.
73  */
74  if (portal->strategy != PORTAL_ONE_SELECT)
75  ereport(ERROR,
76  (errcode(ERRCODE_INVALID_CURSOR_STATE),
77  errmsg("cursor \"%s\" is not a SELECT query",
78  cursor_name)));
79  queryDesc = portal->queryDesc;
80  if (queryDesc == NULL || queryDesc->estate == NULL)
81  ereport(ERROR,
82  (errcode(ERRCODE_INVALID_CURSOR_STATE),
83  errmsg("cursor \"%s\" is held from a previous transaction",
84  cursor_name)));
85 
86  /*
87  * We have two different strategies depending on whether the cursor uses
88  * FOR UPDATE/SHARE or not. The reason for supporting both is that the
89  * FOR UPDATE code is able to identify a target table in many cases where
90  * the other code can't, while the non-FOR-UPDATE case allows use of WHERE
91  * CURRENT OF with an insensitive cursor.
92  */
93  if (queryDesc->estate->es_rowMarks)
94  {
95  ExecRowMark *erm;
96  ListCell *lc;
97 
98  /*
99  * Here, the query must have exactly one FOR UPDATE/SHARE reference to
100  * the target table, and we dig the ctid info out of that.
101  */
102  erm = NULL;
103  foreach(lc, queryDesc->estate->es_rowMarks)
104  {
105  ExecRowMark *thiserm = (ExecRowMark *) lfirst(lc);
106 
107  if (!RowMarkRequiresRowShareLock(thiserm->markType))
108  continue; /* ignore non-FOR UPDATE/SHARE items */
109 
110  if (thiserm->relid == table_oid)
111  {
112  if (erm)
113  ereport(ERROR,
114  (errcode(ERRCODE_INVALID_CURSOR_STATE),
115  errmsg("cursor \"%s\" has multiple FOR UPDATE/SHARE references to table \"%s\"",
116  cursor_name, table_name)));
117  erm = thiserm;
118  }
119  }
120 
121  if (erm == NULL)
122  ereport(ERROR,
123  (errcode(ERRCODE_INVALID_CURSOR_STATE),
124  errmsg("cursor \"%s\" does not have a FOR UPDATE/SHARE reference to table \"%s\"",
125  cursor_name, table_name)));
126 
127  /*
128  * The cursor must have a current result row: per the SQL spec, it's
129  * an error if not.
130  */
131  if (portal->atStart || portal->atEnd)
132  ereport(ERROR,
133  (errcode(ERRCODE_INVALID_CURSOR_STATE),
134  errmsg("cursor \"%s\" is not positioned on a row",
135  cursor_name)));
136 
137  /* Return the currently scanned TID, if there is one */
138  if (ItemPointerIsValid(&(erm->curCtid)))
139  {
140  *current_tid = erm->curCtid;
141  return true;
142  }
143 
144  /*
145  * This table didn't produce the cursor's current row; some other
146  * inheritance child of the same parent must have. Signal caller to
147  * do nothing on this table.
148  */
149  return false;
150  }
151  else
152  {
153  /*
154  * Without FOR UPDATE, we dig through the cursor's plan to find the
155  * scan node. Fail if it's not there or buried underneath
156  * aggregation.
157  */
158  ScanState *scanstate;
159 
160  scanstate = search_plan_tree(queryDesc->planstate, table_oid);
161  if (!scanstate)
162  ereport(ERROR,
163  (errcode(ERRCODE_INVALID_CURSOR_STATE),
164  errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
165  cursor_name, table_name)));
166 
167  /*
168  * The cursor must have a current result row: per the SQL spec, it's
169  * an error if not. We test this at the top level, rather than at the
170  * scan node level, because in inheritance cases any one table scan
171  * could easily not be on a row. We want to return false, not raise
172  * error, if the passed-in table OID is for one of the inactive scans.
173  */
174  if (portal->atStart || portal->atEnd)
175  ereport(ERROR,
176  (errcode(ERRCODE_INVALID_CURSOR_STATE),
177  errmsg("cursor \"%s\" is not positioned on a row",
178  cursor_name)));
179 
180  /* Now OK to return false if we found an inactive scan */
181  if (TupIsNull(scanstate->ss_ScanTupleSlot))
182  return false;
183 
184  /*
185  * Extract TID of the scan's current row. The mechanism for this is
186  * in principle scan-type-dependent, but for most scan types, we can
187  * just dig the TID out of the physical scan tuple.
188  */
189  if (IsA(scanstate, IndexOnlyScanState))
190  {
191  /*
192  * For IndexOnlyScan, the tuple stored in ss_ScanTupleSlot may be
193  * a virtual tuple that does not have the ctid column, so we have
194  * to get the TID from xs_ctup.t_self.
195  */
196  IndexScanDesc scan = ((IndexOnlyScanState *) scanstate)->ioss_ScanDesc;
197 
198  *current_tid = scan->xs_ctup.t_self;
199  }
200  else
201  {
202  /*
203  * Default case: try to fetch TID from the scan node's current
204  * tuple. As an extra cross-check, verify tableoid in the current
205  * tuple. If the scan hasn't provided a physical tuple, we have
206  * to fail.
207  */
208  Datum ldatum;
209  bool lisnull;
210  ItemPointer tuple_tid;
211 
212 #ifdef USE_ASSERT_CHECKING
213  if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
215  &ldatum,
216  &lisnull))
217  ereport(ERROR,
218  (errcode(ERRCODE_INVALID_CURSOR_STATE),
219  errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
220  cursor_name, table_name)));
221  Assert(!lisnull);
222  Assert(DatumGetObjectId(ldatum) == table_oid);
223 #endif
224 
225  if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
227  &ldatum,
228  &lisnull))
229  ereport(ERROR,
230  (errcode(ERRCODE_INVALID_CURSOR_STATE),
231  errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
232  cursor_name, table_name)));
233  Assert(!lisnull);
234  tuple_tid = (ItemPointer) DatumGetPointer(ldatum);
235 
236  *current_tid = *tuple_tid;
237  }
238 
239  Assert(ItemPointerIsValid(current_tid));
240 
241  return true;
242  }
243 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
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:485
bool slot_getsysattr(TupleTableSlot *slot, int attnum, Datum *value, bool *isnull)
Definition: heaptuple.c:1751
int errcode(int sqlerrcode)
Definition: elog.c:575
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:994
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ItemPointerData curCtid
Definition: execnodes.h:605
#define ERROR
Definition: elog.h:43
PlanState * planstate
Definition: execdesc.h:49
ItemPointerData t_self
Definition: htup.h:65
#define TupIsNull(slot)
Definition: tuptable.h:146
#define TableOidAttributeNumber
Definition: sysattr.h:27
char * cursor_name
Definition: primnodes.h:1286
PortalStrategy strategy
Definition: portal.h:143
#define ereport(elevel, rest)
Definition: elog.h:122
QueryDesc * queryDesc
Definition: portal.h:154
#define PortalIsValid(p)
Definition: portal.h:201
static ScanState * search_plan_tree(PlanState *node, Oid table_oid)
Definition: execCurrent.c:296
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
RowMarkType markType
Definition: execnodes.h:601
HeapTupleData xs_ctup
Definition: relscan.h:121
List * es_rowMarks
Definition: execnodes.h:527
#define DatumGetPointer(X)
Definition: postgres.h:534
bool atStart
Definition: portal.h:188
int errmsg(const char *fmt,...)
Definition: elog.c:797
static char * fetch_cursor_param_value(ExprContext *econtext, int paramId)
Definition: execCurrent.c:251
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
#define elog
Definition: elog.h:219
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 251 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().

252 {
253  ParamListInfo paramInfo = econtext->ecxt_param_list_info;
254 
255  if (paramInfo &&
256  paramId > 0 && paramId <= paramInfo->numParams)
257  {
258  ParamExternData *prm;
259  ParamExternData prmdata;
260 
261  /* give hook a chance in case parameter is dynamic */
262  if (paramInfo->paramFetch != NULL)
263  prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
264  else
265  prm = &paramInfo->params[paramId - 1];
266 
267  if (OidIsValid(prm->ptype) && !prm->isnull)
268  {
269  /* safety check in case hook did something unexpected */
270  if (prm->ptype != REFCURSOROID)
271  ereport(ERROR,
272  (errcode(ERRCODE_DATATYPE_MISMATCH),
273  errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
274  paramId,
275  format_type_be(prm->ptype),
276  format_type_be(REFCURSOROID))));
277 
278  /* We know that refcursor uses text's I/O routines */
279  return TextDatumGetCString(prm->value);
280  }
281  }
282 
283  ereport(ERROR,
284  (errcode(ERRCODE_UNDEFINED_OBJECT),
285  errmsg("no value found for parameter %d", paramId)));
286  return NULL;
287 }
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
Definition: params.h:124
Datum value
Definition: params.h:92
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:328
#define OidIsValid(objectId)
Definition: c.h:605
ParamFetchHook paramFetch
Definition: params.h:112
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:122
#define TextDatumGetCString(d)
Definition: builtins.h:96
int errmsg(const char *fmt,...)
Definition: elog.c:797
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:230
bool isnull
Definition: params.h:93

◆ search_plan_tree()

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

Definition at line 296 of file execCurrent.c.

References AppendState::appendplans, AppendState::as_nplans, 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().

297 {
298  if (node == NULL)
299  return NULL;
300  switch (nodeTag(node))
301  {
302  /*
303  * Relation scan nodes can all be treated alike
304  */
305  case T_SeqScanState:
306  case T_SampleScanState:
307  case T_IndexScanState:
310  case T_TidScanState:
311  case T_ForeignScanState:
312  case T_CustomScanState:
313  {
314  ScanState *sstate = (ScanState *) node;
315 
316  if (RelationGetRelid(sstate->ss_currentRelation) == table_oid)
317  return sstate;
318  break;
319  }
320 
321  /*
322  * For Append, we must look through the members; watch out for
323  * multiple matches (possible if it was from UNION ALL)
324  */
325  case T_AppendState:
326  {
327  AppendState *astate = (AppendState *) node;
328  ScanState *result = NULL;
329  int i;
330 
331  for (i = 0; i < astate->as_nplans; i++)
332  {
333  ScanState *elem = search_plan_tree(astate->appendplans[i],
334  table_oid);
335 
336  if (!elem)
337  continue;
338  if (result)
339  return NULL; /* multiple matches */
340  result = elem;
341  }
342  return result;
343  }
344 
345  /*
346  * Similarly for MergeAppend
347  */
348  case T_MergeAppendState:
349  {
350  MergeAppendState *mstate = (MergeAppendState *) node;
351  ScanState *result = NULL;
352  int i;
353 
354  for (i = 0; i < mstate->ms_nplans; i++)
355  {
356  ScanState *elem = search_plan_tree(mstate->mergeplans[i],
357  table_oid);
358 
359  if (!elem)
360  continue;
361  if (result)
362  return NULL; /* multiple matches */
363  result = elem;
364  }
365  return result;
366  }
367 
368  /*
369  * Result and Limit can be descended through (these are safe
370  * because they always return their input's current row)
371  */
372  case T_ResultState:
373  case T_LimitState:
374  return search_plan_tree(node->lefttree, table_oid);
375 
376  /*
377  * SubqueryScan too, but it keeps the child in a different place
378  */
379  case T_SubqueryScanState:
380  return search_plan_tree(((SubqueryScanState *) node)->subplan,
381  table_oid);
382 
383  default:
384  /* Otherwise, assume we can't descend through it */
385  break;
386  }
387  return NULL;
388 }
Relation ss_currentRelation
Definition: execnodes.h:1193
struct PlanState * lefttree
Definition: execnodes.h:931
PlanState ** mergeplans
Definition: execnodes.h:1114
static ScanState * search_plan_tree(PlanState *node, Oid table_oid)
Definition: execCurrent.c:296
#define nodeTag(nodeptr)
Definition: nodes.h:522
PlanState ** appendplans
Definition: execnodes.h:1088
int i
#define RelationGetRelid(relation)
Definition: rel.h:407