PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
execCurrent.c File Reference
#include "postgres.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

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

Definition at line 41 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(), ItemPointerIsValid, lfirst, ExecRowMark::markType, NULL, PG_USED_FOR_ASSERTS_ONLY, QueryDesc::planstate, PORTAL_ONE_SELECT, PortalGetQueryDesc, PortalIsValid, ExecRowMark::relid, RowMarkRequiresRowShareLock, search_plan_tree(), SelfItemPointerAttributeNumber, slot_getattr(), ScanState::ss_ScanTupleSlot, PortalData::strategy, TableOidAttributeNumber, and TupIsNull.

Referenced by TidListEval().

45 {
46  char *cursor_name;
47  char *table_name;
48  Portal portal;
49  QueryDesc *queryDesc;
50 
51  /* Get the cursor name --- may have to look up a parameter reference */
52  if (cexpr->cursor_name)
53  cursor_name = cexpr->cursor_name;
54  else
55  cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
56 
57  /* Fetch table name for possible use in error messages */
58  table_name = get_rel_name(table_oid);
59  if (table_name == NULL)
60  elog(ERROR, "cache lookup failed for relation %u", table_oid);
61 
62  /* Find the cursor's portal */
63  portal = GetPortalByName(cursor_name);
64  if (!PortalIsValid(portal))
65  ereport(ERROR,
66  (errcode(ERRCODE_UNDEFINED_CURSOR),
67  errmsg("cursor \"%s\" does not exist", cursor_name)));
68 
69  /*
70  * We have to watch out for non-SELECT queries as well as held cursors,
71  * both of which may have null queryDesc.
72  */
73  if (portal->strategy != PORTAL_ONE_SELECT)
74  ereport(ERROR,
75  (errcode(ERRCODE_INVALID_CURSOR_STATE),
76  errmsg("cursor \"%s\" is not a SELECT query",
77  cursor_name)));
78  queryDesc = PortalGetQueryDesc(portal);
79  if (queryDesc == NULL || queryDesc->estate == NULL)
80  ereport(ERROR,
81  (errcode(ERRCODE_INVALID_CURSOR_STATE),
82  errmsg("cursor \"%s\" is held from a previous transaction",
83  cursor_name)));
84 
85  /*
86  * We have two different strategies depending on whether the cursor uses
87  * FOR UPDATE/SHARE or not. The reason for supporting both is that the
88  * FOR UPDATE code is able to identify a target table in many cases where
89  * the other code can't, while the non-FOR-UPDATE case allows use of WHERE
90  * CURRENT OF with an insensitive cursor.
91  */
92  if (queryDesc->estate->es_rowMarks)
93  {
94  ExecRowMark *erm;
95  ListCell *lc;
96 
97  /*
98  * Here, the query must have exactly one FOR UPDATE/SHARE reference to
99  * the target table, and we dig the ctid info out of that.
100  */
101  erm = NULL;
102  foreach(lc, queryDesc->estate->es_rowMarks)
103  {
104  ExecRowMark *thiserm = (ExecRowMark *) lfirst(lc);
105 
106  if (!RowMarkRequiresRowShareLock(thiserm->markType))
107  continue; /* ignore non-FOR UPDATE/SHARE items */
108 
109  if (thiserm->relid == table_oid)
110  {
111  if (erm)
112  ereport(ERROR,
113  (errcode(ERRCODE_INVALID_CURSOR_STATE),
114  errmsg("cursor \"%s\" has multiple FOR UPDATE/SHARE references to table \"%s\"",
115  cursor_name, table_name)));
116  erm = thiserm;
117  }
118  }
119 
120  if (erm == NULL)
121  ereport(ERROR,
122  (errcode(ERRCODE_INVALID_CURSOR_STATE),
123  errmsg("cursor \"%s\" does not have a FOR UPDATE/SHARE reference to table \"%s\"",
124  cursor_name, table_name)));
125 
126  /*
127  * The cursor must have a current result row: per the SQL spec, it's
128  * an error if not.
129  */
130  if (portal->atStart || portal->atEnd)
131  ereport(ERROR,
132  (errcode(ERRCODE_INVALID_CURSOR_STATE),
133  errmsg("cursor \"%s\" is not positioned on a row",
134  cursor_name)));
135 
136  /* Return the currently scanned TID, if there is one */
137  if (ItemPointerIsValid(&(erm->curCtid)))
138  {
139  *current_tid = erm->curCtid;
140  return true;
141  }
142 
143  /*
144  * This table didn't produce the cursor's current row; some other
145  * inheritance child of the same parent must have. Signal caller to
146  * do nothing on this table.
147  */
148  return false;
149  }
150  else
151  {
152  ScanState *scanstate;
153  bool lisnull;
154  Oid tuple_tableoid PG_USED_FOR_ASSERTS_ONLY;
155  ItemPointer tuple_tid;
156 
157  /*
158  * Without FOR UPDATE, we dig through the cursor's plan to find the
159  * scan node. Fail if it's not there or buried underneath
160  * aggregation.
161  */
162  scanstate = search_plan_tree(queryDesc->planstate, table_oid);
163  if (!scanstate)
164  ereport(ERROR,
165  (errcode(ERRCODE_INVALID_CURSOR_STATE),
166  errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
167  cursor_name, table_name)));
168 
169  /*
170  * The cursor must have a current result row: per the SQL spec, it's
171  * an error if not. We test this at the top level, rather than at the
172  * scan node level, because in inheritance cases any one table scan
173  * could easily not be on a row. We want to return false, not raise
174  * error, if the passed-in table OID is for one of the inactive scans.
175  */
176  if (portal->atStart || portal->atEnd)
177  ereport(ERROR,
178  (errcode(ERRCODE_INVALID_CURSOR_STATE),
179  errmsg("cursor \"%s\" is not positioned on a row",
180  cursor_name)));
181 
182  /* Now OK to return false if we found an inactive scan */
183  if (TupIsNull(scanstate->ss_ScanTupleSlot))
184  return false;
185 
186  /* Use slot_getattr to catch any possible mistakes */
187  tuple_tableoid =
190  &lisnull));
191  Assert(!lisnull);
192  tuple_tid = (ItemPointer)
195  &lisnull));
196  Assert(!lisnull);
197 
198  Assert(tuple_tableoid == table_oid);
199 
200  *current_tid = *tuple_tid;
201 
202  return true;
203  }
204 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:59
bool atEnd
Definition: portal.h:187
EState * estate
Definition: execdesc.h:48
Portal GetPortalByName(const char *name)
Definition: portalmem.c:129
#define DatumGetObjectId(X)
Definition: postgres.h:506
int errcode(int sqlerrcode)
Definition: elog.c:575
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1061
unsigned int Oid
Definition: postgres_ext.h:31
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:962
ItemPointerData * ItemPointer
Definition: itemptr.h:48
ItemPointerData curCtid
Definition: execnodes.h:520
#define ERROR
Definition: elog.h:43
PlanState * planstate
Definition: execdesc.h:49
#define TupIsNull(slot)
Definition: tuptable.h:138
#define TableOidAttributeNumber
Definition: sysattr.h:27
char * cursor_name
Definition: primnodes.h:1277
PortalStrategy strategy
Definition: portal.h:143
#define ereport(elevel, rest)
Definition: elog.h:122
#define PortalIsValid(p)
Definition: portal.h:199
static ScanState * search_plan_tree(PlanState *node, Oid table_oid)
Definition: execCurrent.c:254
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
#define lfirst(lc)
Definition: pg_list.h:106
RowMarkType markType
Definition: execnodes.h:516
List * es_rowMarks
Definition: execnodes.h:452
#define DatumGetPointer(X)
Definition: postgres.h:555
#define PortalGetQueryDesc(portal)
Definition: portal.h:204
bool atStart
Definition: portal.h:186
int errmsg(const char *fmt,...)
Definition: elog.c:797
static char * fetch_cursor_param_value(ExprContext *econtext, int paramId)
Definition: execCurrent.c:212
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1141
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
#define elog
Definition: elog.h:219
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:990
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1726
static char * fetch_cursor_param_value ( ExprContext econtext,
int  paramId 
)
static

