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.
This commit is contained in:
parent
bdc71c2cb1
commit
a9545b3aef
@ -1,4 +1,4 @@
|
|||||||
<!-- $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> <
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
* 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,
|
|||||||
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);
|
||||||
|
|
||||||
*current_tid = tup->t_self;
|
Assert(tuple_tableoid == table_oid);
|
||||||
|
|
||||||
|
*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 = ¶mInfo->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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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));
|
||||||
|
Assert(!lisnull);
|
||||||
|
tuple_tid = (ItemPointer)
|
||||||
|
DatumGetPointer(slot_getattr(econtext->ecxt_scantuple,
|
||||||
|
SelfItemPointerAttributeNumber,
|
||||||
|
&lisnull));
|
||||||
|
Assert(!lisnull);
|
||||||
|
|
||||||
if (execCurrentOf(cexpr->cursor_name, tup->t_tableOid, &cursor_tid))
|
if (execCurrentOf(cexpr, econtext, tableoid, &cursor_tid))
|
||||||
result = ItemPointerEquals(&cursor_tid, &(tup->t_self));
|
result = ItemPointerEquals(&cursor_tid, tuple_tid);
|
||||||
else
|
else
|
||||||
result = false;
|
result = false;
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
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 @@
|
|||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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:
|
|||||||
| 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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);
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
* 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
|
|||||||
* 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;
|
||||||
|
|
||||||
/*--------------------
|
/*--------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user