1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-14 18:42:34 +03:00

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:
Tom Lane
2007-06-11 22:22:42 +00:00
parent bdc71c2cb1
commit a9545b3aef
13 changed files with 225 additions and 72 deletions

View File

@ -8,7 +8,7 @@
*
*
* 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 *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
char *relname, int location);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
static Node *typecast_expression(ParseState *pstate, Node *expr,
@ -244,19 +244,8 @@ transformExpr(ParseState *pstate, Node *expr)
break;
case T_CurrentOfExpr:
{
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;
}
result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
break;
/*********************************************
* Quietly accept node types that may be presented when we are
@ -549,57 +538,69 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
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;
ParseState *toppstate;
Param *param;
Oid *result;
/*
* Find topmost ParseState, which is where paramtype info lives.
*/
toppstate = pstate;
while (toppstate->parentParseState != NULL)
toppstate = toppstate->parentParseState;
while (pstate->parentParseState != NULL)
pstate = pstate->parentParseState;
/* Check parameter number is in range */
if (paramno <= 0) /* probably can't happen? */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
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,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d",
paramno)));
/* Okay to enlarge param array */
if (toppstate->p_paramtypes)
toppstate->p_paramtypes =
(Oid *) repalloc(toppstate->p_paramtypes,
paramno * sizeof(Oid));
if (pstate->p_paramtypes)
pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes,
paramno * sizeof(Oid));
else
toppstate->p_paramtypes =
(Oid *) palloc(paramno * sizeof(Oid));
pstate->p_paramtypes = (Oid *) palloc(paramno * sizeof(Oid));
/* Zero out the previously-unreferenced slots */
MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
MemSet(pstate->p_paramtypes + pstate->p_numparams,
0,
(paramno - toppstate->p_numparams) * sizeof(Oid));
toppstate->p_numparams = paramno;
(paramno - pstate->p_numparams) * sizeof(Oid));
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 (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
if (*result == InvalidOid)
*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->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = toppstate->p_paramtypes[paramno - 1];
param->paramtype = *pptype;
param->paramtypmod = -1;
return (Node *) param;
@ -1596,6 +1597,43 @@ transformBooleanTest(ParseState *pstate, BooleanTest *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.*".
*