1 /*-------------------------------------------------------------------------
4 * executor support for WHERE CURRENT OF cursor
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 *-------------------------------------------------------------------------
15 #include "access/sysattr.h"
16 #include "catalog/pg_type.h"
17 #include "executor/executor.h"
18 #include "utils/builtins.h"
19 #include "utils/lsyscache.h"
20 #include "utils/portal.h"
23 static char *fetch_param_value(ExprContext
*econtext
, int paramId
);
24 static ScanState
*search_plan_tree(PlanState
*node
, Oid table_oid
);
30 * Given a CURRENT OF expression and the OID of a table, determine which row
31 * of the table is currently being scanned by the cursor named by CURRENT OF,
32 * and return the row's TID into *current_tid.
34 * Returns TRUE if a row was identified. Returns FALSE if the cursor is valid
35 * for the table but is not currently scanning a row of the table (this is a
36 * legal situation in inheritance cases). Raises error if cursor is not a
37 * valid updatable scan of the specified table.
40 execCurrentOf(CurrentOfExpr
*cexpr
,
41 ExprContext
*econtext
,
43 ItemPointer current_tid
)
50 /* Get the cursor name --- may have to look up a parameter reference */
51 if (cexpr
->cursor_name
)
52 cursor_name
= cexpr
->cursor_name
;
54 cursor_name
= fetch_param_value(econtext
, cexpr
->cursor_param
);
56 /* Fetch table name for possible use in error messages */
57 table_name
= get_rel_name(table_oid
);
58 if (table_name
== NULL
)
59 elog(ERROR
, "cache lookup failed for relation %u", table_oid
);
61 /* Find the cursor's portal */
62 portal
= GetPortalByName(cursor_name
);
63 if (!PortalIsValid(portal
))
65 (errcode(ERRCODE_UNDEFINED_CURSOR
),
66 errmsg("cursor \"%s\" does not exist", cursor_name
)));
69 * We have to watch out for non-SELECT queries as well as held cursors,
70 * both of which may have null queryDesc.
72 if (portal
->strategy
!= PORTAL_ONE_SELECT
)
74 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
75 errmsg("cursor \"%s\" is not a SELECT query",
77 queryDesc
= PortalGetQueryDesc(portal
);
78 if (queryDesc
== NULL
|| queryDesc
->estate
== NULL
)
80 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
81 errmsg("cursor \"%s\" is held from a previous transaction",
85 * We have two different strategies depending on whether the cursor uses
86 * FOR UPDATE/SHARE or not. The reason for supporting both is that the
87 * FOR UPDATE code is able to identify a target table in many cases where
88 * the other code can't, while the non-FOR-UPDATE case allows use of WHERE
89 * CURRENT OF with an insensitive cursor.
91 if (queryDesc
->estate
->es_rowMarks
)
97 * Here, the query must have exactly one FOR UPDATE/SHARE reference to
98 * the target table, and we dig the ctid info out of that.
101 foreach(lc
, queryDesc
->estate
->es_rowMarks
)
103 ExecRowMark
*thiserm
= (ExecRowMark
*) lfirst(lc
);
105 if (RelationGetRelid(thiserm
->relation
) == table_oid
)
109 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
110 errmsg("cursor \"%s\" has multiple FOR UPDATE/SHARE references to table \"%s\"",
111 cursor_name
, table_name
)));
118 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
119 errmsg("cursor \"%s\" does not have a FOR UPDATE/SHARE reference to table \"%s\"",
120 cursor_name
, table_name
)));
123 * The cursor must have a current result row: per the SQL spec, it's
126 if (portal
->atStart
|| portal
->atEnd
)
128 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
129 errmsg("cursor \"%s\" is not positioned on a row",
132 /* Return the currently scanned TID, if there is one */
133 if (ItemPointerIsValid(&(erm
->curCtid
)))
135 *current_tid
= erm
->curCtid
;
140 * This table didn't produce the cursor's current row; some other
141 * inheritance child of the same parent must have. Signal caller to
142 * do nothing on this table.
148 ScanState
*scanstate
;
151 ItemPointer tuple_tid
;
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
158 scanstate
= search_plan_tree(ExecGetActivePlanTree(queryDesc
),
162 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
163 errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
164 cursor_name
, table_name
)));
167 * The cursor must have a current result row: per the SQL spec, it's
168 * an error if not. We test this at the top level, rather than at the
169 * scan node level, because in inheritance cases any one table scan
170 * could easily not be on a row. We want to return false, not raise
171 * error, if the passed-in table OID is for one of the inactive scans.
173 if (portal
->atStart
|| portal
->atEnd
)
175 (errcode(ERRCODE_INVALID_CURSOR_STATE
),
176 errmsg("cursor \"%s\" is not positioned on a row",
179 /* Now OK to return false if we found an inactive scan */
180 if (TupIsNull(scanstate
->ss_ScanTupleSlot
))
183 /* Use slot_getattr to catch any possible mistakes */
185 DatumGetObjectId(slot_getattr(scanstate
->ss_ScanTupleSlot
,
186 TableOidAttributeNumber
,
189 tuple_tid
= (ItemPointer
)
190 DatumGetPointer(slot_getattr(scanstate
->ss_ScanTupleSlot
,
191 SelfItemPointerAttributeNumber
,
195 Assert(tuple_tableoid
== table_oid
);
197 *current_tid
= *tuple_tid
;
206 * Fetch the string value of a param, verifying it is of type REFCURSOR.
209 fetch_param_value(ExprContext
*econtext
, int paramId
)
211 ParamListInfo paramInfo
= econtext
->ecxt_param_list_info
;
214 paramId
> 0 && paramId
<= paramInfo
->numParams
)
216 ParamExternData
*prm
= ¶mInfo
->params
[paramId
- 1];
218 if (OidIsValid(prm
->ptype
) && !prm
->isnull
)
220 Assert(prm
->ptype
== REFCURSOROID
);
221 /* We know that refcursor uses text's I/O routines */
222 return TextDatumGetCString(prm
->value
);
227 (errcode(ERRCODE_UNDEFINED_OBJECT
),
228 errmsg("no value found for parameter %d", paramId
)));
235 * Search through a PlanState tree for a scan node on the specified table.
236 * Return NULL if not found or multiple candidates.
239 search_plan_tree(PlanState
*node
, Oid table_oid
)
243 switch (nodeTag(node
))
246 * scan nodes can all be treated alike
249 case T_IndexScanState
:
250 case T_BitmapHeapScanState
:
253 ScanState
*sstate
= (ScanState
*) node
;
255 if (RelationGetRelid(sstate
->ss_currentRelation
) == table_oid
)
261 * For Append, we must look through the members; watch out for
262 * multiple matches (possible if it was from UNION ALL)
266 AppendState
*astate
= (AppendState
*) node
;
267 ScanState
*result
= NULL
;
270 for (i
= 0; i
< astate
->as_nplans
; i
++)
272 ScanState
*elem
= search_plan_tree(astate
->appendplans
[i
],
278 return NULL
; /* multiple matches */
285 * Result and Limit can be descended through (these are safe
286 * because they always return their input's current row)
290 return search_plan_tree(node
->lefttree
, table_oid
);
293 * SubqueryScan too, but it keeps the child in a different place
295 case T_SubqueryScanState
:
296 return search_plan_tree(((SubqueryScanState
*) node
)->subplan
,
300 /* Otherwise, assume we can't descend through it */