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:
@ -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.*".
|
||||
*
|
||||
|
Reference in New Issue
Block a user