Commit a9545b3a authored by Tom Lane's avatar Tom Lane

Improve UPDATE/DELETE WHERE CURRENT OF so that they can be used from plpgsql

with a plpgsql-defined cursor.  The underlying mechanism for this is that the
main SQL engine will now take "WHERE CURRENT OF $n" where $n is a refcursor
parameter.  Not sure if we should document that fact or consider it an
implementation detail.  Per discussion with Pavel Stehule.
parent bdc71c2c
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.111 2007/06/11 15:08:32 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.112 2007/06/11 22:22:40 tgl Exp $ -->
<chapter id="plpgsql"> <chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
...@@ -2614,6 +2614,31 @@ MOVE <optional> <replaceable>direction</replaceable> { FROM | IN } </optional> < ...@@ -2614,6 +2614,31 @@ MOVE <optional> <replaceable>direction</replaceable> { FROM | IN } </optional> <
MOVE curs1; MOVE curs1;
MOVE LAST FROM curs3; MOVE LAST FROM curs3;
MOVE RELATIVE -2 FROM curs4; MOVE RELATIVE -2 FROM curs4;
</programlisting>
</para>
</sect3>
<sect3>
<title><literal>UPDATE/DELETE WHERE CURRENT OF</></title>
<synopsis>
UPDATE <replaceable>table</replaceable> SET ... WHERE CURRENT OF <replaceable>cursor</replaceable>;
DELETE FROM <replaceable>table</replaceable> WHERE CURRENT OF <replaceable>cursor</replaceable>;
</synopsis>
<para>
When a cursor is positioned on a table row, that row can be updated
or deleted using the cursor to identify the row. Note that this
only works for simple (non-join, non-grouping) cursor queries.
For additional information see the
<xref linkend="sql-declare" endterm="sql-declare-title">
reference page.
</para>
<para>
An example:
<programlisting>
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
</programlisting> </programlisting>
</para> </para>
</sect3> </sect3>
......
...@@ -6,26 +6,29 @@ ...@@ -6,26 +6,29 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.1 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.2 2007/06/11 22:22:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_type.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/portal.h" #include "utils/portal.h"
static char *fetch_param_value(ExprContext *econtext, int paramId);
static ScanState *search_plan_tree(PlanState *node, Oid table_oid); static ScanState *search_plan_tree(PlanState *node, Oid table_oid);
/* /*
* execCurrentOf * execCurrentOf
* *
* Given the name of a cursor and the OID of a table, determine which row * Given a CURRENT OF expression and the OID of a table, determine which row
* of the table is currently being scanned by the cursor, and return its * of the table is currently being scanned by the cursor named by CURRENT OF,
* TID into *current_tid. * and return the row's TID into *current_tid.
* *
* Returns TRUE if a row was identified. Returns FALSE if the cursor is valid * Returns TRUE if a row was identified. Returns FALSE if the cursor is valid
* for the table but is not currently scanning a row of the table (this is a * for the table but is not currently scanning a row of the table (this is a
...@@ -33,14 +36,25 @@ static ScanState *search_plan_tree(PlanState *node, Oid table_oid); ...@@ -33,14 +36,25 @@ static ScanState *search_plan_tree(PlanState *node, Oid table_oid);
* valid updatable scan of the specified table. * valid updatable scan of the specified table.
*/ */
bool bool
execCurrentOf(char *cursor_name, Oid table_oid, execCurrentOf(CurrentOfExpr *cexpr,
ExprContext *econtext,
Oid table_oid,
ItemPointer current_tid) ItemPointer current_tid)
{ {
char *cursor_name;
char *table_name; char *table_name;
Portal portal; Portal portal;
QueryDesc *queryDesc; QueryDesc *queryDesc;
ScanState *scanstate; ScanState *scanstate;
HeapTuple tup; bool lisnull;
Oid tuple_tableoid;
ItemPointer tuple_tid;
/* Get the cursor name --- may have to look up a parameter reference */
if (cexpr->cursor_name)
cursor_name = cexpr->cursor_name;
else
cursor_name = fetch_param_value(econtext, cexpr->cursor_param);
/* Fetch table name for possible use in error messages */ /* Fetch table name for possible use in error messages */
table_name = get_rel_name(table_oid); table_name = get_rel_name(table_oid);
...@@ -100,16 +114,54 @@ execCurrentOf(char *cursor_name, Oid table_oid, ...@@ -100,16 +114,54 @@ execCurrentOf(char *cursor_name, Oid table_oid,
if (TupIsNull(scanstate->ss_ScanTupleSlot)) if (TupIsNull(scanstate->ss_ScanTupleSlot))
return false; return false;
tup = scanstate->ss_ScanTupleSlot->tts_tuple; /* Use slot_getattr to catch any possible mistakes */
if (tup == NULL) tuple_tableoid = DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot,
elog(ERROR, "CURRENT OF applied to non-materialized tuple"); TableOidAttributeNumber,
Assert(tup->t_tableOid == table_oid); &lisnull));
Assert(!lisnull);
tuple_tid = (ItemPointer)
DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot,
SelfItemPointerAttributeNumber,
&lisnull));
Assert(!lisnull);
Assert(tuple_tableoid == table_oid);
*current_tid = tup->t_self; *current_tid = *tuple_tid;
return true; return true;
} }
/*
* fetch_param_value
*
* Fetch the string value of a param, verifying it is of type REFCURSOR.
*/
static char *
fetch_param_value(ExprContext *econtext, int paramId)
{
ParamListInfo paramInfo = econtext->ecxt_param_list_info;
if (paramInfo &&
paramId > 0 && paramId <= paramInfo->numParams)
{
ParamExternData *prm = &paramInfo->params[paramId - 1];
if (OidIsValid(prm->ptype) && !prm->isnull)
{
Assert(prm->ptype == REFCURSOROID);
/* We know that refcursor uses text's I/O routines */
return DatumGetCString(DirectFunctionCall1(textout,
prm->value));
}
}
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("no value found for parameter %d", paramId)));
return NULL;
}
/* /*
* search_plan_tree * search_plan_tree
* *
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.219 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.220 2007/06/11 22:22:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3633,7 +3633,9 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, ...@@ -3633,7 +3633,9 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
{ {
CurrentOfExpr *cexpr = (CurrentOfExpr *) exprstate->expr; CurrentOfExpr *cexpr = (CurrentOfExpr *) exprstate->expr;
bool result; bool result;
HeapTuple tup; bool lisnull;
Oid tableoid;
ItemPointer tuple_tid;
ItemPointerData cursor_tid; ItemPointerData cursor_tid;
if (isDone) if (isDone)
...@@ -3643,12 +3645,19 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, ...@@ -3643,12 +3645,19 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
Assert(cexpr->cvarno != INNER); Assert(cexpr->cvarno != INNER);
Assert(cexpr->cvarno != OUTER); Assert(cexpr->cvarno != OUTER);
Assert(!TupIsNull(econtext->ecxt_scantuple)); Assert(!TupIsNull(econtext->ecxt_scantuple));
tup = econtext->ecxt_scantuple->tts_tuple; /* Use slot_getattr to catch any possible mistakes */
if (tup == NULL) tableoid = DatumGetObjectId(slot_getattr(econtext->ecxt_scantuple,
elog(ERROR, "CURRENT OF applied to non-materialized tuple"); TableOidAttributeNumber,
&lisnull));
if (execCurrentOf(cexpr->cursor_name, tup->t_tableOid, &cursor_tid)) Assert(!lisnull);
result = ItemPointerEquals(&cursor_tid, &(tup->t_self)); tuple_tid = (ItemPointer)
DatumGetPointer(slot_getattr(econtext->ecxt_scantuple,
SelfItemPointerAttributeNumber,
&lisnull));
Assert(!lisnull);
if (execCurrentOf(cexpr, econtext, tableoid, &cursor_tid))
result = ItemPointerEquals(&cursor_tid, tuple_tid);
else else
result = false; result = false;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.54 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.55 2007/06/11 22:22:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -153,7 +153,7 @@ TidListCreate(TidScanState *tidstate) ...@@ -153,7 +153,7 @@ TidListCreate(TidScanState *tidstate)
CurrentOfExpr *cexpr = (CurrentOfExpr *) expr; CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
ItemPointerData cursor_tid; ItemPointerData cursor_tid;
if (execCurrentOf(cexpr->cursor_name, if (execCurrentOf(cexpr, econtext,
RelationGetRelid(tidstate->ss.ss_currentRelation), RelationGetRelid(tidstate->ss.ss_currentRelation),
&cursor_tid)) &cursor_tid))
{ {
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.378 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.379 2007/06/11 22:22:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1309,6 +1309,7 @@ _copyCurrentOfExpr(CurrentOfExpr *from) ...@@ -1309,6 +1309,7 @@ _copyCurrentOfExpr(CurrentOfExpr *from)
COPY_SCALAR_FIELD(cvarno); COPY_SCALAR_FIELD(cvarno);
COPY_STRING_FIELD(cursor_name); COPY_STRING_FIELD(cursor_name);
COPY_SCALAR_FIELD(cursor_param);
return newnode; return newnode;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.309 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.310 2007/06/11 22:22:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -603,6 +603,7 @@ _equalCurrentOfExpr(CurrentOfExpr *a, CurrentOfExpr *b) ...@@ -603,6 +603,7 @@ _equalCurrentOfExpr(CurrentOfExpr *a, CurrentOfExpr *b)
{ {
COMPARE_SCALAR_FIELD(cvarno); COMPARE_SCALAR_FIELD(cvarno);
COMPARE_STRING_FIELD(cursor_name); COMPARE_STRING_FIELD(cursor_name);
COMPARE_SCALAR_FIELD(cursor_param);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.310 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.311 2007/06/11 22:22:40 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1065,6 +1065,7 @@ _outCurrentOfExpr(StringInfo str, CurrentOfExpr *node) ...@@ -1065,6 +1065,7 @@ _outCurrentOfExpr(StringInfo str, CurrentOfExpr *node)
WRITE_UINT_FIELD(cvarno); WRITE_UINT_FIELD(cvarno);
WRITE_STRING_FIELD(cursor_name); WRITE_STRING_FIELD(cursor_name);
WRITE_INT_FIELD(cursor_param);
} }
static void static void
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.208 2007/06/11 01:16:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.209 2007/06/11 22:22:41 tgl Exp $
* *
* NOTES * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -883,6 +883,7 @@ _readCurrentOfExpr(void) ...@@ -883,6 +883,7 @@ _readCurrentOfExpr(void)
READ_UINT_FIELD(cvarno); READ_UINT_FIELD(cvarno);
READ_STRING_FIELD(cursor_name); READ_STRING_FIELD(cursor_name);
READ_INT_FIELD(cursor_param);
READ_DONE(); READ_DONE();
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.592 2007/06/11 01:16:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.593 2007/06/11 22:22:41 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -6568,7 +6568,17 @@ where_or_current_clause: ...@@ -6568,7 +6568,17 @@ where_or_current_clause:
| WHERE CURRENT_P OF name | WHERE CURRENT_P OF name
{ {
CurrentOfExpr *n = makeNode(CurrentOfExpr); CurrentOfExpr *n = makeNode(CurrentOfExpr);
/* cvarno is filled in by parse analysis */
n->cursor_name = $4; n->cursor_name = $4;
n->cursor_param = 0;
$$ = (Node *) n;
}
| WHERE CURRENT_P OF PARAM
{
CurrentOfExpr *n = makeNode(CurrentOfExpr);
/* cvarno is filled in by parse analysis */
n->cursor_name = NULL;
n->cursor_param = $4;
$$ = (Node *) n; $$ = (Node *) n;
} }
| /*EMPTY*/ { $$ = NULL; } | /*EMPTY*/ { $$ = NULL; }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.219 2007/06/11 01:16:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.220 2007/06/11 22:22:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -59,10 +59,10 @@ static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); ...@@ -59,10 +59,10 @@ static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x); static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
char *relname, int location); char *relname, int location);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformIndirection(ParseState *pstate, Node *basenode, static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection); List *indirection);
static Node *typecast_expression(ParseState *pstate, Node *expr, static Node *typecast_expression(ParseState *pstate, Node *expr,
...@@ -244,19 +244,8 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -244,19 +244,8 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
case T_CurrentOfExpr: case T_CurrentOfExpr:
{ result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
CurrentOfExpr *c = (CurrentOfExpr *) expr;
int sublevels_up;
/* CURRENT OF can only appear at top level of UPDATE/DELETE */
Assert(pstate->p_target_rangetblentry != NULL);
c->cvarno = RTERangeTablePosn(pstate,
pstate->p_target_rangetblentry,
&sublevels_up);
Assert(sublevels_up == 0);
result = expr;
break; break;
}
/********************************************* /*********************************************
* Quietly accept node types that may be presented when we are * Quietly accept node types that may be presented when we are
...@@ -549,57 +538,69 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -549,57 +538,69 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return node; return node;
} }
static Node * /*
transformParamRef(ParseState *pstate, ParamRef *pref) * Locate the parameter type info for the given parameter number, and
* return a pointer to it.
*/
static Oid *
find_param_type(ParseState *pstate, int paramno)
{ {
int paramno = pref->number; Oid *result;
ParseState *toppstate;
Param *param;
/* /*
* Find topmost ParseState, which is where paramtype info lives. * Find topmost ParseState, which is where paramtype info lives.
*/ */
toppstate = pstate; while (pstate->parentParseState != NULL)
while (toppstate->parentParseState != NULL) pstate = pstate->parentParseState;
toppstate = toppstate->parentParseState;
/* Check parameter number is in range */ /* Check parameter number is in range */
if (paramno <= 0) /* probably can't happen? */ if (paramno <= 0) /* probably can't happen? */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER), (errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno))); errmsg("there is no parameter $%d", paramno)));
if (paramno > toppstate->p_numparams) if (paramno > pstate->p_numparams)
{ {
if (!toppstate->p_variableparams) if (!pstate->p_variableparams)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER), (errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", errmsg("there is no parameter $%d",
paramno))); paramno)));
/* Okay to enlarge param array */ /* Okay to enlarge param array */
if (toppstate->p_paramtypes) if (pstate->p_paramtypes)
toppstate->p_paramtypes = pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes,
(Oid *) repalloc(toppstate->p_paramtypes,
paramno * sizeof(Oid)); paramno * sizeof(Oid));
else else
toppstate->p_paramtypes = pstate->p_paramtypes = (Oid *) palloc(paramno * sizeof(Oid));
(Oid *) palloc(paramno * sizeof(Oid));
/* Zero out the previously-unreferenced slots */ /* Zero out the previously-unreferenced slots */
MemSet(toppstate->p_paramtypes + toppstate->p_numparams, MemSet(pstate->p_paramtypes + pstate->p_numparams,
0, 0,
(paramno - toppstate->p_numparams) * sizeof(Oid)); (paramno - pstate->p_numparams) * sizeof(Oid));
toppstate->p_numparams = paramno; pstate->p_numparams = paramno;
} }
if (toppstate->p_variableparams)
result = &pstate->p_paramtypes[paramno - 1];
if (pstate->p_variableparams)
{ {
/* If not seen before, initialize to UNKNOWN type */ /* If not seen before, initialize to UNKNOWN type */
if (toppstate->p_paramtypes[paramno - 1] == InvalidOid) if (*result == InvalidOid)
toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID; *result = UNKNOWNOID;
} }
return result;
}
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
int paramno = pref->number;
Oid *pptype = find_param_type(pstate, paramno);
Param *param;
param = makeNode(Param); param = makeNode(Param);
param->paramkind = PARAM_EXTERN; param->paramkind = PARAM_EXTERN;
param->paramid = paramno; param->paramid = paramno;
param->paramtype = toppstate->p_paramtypes[paramno - 1]; param->paramtype = *pptype;
param->paramtypmod = -1; param->paramtypmod = -1;
return (Node *) param; return (Node *) param;
...@@ -1596,6 +1597,43 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b) ...@@ -1596,6 +1597,43 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b)
return (Node *) b; return (Node *) b;
} }
static Node *
transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
{
int sublevels_up;
/* CURRENT OF can only appear at top level of UPDATE/DELETE */
Assert(pstate->p_target_rangetblentry != NULL);
cexpr->cvarno = RTERangeTablePosn(pstate,
pstate->p_target_rangetblentry,
&sublevels_up);
Assert(sublevels_up == 0);
/* If a parameter is used, it must be of type REFCURSOR */
if (cexpr->cursor_name == NULL)
{
Oid *pptype = find_param_type(pstate, cexpr->cursor_param);
if (pstate->p_variableparams && *pptype == UNKNOWNOID)
{
/* resolve unknown param type as REFCURSOR */
*pptype = REFCURSOROID;
}
else if (*pptype != REFCURSOROID)
{
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d",
cexpr->cursor_param),
errdetail("%s versus %s",
format_type_be(*pptype),
format_type_be(REFCURSOROID))));
}
}
return (Node *) cexpr;
}
/* /*
* Construct a whole-row reference to represent the notation "relation.*". * Construct a whole-row reference to represent the notation "relation.*".
* *
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.260 2007/06/11 01:16:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.261 2007/06/11 22:22:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -4136,8 +4136,16 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -4136,8 +4136,16 @@ get_rule_expr(Node *node, deparse_context *context,
break; break;
case T_CurrentOfExpr: case T_CurrentOfExpr:
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
if (cexpr->cursor_name)
appendStringInfo(buf, "CURRENT OF %s", appendStringInfo(buf, "CURRENT OF %s",
quote_identifier(((CurrentOfExpr *) node)->cursor_name)); quote_identifier(cexpr->cursor_name));
else
appendStringInfo(buf, "CURRENT OF $%d",
cexpr->cursor_param);
}
break; break;
case T_List: case T_List:
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.140 2007/06/11 01:16:30 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.141 2007/06/11 22:22:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -73,7 +73,9 @@ extern bool ExecMayReturnRawTuples(PlanState *node); ...@@ -73,7 +73,9 @@ extern bool ExecMayReturnRawTuples(PlanState *node);
/* /*
* prototypes from functions in execCurrent.c * prototypes from functions in execCurrent.c
*/ */
extern bool execCurrentOf(char *cursor_name, Oid table_oid, extern bool execCurrentOf(CurrentOfExpr *cexpr,
ExprContext *econtext,
Oid table_oid,
ItemPointer current_tid); ItemPointer current_tid);
/* /*
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.131 2007/06/11 01:16:30 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.132 2007/06/11 22:22:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -922,12 +922,17 @@ typedef struct SetToDefault ...@@ -922,12 +922,17 @@ typedef struct SetToDefault
* of the target relation being constrained; this aids placing the expression * of the target relation being constrained; this aids placing the expression
* correctly during planning. We can assume however that its "levelsup" is * correctly during planning. We can assume however that its "levelsup" is
* always zero, due to the syntactic constraints on where it can appear. * always zero, due to the syntactic constraints on where it can appear.
*
* The referenced cursor can be represented either as a hardwired string
* or as a reference to a run-time parameter of type REFCURSOR. The latter
* case is for the convenience of plpgsql.
*/ */
typedef struct CurrentOfExpr typedef struct CurrentOfExpr
{ {
Expr xpr; Expr xpr;
Index cvarno; /* RT index of target relation */ Index cvarno; /* RT index of target relation */
char *cursor_name; /* name of referenced cursor */ char *cursor_name; /* name of referenced cursor, or NULL */
int cursor_param; /* refcursor parameter number, or 0 */
} CurrentOfExpr; } CurrentOfExpr;
/*-------------------- /*--------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment