提交 2ace38d2 编写于 作者: T Tom Lane

Fix WHERE CURRENT OF to work as designed within plpgsql. The argument

can be the name of a plpgsql cursor variable, which formerly was converted
to $N before the core parser saw it, but that's no longer the case.
Deal with plain name references to plpgsql variables, and add a regression
test case that exposes the failure.
上级 39bd3fd1
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, 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.13 2009/11/04 22:26:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.14 2009/11/09 02:36:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "utils/portal.h" #include "utils/portal.h"
static char *fetch_param_value(ExprContext *econtext, int paramId); static char *fetch_cursor_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);
...@@ -51,7 +51,7 @@ execCurrentOf(CurrentOfExpr *cexpr, ...@@ -51,7 +51,7 @@ execCurrentOf(CurrentOfExpr *cexpr,
if (cexpr->cursor_name) if (cexpr->cursor_name)
cursor_name = cexpr->cursor_name; cursor_name = cexpr->cursor_name;
else else
cursor_name = fetch_param_value(econtext, cexpr->cursor_param); cursor_name = fetch_cursor_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);
...@@ -203,12 +203,12 @@ execCurrentOf(CurrentOfExpr *cexpr, ...@@ -203,12 +203,12 @@ execCurrentOf(CurrentOfExpr *cexpr,
} }
/* /*
* fetch_param_value * fetch_cursor_param_value
* *
* Fetch the string value of a param, verifying it is of type REFCURSOR. * Fetch the string value of a param, verifying it is of type REFCURSOR.
*/ */
static char * static char *
fetch_param_value(ExprContext *econtext, int paramId) fetch_cursor_param_value(ExprContext *econtext, int paramId)
{ {
ParamListInfo paramInfo = econtext->ecxt_param_list_info; ParamListInfo paramInfo = econtext->ecxt_param_list_info;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.688 2009/11/05 23:24:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.689 2009/11/09 02:36:56 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -7979,14 +7979,6 @@ where_or_current_clause: ...@@ -7979,14 +7979,6 @@ where_or_current_clause:
n->cursor_param = 0; n->cursor_param = 0;
$$ = (Node *) n; $$ = (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;
}
| /*EMPTY*/ { $$ = NULL; } | /*EMPTY*/ { $$ = NULL; }
; ;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.247 2009/10/31 01:41:31 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.248 2009/11/09 02:36:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1963,32 +1963,42 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) ...@@ -1963,32 +1963,42 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
Assert(sublevels_up == 0); Assert(sublevels_up == 0);
/* /*
* If a parameter is used, it must be of type REFCURSOR. To verify * Check to see if the cursor name matches a parameter of type REFCURSOR.
* that the parameter hooks think so, build a dummy ParamRef and * If so, replace the raw name reference with a parameter reference.
* transform it. * (This is a hack for the convenience of plpgsql.)
*/ */
if (cexpr->cursor_name == NULL) if (cexpr->cursor_name != NULL) /* in case already transformed */
{ {
ParamRef *p = makeNode(ParamRef); ColumnRef *cref = makeNode(ColumnRef);
Node *n; Node *node = NULL;
p->number = cexpr->cursor_param; /* Build an unqualified ColumnRef with the given name */
p->location = -1; cref->fields = list_make1(makeString(cexpr->cursor_name));
n = transformParamRef(pstate, p); cref->location = -1;
/* Allow the parameter type to be inferred if it's unknown */
if (exprType(n) == UNKNOWNOID) /* See if there is a translation available from a parser hook */
n = coerce_type(pstate, n, UNKNOWNOID, if (pstate->p_pre_columnref_hook != NULL)
REFCURSOROID, -1, node = (*pstate->p_pre_columnref_hook) (pstate, cref);
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, if (node == NULL && pstate->p_post_columnref_hook != NULL)
-1); node = (*pstate->p_post_columnref_hook) (pstate, cref, NULL);
if (exprType(n) != REFCURSOROID)
ereport(ERROR, /*
(errcode(ERRCODE_AMBIGUOUS_PARAMETER), * XXX Should we throw an error if we get a translation that isn't
errmsg("inconsistent types deduced for parameter $%d", * a refcursor Param? For now it seems best to silently ignore
cexpr->cursor_param), * false matches.
errdetail("%s versus %s", */
format_type_be(exprType(n)), if (node != NULL && IsA(node, Param))
format_type_be(REFCURSOROID)))); {
Param *p = (Param *) node;
if (p->paramkind == PARAM_EXTERN &&
p->paramtype == REFCURSOROID)
{
/* Matches, so convert CURRENT OF to a param reference */
cexpr->cursor_name = NULL;
cexpr->cursor_param = p->paramid;
}
}
} }
return (Node *) cexpr; return (Node *) cexpr;
......
...@@ -3292,6 +3292,52 @@ select * from forc_test; ...@@ -3292,6 +3292,52 @@ select * from forc_test;
1000 | 20 1000 | 20
(10 rows) (10 rows)
-- same, with a cursor whose portal name doesn't match variable name
create or replace function forc01() returns void as $$
declare
c refcursor := 'fooled_ya';
r record;
begin
open c for select * from forc_test;
loop
fetch c into r;
exit when not found;
raise notice '%, %', r.i, r.j;
update forc_test set i = i * 100, j = r.j * 2 where current of c;
end loop;
end;
$$ language plpgsql;
select forc01();
NOTICE: 100, 2
NOTICE: 200, 4
NOTICE: 300, 6
NOTICE: 400, 8
NOTICE: 500, 10
NOTICE: 600, 12
NOTICE: 700, 14
NOTICE: 800, 16
NOTICE: 900, 18
NOTICE: 1000, 20
forc01
--------
(1 row)
select * from forc_test;
i | j
--------+----
10000 | 4
20000 | 8
30000 | 12
40000 | 16
50000 | 20
60000 | 24
70000 | 28
80000 | 32
90000 | 36
100000 | 40
(10 rows)
drop function forc01(); drop function forc01();
-- fail because cursor has no query bound to it -- fail because cursor has no query bound to it
create or replace function forc_bad() returns void as $$ create or replace function forc_bad() returns void as $$
......
...@@ -2689,6 +2689,26 @@ select forc01(); ...@@ -2689,6 +2689,26 @@ select forc01();
select * from forc_test; select * from forc_test;
-- same, with a cursor whose portal name doesn't match variable name
create or replace function forc01() returns void as $$
declare
c refcursor := 'fooled_ya';
r record;
begin
open c for select * from forc_test;
loop
fetch c into r;
exit when not found;
raise notice '%, %', r.i, r.j;
update forc_test set i = i * 100, j = r.j * 2 where current of c;
end loop;
end;
$$ language plpgsql;
select forc01();
select * from forc_test;
drop function forc01(); drop function forc01();
-- fail because cursor has no query bound to it -- fail because cursor has no query bound to it
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册