Definition at line 212 of file execCurrent.c.

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

Referenced by execCurrentOf().

213 {
214  ParamListInfo paramInfo = econtext->ecxt_param_list_info;
215 
216  if (paramInfo &&
217  paramId > 0 && paramId <= paramInfo->numParams)
218  {
219  ParamExternData *prm = &paramInfo->params[paramId - 1];
220 
221  /* give hook a chance in case parameter is dynamic */
222  if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
223  (*paramInfo->paramFetch) (paramInfo, paramId);
224 
225  if (OidIsValid(prm->ptype) && !prm->isnull)
226  {
227  /* safety check in case hook did something unexpected */
228  if (prm->ptype != REFCURSOROID)
229  ereport(ERROR,
230  (errcode(ERRCODE_DATATYPE_MISMATCH),
231  errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
232  paramId,
233  format_type_be(prm->ptype),
235 
236  /* We know that refcursor uses text's I/O routines */
237  return TextDatumGetCString(prm->value);
238  }
239  }
240 
241  ereport(ERROR,
242  (errcode(ERRCODE_UNDEFINED_OBJECT),
243  errmsg("no value found for parameter %d", paramId)));
244  return NULL;
245 }
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
Definition: params.h:76
Datum value
Definition: params.h:56
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
#define OidIsValid(objectId)
Definition: c.h:538
ParamFetchHook paramFetch
Definition: params.h:70
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:122
#define REFCURSOROID
Definition: pg_type.h:558
#define TextDatumGetCString(d)
Definition: builtins.h:92
#define NULL
Definition: c.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:797
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:207
bool isnull
Definition: params.h:57
static ScanState * search_plan_tree ( PlanState node,
Oid  table_oid 
)
static

Definition at line 254 of file execCurrent.c.

References AppendState::appendplans, AppendState::as_nplans, i, PlanState::lefttree, MergeAppendState::mergeplans, MergeAppendState::ms_nplans, nodeTag, NULL, RelationGetRelid, result, 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().

255 {
256  if (node == NULL)
257  return NULL;
258  switch (nodeTag(node))
259  {
260  /*
261  * Relation scan nodes can all be treated alike
262  */
263  case T_SeqScanState:
264  case T_SampleScanState:
265  case T_IndexScanState:
268  case T_TidScanState:
269  case T_ForeignScanState:
270  case T_CustomScanState:
271  {
272  ScanState *sstate = (ScanState *) node;
273 
274  if (RelationGetRelid(sstate->ss_currentRelation) == table_oid)
275  return sstate;
276  break;
277  }
278 
279  /*
280  * For Append, we must look through the members; watch out for
281  * multiple matches (possible if it was from UNION ALL)
282  */
283  case T_AppendState:
284  {
285  AppendState *astate = (AppendState *) node;
286  ScanState *result = NULL;
287  int i;
288 
289  for (i = 0; i < astate->as_nplans; i++)
290  {
291  ScanState *elem = search_plan_tree(astate->appendplans[i],
292  table_oid);
293 
294  if (!elem)
295  continue;
296  if (result)
297  return NULL; /* multiple matches */
298  result = elem;
299  }
300  return result;
301  }
302 
303  /*
304  * Similarly for MergeAppend
305  */
306  case T_MergeAppendState:
307  {
308  MergeAppendState *mstate = (MergeAppendState *) node;
309  ScanState *result = NULL;
310  int i;
311 
312  for (i = 0; i < mstate->ms_nplans; i++)
313  {
314  ScanState *elem = search_plan_tree(mstate->mergeplans[i],
315  table_oid);
316 
317  if (!elem)
318  continue;
319  if (result)
320  return NULL; /* multiple matches */
321  result = elem;
322  }
323  return result;
324  }
325 
326  /*
327  * Result and Limit can be descended through (these are safe
328  * because they always return their input's current row)
329  */
330  case T_ResultState:
331  case T_LimitState:
332  return search_plan_tree(node->lefttree, table_oid);
333 
334  /*
335  * SubqueryScan too, but it keeps the child in a different place
336  */
337  case T_SubqueryScanState:
338  return search_plan_tree(((SubqueryScanState *) node)->subplan,
339  table_oid);
340 
341  default:
342  /* Otherwise, assume we can't descend through it */
343  break;
344  }
345  return NULL;
346 }
return result
Definition: formatting.c:1632
Relation ss_currentRelation
Definition: execnodes.h:1059
struct PlanState * lefttree
Definition: execnodes.h:828
PlanState ** mergeplans
Definition: execnodes.h:980
static ScanState * search_plan_tree(PlanState *node, Oid table_oid)
Definition: execCurrent.c:254
#define NULL
Definition: c.h:229
#define nodeTag(nodeptr)
Definition: nodes.h:514
PlanState ** appendplans
Definition: execnodes.h:961
int as_nplans
Definition: execnodes.h:962
int i
#define RelationGetRelid(relation)
Definition: rel.h:417